1 numpy
1.1 如何创建array
创建array的几种方式
numpy.array()
An array, any object exposing the array interface, an object whose
__array__ method returns an array, or any (nested) sequence
numpy.asarray()
array_like
Input data, in any form that can be converted to an array. This includes lists, lists of tuples, tuples, tuples of tuples, tuples of lists and ndarrays.
自动生成数组 numpy.arange numpy.logspace numpy.linspace 用于创建等差 等比 array(arange和linspace创建等差array的方式有区别)
numpy.full numpy.zeros numpy.ones
numpy.fromstring numpy.frombuffer numpy.from function
1.3 存取元素
有了创造,就要有访问,那么该怎么访问元素呢?
- 切片
正常的切片方式,比如连续切片 a[3:5] 非连续切片a[[1,3,5]]
一维数组和多维数组的存取
我们可以用数组作为索引,来构造新数组
这里有一个公式 output = method(input)
首先我们要明确input和output是什么,然后才能找到适合的公式来套用。
下面是一个简单的总结,其中多维我们先用二维来替代。
input | output | note | sample |
---|---|---|---|
一维 | 一维 | 从一维中取数,组成一维array | |
一维 | 多维 | 从一维中取数,组成N维array | |
多维 | 一维 | 从N维中取数,组成一维array | |
多维 | 多维 | 从N维中取数,组成N维array |
本来想着列个表就能简单的说清楚,但现在看,还是有些复杂的,下面就展开来说吧。
下面有几种写法,让我们看一看
input : 一维数组 output : 一维数组和二维数组
a = np.array(1, 10, 1)
print(a[1, 3, 5]) # 错误,因为a是一维数组,但提供了3维索引
print(a[[1, 3, 5], [2, 4, 6]]) # 错误,跟上面一样,提供了两个index
x = [[1, 3, 5], [2, 4, 6]]
print(a[x]) # 错误,IndexError: too many indices for array: array is 1-dimensional, but 2 were indexed
# 上述语句还有warning FutureWarning: Using a non-tuple sequence for multidimensional indexing is deprecated; use `arr[tuple(seq)]` instead of `arr[seq]`. In the future this will be interpreted as an array index, `arr[np.array(seq)]`, which will result either in an error or a different result.
y = np.array([[1, 3, 5], [2, 4, 6]])
print(numpy.array(y)) # 正确
从上面的例子中,我们可以得到以下几个简单的结论
- input为一维数组,索引必须也是一维的,不管输出是怎么样,要确保能访问到input中的元素,下标必须为一维,所以a[[1, 3, 5]]是正确的,a[1, 3, 5]是错误的
但这里有一个问题,就是a[x]和a[np.array(x)]有什么区别?
通过help(x)可以得知一个是numpy.ndarray,一个是list。个人猜测,list是不确定大小的,也就是我们拿到一个list,里面的元素大小也不一定相同,所以需要转成ndarray,明确是一个正常的数组。否则,就会报上面的IndexError和Warning。
input : 二维数组 output : 一维数组和二维数组
a = np.arange(1, 10, 1).reshape(-1,1) + np.arange(10, 60, 5)
x = [[1, 3, 5], [2, 4, 6]]
print(a[x]) # 正确,但会报warning,输出一个1*3的数组array
m = ([1,2,3], [4,5,6])
print(a[m]) # 正确,无warning,下标是有两个元素的元组,输出结果是1*3的数组array
print(a(np.array(x))) # 正确,但输出是一个三维数组
y = [[1,2], [3,4]]
z = [[5,6], [7,8]]
print(a[y,z]) #正确
上面我们看到,print(a[x])会报warning, _FutureWarning: Using a non-tuple sequence for multidimensional indexing is deprecated; use arr[tuple(seq)]
instead of arr[seq]
. In the future this will be interpreted as an array index, arr[np.array(seq)]
, which will result either in an error or a different result._。
也就是说,对于多维数组的index操作,我们需要传入一个tuple sequence,传入non-tuple sequence的方法已经被废弃了。
至于print(a(np.array(x)))输出一个三维数组,可以这样理解,首先np.array(x)得到的是一个array,但只有一个元素,在书中有这么一句话 当所有轴都用形状仙童的整数数组作为下标时,得到的数组和下标数组的形状相同。 但当我们传入的是只有一个元素的tuple的时候(不管这个元素内部是多么复杂), 当我们没有指定第一轴的下标的时候,我们用:来代替。此时,我们要重新明确一下a[m,n]中代表的涵义,m是0轴坐标,n是1轴坐标,既然n为:,那么所有的列数据都被占用。
总结
说实话,这块理解还是挺难的,而且实际使用中有些内容已经有差别了,比如被废弃的方法,还是要时常温故,多加体会。
关于存取元素时是否与原数据共用
中心思想,是抓住问题的核心,如果我们可以仅仅通过修改stride shape就能得到一份新数据,这时候肯定是不用拷贝最合算,如果没办法通过修改stride shape得到数据,那么就只能拷贝了。 两个简单的例子, a[::2, :]是无需拷贝的,只需要把元素的stride*2就可以“得到”我们想要的数据;a[[1,4,7,9]]是需要拷贝的,此时我们不能通过简单修改stride就得到我们想要的数据。
1.4 frompyfunc
感觉类似于函数指针typedef,不过问题是看上去它只有一个可以遍历的arg.
def calc_test(a, b, c, d):
print(a + c + b + d)
def create():
a = np.arange(1, 10, 1) # 正确
a = 10 # 正确
b = np.arange(10, 100, 10)
c = np.arange(100, 1000, 100)
d = np.arange(1000, 10000, 1000)
calc_test_func = np.frompyfunc(calc_test, 4, 1)
print(calc_test_func(a, b, c, d))
上面这段代码,经过几次测试,得出的规律是,frompyfunc的几个特点
m = np.arange(1, 10, 5) + np.arange(10, 100, 10) # 错误 ValueError: operands could not be broadcast together with shapes (2,) (9,)
如果两个或者多个数组size不一致,那么这时候就出发挥 广播机制