Numpy处理遇到的一些问题

前言

这里主要记录numpy遇到的一些问题,目录有点凌乱,日后再做整理


Next:寻找非0元素

问题描述
寻找numpy中非的0元素

问题解决
方法有三种,如下

# 1,返回的是两个adarray,代表了位置索引
a = np.array()
row, col = [i for i in np.nonzero(a)]
nonzero = []
for i, j in zip(row, col):
	nonzero.append(a[i, j])  # 位置索引


# 2,直接返回非0的值
a[a!=0]

# 3
where = np.where(a==0)
row, col = [i for i in where]
之后和1一样

Next:扩张与压缩维度

问题描述
想压缩数据的维度,有维度是多余的,如何将他们去掉

问题解决
运用numpy的np.squeeze函数

a = np.random.random((1,4))
print(a)

b = np.squeeze(a, axis=0)
print(b)
print(b.shape)

# out
[[0.87072838 0.55459801 0.20885144 0.72031326]]
[0.87072838 0.55459801 0.20885144 0.72031326]
(4,)

需要注意的是:

  1. np.squeeze只能够压缩本来是1维的数据,如果压缩不是1维度的维度则会产生报错
  2. np, squeeze相反的操作是np.expand_dims,能够扩张维度。这在pytorch中,相同功能的操作是torch.unsqueeze

Next:数据的重复repeat

问题描述
当我们说重复数据的时候,一般会有两种方式

  1. 以数据本身一个整体为单位,重复n次,这种操作实际上就是拼接操作
  2. 复制数据的某一行,或者是某一列,使其重复n次
    上述两种操作的实现方法是不同的,第一种操作可以调用numpy的repeat方法,第二种操作可以写循环用np.concatenate进行实现
a = np.array([[1,3,4],[4,5,6]])
a

# out
[[1 3 4]
 [4 5 6]]

第一种操作的实现,以整体为单位,重复复制n次

# 复制某一行
n = 5
for index in range(n):
    concat = np.concatenate([concat, a], axis=0) if index!= 0 else a
print(concat)

# out, a has been concat n times 
[[1, 3, 4],
 [4, 5, 6],
 [1, 3, 4],
 [4, 5, 6],
 [1, 3, 4],
 [4, 5, 6],
 [1, 3, 4],
 [4, 5, 6],
 [1, 3, 4],
 [4, 5, 6]]

第二种操作的实现,以某一行或者某一列为单位进行复制,调用repeat函数,调用有两种方式

  • 通过函数方式调用,np.repeat()
  • 通过NumPy.ndarray对象调用对象方法实现object.repreat()
a = np.array([[1,3,4],[4,5,6]])
np.repeat(a, 2, axis=0)

# 输出
array([[1, 3, 4],
       [1, 3, 4],
       [4, 5, 6],
       [4, 5, 6]])

# 通过对象方法调用
a.repeat(4, axis=0)

# 输出
array([[1, 3, 4],
       [1, 3, 4],
       [4, 5, 6],
       [4, 5, 6]])

注意: np.repeat方法是对每一行,或者每一列进行调用的,而pytorch则不同,其是以数据整体为一个单位调用的,这种方式会灵活许多,因为其可以通过repeat + reshape的方法实现对行、列元素重复n次的操作


Next:铺开数组

问题描述
想将数组铺开,进行操作

思路
通过np.ravel方法能够将数组铺开为一维数组并对其进行修改

a = np.arange(12).reshape(4,3)
a.ravel()

# out
[1,2,3,4,5,6,7,8,9,10,11,12]

Next:numpy的reshape机制

问题描述
最近偶尔会在别人论文的一些代码中看见ndarray, torch.tensor变换形状的一些骚操作,可以大大提高代码的效率,由于自己基础非常弱,打算把reshape的机制完全搞清楚

问题解决
首先,需要说明np.reshape方法的机制,其处理顺序如下:

  1. 首先将待处理的ndarray铺平成维度为1的array(有不同的铺开方式,可以是沿着行铺开,也可以是沿着列铺开)
  2. 然后对该铺平的ndarray切割操作,也就是说,如果reshape中order的参数为‘C’的话,则用该array的长度除以新size的行数,可以得到切割点的index,然后以切割点将铺平的ndarray切割为n个ndarray,再依次以axis=0的方式将这些切割出来的ndarray进行拼接,则可以得到reshape后的矩阵
  3. 如果是三维数组的话,方式也是完全一样的,只不过3个维度需要2次切割,2维数组只需要1次切割,具体可以看下面的代码
a = np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]])
print(a.shape)
print(a)

b = a.ravel(order='c')  # return a flatterned ndarray, data not changed
print('\n', b.shape)
print(b)

# transform into shape (3, 2, 2)
c = a.reshape((3,2,2))
print(c.shape)
print(c)

# output
# a
(2, 2, 3)
[[[ 1  2  3]
  [ 4  5  6]]
 [[ 7  8  9]
  [10 11 12]]]
# b
 (12,)
[ 1  2  3  4  5  6  7  8  9 10 11 12]
#c
(3, 2, 2)
[[[ 1  2]
  [ 3  4]]
 [[ 5  6]
  [ 7  8]]
 [[ 9 10]
  [11 12]]]

注意:在reshape函数中,有一个参数为order,其实这个参数就是指铺开待重塑数组时的方式而已,如果为c的话,就是默认按照行进行铺开。如果为F的话,就是默认按照列进行铺开,并且铺开之后得到切割点切割后的ndarray是按照axis=1也就是沿着列的方式进行拼接的,由于我们一般都是对行进行操作,这里就不继续写代码了,理解即可。

想继续了解order="F"的操作可以看这篇文章这篇文章


Next:删除Numpy中的某个元素

问题描述
如何删除NumPy.ndarray中的某个元素,或者某个行

思路
二维的ndarray中,是不能删除某个元素的,因为ndarray是结构化数据,删除了某个元素这个结构就没有办法表现出来(列入中间删除了某个数据,那么这个维度依然是二维)。因此在二维的ndarray中只能够删除某一行、或者某一列。在一维的ndarray中,因为删除增加元素是可以更改维度的,因此可以删除某一个元素。

元素的删除用np.delete方法来实现,需要设置两个参数,一个是删除元素的位置索引,可以传入list, ndarray,一个是axis参数,代表想要删除的是行还是列,在一维数组中不需要设置,因为只有一个维度

# ndarray with 1 dim
a = np.array([1,2,3,4,5,6,7,8])
print(a)
np.delete(a, [0,1,2],)

# out
array([1, 2, 3, 4, 5, 6, 7, 8])  # a
array([4, 5, 6, 7, 8]). # elements deleted according to index


# ndarray with 2 dims
a = np.array([[1,3,4],[4,5,6],[1,1,1]])
print(a)
np.delete(a, [0,1], axis=0)  # delete row 0 and 1

# out
array([[1, 3, 4],
       [4, 5, 6],
       [1, 1, 1]])
       
array([[1, 1, 1]])  # only row 2 left

注意:想要获得的某个索引的具体位置,可以用np.where函数来获得,并将得到的loc参数传入想要删除的ndarray中就可以了


Next:np.where函数

问题描述
️,这是一个比较有用的函数

问题解决
该函数可以用来定位元素,也可以用来赋值。

  1. 当用np.where来定位元素的时候,其功能和np.asarray(object).nonzero()其返回和数组尾数相同的tuple,其中含有由索引构成的ndarray。注意:如object.nonzero()中的object已经是根据逻辑运算判断过后的矩阵,那么nonzero()就是返回这个矩阵中为True的那些位置
  2. 当用np.where来赋值的时候,其遵循的代码是np.where(condition, x, y),当condition中的元素值为True时,取x的值,为False时,取y的值。注意:要么condition, x, y三者的维度相同,要么x, y为标量,否则会报错
  3. np.whereDataFrame对象也有用

代码如下:

a = np.random.randint(0,4, (3,3))
threshold = 1
np.where(a>0)

# output
(array([0, 0, 1, 1, 2, 2]), array([0, 2, 0, 1, 0, 2]))

可以看到,返回的是一个tuple,其长度和输入数据的维度数量是相同的。如果输入数组是二维数组,那么tuple中的第一个 array返回的就是满足条件的行索引,第二个array返回的是满足条件的列索引


Next:Numpy取切片操作

取切片

问题描述
取切片索引

问题解决
np.s_[start_index:end_index:step]没啥特别的,生成的就是一个array。如果要省略某一个参数,直接不填值即可

注意:这里的np.s_其实就是类似于slice函数

a = np.array([1,2,3,4,5,6,7])
s = np.s_[0::2]  # start from zero, end as the last, step by 2
print(s)
print(a[s])

# output
slice(0, None, 2)
[1 3 5 7]

slice函数

描述
slice函数返回的就是一个slice对象,他能够接受三个参数,start, stop, step,其中step是可选的参数

注:如果slice函数中含有None,他代表的是"slice nothing",也就是不切片任何操作,所以就等同于:(全取)

a = [0,1,2,3]
s_ = slice(0, None, 2)
a[s_]
a[0::2]

>>> output
[0, 2] 
[0, 2]

Next:numpy更改维度的方法

问题描述
在实际中为了维度匹配,经常需要更改ndarray的维度,比较复杂的扩张以及压缩维度已经有np.expand_dims以及np.squeeze来解决,这里介绍两种非常简单的日常方法

问题解决

  1. 通过reshape方法
a = np.arange(12)
a = a.reshape(-1, 1)  # 通过reshape函数
  1. 通过np.newaxis
a = np.arange(12)
a[np.newaxis, :]  # 0维度新添加数据,其余维度切片全部数据
  1. None方法
a = np.arange(12)
a[None, :]

Next:逐行操作apply_along_axis

问题描述
想要知道numpy的逐行操作

问题解决
因为numpy是储存的全部为数值,所以如果要对每个元素进行操作,直接用numpy的操作就可以了。但是如果每一行一列的操作是不一样的,这个时候就要用到apply_along_axis操作。

func1d是函数,接受匿名函数lambda xaxis是对哪一行/列进行操作,arr是要操作的ndarray

a = np.arange(8).reshape(2,4)
print(a)

norm = lambda x: x/x.sum()
b = np.apply_along_axis(func1d=norm, axis=1, arr=a)
print(b)

# output
[[0 1 2 3]
 [4 5 6 7]]

 [[0.         0.16666667 0.33333333 0.5       ]
 [0.18181818 0.22727273 0.27272727 0.31818182]]

完美解决


Next:如何生成移动窗口数据

问题描述
在进行股票数据处理的时候,经常需要生成移动窗口的数据,但是每次都自己写循环写的方法其实很笨,因此去stackoverflow上面看了一下,发现大家的方法都好灵活好厉害,在此记录两个方法在这里插入代码片

问题解决

  1. 巧妙的切片slice,并循环利用hstack方法:
a = np.array([[00,01], [10,11], [20,21], [30,31], [40,41], [50,51]])
w = np.hstack((a[:-2],a[1:-1],a[2:]))
w

>>> output
array([[ 0,  1, 10, 11, 20, 21],
       [10, 11, 20, 21, 30, 31],
       [20, 21, 30, 31, 40, 41],
       [30, 31, 40, 41, 50, 51]])

其方法的巧妙住处在于,处理后数据的每一行与上一行的差距都是固定的,因此其直接通过在axis=0的维上直接去切片,然后进行拼接操作,写成函数就是

def window_stack(a, stepsize=1, width=3):
    n = a.shape[0]
    return np.hstack( a[i:1+n+i-width:stepsize] for i in range(0,width) )

注意:此方法同样适用于维度为2以上的情况

  1. 通过巧妙的创建索引
>>> a = np.array([[00,01], [10,11], [20,21], [30,31], [40,41], [50,51]])

>>> a
array([[ 0,  1],
       [10, 11],
       [20, 21],                      #define our 2d numpy array
       [30, 31],
       [40, 41],
       [50, 51]])

>>> a = a.flatten()

>>> a
array([ 0,  1, 10, 11, 20, 21, 30, 31, 40, 41, 50, 51])    #flattened numpy array

>>> indexer = np.arange(6)[None, :] + 2*np.arange(4)[:, None]

>>> indexer
array([[ 0,  1,  2,  3,  4,  5],
       [ 2,  3,  4,  5,  6,  7],            #sliding window indices
       [ 4,  5,  6,  7,  8,  9],
       [ 6,  7,  8,  9, 10, 11]])

>>> a[indexer]
array([[ 0,  1, 10, 11, 20, 21],
       [10, 11, 20, 21, 30, 31],            #values of a over sliding window
       [20, 21, 30, 31, 40, 41],
       [30, 31, 40, 41, 50, 51]])

>>> np.sum(a[indexer], axis=1)
array([ 63, 123, 183, 243])         #sum of values in 'a' under the sliding window.

下一次再进行解释

你可能感兴趣的:(我的笔记)