利用python进行数据分析_从删库到跑路

目录

前言

一 numpy模块

1.numpy的数据结构:多维数组ndarray

数组转置和轴对换

矩阵内积

通用函数

利用数组进行数据处理(矢量化)

将条件逻辑表述为数组运算

数组和统计法方法 

约简

排序sort

唯一化和其他集合逻辑

数组中的集合运算

用于数组的文件输入输出

线性代数

随机数生成

部分numpy.random函数

随即漫步

数组重塑

扁平化或散开

C和Fortran顺序

数组的合并与拆分

元素的重复操作:tile和repeat

花式索引的等价函数:take和put

广播

三维数组的广播

ufunc高级应用

自定义ufunc

结构化和记录式数组

嵌套dtype和多维字段

排序

间接排序:argsort和lexsort

在有序数组中查找元素:searchsorted方法和digitize函数

numpy的matrix类

内存映像文件

性能建议

Cpython和numpy

二 pandas模块

1.pandas的数据结构:Series和DataFrame

三 matplotlib模块

四    scipy模块

 

@python3.6.6;ubuntu18.04

前言

常用模块为numpy、pandas、matplotlib、scipy

代码编写工具:jupyter

一 numpy模块

1.numpy的数据结构:多维数组ndarray

Numpy最重要的一个特点就是其N维数组对象ndarray,ndarray是一个通用的同构数据多维容器,也就是说其中所有的元组必须是相同类型的,他接收一切序列类型的对象(包括其他数组)。Numpy的ndarray提供了一种将同质数据块(可以是连续或非连续存储的)解释为多维数组对象的方式,数据类型dtype决定了数据的解释方式,比如浮点数、整数、布尔值等

ndarray如此强大的原因部分是因为所有数组对象都是数据块的一个跨度视图(strided view),ndarray不只是一块内存和一个dtype,它还有跨度信息,这使得数组能以各种步幅(step size)在内存中移动,更准确的说,ndarray由以下内容组成:

一个指向数组(一个系统内存块)的指针

一个表示数组形状(shape)的元组,如3*3的数组形状元组为(3,3)

一个跨度元组(stride),其中整数指的是为了前进到当前纬度下一个元素需要跨过的字节数,如3*4*5的foalt64数组,其跨度为(160,40,8)

下图简单地说明了ndarray的内部结构

利用python进行数据分析_从删库到跑路_第1张图片

1.0 ndarray的数据类型(dtype)体系

你可能需要检查数组中所包含的数据是否是整数、浮点数、字符串或python对象,因为浮点数的种类很多,判断dtype是否属于某个大类的工作非常繁琐,但是,dtype都有一个超类(比如np.interger和np.floating),他们可以和np.issubdtype函数结合,调用dtype的mro方法即可查看dtype的所有父类(有些数据类型后面带有下划线,是为了和python的数据类型区别开来)

In [9]: ints=np.ones(10,dtype=np.uint16)

In [10]: floats=np.ones(10,dtype=np.float32)

In [11]: np.issubdtype(ints.dtype,np.integer)#判断数据类型,同python的isinstance
Out[11]: True

In [12]: np.issubdtype(floats.dtype,np.floating)
Out[12]: True

#调用dtype的mro方法即可查看其所有的父类

In [13]: np.float64.mro()
Out[13]: 
[numpy.float64,
 numpy.floating,
 numpy.inexact,
 numpy.number,
 numpy.generic,
 float,
 object]

下图简单说明了dtype体系以及父子类关系

利用python进行数据分析_从删库到跑路_第2张图片

1.1 创建数组

import numpy as np
In [15]: np.array([1,2,3])
Out[15]: array([1, 2, 3])#传入列表,产生一维数组

In [16]: np.array([[1,2,3],[4,5,6]])
Out[16]: 
array([[1, 2, 3],
       [4, 5, 6]])#传入嵌套列表,产生二维数组

#传入一个数组
In [22]: rnd=np.random.randn(12)

In [23]: rnd_re=rnd.reshape(3,4)#reshape,重新构造数组结构

In [24]: rnd
Out[24]: 
array([ 0.65986137,  0.59780991, -0.29968381, -0.13900897, -0.91233434,
       -1.11650251,  0.10361076, -0.99113903, -0.18876077, -1.07867959,
       -1.35017413, -0.02826018])

In [25]: rnd_re
Out[25]: 
array([[ 0.65986137,  0.59780991, -0.29968381, -0.13900897],
       [-0.91233434, -1.11650251,  0.10361076, -0.99113903],
       [-0.18876077, -1.07867959, -1.35017413, -0.02826018]])

In [26]: np.array(rnd_re)
Out[26]: 
array([[ 0.65986137,  0.59780991, -0.29968381, -0.13900897],
       [-0.91233434, -1.11650251,  0.10361076, -0.99113903],
       [-0.18876077, -1.07867959, -1.35017413, -0.02826018]])

1.2 shape和dtype

shape表示各维度大小的元组,tdype用于说明数组数据类型的对象

In [1]: import numpy as np

In [2]: data=np.array([1,2,3])

In [3]: data.shape
Out[3]: (3,)

In [4]: data=np.array([[1,2,3],[4,5,6]])

In [5]: data.shape
Out[5]: (2, 3)

In [6]: data.dtype
Out[6]: dtype('int64')

1.3 empty、zeros、ones

empty创建一个没有任何具体值的数组(应该是无意义的虚数)

zeros创建一个全是0的数组

ones创建一个全是1的数组

In [30]: np.empty((3,3))
Out[30]: 
array([[1.96923895e-316, 2.04482388e-316, 3.92581470e+170],
       [2.33607195e-301, 5.94613337e-302, 2.29457644e+156],
       [3.66137200e-244, 4.10074486e-322, 3.95252517e-322]])

In [32]: np.zeros((3,3))
Out[32]: 
array([[0., 0., 0.],
       [0., 0., 0.],
       [0., 0., 0.]])

In [33]: np.ones((3,3))
Out[33]: 
array([[1., 1., 1.],
       [1., 1., 1.],
       [1., 1., 1.]])

1.4 arange

arange是python内置函数range的数组版

In [34]: np.arange(10)
Out[34]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

1.5 ndarray的数据类型

dtype(数据类型)是一个特殊对象,它含有ndarray将一块内存解释为特定数据类型所需的信息

主要有int64,int32,float64,float32等(不涉及底层工作,很少涉及,不用记)

#数值型dtype的命名方式相同:一个类型名(如int,float),后面跟一个表示各元素位长的数字,如float64
In [36]: np.array([1,2,3]).dtype
Out[36]: dtype('int64')

In [37]: data=np.array([1,2,3],dtype=np.float64)

In [38]: data.dtype
Out[38]: dtype('float64')

1.6 astype

通过astype可以显式的转换数组的数据类型

In [41]: data=np.array(['1','2','3'])

In [42]: data.dtype
Out[42]: dtype('

1.7 数组和标量的运算

矢量化(vectorization),数组可以让你不是用循环即可对数据执行批量运算,这通常叫做矢量化

数组和标量之间的运算,会将运算应用到元素级,同样的,大小相等的数组之间的任何运算都会应用到元素级

广播:不同大小数组之间的运算叫做广播

In [47]: data=np.array([[1,2,3],[4,5,6]])

In [49]: data*data
Out[49]: 
array([[ 1,  4,  9],
       [16, 25, 36]])

In [52]: data*3
Out[52]: 
array([[ 3,  6,  9],
       [12, 15, 18]])

1.8索引和切片

一维数组很简单,类似python数据类型

In [53]: data=np.array([1,2,3])

In [54]: data[1]
Out[54]: 2

当你将一个标量值赋值给一个切片时,该值会自动传播到整个选区,跟列表最重要的区别在于,数组切片是原始数据的视图,即和数据不会被复制,视图上的任何修改都会直接反映到源数据,若需要得到数组的副本,需要使用data.copy()显式的复制

In [59]: data_arr=np.array([x for x in range(10)])

In [61]: arr_slice=data_arr[5:8]

In [62]: data_arr
Out[62]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [64]: arr_slice
Out[64]: array([5, 6, 7])

In [65]: arr_slice[1]=12345

In [66]: arr_slice
Out[66]: array([    5, 12345,     7])

In [67]: data_arr
Out[67]: 
array([    0,     1,     2,     3,     4,     5, 12345,     7,     8,
           9])
#给数组arr_slice赋值,会改变其源数据data_arr,而列表则不会
In [68]: data_list=[x for x in range(10)]

In [69]: list_slice=data_list[5:8]

In [70]: data_list
Out[70]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [71]: list_slice
Out[71]: [5, 6, 7]

In [72]: list_slice[1]=12345

In [73]: list_slice
Out[73]: [5, 12345, 7]

In [74]: data_list
Out[74]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
#使用copy()方法显式的复制源数据,则不会修改源数据
In [75]: data_arr=np.array([x for x in range(10)])

In [76]: data_arr
Out[76]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [77]: arr_slice=data_arr[5:8].copy()

In [78]: arr_slice
Out[78]: array([5, 6, 7])

In [79]: arr_slice=12345

In [80]: arr_slice
Out[80]: 12345

In [81]: data_arr
Out[81]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

二维数组中,索引位置上的元素不再是标量,而是一维数组,因此对各个元素进行递归访问需要的方法不同,其切片有两种方法实现,其中第一种类似嵌套列表的切片

In [84]: data_arr=np.array([[x for x in range(10)],[x for x in range(10,20)]])

In [85]: data_arr
Out[85]: 
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]])

In [86]: data_arr[1][1]
Out[86]: 11

In [87]: data_arr[1,1]
Out[87]: 11

三维数组,切片取值可以理解为三层嵌套列表,同样可以使用两种方法表达

In [90]: data_arr=np.array([[[x for x in range(10)],[x for x in range(10,20)]],[
    ...: [x for x in range(20,30)],[x for x in range(40,50)]]])

In [91]: data_arr
Out[91]: 
array([[[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
        [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]],

       [[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
        [40, 41, 42, 43, 44, 45, 46, 47, 48, 49]]])

In [95]: data_arr[1,1,1]
Out[95]: 41


In [92]: data_arr[1]
Out[92]: 
array([[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49]])

In [93]: data_arr[1][1]
Out[93]: array([40, 41, 42, 43, 44, 45, 46, 47, 48, 49])

In [94]: data_arr[1][1][1]
Out[94]: 41

布尔型索引

跟算术运算一样,数组的比较运算(如==)也是矢量花的,因此,对数据和字符串的比较运算将会产生一个布尔型数组,布尔型数组的轴长度跟被索引的数组的轴长度一致

In [105]: data=np.array(['a','b','c'])

In [106]: data=='d'
Out[106]: array([False, False, False])

In [107]: data!='d'
Out[107]: array([ True,  True,  True])

布尔型数组和整数数组混合使用

In [108]: data1=np.array(['a','b','c'])

In [109]: data2=np.array([1,2,3])

In [110]: data_boolean=data1=='a'

In [111]: data_boolean
Out[111]: array([ True, False, False])

In [112]: data2[data_boolean]
Out[112]: array([1])

布尔算术运算符:& |

python中的and和or在numpy中无效

In [108]: data1=np.array(['a','b','c'])
In [109]: data2=np.array([1,2,3])

In [121]: data_boolean=(data1=='a')&(data1=='b')

In [122]: data_boolean
Out[122]: array([False, False, False])

In [123]: data_boolean=(data1=='a')|(data1=='b')

In [124]: data_boolean
Out[124]: array([ True,  True, False])

In [125]: data2[data_boolean]
Out[125]: array([1, 2])

花式索引

花式索引(Fancy indexing)是一个Numpy术语,它指利用整数数组进行索引

#子集赋值
In [126]: arr=np.empty((8,4))

In [127]: arr
Out[127]: 
array([[1.78873028e-316, 6.92799192e-310, 6.92799191e-310,
        6.92799192e-310],
       [6.92799191e-310, 6.92799191e-310, 6.92799191e-310,
        6.92799191e-310],
       [6.92799191e-310, 6.92799191e-310, 6.92799192e-310,
        6.92799191e-310],
       [6.92799192e-310, 6.92799191e-310, 6.92799191e-310,
        6.92799192e-310],
       [6.92799191e-310, 6.92799191e-310, 6.92799192e-310,
        6.92799192e-310],
       [6.92799191e-310, 6.92799191e-310, 5.41734680e-317,
        6.92799191e-310],
       [6.92799191e-310, 6.92799191e-310, 6.92799191e-310,
        6.92799191e-310],
       [6.92799192e-310, 6.92799192e-310, 6.92799192e-310,
        6.92799191e-310]])

In [128]: for i in range(8):
     ...:     arr[i]=i
     ...:     

In [129]: arr
Out[129]: 
array([[0., 0., 0., 0.],
       [1., 1., 1., 1.],
       [2., 2., 2., 2.],
       [3., 3., 3., 3.],
       [4., 4., 4., 4.],
       [5., 5., 5., 5.],
       [6., 6., 6., 6.],
       [7., 7., 7., 7.]])

#为了以特定的顺序选取子集,只需要传入一个用于指定顺序的整数列表或ndarray即可
In [130]: arr[[4,3,0,6]]
Out[130]: 
array([[4., 4., 4., 4.],
       [3., 3., 3., 3.],
       [0., 0., 0., 0.],
       [6., 6., 6., 6.]])
#一次传入多个索引数组会有一点特别,他返回一个一维数组,其中的元素对应各个索引元组


In [133]: arr[[1,2,3,4],[5,6,7,0]]#第二个列表表示列的排序,不能超过3
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
 in ()
----> 1 arr[[1,2,3,4],[5,6,7,0]]

IndexError: index 5 is out of bounds for axis 1 with size 4

In [134]: arr[[1,4,5,7],[0,3,1,2]]#返回的是数组的第一列数据,即默认的索引
Out[134]: array([1., 4., 5., 7.])

In [143]: arr[[1,4,5,7]][:,[0,3,1,2]]#前面的列表表示子集,后面列表表示选取的子集的数据排列
Out[143]: 
array([[ 4,  7,  5,  6],
       [16, 19, 17, 18],
       [20, 23, 21, 22],
       [28, 31, 29, 30]])

#也可以使用np.ix_函数,它可以将两个一维整数数组转换为一个用于选取方形区域的索引
In [144]: arr[np.ix_([1,4,5,7],[0,3,1,2])]
Out[144]: 
array([[ 4,  7,  5,  6],
       [16, 19, 17, 18],
       [20, 23, 21, 22],
       [28, 31, 29, 30]])

数组转置和轴对换

transpose和T

简单转置用T,复杂用transpose

数组转置是重塑的一种特殊形式,它返回的是源数据的视图(不会进行任何复制),数组不仅有transpose方法,还有一个T属性

In [5]: data=np.arange(12).reshape((3,4))

In [6]: data
Out[6]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

In [7]: data.T
Out[7]: 
array([[ 0,  4,  8],
       [ 1,  5,  9],
       [ 2,  6, 10],
       [ 3,  7, 11]])

对于高维数组,transpose需要得到一个由轴编号组成的元组,才能对数组进行转置

array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]]])

In [12]: arr.transpose((1,0,2))#轴1和轴0互换,轴2不变,原本的顺序是(0,1,2),分别表示总共有三个轴
Out[12]: 
array([[[ 0,  1,  2,  3],
        [ 8,  9, 10, 11]],

       [[ 4,  5,  6,  7],
        [12, 13, 14, 15]]])

矩阵内积

np.dot

In [9]: np.dot(data,data.T)
Out[9]: 
array([[ 14,  38,  62],
       [ 38, 126, 214],
       [ 62, 214, 366]])

通用函数

即(ufunc)既是一种ndarray中的数据执行元素级运算的函数,你可以将其看作简单函数的矢量花包装器

简单函数是指:接受一个或多个标量值,产生一个或多个标量值

sqrt和exp

接收一个变量的ufunc称为一元ufunc,其他如add和maximum称为二元ufunc

In [19]: arr=np.arange(10)

In [20]: np.sqrt(arr)#平方根
Out[20]: 
array([0.        , 1.        , 1.41421356, 1.73205081, 2.        ,
       2.23606798, 2.44948974, 2.64575131, 2.82842712, 3.        ])

In [21]: np.exp(arr)#以自然常数e为底数的指数函数,e是一个2.71828....的常数
Out[21]: 
array([1.00000000e+00, 2.71828183e+00, 7.38905610e+00, 2.00855369e+01,
       5.45981500e+01, 1.48413159e+02, 4.03428793e+02, 1.09663316e+03,
       2.98095799e+03, 8.10308393e+03])

add和maximum

In [22]: arr1=np.array([1,2,3])

In [23]: arr2=np.array([4,5,6])

In [24]: np.add(arr1,arr2)#数组1和数组2相加
Out[24]: array([5, 7, 9])


In [29]: arr1=np.array([1,3,5])

In [30]: arr2=np.array([1,2,6])

In [31]: np.maximum(arr1,arr2)#取数组1和数组2对应元素的最大值,组成新数组
Out[31]: array([1, 3, 6])

以下表格是一些常见的通用函数

一元ufunc  
abs、fabs 计算整数、浮点数和复数的绝对值,非复数,使用fabs更快
sqrt 计算各元素的平方根,相当于arr**0.5
square 计算各元素的平方,相当于arr**2
exp 计算各元素指数e
log,log10,log2,log1p 分别为自然对数(底数为e),底数为10的对数,底数为2的对数,log(1+x)
sign 计算各元素的正负号,正为1,0为零,负为-1
ceil 计算各元素的ceiling值,即大于等于该值的最小整数
floor 计算各元素的floor值,即小于等于该值的最小整数
rint 将各元素四舍五入到最接近的整数
modf 将数组的元素的整数和小数部分以两个独立的数组返回
isnan 返回一个哪些值是控制NaN的布尔型数组
isfinite,isinf 是否是有穷(finite),是否是无穷的(isinf),返回一个布尔型数组
cos,cosh,sin,sinh,tan,tanh 普通型和双曲型三角函数
arccos,arccosh,arcsin,arcsinh,arctan,arctanh 反三角函数
logical_not 计算各元素not x的真值,相当于-arr
二元ufunc  
add 将数组中对应元素相加
subtract 将数组中对应元素相减
multiply 数组元素相乘
divide,floor_divide 除法,向下整除(去掉余数)
power 对第一个数组中的元素A,根据第二个数组中的元素B,计算A的B次方
maximum,fmax 元素级的最大值计算,fmax忽略NaN值
minimum,fimin 元素级的最小值计算,fmin忽略NaN值
mod 元素级的求模运算(除法的余数)
copysign 将第二个数组的值的符号复制给第一个数组中的值
greater,greater_equal,less,less_equal,equal,not_equal 元素级比较运算,最终产生布尔型数组
logical_and,logical_or,logical_xor

元素级逻辑运算,与或非,相当于&、|、^

   
   
   

利用数组进行数据处理(矢量化)

Numpy数组使多种数据处理任务可以简单表述为数组表达式(否则需要写循环)

用数组表达式替代循环的做法,通常称作矢量化。

一般来说数组化运算要比纯python的等价运算要快上一两各数量级,尤其是各种数值运算

作为简单的例子,假设我们想要在一组值(网格型)上计算函数sqrt(x^2+y^2)。np.meshgrid函数接受两个一维数组,并产生两个二维矩阵(对应于两个数组中所有的(x,y)对):

In [48]: points=np.arange(-5,5,0.01)

In [49]: x,y=np.meshgrid(points,points)

In [50]: x
Out[50]: 
array([[-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99],
       [-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99],
       [-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99],
       ...,
       [-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99],
       [-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99],
       [-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99]])

In [51]: y
Out[51]: 
array([[-5.  , -5.  , -5.  , ..., -5.  , -5.  , -5.  ],
       [-4.99, -4.99, -4.99, ..., -4.99, -4.99, -4.99],
       [-4.98, -4.98, -4.98, ..., -4.98, -4.98, -4.98],
       ...,
       [ 4.97,  4.97,  4.97, ...,  4.97,  4.97,  4.97],
       [ 4.98,  4.98,  4.98, ...,  4.98,  4.98,  4.98],
       [ 4.99,  4.99,  4.99, ...,  4.99,  4.99,  4.99]])

In [53]: z=np.sqrt(x**2+y**2)

In [54]: z
Out[54]: 
array([[7.07106781, 7.06400028, 7.05693985, ..., 7.04988652, 7.05693985,
        7.06400028],
       [7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815,
        7.05692568],
       [7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354,
        7.04985815],
       ...,
       [7.04988652, 7.04279774, 7.03571603, ..., 7.0286414 , 7.03571603,
        7.04279774],
       [7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354,
        7.04985815],
       [7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815,
        7.05692568]])

将条件逻辑表述为数组运算

np.where

np.where函数是三元表达式x if condition else y的矢量化版本。假设我们有一个布尔数组和两个值数组

np.where的第二个和第三个参数不必是数组,它们都可以是标量值。在数据分析工作中,where通常用于根据另一个数组而产生一个新的数组。

In [56]: arr1=np.array([1,2,3,4,5])

In [57]: arr2=np.array([-1,-2,-3,-4,-5])

In [58]: arr3=np.array([True,False,True,False,True])

In [62]: arr=np.array([x if z else y for x,y,z in zip(arr1,arr2,arr3)])

In [63]: arr
Out[63]: array([ 1, -2,  3, -4,  5])

In [64]: arr=np.array(np.where(arr3,arr1,arr2))#np.where返回的是数组

In [65]: arr
Out[65]: array([ 1, -2,  3, -4,  5])

#将np.where用于替换值,如将数组的负数改为2:

In [67]: arr=np.random.randn(10)

In [68]: arr
Out[68]: 
array([-0.43310012, -0.78072221,  0.05956466,  0.07686748, -0.29438555,
       -0.53863612,  0.96500903, -1.40994316, -1.03975959,  1.83357067])

In [69]: arr_new=np.where(arr<0,2,arr)

In [70]: arr_new
Out[70]: 
array([2.        , 2.        , 0.05956466, 0.07686748, 2.        ,
       2.        , 0.96500903, 2.        , 2.        , 1.83357067])

数学和统计方法 

约简

通过数组上的一组数学函数对整个数组或者某条轴上的数据进行统计计算,如求和sum、平均值mean以及标准差std等聚合计算(aggregation,通常叫做约简(reduction))既可以当做数组的实例方法调用,也可以当做顶级Numpy函数使用

In [4]: arr=np.random.randn(5)#正态分布的随即数据

In [5]: arr
Out[5]: array([-0.11860027,  1.05271905,  0.57255691, -0.49385782,  0.37800909])

In [6]: arr.sum()
Out[6]: 1.3908269625978642

In [7]: np.sum(arr)
Out[7]: 1.3908269625978642

mean和sum

这类函数可以接受一个axis参数,用于计算该轴向上的统计值,最终返回一个少一维的数组

In [9]: arr=np.arange(20).reshape(5,4)

In [10]: arr
Out[10]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19]])


In [14]: arr.sum()#全部元素
Out[14]: 190

In [15]: arr.sum(0)#在数组上一个列表表示一列,axis=0表示的是行,计算的是所有列表每个索引上的和
Out[15]: array([40, 45, 50, 55])

In [16]: arr.sum(1)#数组上一个列表表示一列,axis=1表示的是列,计算的一个列表的和
Out[16]: array([ 6, 22, 38, 54, 70])

cumsum和cumprod

累计和,累计积这类方法则不聚合,而是产生一个由中间结果组成的数组

In [17]: arr
Out[17]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19]])

In [18]: arr.cumsum(axis=1)#列表内的累计和
Out[18]: 
array([[ 0,  1,  3,  6],
       [ 4,  9, 15, 22],
       [ 8, 17, 27, 38],
       [12, 25, 39, 54],
       [16, 33, 51, 70]])

In [19]: arr.cumprod(axis=1)#列表内的累计积
Out[19]: 
array([[    0,     0,     0,     0],
       [    4,    20,   120,   840],
       [    8,    72,   720,  7920],
       [   12,   156,  2184, 32760],
       [   16,   272,  4896, 93024]])

基本数组统计方法

sum 全部或某轴向的元素求和,零长度的数组的sum为0
mean 全部或某轴向的元素平均值,零长度的数组的mean为NaN
std、var

分别为标准差和方差,自由度可调(默认为n)

min、max 全部或某轴向上的最大值和最小值
argmin、argmax 全部或某轴向上的最大最小值的索引
cumsum 全部或某轴向上的累计和
cumprod 全部或某轴向上的累计积

用于布尔型数组的方法

在使用统计方法时,布尔值会强制转换为1(True)和0(Flase),因此sum经常被用来对布尔型数组中True值的计数

In [28]: arr=np.random.randn(100)

In [29]: (arr>0).sum()
Out[29]: 52

any和all

any用于判断存在一个或多个,all用于判断全部都是

In [28]: arr=np.random.randn(100)

In [30]: (arr>0).any()
Out[30]: True

In [31]: (arr>0).all()
Out[31]: False

排序sort

arr.sort和np.sort

通过arr.sort方法就地排序(直接改变源数据),np.sort()返回的是数组的已排序副本(即不修改源数据),可以对全部元素或者某轴向上的元素进行排序

In [46]: arr=np.random.randn(12).reshape(4,3)

In [47]: arr
Out[47]: 
array([[ 5.82831352e-02, -7.74374388e-01,  1.47850625e+00],
       [ 8.30603959e-01, -5.74236357e-01, -4.13521434e-01],
       [ 8.70914589e-04, -5.46720474e-01, -5.41121958e-01],
       [-7.03866197e-01,  1.95273830e+00, -5.85538385e-01]])

In [48]: arr.sort()#默认axis=1,在列上排序

In [49]: arr
Out[49]: 
array([[-7.74374388e-01,  5.82831352e-02,  1.47850625e+00],
       [-5.74236357e-01, -4.13521434e-01,  8.30603959e-01],
       [-5.46720474e-01, -5.41121958e-01,  8.70914589e-04],
       [-7.03866197e-01, -5.85538385e-01,  1.95273830e+00]])

In [50]: arr.sort(0)#行上排序,会改变列表的值

In [51]: arr
Out[51]: 
array([[-7.74374388e-01, -5.85538385e-01,  8.70914589e-04],
       [-7.03866197e-01, -5.41121958e-01,  8.30603959e-01],
       [-5.74236357e-01, -4.13521434e-01,  1.47850625e+00],
       [-5.46720474e-01,  5.82831352e-02,  1.95273830e+00]])

np.sort(arr)

In [52]: arr=np.random.randn(12).reshape(4,3)

In [53]: arr
Out[53]: 
array([[-1.0651435 , -0.70624382,  0.05124272],
       [ 0.69190929,  1.03548097,  0.64937742],
       [ 1.54626701, -0.08302486,  0.39043255],
       [-1.03014956, -0.12934306,  1.47813622]])

In [54]: np.sort(arr)
Out[54]: 
array([[-1.0651435 , -0.70624382,  0.05124272],
       [ 0.64937742,  0.69190929,  1.03548097],
       [-0.08302486,  0.39043255,  1.54626701],
       [-1.03014956, -0.12934306,  1.47813622]])

In [55]: arr
Out[55]: 
array([[-1.0651435 , -0.70624382,  0.05124272],
       [ 0.69190929,  1.03548097,  0.64937742],
       [ 1.54626701, -0.08302486,  0.39043255],
       [-1.03014956, -0.12934306,  1.47813622]])

唯一化和其他集合逻辑

np.unique

numpy提供了一些针对一维ndarray的基本集合运算,最常用的时np.unique,它用于找出数组中的唯一值并返回已排序的结果

In [61]: arr=np.array([1,1,2,3,4,5,2,3,4,5,6])

In [62]: np.unique(arr)
Out[62]: array([1, 2, 3, 4, 5, 6])

np.in1d

用于测试一个数组中的值在另一个数组中的成员资格,返回一个布尔型数组,第一个参数为源数据,验证第二个参数在源数据中的成员资格

In [68]: arr1=np.array([1,2,3,4,5])

In [69]: arr2=np.array([1,2,3,4,5,6])

In [70]: np.in1d(arr1,arr2)
Out[70]: array([ True,  True,  True,  True,  True])

In [71]: np.in1d(arr1,[1,2
    ...: ])
Out[71]: array([ True,  True, False, False, False])

数组中的集合运算

unique() 计算x中的唯一元素(去重),并返回有序结果
intersect1d(x,y) 计算x,y的公共元素,并返回有序结果
union1d(x,y)

计算x,y的并集,并返回有序结果

in1d(x,y) 得到一个表示'y元素是否在x中'的布尔型数组,数组长度等于x
setdiff1d(x,y) 集合的差,即元素在x中且不在y中
setxor1d(x,y) 集合的对称差,即存在于一个数组中,但不同时存在于两个数组中的元素,即'异或'

用于数组的文件输入输出

np.load和np.save, 用于读写磁盘数组数据,默认情况下,数组是以未压缩的原始二进制格式保存在扩展名为.npy的文件中的

如果save的文件路径末尾没有扩展名.npy,则该扩展名会被自动加速,通过np.load读取磁盘数据时需要加入后缀名

In [72]: arr=np.arange(10)

In [73]: np.save('save_arr',arr)
In [75]: np.load('save_arr.npy')
Out[75]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

np.savez

将多个数组保存到一个压缩文件中,将数组以关键字参数的形式传入即可,后缀名为.npz的文件,

加载.npz文件时,你会得到一个类似字典的对象,该对象会对各个数组进行延迟加载:

In [77]: np.savez('savez_arr',aaa=np.array([12,34]),bbb=np.array([3,4,2,2]))

In [79]: np.load('savez_arr.npz')
Out[79]: 

In [80]: ar=np.load('savez_arr.npz')

In [81]: ar['aaa']
Out[81]: array([12, 34])

np.loadtxt和np.genformtxt

将普通数据加载到普通的numpy的数组中,可以指定各种分隔符、针对特定列的转换器函数、需要跳国的行数等

zelin@2018:~$ cat nppp.txt
1,1,1,1,1,1,1
2,2,2,2,2,2,2
3,3,3,3,3,3,3


In [86]: np.loadtxt('nppp.txt',delimiter=',')#必须指定分隔符
Out[86]: 
array([[1., 1., 1., 1., 1., 1., 1.],
       [2., 2., 2., 2., 2., 2., 2.],
       [3., 3., 3., 3., 3., 3., 3.]])

In [88]: np.genfromtxt('nppp.txt')#面向的时结构化数组和缺失数据处理
Out[88]: array([nan, nan, nan])

np.savetxt('filedir','data')将二维数组保存到txt文件中

In [94]: np.loadtxt('nppp.txt',delimiter=',').shape
Out[94]: (3, 7)

In [95]: np.savetxt('ssss.txt',np.loadtxt('nppp.txt',delimiter=','))

zelin@2018:~$ cat ssss.txt
1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00
2.000000000000000000e+00 2.000000000000000000e+00 2.000000000000000000e+00 2.000000000000000000e+00 2.000000000000000000e+00 2.000000000000000000e+00 2.000000000000000000e+00
3.000000000000000000e+00 3.000000000000000000e+00 3.000000000000000000e+00 3.000000000000000000e+00 3.000000000000000000e+00 3.000000000000000000e+00 3.000000000000000000e+00

线性代数

np.dot(x,y)和x.dot(y)得到的结果一样

如矩阵乘法、矩阵分解、行列式以及其他方阵数学,numpt提供了一个用于矩阵乘法的dot函数(既是一个数组方法也是numpy命名空间中的一个函数)

In [96]: x=np.array([[1,2,3],[4,5,6]])

In [97]: y=np.array([[7,8],[9,11],[12,13]])

In [98]: np.dot(x,y)
Out[98]: 
array([[ 61,  69],
       [145, 165]])

In [99]: x.dot(y)
Out[99]: 
array([[ 61,  69],
       [145, 165]])

一个二维数组跟一个大小合适的一维数组的矩阵点积运算之后将会得到一个一维数组:

In [100]: x=np.array([[1,2,3],[4,5,6]])

In [101]: y=np.array([1,2,3])

In [102]: x.dot(y)
Out[102]: array([14, 32])

numpy.linalg

linalg中由一组标准矩阵分解运算以及诸如求逆和行列式之类的东西

In [126]: x=np.random.randn(5,5)

In [127]: mat=x.T.dot(x)

In [128]: inv(mat)#计算乘法的逆
Out[128]: 
array([[ 383.46895288,  440.98999139,  149.94498526, -163.43061669,
        -297.78805292],
       [ 440.98999139,  507.78623867,  172.63437908, -188.1462194 ,
        -343.09305341],
       [ 149.94498526,  172.63437908,   59.30910465,  -64.09383187,
        -116.02238766],
       [-163.43061669, -188.1462194 ,  -64.09383187,   69.91440892,
         126.83237131],
       [-297.78805292, -343.09305341, -116.02238766,  126.83237131,
         233.04712006]])

In [129]: mat.dot(inv(mat))#计算矩阵的qr分解
Out[129]: 
array([[ 1.00000000e+00,  5.68434189e-14, -1.27897692e-13,
         2.84217094e-14,  0.00000000e+00],
       [ 0.00000000e+00,  1.00000000e+00,  5.68434189e-14,
        -1.13686838e-13,  0.00000000e+00],
       [ 1.13686838e-13, -1.13686838e-13,  1.00000000e+00,
         5.68434189e-14,  0.00000000e+00],
       [-3.41060513e-13, -2.27373675e-13,  0.00000000e+00,
         1.00000000e+00,  2.27373675e-13],
       [-2.27373675e-13, -1.13686838e-13, -5.68434189e-14,
         1.13686838e-13,  1.00000000e+00]])

In [130]: q,r=qr(mat)

In [131]: q
Out[131]: 
array([[-0.63453898, -0.47094923,  0.18141456,  0.19269952, -0.55274112],
       [ 0.7372586 , -0.20651792,  0.0060628 ,  0.09058399, -0.63683428],
       [-0.14374066,  0.32326252, -0.7934532 ,  0.44597525, -0.21535567],
       [ 0.05296628, -0.70849705, -0.56096271, -0.35372934,  0.23542068],
       [ 0.1741931 , -0.35929159,  0.15101276,  0.79413038,  0.43257184]])

In [132]: r
Out[132]: 
array([[-3.51536372e+00,  8.37098488e+00, -3.43363927e+00,
         4.29370726e+00,  3.78639110e+00],
       [ 0.00000000e+00, -5.77553574e+00,  2.53098177e+00,
        -7.12560115e+00, -3.36626363e+00],
       [ 0.00000000e+00,  0.00000000e+00, -2.65421554e+00,
        -3.56576864e+00,  6.19863832e-01],
       [ 0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
        -8.85238991e-01,  4.85185531e-01],
       [ 0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
         0.00000000e+00,  1.85615613e-03]])

常用的numpy.linalg函数

diag 以一维数组的形式返回方针的对角线元素,或将一维数组转换为方阵
dot 矩阵乘法
trace 计算对角线元素的和
det 计算矩阵行列式
eig 计算方阵的本征值和本征向量
inv 计算方阵的逆
pinv 计算矩阵的Moor-Penrose伪逆
qr 计算QR分解
svd 计算奇异值分解
solve 解线性方程组Ax=b,其中A为一个方阵
lstsq 计算
   
   
   
   
   
   
   

随机数生成

numpy.random模块对python内置的random进行了补充,增加了一些用于高效生成多种嗯概率分布的样本值的函数,例如,用normal来得到一个标准正太分布的4*4样本数组

In [133]: samples=np.random.normal((4,4))#与关键字参数不同

In [134]: samples
Out[134]: array([3.57761124, 2.31337006])

In [135]: samples=np.random.normal(size=(4,4))

In [136]: samples
Out[136]: 
array([[ 0.78330662, -0.56505952, -0.97143979, -0.33209158],
       [-0.34771653, -0.30901418, -0.53225815, -0.08778485],
       [-1.0037474 ,  0.87408366, -0.98363945,  0.5701133 ],
       [-1.42551243,  0.12037672, -0.49017895,  0.65434629]])

python内置的random模块只能一次生成一个样本值,如果需要产生大量样本值,numpy.random快了不只一个数量级

部分numpy.random函数

seed 确定随机数生成器的种子
permutation 返回一个序列的随即排列或返回一个随即排列的范围
shuffle 对一个序列就地随即排列
rand 产生均匀分布的样本值
randint 从给定的上下限范围内随即选区整数
randn 产生正态分布(平均值为0,标准差为1)的样本值
binomial 产生二项分布的样本值
normal 产生正太(高斯)分布的样本值
beta 产生beta分布的样本值
chisquare 产生卡方分布的样本值
gamma 产生gamma分布的样本值
uniform 产生在[1,0)中均匀分布的样本值
   

随机漫步

In [136]: import random
In [137]: position=0

In [138]: walk=[position]

In [139]: steps=1000
#纯python方法                
In [178]: for i in range(steps):
     ...:     step=1 if random.randint(0,2) else -1
     ...:     position+=step
     ...:     walk.append(position)
#numpy方法
In [180]: nsteps=1000

In [181]: draws=np.random.randint(0,2,size=nsteps)

In [182]: steps=np.where(draws,1,-1)

In [183]: walk=np.cumsum(steps)

In [184]: walk

一次模拟多个随即漫步

只要给numpy.random的函数传入一个二元元组就可以产生一个二维数组,然后我们就可以一次性计算5000个随即漫步过程(一行一个)的累计和了

In [185]: nsteps=5000

In [186]: draws=np.random.randint(0,2,(10,500))#10行500列

In [187]: walk=np.where(draws,1,-1)

In [188]: np.cumsum(walk)
Out[188]: array([  1,   0,   1, ..., -60, -59, -60])
In [190]: walks.max()
Out[190]: 33

In [191]: walks.min()
Out[191]: -75

 

数组重塑

reshape

In [15]: arr
Out[15]: array([0, 1, 2, 3, 4, 5, 6, 7])

In [16]: arr.reshape((4,2))
Out[16]: 
array([[0, 1],
       [2, 3],
       [4, 5],
       [6, 7]])
#多维数组也可以重塑
In [17]: arr.reshape((4,2)).reshape(2,4)
Out[17]: 
array([[0, 1, 2, 3],
       [4, 5, 6, 7]])
#作为参数的形状的其中一维可以是-1,表示该维度的大小由数据本身推断而来
In [21]: arr.reshape((5,-1))
Out[21]: 
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11],
       [12, 13, 14]])

In [22]: arr.reshape((3,-1))
Out[22]: 
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])

#由于数组的形状(shape)也是一个元组,因此它可以被传入reshape:
In [33]: arr1=np.ones((3,4))

In [34]: arr2=np.ones((12))

In [35]: arr1
Out[35]: 
array([[1., 1., 1., 1.],
       [1., 1., 1., 1.],
       [1., 1., 1., 1.]])

In [36]: arr2
Out[36]: array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])

In [37]: s=arr1.shape

In [38]: arr2.reshape(s)
Out[38]: 
array([[1., 1., 1., 1.],
       [1., 1., 1., 1.],
       [1., 1., 1., 1.]])

扁平化或散开

与reshape将一维数组转换为多维数组的运算过程相反的运算通常称为扁平化或散开

ravel()和flatten()

功能类似,但ravel不会产生源数据的副本,flatten方法返回的是源数据的副本

In [39]: arr=np.arange(15).reshape((3,5))

In [40]: arr
Out[40]: 
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])

In [41]: arr.ravel()
Out[41]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])

In [42]: arr
Out[42]: 
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])

In [43]: arr.flatten()
Out[43]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])

In [44]: arr
Out[44]: 
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])

C和Fortran顺序

默认情况下,Numpy数组是按行优先顺序创建的,在空间方面,这意味着,对于一个二维数组,每行中的数据项是被存放在相邻内存位置上的,另一种顺序是列优先顺序,它意味着美列中的数据项是被存放在相邻位置上的。

行优先称为C顺序,列优先称为Fortran顺序,像reshape,reval这样的函数,都可以接受一个表示数组数据存放顺序的order参数,一般可以是‘C’或‘F’,得到不同的数组

二维数组重塑过程,C和Fortran顺序的关键区别就是维度的行进顺序

C顺序:先经过更高的维度,如轴1会优先于轴0被处理

Forttran顺序:后经过更高的维度,如轴0会优先于轴1被处理

In [46]: arr
Out[46]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

In [47]: arr.ravel()#默认行优先
Out[47]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

In [48]: arr.ravel(order='C')#显式行优先
Out[48]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

In [49]: arr.ravel(order='F')#列优先
Out[49]: array([ 0,  4,  8,  1,  5,  9,  2,  6, 10,  3,  7, 11])

数组的合并与拆分

concatenate()

可以按指定轴将一个由数组组成的序列(如列表,元组等)连接到一起

In [51]: arr=np.array([[1,2,3],[4,5,6]])

In [52]: arr1=np.array([[7,8,9],[10,11,12]])

In [53]: np.concatenate([arr,arr1],axis=0)
Out[53]: 
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12]])

In [54]: np.concatenate([arr,arr1],axis=1)
Out[54]: 
array([[ 1,  2,  3,  7,  8,  9],
       [ 4,  5,  6, 10, 11, 12]])

vstack()和hstack()

对与常见的连接操作,也可以使用Numpy提供的vstack和hstack函数

In [56]: np.vstack((arr,arr1))#axis=0
Out[56]: 
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12]])

In [57]: np.hstack((arr,arr1))#axis=1
Out[57]: 
array([[ 1,  2,  3,  7,  8,  9],
       [ 4,  5,  6, 10, 11, 12]])

split()

split用于将一个数组沿指定轴拆分为多个数组

In [61]: arr=np.random.randn(5,2)

In [62]: arr
Out[62]: 
array([[-0.82230236,  0.62723821],
       [ 2.18883671, -0.27052682],
       [-0.45726504, -1.66183692],
       [-0.42266318,  0.84994138],
       [-0.30175674,  1.25396072]])

In [63]: first,second,third=np.split(arr,[1,3])

In [64]: first
Out[64]: array([[-0.82230236,  0.62723821]])#半开放区间,边界轴在下一个数组分区

In [65]: second
Out[65]: 
array([[ 2.18883671, -0.27052682],
       [-0.45726504, -1.66183692]])

In [66]: third
Out[66]: 
array([[-0.42266318,  0.84994138],
       [-0.30175674,  1.25396072]])

堆叠辅助类:r_和c_

使数组的堆叠操作更为简洁

In [77]: arr=np.arange(6)

In [78]: arr1=arr.reshape((3,2))

In [79]: arr2=np.random.randn(3,2)

In [80]: np.r_[arr1,arr2]#增加行
Out[80]: 
array([[ 0.        ,  1.        ],
       [ 2.        ,  3.        ],
       [ 4.        ,  5.        ],
       [ 0.19138704, -1.34381651],
       [-2.13175216, -1.28283516],
       [-1.79236481,  0.11647346]])

In [81]: np.c_[np.r_[arr1,arr2],arr]#增加列
Out[81]: 
array([[ 0.        ,  1.        ,  0.        ],
       [ 2.        ,  3.        ,  1.        ],
       [ 4.        ,  5.        ,  2.        ],
       [ 0.19138704, -1.34381651,  3.        ],
       [-2.13175216, -1.28283516,  4.        ],
       [-1.79236481,  0.11647346,  5.        ]])

In [82]: np.c_[1:6,-10:-5]#生成数组
Out[82]: 
array([[  1, -10],
       [  2,  -9],
       [  3,  -8],
       [  4,  -7],
       [  5,  -6]])

元素的重复操作:tile和repeat

对数组进行重复以产生更大数组的工具主要是repeat和tile这两个函数,repeat会将数组中的各个元素重复一定次数,从而产生一个更大的数组

In [84]: arr=np.array([1,2,3])

In [85]: arr.repeat(3)
Out[85]: array([1, 1, 1, 2, 2, 2, 3, 3, 3])
#默认情况下,如果传入的是一个整数,则各元素都会重复整数次,如果传入一组整数(元组形式),则各元素就可以重复不同的次数
In [88]: arr.repeat((3,2,3))#元组长度需要与数组列数相同
Out[88]: array([1, 1, 1, 2, 2, 3, 3, 3])
#多维数组可以沿指定轴重复
In [92]: arr=np.random.randn(4,5)

In [93]: arr.repeat((1,2,3,4),axis=0)#按行重复
Out[93]: 
array([[ 0.1374145 , -0.17461463, -0.3860606 ,  0.37310723, -0.33203066],
       [-0.94992704, -0.21039861, -0.46802276,  2.23576964, -0.01360194],
       [-0.94992704, -0.21039861, -0.46802276,  2.23576964, -0.01360194],
       [-1.10704975,  2.15029587,  1.06162111, -0.76405616, -0.01180898],
       [-1.10704975,  2.15029587,  1.06162111, -0.76405616, -0.01180898],
       [-1.10704975,  2.15029587,  1.06162111, -0.76405616, -0.01180898],
       [ 1.39670354,  0.45674728,  1.4260907 ,  2.22409618,  0.61247909],
       [ 1.39670354,  0.45674728,  1.4260907 ,  2.22409618,  0.61247909],
       [ 1.39670354,  0.45674728,  1.4260907 ,  2.22409618,  0.61247909],
       [ 1.39670354,  0.45674728,  1.4260907 ,  2.22409618,  0.61247909]])

In [95]: arr.repeat((1,2,3,4,5),axis=1)#按列重复,即重复元素
Out[95]: 
array([[ 0.1374145 , -0.17461463, -0.17461463, -0.3860606 , -0.3860606 ,
        -0.3860606 ,  0.37310723,  0.37310723,  0.37310723,  0.37310723,
        -0.33203066, -0.33203066, -0.33203066, -0.33203066, -0.33203066],
       [-0.94992704, -0.21039861, -0.21039861, -0.46802276, -0.46802276,
        -0.46802276,  2.23576964,  2.23576964,  2.23576964,  2.23576964,
        -0.01360194, -0.01360194, -0.01360194, -0.01360194, -0.01360194],
       [-1.10704975,  2.15029587,  2.15029587,  1.06162111,  1.06162111,
         1.06162111, -0.76405616, -0.76405616, -0.76405616, -0.76405616,
        -0.01180898, -0.01180898, -0.01180898, -0.01180898, -0.01180898],
       [ 1.39670354,  0.45674728,  0.45674728,  1.4260907 ,  1.4260907 ,
         1.4260907 ,  2.22409618,  2.22409618,  2.22409618,  2.22409618,
         0.61247909,  0.61247909,  0.61247909,  0.61247909,  0.61247909]])

tile是沿指定轴向堆叠数组的副本,可以将其想象成'铺瓷砖',

第二参数表示瓷砖的数量,对于标量,瓷砖是水平铺设的,而不是垂直铺设,可以传入一个表示铺设布局的元组

In [96]: arr=np.random.randn(2,2)

In [98]: np.tile(arr,2)#堆叠数组的副本,默认堆叠元素
Out[98]: 
array([[ 0.42442511,  0.61368951,  0.42442511,  0.61368951],
       [ 0.25196689, -0.54785916,  0.25196689, -0.54785916]])

In [99]: np.tile(arr,(2,1))#堆叠行
Out[99]: 
array([[ 0.42442511,  0.61368951],
       [ 0.25196689, -0.54785916],
       [ 0.42442511,  0.61368951],
       [ 0.25196689, -0.54785916]])

In [100]: np.tile(arr,(2,3))#堆叠行2,堆叠列(元素)3
Out[100]: 
array([[ 0.42442511,  0.61368951,  0.42442511,  0.61368951,  0.42442511,
         0.61368951],
       [ 0.25196689, -0.54785916,  0.25196689, -0.54785916,  0.25196689,
        -0.54785916],
       [ 0.42442511,  0.61368951,  0.42442511,  0.61368951,  0.42442511,
         0.61368951],
       [ 0.25196689, -0.54785916,  0.25196689, -0.54785916,  0.25196689,
        -0.54785916]])

花式索引的等价函数:take和put

获取和设置数组子集的一个办法是通过整数数组使用花式索引:

In [105]: arr=np.arange(10)*100

In [106]: arr[[7,2,1,3]]
Out[106]: array([700, 200, 100, 300])

ndarray有两个方法专门用于获取和设置单个轴向上的选区:

take 是花式索引的等价函数,行/列上的参数为axis=0或axis=1

put 是将花式索引表达的元素进行替换,put不支持axis参数,只能在数组的扁平化版本(一维,C顺序)上进行索引

最好的选择还是直接使用花式索引,而不是函数

In [107]: arr=np.arange(10)*100

In [108]: arr.take([7,1,2,6])#花式索引的等价函数
Out[108]: array([700, 100, 200, 600])

In [109]: arr.put([7,1,2,6],42)#花式索引值替换

In [110]: arr
Out[110]: array([  0,  42,  42, 300, 400, 500,  42,  42, 800, 900])

In [111]: arr.put([7,1,2,6],[11,22,33,44,55])#传如入一个等长列表,同步替换

In [112]: arr
Out[112]: array([  0,  22,  33, 300, 400, 500,  44,  11, 800, 900])

广播

广播(broadcasting)指的是不同形状的数组之间的算术运算的执行方式,将标量值跟数组合并就会发生最简单的广播,即标量值被广播到了数组的所有元素上,不同形状指的是行数不同,列数相同才能进行广播。(广播的原则:如果两个数组的后缘维度(trailing dimension ,即从末尾开始计算起的维度)的轴长度相符或其中一方的长度为1,则认为他们是广播兼容的,广播会在缺失和1长度为1的维度上进行)

距平化:即系列数值与其平均值的差

In [120]: arr=np.random.randn(4,3)

In [121]: arr.mean(0)
Out[121]: array([-0.07543098,  0.83298458, -0.02126994])

In [122]: arr-arr.mean(0)#由于mean(0)的长度正好为3,所以可以直接相减
Out[122]: 
array([[-0.27645797,  1.38328286, -1.22593655],
       [ 1.59969598, -0.03434764,  1.62403164],
       [-1.03478433,  0.31967476,  0.14974559],
       [-0.28845368, -1.66860997, -0.54784068]])
#由于arr.mean(0)的长度为3,所以可以直接在0轴上进行广播,因为mean(0)的后缘长度为3,所以他们是兼容的,根据广播原则,要在1轴上作减法,则较小的那个数组形状必须是(4,1),其中行必须是4
In [133]: res=arr.mean(1).reshape((4,1))

In [134]: res
Out[134]: 
array([[ 0.19789142],
       [-0.19232197],
       [-0.64339871],
       [ 0.14236788]])

In [135]: arr-res
Out[135]: 
array([[-0.90398182,  0.37917518,  0.52480664],
       [-0.23696029, -0.24588066,  0.48284096],
       [ 0.01379453, -0.22597313,  0.21217861],
       [-0.28368762,  0.34259934, -0.05891172]])

三维数组的广播

高纬度数组的广播也是遵循广播原则的。人们经常通过算术运算过程将较低维度的数组在0轴以外的其他轴向上广播,根据广播原则,较小数组的'广播维'必须是1,所以在进行距平化的广播中,较小的数组形状必须是(N,1),不能是(N,),虽然他们表达数组视图是相同的

下图简单说明了能在三维数组上广播的二维数组

利用python进行数据分析_从删库到跑路_第3张图片

np.newaxis属性以及'全'切片

对于三维数组,在三维中的任何一维上广播其实也是将数据重塑为兼容的形状而已,于是就有一个非常普遍的操作,即专门为了广播而添加一个长度为1的新轴,Numpy数组提供了一种通过索引机制插入轴的特殊语法

In [144]: arr=np.zeros((4,4))

In [145]: arr_3d=arr[:,np.newaxis,:]

In [146]: arr_3d.shape
Out[146]: (4, 1, 4)

In [147]: arr_1d=np.random.normal(size=3)

In [148]: arr_1d
Out[148]: array([ 0.95908759,  0.67838227, -1.21728343])

In [149]: arr_1d[:,np.newaxis]
Out[149]: 
array([[ 0.95908759],
       [ 0.67838227],
       [-1.21728343]])

In [150]: arr_1d[np.newaxis,:]
Out[150]: array([[ 0.95908759,  0.67838227, -1.21728343]])

假设我们有一个三维数组,需要在轴2上进行距平化

In [153]: arr=np.random.randn(3,4,5)

In [154]: arr
Out[154]: 
array([[[ 0.28431603,  0.44861501, -0.20777097,  0.28023136,
          0.17530054],
        [ 0.55700422, -0.14012566,  0.18500479,  1.04806987,
         -0.9508947 ],
        [-0.18838964,  0.04586741,  0.84066257, -0.07401783,
          1.46106524],
        [-0.15170269, -1.36097563,  0.59832985,  0.45333389,
          1.66786734]],

       [[-1.17441825, -0.00574066,  0.26356278,  0.82866366,
         -0.76109032],
        [ 0.10570107, -1.15842887,  0.43370703, -0.56219272,
          1.90148085],
        [-1.24608118, -0.54931805, -0.91075277,  0.21715073,
          0.59751696],
        [ 0.3297414 , -1.10564384, -0.54772233, -1.59229321,
          0.19203631]],

       [[ 2.03384505,  0.17328362,  1.59381343,  1.11969745,
         -0.62698893],
        [ 1.02854294, -0.97755266,  0.0048308 , -0.70172524,
         -0.12799296],
        [-0.42962065,  0.05177602,  0.18382527, -0.6816369 ,
          0.75557744],
        [ 0.19051328, -0.82344915,  0.90430084,  1.30163513,
         -0.61083626]]])

In [155]: depth_means=arr.mean(2)

In [156]: depth_means
Out[156]: 
array([[ 0.19613839,  0.1398117 ,  0.41703755,  0.24137055],
       [-0.16980456,  0.14405347, -0.37829686, -0.54477633],
       [ 0.85873013, -0.15477942, -0.02401576,  0.19243277]])

In [157]: depth_means.shape
Out[157]: (3, 4)

In [158]: demeaned=arr-depth_means[:,:,np.newaxis]

In [159]: demeaned
Out[159]: 
array([[[ 0.08817763,  0.25247662, -0.40390936,  0.08409296,
         -0.02083785],
        [ 0.41719252, -0.27993736,  0.04519309,  0.90825817,
         -1.09070641],
        [-0.60542719, -0.37117014,  0.42362502, -0.49105538,
          1.04402769],
        [-0.39307324, -1.60234618,  0.35695929,  0.21196334,
          1.42649679]],

       [[-1.00461369,  0.1640639 ,  0.43336734,  0.99846822,
         -0.59128577],
        [-0.03835241, -1.30248234,  0.28965356, -0.7062462 ,
          1.75742738],
        [-0.86778431, -0.17102119, -0.53245591,  0.59544759,
          0.97581382],
        [ 0.87451774, -0.56086751, -0.002946  , -1.04751687,
          0.73681264]],

       [[ 1.17511493, -0.6854465 ,  0.7350833 ,  0.26096733,
         -1.48571906],
        [ 1.18332236, -0.82277324,  0.15961023, -0.54694581,
          0.02678646],
        [-0.40560488,  0.07579178,  0.20784104, -0.65762114,
          0.7795932 ],
        [-0.00191949, -1.01588192,  0.71186807,  1.10920236,
         -0.80326902]]])

In [161]: d=depth_means[:,:,np.newaxis]

In [162]: d.shape
Out[162]: (3, 4, 1)

通过广播设置数组的值

算术运算所遵循的广播原则同样也适用于通过索引机制设置数组值的操作,假设我们用一个一维数组来设置目标数组的各列,只要保证兼容就可以了

In [168]: col=np.array([1.12,-0.43,0.44,1.6])

In [169]: arr=np.zeros((4,3))

In [170]: arr[:]=col[:,np.newaxis]

In [171]: arr
Out[171]: 
array([[ 1.12,  1.12,  1.12],
       [-0.43, -0.43, -0.43],
       [ 0.44,  0.44,  0.44],
       [ 1.6 ,  1.6 ,  1.6 ]])

In [172]: arr[:2]=[[222],[333]]

In [173]: arr
Out[173]: 
array([[222.  , 222.  , 222.  ],
       [333.  , 333.  , 333.  ],
       [  0.44,   0.44,   0.44],
       [  1.6 ,   1.6 ,   1.6 ]])

ufunc高级应用

ufunc(universal function)是指能够应用到ndarray对象的每一个元素上,而不仅仅是ndarray对象

reduce接受一个数组参数,并通过一系列的二元运算对其值进行聚合(可指名轴向)

In [174]: arr=np.arange(10)

In [175]: np.add.reduce(arr)
Out[175]: 45

In [176]: arr.sum()
Out[176]: 45

np.logical_and用于检查数组各行中的值是否是有序的,

In [177]: arr=np.random.randn(5,5)

In [178]: arr[::2].sort(1)#对部分行进行排序

In [179]: arr[:,:-1]

accumulate跟reduce的关系就像cumsum和sum的关系一样,它产生一个跟原数组大小相同的中间'累计'值数组

In [181]: arr=np.arange(15).reshape(3,5)

In [182]: np.add.accumulate(arr,axis=1)
Out[182]: 
array([[ 0,  1,  3,  6, 10],
       [ 5, 11, 18, 26, 35],
       [10, 21, 33, 46, 60]])

outer用于计算两个数组的叉积,outer输出结果的维度是两个输入数据的维度之和

In [183]: arr=np.arange(3).repeat([1,2,3])

In [184]: arr
Out[184]: array([0, 1, 1, 2, 2, 2])

In [186]: np.multiply.outer(arr,np.arange(5))
Out[186]: 
array([[0, 0, 0, 0, 0],
       [0, 1, 2, 3, 4],
       [0, 1, 2, 3, 4],
       [0, 2, 4, 6, 8],
       [0, 2, 4, 6, 8],
       [0, 2, 4, 6, 8]])

#维度
In [187]: res=np.subtract.outer(np.random.randn(4,5),np.random.randn(5))

In [188]: res.shape
Out[188]: (4, 5, 5)

reduceat用于计算'局部约简',其实就是一个对数据各切片进行聚合的groupby运算,虽然其灵活性不如pandas的groupby功能,但适当的情况下运算会非常快,它接受一组用于指示如何对值进行拆分和聚合的'面元边界'

In [189]: arr=np.arange(10)

In [190]: np.add.reduceat(arr,[0,5,8])
Out[190]: array([10, 18, 17])
#其最终结果是在arr[0:5],arr[5:8]和arr[8:]上执行约简(本例中就是求和),和其他方法亿元,也可传入一个axis参数
In [191]: arr=np.multiply.outer(np.arange(4),np.arange(5))

In [192]: arr
Out[192]: 
array([[ 0,  0,  0,  0,  0],
       [ 0,  1,  2,  3,  4],
       [ 0,  2,  4,  6,  8],
       [ 0,  3,  6,  9, 12]])

In [193]: np.add.reduceat(arr,[0,2,4],axis=1)
Out[193]: 
array([[ 0,  0,  0],
       [ 1,  5,  4],
       [ 2, 10,  8],
       [ 3, 15, 12]])

自定义ufunc

使自定义的函数能够像ufunc一样使用

frompyfunc和vectorize

numpy.frompyfunc接受一个python函数以及两个分别表示输入输出参数数量的整数,使用frompyfunc创建的自定义ufunc只能返回一个python对象数组,numpy.vectorize则返回一个数组对象。

虽然这两个函数提供了一种创建ufunc型函数的手段,但他们非常曼,因为他们在计算每个元素的时候都要执行一次python函数调用,比numpy基于C的ufunc慢很多

In [194]: def add_element(x,y):
     ...:     return x+y
     ...: 

In [195]: add_them=np.frompyfunc(add_element,2,1)

In [196]: add_them(np.arange(8),np.arange(8))
Out[196]: array([0, 2, 4, 6, 8, 10, 12, 14], dtype=object)

#vectorize返回一个数组对象
In [197]: add_them2=np.vectorize(add_element,otypes=[np.float64])

In [198]: add_them2(np.arange(8),np.arange(8))
Out[198]: array([ 0.,  2.,  4.,  6.,  8., 10., 12., 14.])

结构化和记录式数组

ndarray是一种同质数据容器,也就是说它所表示的内存块中,各元素所占用的字节数相同(具体数根据dtype而定),

结构化数组是一种特殊的ndarray,其中各个元素可以被看作C语言中的结构体(struct,这就是‘结构化'的由来)或SQL表中带有多个命名字段的行

跟pandas的DataFrame相比,结构化数组是一种相对较低级的工具,它可以将单个内存块解释为代有任意复杂嵌套列的表格型结构,由于数组中的每个元素在内存中都被表示为固定的字节数,所以结构化数组能够提供翡翠快速高效的的磁盘数据读写、网络传输等功能

结构化数组的另一个用法是将数据文件写成定长记录字节流,只要知道文件的格式(记录的大小、元素的顺序、字节数以及数据类型),就可以用np.fromfile将数据读入内存---了解即可

numpy.lib.recfunctions模块可用于操作结构化数组--了解即可

In [199]: dtype=[('x',np.float64),('y',np.int32)]

In [200]: sarr=np.array([(1.5,6),(np.pi,-2)],dtype=dtype)#np.pi 圆周率

In [201]: sarr
Out[201]: 
array([(1.5       ,  6), (3.14159265, -2)],
      dtype=[('x', ' in ()
----> 1 sarr[2]

IndexError: index 2 is out of bounds for axis 0 with size 2

In [205]: sarr[0]['x']
Out[205]: 1.5

In [206]: sarr[0]['y']
Out[206]: 6

In [207]: sarr['y']
Out[207]: array([ 6, -2], dtype=int32)
#字段名保存在dtype.name属性中,在访问结构化数组在的某个字段时,返回的是该数据的视图,所以不会发生数据复制

嵌套dtype和多维字段

在定义结构化dtype时,可以再设置一个形状(可以是个整数,也可以是一个元组)

In [208]: dtype=[('x',np.float64,3),('',np.int32)]

In [209]: arr=np.zeros(4,dtype=dtype)

In [210]: arr
Out[210]: 
array([([0., 0., 0.], 0), ([0., 0., 0.], 0), ([0., 0., 0.], 0),
       ([0., 0., 0.], 0)], dtype=[('x', '

这使我们能够用单个数组的内存块存放复杂的数据结构,既然dtype可以想怎么复杂就怎么复杂,我们可以试试嵌套dtype,与此相比,pandas的分层索引机制跟这个差不多

In [214]: dtype=[('x',[('a','f8'),('b','f4')]),('y',np.int32)]

In [215]: data=np.array([((1,2),5),((3,4),6)],dtype=dtype)

In [216]: data['x']
Out[216]: array([(1., 2.), (3., 4.)], dtype=[('a', '

排序

跟python内置的列表一样,ndarray的sort实例方法也是就地排序,即,数组内容的重新排序是在源数组上排序的,不会产生新数组的,如果排序的目标数组只是一个视图,则源数组将会被修改

In [219]: arr=np.random.randn(6)

In [220]: arr
Out[220]: 
array([ 0.44345783, -0.67660014,  0.13668712, -0.04890943, -0.83586259,
        0.61457904])

In [221]: arr.sort()

In [222]: arr
Out[222]: 
array([-0.83586259, -0.67660014, -0.04890943,  0.13668712,  0.44345783,
        0.61457904])
#切片排序也会影响源数组
In [226]: arr=np.random.randn(6)

In [227]: arr
Out[227]: 
array([ 1.3510239 , -0.42457162,  0.70866059, -0.07718412,  1.0291165 ,
        1.26459721])

In [228]: arr[:5].sort()

In [229]: arr
Out[229]: 
array([-0.42457162, -0.07718412,  0.70866059,  1.0291165 ,  1.3510239 ,
        1.26459721])

numpy.sort会为源数组创建一个已排序副本,它接收一个数组作为参数

In [230]: arr=np.random.randn(6)

In [231]: arr
Out[231]: 
array([-0.58542925,  1.35982495, -0.80136903, -0.22464326, -0.48581381,
       -0.98496352])

In [232]: np.sort(arr)
Out[232]: 
array([-0.98496352, -0.80136903, -0.58542925, -0.48581381, -0.22464326,
        1.35982495])

ndarray.sort和np.sort(ndarray)两个排序方法都可以接受一个axis参数,以便沿指定轴向对各块数据进行单独排序

In [233]: arr=np.random.randn(3,5)

In [234]: arr
Out[234]: 
array([[-1.01999619,  0.73470564, -0.38008688, -1.07978726,  1.5174322 ],
       [-0.45355041,  0.07185554, -1.97314749,  0.00603521,  0.05596924],
       [-1.63445938, -0.49933928, -1.11881907,  0.03452264,  0.97180303]])

In [235]: arr.sort(axis=1)

In [236]: arr
Out[236]: 
array([[-1.07978726, -1.01999619, -0.38008688,  0.73470564,  1.5174322 ],
       [-1.97314749, -0.45355041,  0.00603521,  0.05596924,  0.07185554],
       [-1.63445938, -1.11881907, -0.49933928,  0.03452264,  0.97180303]])

In [237]: arr.sort(axis=0)

In [238]: arr
Out[238]: 
array([[-1.97314749, -1.11881907, -0.49933928,  0.03452264,  0.07185554],
       [-1.63445938, -1.01999619, -0.38008688,  0.05596924,  0.97180303],
       [-1.07978726, -0.45355041,  0.00603521,  0.73470564,  1.5174322 ]])

ndarray.sort()和np.sort(ndarray)两种方法都不支持降序排列,需要在排序后使用[::-1]返回一个反序的列表

In [239]: arr=np.arange(6)

In [240]: arr
Out[240]: array([0, 1, 2, 3, 4, 5])

In [241]: arr[::-1]
Out[241]: array([5, 4, 3, 2, 1, 0])

间接排序:argsort和lexsort

在数据分析工作中,常常需要根据一个或多个键对数据集进行排序,例如,一个有关学生信息的数据表可能需要以姓和名进行排序,这就是间接排序

给定一个或多个键,可以得到一个由整数组成的索引数组(称之为索引器),其中索引值说明了数据在新顺序下的位置

argsort和lexsort就是实现该功能的两个主要方法,pandas对象Series和DateFrame的sortindex以及Series的order方法就是通过这些函数的变体实现的(pandas对象需要考虑缺失值)

In [242]: values=np.array([5,0,1,3,2])

In [243]: indexer=values.argsort()

In [244]: indexer
Out[244]: array([1, 2, 4, 3, 0])

In [245]: values[indexer]
Out[245]: array([0, 1, 2, 3, 5])
#根据数组的第一行对其进行排序
In [246]: arr=np.random.randn(3,5)

In [247]: arr[0]=values

In [248]: arr
Out[248]: 
array([[ 5.        ,  0.        ,  1.        ,  3.        ,  2.        ],
       [-0.62471185,  0.25591603,  0.28292862, -0.02259515, -2.22019292],
       [-1.29263396,  0.61927654,  0.66283367,  0.17198383,  0.13491433]])

In [250]: arr[:,arr[0].argsort()]
Out[250]: 
array([[ 0.        ,  1.        ,  2.        ,  3.        ,  5.        ],
       [ 0.25591603,  0.28292862, -2.22019292, -0.02259515, -0.62471185],
       [ 0.61927654,  0.66283367,  0.13491433,  0.17198383, -1.29263396]])
#lexsorth和argsort差不多,只不过它可以一次性对多个键数组执行间接排序(字典序)
In [251]: first_name=np.array(['Bob','Jane','Steve','Bill','Barbara'])
In [253]: last_name=np.array(['Jones','Arnold','Arnold','Jones','Walters'])

In [255]: sorter=np.lexsort((first_name,last_name))

In [257]: zip(last_name[sorter],first_name[sorter])
Out[257]: 

In [258]: result=zip(last_name[sorter],first_name[sorter])

In [259]: for i in result:
     ...:     print(i)
     ...:     
('Arnold', 'Jane')
('Arnold', 'Steve')
('Jones', 'Bill')
('Jones', 'Bob')
('Walters', 'Barbara')
#因为键的应用化工顺序是从最后一个传入算起的,last_name是先于first_name被应用的

在有序数组中查找元素:searchsorted方法和digitize函数

searchsorted是一个在有序数组上执行二分查找的数组方法,只要将值插入到它返回的那个位置就能维持数组的有序性

In [260]: arr=np.array([0,1,3,4,6,8,9])

In [261]: arr.searchsorted(7)
Out[261]: 5
#传入一组值就能得到一组索引
In [262]: arr.searchsorted([0,2,3,8])
Out[262]: array([0, 2, 2, 5])
#默认返回相等值组的左侧索引,所以0返回的是0,可设置参数side='right'返回右侧的索引
In [264]: arr.searchsorted([0,2,3,8],side='right')
Out[264]: array([1, 2, 3, 6])

searchsorted的另一个用法

#假设我们有一个数据数组,还有一个表示'面元边界'的数组
In [265]: data=np.floor(np.random.uniform(0,10000,size=50))

In [266]: bins=np.array([0,100,1000,5000,10000])#面元边界

In [267]: data
Out[267]: 
array([6618.,  352., 9113.,  548., 4637., 5329., 2652., 4140., 7872.,
       9155., 8363., 8999., 4795., 6819., 7908., 9426., 9311., 1404.,
       1714., 9879., 9805., 2066.,  664., 4675.,  517., 5033.,  489.,
       7429., 8766., 3503., 8062., 8565., 8018., 1598.,  949., 7391.,
       4964., 7647., 7935., 7558., 5278., 6102., 2964., 9117., 9239.,
       7934., 4382., 4984., 2188., 1298.])
#为了得到各数据点所属区间的编号(其中1表示面元[0,100],以此类推)
In [268]: labels=bins.searchsorted(data)

In [269]: labels
Out[269]: 
array([4, 2, 4, 2, 3, 4, 3, 3, 4, 4, 4, 4, 3, 4, 4, 4, 4, 3, 3, 4, 4, 3,
       2, 3, 2, 4, 2, 4, 4, 3, 4, 4, 4, 3, 2, 4, 3, 4, 4, 4, 4, 4, 3, 4,
       4, 4, 3, 3, 3, 3])
#通过pandas的groupby技术可以对源数据集进行拆分
In [271]: pd.Series(data).groupby(labels)
Out[271]: 

In [272]: pd.Series(data).groupby(labels).mean()
Out[272]: 
2     586.500000
3    3247.750000
4    7952.535714
dtype: float64
#Numpy的digitize函数也可用于计算面元编号,
In [273]: np.digitize(data,bins)
Out[273]: 
array([4, 2, 4, 2, 3, 4, 3, 3, 4, 4, 4, 4, 3, 4, 4, 4, 4, 3, 3, 4, 4, 3,
       2, 3, 2, 4, 2, 4, 4, 3, 4, 4, 4, 3, 2, 4, 3, 4, 4, 4, 4, 4, 3, 4,
       4, 4, 3, 3, 3, 3])

numpy的matrix类

从二维数组中选区一行[1,:]或一列[;,1]将会产生一个一维数组,这样不利于矩阵运算,numpy提供了一个matrix类,其单行或列会以二维形式返回,且使用星号(*)的乘法直接就是矩阵乘法

不建议用numpy.matrix替代正规的ndarray,因为他们的应用面较窄,对于拥有大量线性代数运算的函数,可以考虑

In [274]: arr=np.arange(12).reshape((2,6))
#返回单行或单列返回一维数组
In [275]: arr
Out[275]: 
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11]])

In [276]: arr[:,1]
Out[276]: array([1, 7])

In [278]: arr[1,:]
Out[278]: array([ 6,  7,  8,  9, 10, 11])
#matrix类返回二维数组
In [279]: arr_m=np.matrix(arr)

In [280]: arr_m[:,1]
Out[280]: 
matrix([[1],
        [7]])

In [281]: arr_m[1,:]
Out[281]: matrix([[ 6,  7,  8,  9, 10, 11]])

matrix还有一个特殊属性I,其功能是返回矩阵的逆

In [282]: arr=np.arange(12).reshape((2,6))

In [283]: arr_m=np.matrix(arr)

In [284]: arr_m.I
Out[284]: 
matrix([[-0.23015873,  0.08730159],
        [-0.14920635,  0.06349206],
        [-0.06825397,  0.03968254],
        [ 0.01269841,  0.01587302],
        [ 0.09365079, -0.00793651],
        [ 0.17460317, -0.03174603]])

内存映像文件

内存映像,功能是能够处理在内存中放不下的数据集

内存映像文件是一种将磁盘上的非常大的二进制数据文件当作内存中的数组进行处理的方式,numpy实现了一个类似于ndarray的memmap对象,它允许将大文件分成小段进行读写,而不是一次性将整个数组读入内存。memmap也拥有和数组一样的方法,基本上只要是能用于ndarray的算法,也能用于memmap

使用函数np.memmap并传入文件路径、数据类型、形状以及文件模式,即可创建一个memmap

In [285]: mmap=np.memmap('mymmap',dtype='float64',mode='w+',shape=(10000,10000))
     ...: 

In [286]: mmap
Out[286]: 
memmap([[0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        ...,
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.]])
#对memmap切片会返回磁盘上的数据的视图,如果将数据赋值给这些视图,数据会显缓存在内存中(就向python的文件对象),调用flush即可将其写入磁盘

In [287]: mmap[:5]
Out[287]: 
memmap([[0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.]])

In [288]: section=mmap[:5]

In [289]: section[:]=np.random.randn(5,10000)

In [290]: mmap.flush()

In [291]: mmap
Out[291]: 
memmap([[ 1.58111002,  1.90645244, -0.63471948, ..., -1.35606172,
          1.16922047,  0.3476418 ],
        [ 0.48324066,  0.63520092, -0.5317452 , ...,  0.69026956,
          0.35725548, -0.33512981],
        [ 0.43491278, -1.47961165, -0.17788593, ...,  2.36128756,
          0.93145793, -0.38050151],
        ...,
        [ 0.        ,  0.        ,  0.        , ...,  0.        ,
          0.        ,  0.        ],
        [ 0.        ,  0.        ,  0.        , ...,  0.        ,
          0.        ,  0.        ],
        [ 0.        ,  0.        ,  0.        , ...,  0.        ,
          0.        ,  0.        ]])

In [292]: del mmap  #删除内存映像

#只要某个内存映像超出了作用域,它就会被垃圾回收器回收,之前对其所作的任何修改都会被写入磁盘,当打开一个已经存在的内存映像时,仍然需要指明数据类型和形状,因为磁盘上的那个文件只是一块二进制数据而已,没有任何元数据
In [293]: mmap=np.memmap('mymmap',dtype='float64',shape=(10000,10000))

In [294]: mmap
Out[294]: 
memmap([[ 1.58111002,  1.90645244, -0.63471948, ..., -1.35606172,
          1.16922047,  0.3476418 ],
        [ 0.48324066,  0.63520092, -0.5317452 , ...,  0.69026956,
          0.35725548, -0.33512981],
        [ 0.43491278, -1.47961165, -0.17788593, ...,  2.36128756,
          0.93145793, -0.38050151],
        ...,
        [ 0.        ,  0.        ,  0.        , ...,  0.        ,
          0.        ,  0.        ],
        [ 0.        ,  0.        ,  0.        , ...,  0.        ,
          0.        ,  0.        ],
        [ 0.        ,  0.        ,  0.        , ...,  0.        ,
          0.        ,  0.        ]])

性能建议

使用numpy的数组运算一般都比纯python循环快得多,大致注意以下事项:

将python循环和条件逻辑转换为数组运算和布尔数组运算

尽量使用广播

避免复制数据,尽量使用数组视图(即切片)

利用ufunc及其各种方法

Cpython和numpy

可以将Cpython看作带有静态类型并能嵌入C函数的python,Cpython实现的代码运行速度很快,下面是简单的Cpython函数用于对一个和一维数组求和

from numpy cimport ndarray,float64_t
def sum_element(ndarray[float64_t] arr):
    cdef py_ssize_t i,n=len(arr)
    cdef float64_t result=0
    for i in range(n):
        result+=arr[i]
    return result

#Cpython处理这段代码时,先将其翻译为C代码,然后编译这些C代码并创建一个python扩展,一般的工作流程是:得到能在python中运行的算法,然后再将其翻译为Cpython(只需添加类型定义并完成一些其他必要的工作即可--只需你妹!!)

 

 

二 pandas模块

1.pandas的数据结构:

Series

Series 是一维标签数组(Data must be 1-dimension),能够保存任何数据类型(整型,浮点型,字符串或其他Python对象类型)。轴标签被称为索引

>>> import numpy as np
>>> import pandas as pd
>>> pd.Series(np.random.randn(12))#Series函数用于创建Series
0     0.445457
1     1.410470
2    -0.669725
3    -0.907310
4     0.081393
5     0.034115
6     1.219609
7     0.135690
8    -0.353035
9     0.640904
10    0.218566
11    0.836471
dtype: float64
>>> pd.Series(np.random.randn(12).reshape(3,4))#只能传如一维数组,否则会报错
Traceback (most recent call last):
  File "", line 1, in 
  File "/home/zelin/.local/lib/python3.6/site-packages/pandas/core/series.py", line 275, in __init__
    raise_cast_failure=True)
  File "/home/zelin/.local/lib/python3.6/site-packages/pandas/core/series.py", line 4165, in _sanitize_array
    raise Exception('Data must be 1-dimensional')
Exception: Data must be 1-dimensional

Series的字符串表现形式为:索引在左边,值在右边,如果没有为数据指定索引,Series会自动创建一个0到N-1(N为数据长度)的整数型索引

values、index和name

Series的values属性和index属性为数组类型,Series调用values或index后返回一个带由值或索引的数组

Series对象本身和其索引都有一个name属性,该功能和pandas的其他关键功能关系非常密切(需要设置,默认为空)

In [192]: obj=pd.Series([111,222,333,444,555])

In [193]: obj
Out[193]: 
0    111
1    222
2    333
3    444
4    555
dtype: int64

In [194]: obj.index
Out[194]: RangeIndex(start=0, stop=5, step=1)

In [195]: obj.values
Out[195]: array([111, 222, 333, 444, 555])

创建Series索引

Series的索引/切片形式同python的数据类型列表字典类似,可以将Series看成是一个定长的有序字典,因为它是索引值到数据值的一个映射,同样,Series也可以进行索引赋值就地修改

In [197]: ser=pd.Series([111,222,333],index=['a','b','c'])

In [198]: ser
Out[198]: 
a    111
b    222
c    333
dtype: int64

可以传入字典来创建一个Series,如果只传字典一个参数,则字典的键作为Series的索引,字典的值作为Series的值

如果传入字典参数后,另外指定索引参数,则索引如果与字典的键不匹配,则以索引产生一个NaN值,如果索引与字典的键匹配,则产生的值等于字典的值,即字典的键不产生作用

In [199]: pd.Series({'a':111,'b':222})
Out[199]: 
a    111
b    222
dtype: int64

In [200]: 

In [200]: pd.Series({'a':111,'b':222},index=['ccc','ddd'])
Out[200]: 
ccc   NaN
ddd   NaN
dtype: float64

Series最重要的一个功能是,它在算术运算中会自动对其不同索引的数据,即两个Series的相同索引进行运算,不同索引产生NaN值

In [201]: ser1=pd.Series({'a':1,'b':2,'c':3})

In [202]: ser2=pd.Series({'c':3,'d':4,'e':5})

In [203]: ser1+ser2
Out[203]: 
a    NaN
b    NaN
c    6.0
d    NaN
e    NaN
dtype: float64

isnull和notnull

判断Series数据是否为NaN值

In [212]: ser=pd.Series({'a':1,'b':2,'c':3,'d':np.nan})

In [213]: pd.isnull(ser)
Out[213]: 
a    False
b    False
c    False
d     True
dtype: bool

In [214]: pd.notnull(ser)
Out[214]: 
a     True
b     True
c     True
d    False
dtype: bool

 

 

DataFrame

DataFrame是一个2维标签的数据结构,是一种表格型数据结构,它的列可以存在不同的类型,你可以把它简单的想成Excel表格或SQL Table,或者是包含字典类型的Series。它是最常用的Pandas对象。和Series一样,DataFrame接受许多不同的类型输入:

    包含1维ndarray,列表对象,字典对象或者Series对象的字典对象
    2维的ndarray对象
    结构化或记录型的ndarray
    Series对象
    另一个DataFrame对象

#np.random.randn(12)产生一个服从正态分布的12个数字的一维数组,reshape(3,4)将数组重构为3行4列的二维数组
>>> pd.DataFrame(np.random.randn(12).reshape(3,4))#DataFrame函数用于创建DataFrame
          0         1         2         3
0 -0.862802  1.176531 -0.009699 -0.455003
1  1.051637 -0.199708  0.402293  0.014919
2  0.588871  0.059458  1.911373  2.224995

创建DataFrame

创建DataFrame的方法有很多很多,最常用的一种是直接传入一个由长列表或数组组成的数组

data_dict={'states':['oooo','ssss','dddd'],'age':[11,12,12]}
In [3]: df=pd.DataFrame(data_dict,index
   ...: =['aaa','bbb','ccc'])

In [4]: df
Out[4]: 
    states  age
aaa   oooo   11
bbb   ssss   12
ccc   dddd   12
#如果制定类列的序列,则会根据序列排序
In [8]: df=pd.DataFrame(data_dict,index=['aaa','bbb','ccc'],columns=['age','stat
   ...: es'])

In [9]: df
Out[9]: 
     age states
aaa   11   oooo
bbb   12   ssss
ccc   12   dddd

#如果传入的列序列在数据中找不到,则会产生NaN值
In [10]: df=pd.DataFrame(data_dict,index=['aaa','bbb','ccc'],columns=['age','sta
    ...: tes','NAN'])
In [11]: df
Out[11]: 
     age states  NAN
aaa   11   oooo  NaN
bbb   12   ssss  NaN
ccc   12   dddd  NaN

索引切片

通过类似字典标记的方法,可以将DataFrame的列转化为一个Series,返回的Series拥有原DataFrame相同的索引,且其name属性也已经被相应地设置好了

In [10]: df=pd.DataFrame(data_dict,index=['aaa','bbb','ccc'],columns=['age','sta
    ...: tes','NAN'])

In [11]: df
Out[11]: 
     age states  NAN
aaa   11   oooo  NaN
bbb   12   ssss  NaN
ccc   12   dddd  NaN

In [12]: df['age']
Out[12]: 
aaa    11
bbb    12
ccc    12
Name: age, dtype: int64

行也可以通过位置或者名称的方式进行获取,比如用索引字段ix

In [15]: df.ix['aaa']
Out[15]: 
age         11
states    oooo
NAN        NaN
Name: aaa, dtype: object

列可以通过赋值的方式进行修改(整列发生改变),若赋值的列名不存在,则会新增一列

In [16]: df['age']=100

In [17]: df
Out[17]: 
     age states  NAN
aaa  100   oooo  NaN
bbb  100   ssss  NaN
ccc  100   dddd  NaN

In [18]: df['num']=200

In [19]: df
Out[19]: 
     age states  NAN  num
aaa  100   oooo  NaN  200
bbb  100   ssss  NaN  200
ccc  100   dddd  NaN  200

通过索引方式返回的列只是源数据的视图,而不是副本,所以对返回的列进行修改,也会反映到源数据,修改列时可能会由警告,keying忽略不计

In [19]: df
Out[19]: 
     age states  NAN  num
aaa  100   oooo  NaN  200
bbb  100   ssss  NaN  200
ccc  100   dddd  NaN  200

In [20]: sr=df['age']

In [21]: sr['aaa']=3000
/home/zelin/anaconda3/bin/ipython:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  #!/home/zelin/anaconda3/bin/python

In [22]: sr
Out[22]: 
aaa    3000
bbb     100
ccc     100
Name: age, dtype: int64

In [23]: df
Out[23]: 
      age states  NAN  num
aaa  3000   oooo  NaN  200
bbb   100   ssss  NaN  200
ccc   100   dddd  NaN  200

如果给DataFrame传入一个嵌套字典,即字典的值也是字典,它就会被解释成:外层字典的键作为列,内层字典的键作为行

In [25]: df=pd.DataFrame({'ooo':{1:1,2:2,3:3},'ppp':{11:11,22:22,33:33,44:44}})

In [27]: df
Out[27]: 
    ooo   ppp
1   1.0   NaN
2   2.0   NaN
3   3.0   NaN
11  NaN  11.0
22  NaN  22.0
33  NaN  33.0
44  NaN  44.0

对DataFrame进行转置,内层字典的键会被合并、排序以形成最终的索引,如果显式指定列索引,则不会这样

In [32]: df2=pd.DataFrame(df,index=(1,2,3,11,33))

In [33]: df2
Out[33]: 
    ooo   ppp
1   1.0   NaN
2   2.0   NaN
3   3.0   NaN
11  NaN  11.0
33  NaN  33.0

可以输入给DataFrame构造器的数据

类型 说明
二维ndarray 数据矩阵,还可以传入行标和列标
由数组、列表、元组组成的字典 每个序列会变成DataFrame的一列,所有序列的长度必须相同
由Series组成的字典 每个Series会成一列。如果没有显式指定索引,则各Series的索引会被合并成结果的行索引
由字典组成的字典 各内层字典会成为一列,键会被合并成结果的行索引,跟‘由Series组成的字典’的情况一样
字典或Series的列表 各项会成为DataFrame的一行,字典键或Series的索引的并集会成为DataFrame的列标
由列表或元组组成的列表 类似二维ndarray
另一个DataFrame 该DataFrame的索引将会被沿用,除非显式指定了其他索引
numpy的maskedArray 类似二维ndarray的情况,只是掩码值在结果DataFrame会变成NA缺失值
   

如果设置了index和columns的name属性,则这些信息会被显式出来

In [34]: df=pd.DataFrame({'ooo':{1:1,2:2,3:3},'ppp':{11:11,22:22,33:33,44:44}})

In [35]: df.index.name='year'

In [36]: df.columns.name='state'

In [37]: df
Out[37]: 
state  ooo   ppp
year            
1      1.0   NaN
2      2.0   NaN
3      3.0   NaN
11     NaN  11.0
22     NaN  22.0
33     NaN  33.0
44     NaN  44.0

跟Series一样,values属性也会以二维ndarray的形式返回DataFrame中的数据

In [38]: df.values
Out[38]: 
array([[ 1., nan],
       [ 2., nan],
       [ 3., nan],
       [nan, 11.],
       [nan, 22.],
       [nan, 33.],
       [nan, 44.]])

如果DataFrame各列的数据类型不同,则值数组的数据类型就会选用能兼容所有列的数据类型、

In [48]: df=pd.DataFrame({'a':1,'b':'b','c':3.44},index=['a','b','c'])

In [49]: df.values
Out[49]: 
array([[1, 'b', 3.44],
       [1, 'b', 3.44],
       [1, 'b', 3.44]], dtype=object)

如果字典的键的值只有一个,DateFrame的行数,会根据显式指定的index的长度而变化,值重复

In [39]: df=pd.DataFrame({'a':1,'b':'b','c':3.44},index=['a'])

In [40]: df
Out[40]: 
   a  b     c
a  1  b  3.44

In [41]: df=pd.DataFrame({'a':1,'b':'b','c':3.44},index=['a','b'])

In [42]: df
Out[42]: 
   a  b     c
a  1  b  3.44
b  1  b  3.44

In [43]: df=pd.DataFrame({'a':1,'b':'b','c':3.44},index=['a','b','c'])

In [44]: df
Out[44]: 
   a  b     c
a  1  b  3.44
b  1  b  3.44
c  1  b  3.44

索引对象

pandas的索引对象负责管理轴标签和其他元数据(比如轴名称等),构建Series或DataFrame时,所用到的任何数组或者其他序列都会被转化成一个index,Index对象是不可修改的(immutable),因此用户不能对其进行修改(--否则会报错)

In [50]: df=pd.DataFrame({'a':1,'b':'b','c':3.44},index=['a','b','c'])

In [51]: df
Out[51]: 
   a  b     c
a  1  b  3.44
b  1  b  3.44
c  1  b  3.44

In [52]: index=df.index

In [53]: index
Out[53]: Index(['a', 'b', 'c'], dtype='object')

In [54]: index[1]
Out[54]: 'b'

In [55]: index[1]='a'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
 in ()
----> 1 index[1]='a'

~/anaconda3/lib/python3.7/site-packages/pandas/core/indexes/base.py in __setitem__(self, key, value)
   2063 
   2064     def __setitem__(self, key, value):
-> 2065         raise TypeError("Index does not support mutable operations")
   2066 
   2067     def __getitem__(self, key):

TypeError: Index does not support mutable operations

不可修改性非常重要,因为这样才能使Index对象在多个数据结构之间安全共享,Index可以被集成从而实现特别的轴索引功能

In [57]: index=pd.Index(np.arange(3))

In [58]: obj2=pd.Series([111,222,333],index=index)

In [59]: obj2.index is index
Out[59]: True

Index长的像数组,功能也类似一个固定大小的集合

In [58]: obj2=pd.Series([111,222,333],index=index)

In [59]: obj2.index is index
Out[59]: True

In [60]: 1 in obj2.index
Out[60]: True

In [61]: 222 in obj2.index
Out[61]: False

每个索引都有一些方法和属性,他们可用于设置逻辑并回答有关该索引所包含的数据的常见问题

方法 说明
append

链接另一个Index对象,产生一个新的Index对象

diff 计算差集,并得到一个Index
intersection 计算交集
union 计算并集
isin 计算一个指示各值是否都包含在参数集合种的布尔型数组
delete 删除索引i处的元素,并得到一个新的Index
drop 删除传入的值,并得到新的Index
insert 将元素插入索引i处,并得到一个新的Index
is_monotonic 当各元素均大于等于前一个值时,返回True
is_unique 当Index没有重复值时,返回True
unique 计算Index中唯一值的数组

Series和DataFrame的基本功能

重新索引reindex,其作用是创建一个适应新索引的新对象,调用Series的reindex将会根据新索引重排,如果某个索引值当前不存在,就引入缺失值,reindex的参数fill_value用以替换数据内的缺失值

In [62]: obj=pd.Series([1,2,3,4],index=['d','b','a','c'])


In [64]: obj
Out[64]: 
d    1
b    2
a    3
c    4
dtype: int64

In [65]: obj.reindex(['a','b','c','d','e'])#重新排序,及某个索引值当前不存在,则引入缺失值
Out[65]: 
a    3.0
b    2.0
c    4.0
d    1.0
e    NaN
dtype: float64

In [66]: obj.reindex(['a','b','c','d','e','f'],fill_value=1.1)#fill_value替换缺失值
Out[66]: 
a    3.0
b    2.0
c    4.0
d    1.0
e    1.1
f    1.1
dtype: float64

如果像时间序列这样的有序数据,重新索引时可能需要做一些插值处理,method选项即可达到此目的,例如使用method=ffill可以实现向前填充(即复制排序前位的值),以及method=bfill向后填充(复制排序后位的值)

In [67]: obj=pd.Series(['blue','yellow','orange'],index=[0,2,4])

In [68]: obj.reindex(np.arange(6))
Out[68]: 
0      blue
1       NaN
2    yellow
3       NaN
4    orange
5       NaN
dtype: object

In [69]: obj.reindex(np.arange(6),method='ffill')
Out[69]: 
0      blue
1      blue
2    yellow
3    yellow
4    orange
5    orange
dtype: object

In [70]: obj=pd.Series(['blue','yellow','orange'],index=[0,2,4])

In [71]: obj.reindex(np.arange(6),method='bfill')#后面没有数据,则仍然为NaN值
Out[71]: 
0      blue
1    yellow
2    yellow
3    orange
4    orange
5       NaN
dtype: object

重新索引行和列或者都修改,传入单个序列默认修改行索引,修改列标需要传参给columns参数,但插值只能按行应用

In [73]: df=pd.DataFrame(np.arange(9).reshape(3,3),index=['a','b','c'],columns=[
    ...: 'va','vb','vc'])

In [74]: df
Out[74]: 
   va  vb  vc
a   0   1   2
b   3   4   5
c   6   7   8

In [75]: df.reindex(['a','b','c','d'])
Out[75]: 
    va   vb   vc
a  0.0  1.0  2.0
b  3.0  4.0  5.0
c  6.0  7.0  8.0
d  NaN  NaN  NaN

In [76]: df.reindex(columns=['vvva','vvvb','vvvc'])
Out[76]: 
   vvva  vvvb  vvvc
a   NaN   NaN   NaN
b   NaN   NaN   NaN
c   NaN   NaN   NaN

利用ix的标签索引功能,重新索引更简洁

In [77]: df=pd.DataFrame(np.arange(9).reshape(3,3),index=['a','b','c'],columns=[
    ...: 'va','vb','vc'])

In [78]: df
Out[78]: 
   va  vb  vc
a   0   1   2
b   3   4   5
c   6   7   8

In [79]: df.ix[['a','b','c','d'],['vva','vvb','vc']]

Out[79]: 
   vva  vvb   vc
a  NaN  NaN  2.0
b  NaN  NaN  5.0
c  NaN  NaN  8.0
d  NaN  NaN  NaN

reindex函数的参数

参数 说明
index 用作索引的新序列,既可以是Index实例,也可以是其他序列型的python数据结构,Index会被完全使用,就像没有任何复制一样
method 插值(填充)方式
fill_value 在重新索引过程种,需要引入缺失值使用的替代值
limit 前向或后向填充时的最大填充量
level 在Multilndex在指定级别上匹配简单索引,否则选取其子集
copy 默认为True,无论如何都复制,如果为False,则新旧相等就不复制

 

删除指定轴上的项

drop方法

丢弃某条轴上的一个或多个项,只要有一个索引数组 或者列表即可,由于需要执行一些数据整理和集合逻辑,所以drop方法返回的是一个在指定轴上删除了指定值的新对象

In [3]: ser=pd.Series(np.arange(5.),index=['a','b','c','d','e'])

In [4]: ser
Out[4]: 
a    0.0
b    1.0
c    2.0
d    3.0
e    4.0
dtype: float64

In [5]: ser.drop('c')#获取到的是新对象,而不是删除源数据
Out[5]: 
a    0.0
b    1.0
d    3.0
e    4.0
dtype: float64

In [6]: ser
Out[6]: 
a    0.0
b    1.0
c    2.0
d    3.0
e    4.0
dtype: float64

索引、选取和过滤

Series的索引的工作方式类似numpy数组的索引,只不过Series的索引不只是整数,而且与普通python的切片不同,其末端是包含的(即封闭区间),索引就是一个数据,切片就是切一片数据

In [12]: obj=pd.Series(np.arange(4.),index=('a','b','c','d'))

In [13]: obj
Out[13]: 
a    0.0
b    1.0
c    2.0
d    3.0
dtype: float64

In [14]: obj['a':'c']
Out[14]: 
a    0.0
b    1.0
c    2.0
dtype: float64

索引方式几种特殊的情况,通过切片或布尔型数组选取行

In [15]: obj=pd.Series(np.arange(4.),index=('a','b','c','d'))

In [16]: obj>2
Out[16]: 
a    False
b    False
c    False
d     True
dtype: bool

In [18]: obj[obj>2]
Out[18]: 
d    3.0
dtype: float64

索引字段ix

可以通过Numpy式的标记法以及轴标签从DataFrame中选取行和列的子集

In [31]: df=pd.DataFrame(np.arange(16).reshape(4,4),index=['o','p','q','r'],colu
    ...: mns=['one','two','three','four'])

In [37]: df
Out[37]: 
   one  two  three  four
o    0    1      2     3
p    4    5      6     7
q    8    9     10    11
r   12   13     14    15



In [32]: df.ix['o',['one','two']]#[行索引,列标]
Out[32]: 
one    0
two    1
Name: o, dtype: int64

In [34]: df.ix[['o','p'],['one','two']]#位置参数,无需指定

Out[34]: 
   one  two
o    0    1
p    4    5

In [36]: df.ix[df.one>3,:3]#one列大于3的所有数据

Out[36]: 
   one  two  three
p    4    5      6
q    8    9     10
r   12   13     14

DataFrame的索引选项

obj[val] 选取DataFrame的单个列或一组列,在一些特殊情况下会比较便利::布尔型数组(过滤行),切片(行切片),布尔型DataFrame(根据条件设置值)
obj.ix[val] 选取DataFrame的单个行或一组行
obj.ix[:,val] 选取单个列或列子集
obj.ix[[vla1,val2]] 同时选取行和列(参数1为行,参数2为列)
reindex方法 将一个或多个轴匹配到新索引
xs方法 根据标签选取单行或单列,并返回一个Series

icol、irow方法

根据整数位置选取单列或单行

get_value、set_value

根据行标签和列标签选取单个值

算术运算和数据对齐

pandas最重要的一个功能,它可以对不同索引的对象进行算术运算,在将对象相加时,如果存在不同的索引对,则该结果的索引对的并集(相同索引相加,不同索引并集,取空值NaN),自动的数据对齐操作在不重叠的索引处引入列NA值,缺失值会在算术运算种传播

In [39]: s1=pd.Series([1,2,3,4],index=['a','c','d','e'])

In [42]: s2=pd.Series([3,3,3,3,3],index=['a','c','e','f','g'])

In [43]: s1+s2
Out[43]: 
a    4.0
c    5.0
d    NaN
e    7.0
f    NaN
g    NaN
dtype: float64

DataFrame的对齐操作

对于DataFrame,对齐操作会同时发生在行和列上,把他们相加后会返回一个新的DataFrame,其索引和列标为原来两个DataFrame的并集(索引和列标不全相同,则为NaN,全相等则相加)

In [45]: df1=pd.DataFrame(np.arange(9).reshape(3,3),columns=list('bcd'),index=li
    ...: st('xyz'))


In [48]: df2=pd.DataFrame(np.arange(12.).reshape(4,3),columns=list('dbe'),index=
    ...: list('xymn'))

In [49]: df1+df2
Out[49]: 
     b   c    d   e
m  NaN NaN  NaN NaN
n  NaN NaN  NaN NaN
x  1.0 NaN  2.0 NaN
y  7.0 NaN  8.0 NaN
z  NaN NaN  NaN NaN

在算术方法中的填充值

两个DataFrame相加,不重叠部分会产生空值NaN,使用add方法将两个DataFrame相加可以,以及一个fill_value参数,可以实现将不重叠部分填充值,然后再相加

In [11]: import numpy as np
 
In [12]: import pandas as pd

In [13]: df1=pd.DataFrame(np.arange(12).reshape((3,4)),columns=list('abcd'))

In [14]: df2=pd.DataFrame(np.arange(20).reshape((4,5)),columns=list('abcde'))

In [15]: df1+df2
Out[15]: 
      a     b     c     d   e
0   0.0   2.0   4.0   6.0 NaN
1   9.0  11.0  13.0  15.0 NaN
2  18.0  20.0  22.0  24.0 NaN
3   NaN   NaN   NaN   NaN NaN

In [16]: df1.add(df2)
Out[16]: 
      a     b     c     d   e
0   0.0   2.0   4.0   6.0 NaN
1   9.0  11.0  13.0  15.0 NaN
2  18.0  20.0  22.0  24.0 NaN
3   NaN   NaN   NaN   NaN NaN

In [17]: df1.add(df2,fill_value=0)#并非在结果中填充,而是将不重叠部分填充为0值
Out[17]: 
      a     b     c     d     e
0   0.0   2.0   4.0   6.0   4.0
1   9.0  11.0  13.0  15.0   9.0
2  18.0  20.0  22.0  24.0  14.0
3  15.0  16.0  17.0  18.0  19.0

与此类似,在对Series和DataFrame重新索引时,也可以指定一个填充值(reindex重新索引产生的是新数据,不会影响源数据,所以df1可以用两次)

In [20]: df1.reindex(list('abcde'),fill_value=0)
Out[20]: 
   a  b  c  d
a  0  0  0  0
b  0  0  0  0
c  0  0  0  0
d  0  0  0  0
e  0  0  0  0

In [21]: df1.reindex(columns=list('abcde'),fill_value=0)#重新索引产生的是新数据,不会影响源数据
Out[21]: 
   a  b   c   d  e
0  0  1   2   3  0
1  4  5   6   7  0
2  8  9  10  11  0

算术方法:加减乘除

df1.add(df2)
df1.sub(df2)
df1.mul(df2)
df1.div(df2)


 

 

 

 

DataFrame和Series之间的运算

跟numpy一样,DataFrame和Series之间的算术运算也是有明确规定的

广播:即DataFrame和Series算术运算,会没广播到全DataFrame都和Series都进行运算,而不仅仅是单列/行

DataFrame和Series之间的算术运算会将Series的索引匹配到DataFrame的列,然后沿着行一直向下广播,如果某个索引值在DataFrame的列或Series的索引种找不到,则参与运算的两个对象就会被重新索引引以形成并集

默认匹配列,沿着行传波

In [25]: df=pd.DataFrame(np.arange(12).reshape((4,3)),index=list('abcd'),columns
    ...: =list('opq'))

In [26]: df
Out[26]: 
   o   p   q
a  0   1   2
b  3   4   5
c  6   7   8
d  9  10  11

In [27]: ser=df.ix['a']#只能以行索引为参数,不然会报错

In [28]: ser
Out[28]: 
o    0
p    1
q    2
Name: a, dtype: int64

In [30]: df.add(ser)
Out[30]: 
   o   p   q
a  0   2   4
b  3   5   7
c  6   8  10
d  9  11  13

In [31]: df+ser
Out[31]: 
   o   p   q
a  0   2   4
b  3   5   7
c  6   8  10
d  9  11  13

如果你希望匹配到行且在列上广播,则必须使用算术运算方法

In [33]: df.add(ser,axis=0)#匹配行,即将Series的列标成为索引与DataFrame 并集
Out[33]: 
    o   p   q
a NaN NaN NaN
b NaN NaN NaN
c NaN NaN NaN
d NaN NaN NaN
o NaN NaN NaN
p NaN NaN NaN
q NaN NaN NaN

函数的应用和映射

Numpy的ufuncs(元素级数组方法)也可以用于操作pandas对象

另一个常见的操作是,将函数应用到由各列或行所形成的一维数组上,DataFrame的apply方法可实现此功能

apply,应用作为参数传入的函数/方法功能

In [10]: df=pd.DataFrame(np.arange(12).reshape((4,3)),columns=list('xyz'),index=
    ...: list('adcd'))

In [11]: f=lambda x: x.max()-x.min()

In [12]: df.apply(f)#使用作为参数传入的函数功能
Out[12]: 
x    9
y    9
z    9
dtype: int64

In [13]: df.apply(f,axis=1)
Out[13]: 
a    2
d    2
c    2
d    2
dtype: int64

applymap 函数应用

元素级的python函数也是可以用的,使用applymap即可

In [15]: df=pd.DataFrame(np.random.randn(4,3),columns=list('xyz'),index=list('ab
    ...: cd'))

In [16]: f=lambda x:'%.2f'%x

In [17]: df.applymap(f)
Out[17]: 
       x      y      z
a   0.33   1.56  -1.21
b   1.18   2.26  -2.17
c   0.59  -0.59  -0.26
d  -0.43  -0.41  -1.09

map,Series的应用函数

之所以用applymap,因为Series也有一个应用函数map方法

In [19]: df['x'].map(f)
Out[19]: 
a     0.33
b     1.18
c     0.59
d    -0.43
Name: x, dtype: object

排序和排名

sort_index,根据索引排序,值也会跟着索引移动,数据默认是按行升序排列,参数axis设置轴向,ascending设置升/降序

In [20]: df=pd.DataFrame(np.random.randn(4,3),columns=list('xyz'),index=list('cd
    ...: ab'))

In [21]: df
Out[21]: 
          x         y         z
c  0.574269  1.004828 -1.653797
d -1.153132 -2.733118  0.119374
a -1.616412 -1.232352 -0.053378
b  0.882328 -0.613165  2.238244

In [22]: df.sort_index()
Out[22]: 
          x         y         z
a -1.616412 -1.232352 -0.053378
b  0.882328 -0.613165  2.238244
c  0.574269  1.004828 -1.653797
d -1.153132 -2.733118  0.119374

sort_index如果需要根据某一列的值进行排序,将列名传参给by参数即可,数据结构单个列字符串,多个列是列表

In [28]: df
Out[28]: 
          x         y         z
c  0.574269  1.004828 -1.653797
d -1.153132 -2.733118  0.119374
a -1.616412 -1.232352 -0.053378
b  0.882328 -0.613165  2.238244


In [29]: df.sort_index(by='x')
/home/zelin/anaconda3/bin/ipython:1: FutureWarning: by argument to sort_index is deprecated, please use .sort_values(by=...)
  #!/home/zelin/anaconda3/bin/python
Out[29]: 
          x         y         z
a -1.616412 -1.232352 -0.053378
d -1.153132 -2.733118  0.119374
c  0.574269  1.004828 -1.653797
b  0.882328 -0.613165  2.238244

In [30]: df.sort_index(by=['x','z'])
/home/zelin/anaconda3/bin/ipython:1: FutureWarning: by argument to sort_index is deprecated, please use .sort_values(by=...)
  #!/home/zelin/anaconda3/bin/python
Out[30]: 
          x         y         z
a -1.616412 -1.232352 -0.053378
d -1.153132 -2.733118  0.119374
c  0.574269  1.004828 -1.653797
b  0.882328 -0.613165  2.238244

 

排名

排名和排序关系密切,且它会增设一个排名值(从1开始,一直到数组中有效数据的数量),它和numpy.argsort产生的间接排序索引差不多,只不过它可以破坏平级关系

rank

pandas对象的rank方法,为各组分配一个平均排名的方式破坏平级关系

In [32]: ser=pd.Series([7,-5,7,4,2,0,4])

In [33]: ser
Out[33]: 
0    7
1   -5
2    7
3    4
4    2
5    0
6    4
dtype: int64

In [34]: ser.rank()
Out[34]: 
0    6.5
1    1.0
2    6.5
3    4.5
4    3.0
5    2.0
6    4.5
dtype: float64

DataFrame可以在行或列上进行排名

In [37]: df.rank()
Out[37]: 
     a    b
0  3.0  1.0
1  2.0  3.0
2  1.0  2.0
3  4.0  4.0

In [38]: df.rank(axis=1)
Out[38]: 
     a    b
0  1.0  2.0
1  1.0  2.0
2  1.0  2.0
3  1.0  2.0

rank的破坏平级method选项

method 说明
average 默认:在相等分组中,为各个值分配平均排名
min 使用整个分组的最小排名
max 使用整个分组的最大排名
first 按值在原始数据种的出现顺序分配排名

 

带由重复值的轴索引

某个索引值对应多个值,则返回一个Series,而对应单个值的,则返回一个标量值

In [39]: ser=pd.Series([7,-5,7,4,2,0,4],index=['a','b','a','b','c','a','d'])

In [40]: ser['a']
Out[40]: 
a    7
a    7
a    0
dtype: int64

In [42]: ser['d']
Out[42]: 4

 

汇总和计算描述统计

pandas对象拥有一组常用的数学和统计方法,大部分属于约简和汇总统计

用于从Series中提取单个值(sum/mean等)或从DataFrame的行或列取一个Series,跟对应的Numpy数组方法相比,他们都是基于没有缺失数据的假设而构建的

In [44]: df=pd.DataFrame([[1.4,np.nan],[7.1,-4.5],[np.nan,np.nan],[0.75,-1.3]],i
    ...: ndex=list('abcd'),columns=['one','two'])

In [45]: df
Out[45]: 
    one  two
a  1.40  NaN
b  7.10 -4.5
c   NaN  NaN
d  0.75 -1.3

In [46]: df.sum()
Out[46]: 
one    9.25
two   -5.80
dtype: float64

In [47]: df.sum(axis=1)#默认等于0列相加,axis=1行相加
Out[47]: 
a    1.40
b    2.60
c    0.00
d   -0.55
dtype: float64

In [48]: df.sum(axis=1,skipna=False)#默认排除NaN值,所以全NaN值则为0
Out[48]: 
a     NaN
b    2.60
c     NaN
d   -0.55
dtype: float64

约简方法的选项和方法

axis DataFrame的行用0,列用1
skipna 排除缺失值,默认值为True
level 如果轴是层次化索引的(即MultilIndex),则根据level分组约简

 

 

 

 

idxmin、idxmax达到最小值和最大值的索引,间接统计

In [51]: df.idxmax()
Out[51]: 
one    b
two    d
dtype: object

 

另一些方法是累计型的:cumsum

In [53]: df
Out[53]: 
    one  two
a  1.40  NaN
b  7.10 -4.5
c   NaN  NaN
d  0.75 -1.3

In [52]: df.cumsum()
Out[52]: 
    one  two
a  1.40  NaN
b  8.50 -4.5
c   NaN  NaN
d  9.25 -5.8


一次性汇总多个统计信息:describe(),数值型和非数值型产生不同汇总信息

In [53]: df1#数值型
Out[53]: 
    one  two
a  1.40  NaN
b  7.10 -4.5
c   NaN  NaN
d  0.75 -1.3


In [54]: df.describe()
Out[54]: 
            one       two
count  3.000000  2.000000
mean   3.083333 -2.900000
std    3.493685  2.262742
min    0.750000 -4.500000
25%    1.075000 -3.700000
50%    1.400000 -2.900000
75%    4.250000 -2.100000
max    7.100000 -1.300000


In [56]: df2#非数值型
Out[56]: 
    a    b
1  aa   aa
2  bb  bbb
3  cc  ddd
4  dd  ccc

In [57]: df.describe()
Out[57]: 
         a    b
count    4    4
unique   4    4
top     bb  ccc
freq     1    1

描述和汇总统计

count 非NA值的数量
describe 针对Series或各DataFrame列计算汇总统计
min、max 计算最小值和最大值
argmin、argmax 计算能够获取到最小值和最大值的索引位置(整数)
idxmin、inxmax 计算能够获取到最小值和最大值的索引值
quantile 计算样本的分位数(0到1)
sum 值的总和
mean 值的平均数
median 值的中位数
mad 根据平均值计算平均绝对离差
var 样本值的方差
std 样本值的标准差
skew 样本值的偏度(三阶矩)
kurt 样本值的峰度(四阶矩)
cumsum 样本值的累计和
cummin、cummax 样本值的leis最小值和累计最大值
cumprod 样本值的累计积
diff 计算一阶差分(多用于时间序列)
pct_change 计算百分数变化
   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分组运算:GroupBy技术

拆分--应用--合并

利用python进行数据分析_从删库到跑路_第4张图片

In [1]: import pandas as pd

In [2]: import numpy as np
#构建DataFrame
In [3]: df=pd.DataFrame({'key1':['a','a','b','b','a'],'key2':['one','two','one',
   ...: 'two','one'],'data1':np.random.randn(5),'data2':np.random.randn(5)})

In [4]: df
Out[4]: 
  key1 key2     data1     data2
0    a  one -0.769863  0.321557
1    a  two  0.163815 -0.635989
2    b  one -1.048893  1.988060
3    b  two -0.427548  0.322831
4    a  one -1.115058  0.663252
#以键key1分组,获取data1的GroupBy对象
In [5]: grouped=df['data1'].groupby(df['key1'])

In [6]: grouped
Out[6]: 
#GroupBy对象可以调用mean,sum等方法求平均值,和等值
In [7]: grouped.mean()#返回一个Series
Out[7]: 
key1
a   -0.573702
b   -0.738221
Name: data1, dtype: float64

时间序列time series

时间序列是一种结构化数据形式,在多个时间点观测或测量到的任何事物都可以形成一段时间序列,时间序列数据的意义取决于具体的应用场景,主要有以下几种:

时间戳timestamp,特定的时刻
固定时期period,如2018全年
时间间隔interval,有起始和结束时间戳表示,时期period可以被看作间隔interval的特例
实验/过程时间,每个时间点都是相对于特定起始时间的一个度量,例如,从放入冰箱时起,每分钟橙子的温度

python标准库包含用于日期date,时间time,日历calendar的datetime,time,calendar模块,datetime.datetime是用得最多的数据类型

详情见python之时间格式(datetime,time,calendar),此处只举例

In [47]: from datetime import datetime#datetime模块的datetime类用的最多

In [48]: import time

In [49]: import calendar

In [50]: datetime.now()#返回一个时间元组
Out[50]: datetime.datetime(2018, 10, 19, 15, 47, 17, 175724)

In [51]: time.time()#返回一个时间戳
Out[51]: 1539935328.026199

In [52]: calendar.calendar(2013)#传入年份,返回一个日历表
Out[52]: '                                  2013\n\n      January                   February                   March\nMo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su\n    1  2  3  4  5  6                   1  2  3                   1  2  3\n 7  8  9 10 11 12 13       4  5  6  7  8  9 10       4  5  6  7  8  9 10\n14 15 16 17 18 19 20      11 12 13 14 15 16 17      11 12 13 14 15 16 17\n21 22 23 24 25 26 27      18 19 20 21 22 23 24      18 19 20 21 22 23 24\n28 29 30 31               25 26 27 28               25 26 27 28 29 30 31\n\n       April                      May                       June\nMo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su\n 1  2  3  4  5  6  7             1  2  3  4  5                      1  2\n 8  9 10 11 12 13 14       6  7  8  9 10 11 12       3  4  5  6  7  8  9\n15 16 17 18 19 20 21      13 14 15 16 17 18 19      10 11 12 13 14 15 16\n22 23 24 25 26 27 28      20 21 22 23 24 25 26      17 18 19 20 21 22 23\n29 30                     27 28 29 30 31            24 25 26 27 28 29 30\n\n        July                     August                  September\nMo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su\n 1  2  3  4  5  6  7                1  2  3  4                         1\n 8  9 10 11 12 13 14       5  6  7  8  9 10 11       2  3  4  5  6  7  8\n15 16 17 18 19 20 21      12 13 14 15 16 17 18       9 10 11 12 13 14 15\n22 23 24 25 26 27 28      19 20 21 22 23 24 25      16 17 18 19 20 21 22\n29 30 31                  26 27 28 29 30 31         23 24 25 26 27 28 29\n                                                    30\n\n      October                   November                  December\nMo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su\n    1  2  3  4  5  6                   1  2  3                         1\n 7  8  9 10 11 12 13       4  5  6  7  8  9 10       2  3  4  5  6  7  8\n14 15 16 17 18 19 20      11 12 13 14 15 16 17       9 10 11 12 13 14 15\n21 22 23 24 25 26 27      18 19 20 21 22 23 24      16 17 18 19 20 21 22\n28 29 30 31               25 26 27 28 29 30         23 24 25 26 27 28 29\n                                                    30 31\n'

时间序列基础

以时间为索引构建Series序列

In [57]: from datetime import datetime
#datetime传入数字参数,表示年,月,日,时,分,秒,年月日为必传参数,时分秒可选
In [62]: dates=[datetime(2018,10,19,10,0,0),datetime(2018,10,19,10,1,1),datetime
    ...: (2018,10,19,10,2,2)]

In [63]: s=pd.Series(np.random.randn(3),index=dates)

In [64]: s
Out[64]: 
2018-10-19 10:00:00    2.450767
2018-10-19 10:01:01   -0.941043
2018-10-19 10:02:02    0.216157
dtype: float64
#这些datetime对象实际上是被放在列一个DatetimeIndex中,DatetimeIndex中的各个标量值是pandas的Timestamp对象
In [68]: type(s)
Out[68]: pandas.core.series.Series#--利用python进行数据分析书中显示为pandas.core.series.TimeSeries,与实际符,预计是新版本改变

In [69]: s.index
Out[69]: 
DatetimeIndex(['2018-10-19 10:00:00', '2018-10-19 10:01:01',
               '2018-10-19 10:02:02'],
              dtype='datetime64[ns]', freq=None)

索引,选取,子集构造

In [82]: dates=[datetime(2018,10,19,10,0,0),datetime(2018,10,19,10,1,1),datetime
    ...: (2018,10,19,10,2,2),datetime(2018,10,19,10,3,3),datetime(2018,10,19,10,
    ...: 4,4)]

In [83]: s=pd.Series(np.random.randn(5),index=dates)

In [84]: s
Out[84]: 
2018-10-19 10:00:00    0.879305
2018-10-19 10:01:01    1.218865
2018-10-19 10:02:02   -0.859521
2018-10-19 10:03:03   -1.272252
2018-10-19 10:04:04    0.200955
dtype: float64

In [85]: stamp=s.index[2]

In [86]: s[stamp]
Out[86]: -0.8595210222802377#获取到第三行的值

#索引也可传入一个可被解释为日期的字符串
In [90]: s['20181019100202']
Out[90]: -0.8595210222802377

In [92]: s['19/10/2018 10:02:02']
Out[92]: -0.8595210222802377
#当传入索引前相同部分的字符串,也可用作切片
In [93]: s['19/10/2018']时间字符串格式之一
Out[93]: 
2018-10-19 10:00:00    0.879305
2018-10-19 10:01:01    1.218865
2018-10-19 10:02:02   -0.859521
2018-10-19 10:03:03   -1.272252
2018-10-19 10:04:04    0.200955
dtype: float64

In [94]: s['20181019']#时间字符串格式之一
Out[94]: 
2018-10-19 10:00:00    0.879305
2018-10-19 10:01:01    1.218865
2018-10-19 10:02:02   -0.859521
2018-10-19 10:03:03   -1.272252
2018-10-19 10:04:04    0.200955
dtype: float64

#由于大部分时间序列数据都是按照时间先后排序的,因此也可以用不存在于该时间序类中的时间戳对其进行切片(即范围查询)
In [98]: s['19/10/2018 10:0:0':'19/10/2018 10:01:02']
Out[98]: 
2018-10-19 10:00:00    0.879305
2018-10-19 10:01:01    1.218865
dtype: float64

#实例方法truncate也能实现截取两个日期时间的Series,所选范围为闭端,即包含所穿参数
In [105]: s.truncate(after='20181019100303',before='20181019100000')
Out[105]: 
2018-10-19 10:00:00    0.879305
2018-10-19 10:01:01    1.218865
2018-10-19 10:02:02   -0.859521
2018-10-19 10:03:03   -1.272252
dtype: float64

带有重复索引的时间序列

#当索引为不重复项时,返回标量值,当索引为重复项时,返回Series序列
In [109]: s['20181001']
Out[109]: 
2018-10-01   -1.217149
2018-10-01   -1.714301
dtype: float64

In [113]: s['20181003']
Out[113]: 0.14757289913298496

注意:datetime不能传入01,02等0开头的参数,会报错

In [122]: dates=[datetime(2011,01,02),datetime(2011,01,05),datetime(2011,01,07),
     ...: datetime(2011,01,08),datetime(2011,01,10),datetime(2011,01,12)]
  File "", line 1
    dates=[datetime(2011,01,02),datetime(2011,01,05),datetime(2011,01,07),datetime(2011,01,08),datetime(2011,01,10),datetime(2011,01,12)]
                          ^
SyntaxError: invalid token


In [123]: dates=[datetime(2011,1,2),datetime(2011,1,5),datetime(2011,1,7),dateti
     ...: me(2011,1,8),datetime(2011,1,10),datetime(2011,1,12)]

生成日期范围

#pandas.date_range可用于指定长度的DatetimeIndex,默认情况下,产生按天计算的时间点,date_range默认保留起始和结束日期,即闭端
In [139]: index=pd.date_range('4/1/2018','4/10/2018')#月日年

In [140]: s=pd.Series(np.random.randn(10),index=index)

In [141]: s
Out[141]: 
2018-04-01   -0.667058
2018-04-02    1.781576
2018-04-03   -0.845532
2018-04-04   -1.121527
2018-04-05    0.572054
2018-04-06   -0.120728
2018-04-07    1.396313
2018-04-08    1.559138
2018-04-09    0.737590
2018-04-10    1.905737
Freq: D, dtype: float64
#如果给参数period传入一个表示索引个数的数字,可以设定索引间隔时间,即时间差/periods
In [149]: index=pd.date_range(start='4/1/2018',end='4/10/2018',periods=5)#表示间隔两天,产生5个索引,即5等分时间差

In [150]: s=pd.Series(np.random.randn(5),index=index)

In [151]: s
Out[151]: 
2018-04-01 00:00:00   -1.440547
2018-04-03 06:00:00    0.295920
2018-04-05 12:00:00    0.297291
2018-04-07 18:00:00   -0.753377
2018-04-10 00:00:00    1.643616
dtype: float64
#如果希望产生一组被规范化到午夜的时间戳,normalize参数可实现该功能,即忽略时间,从开始日期的零点始,结束日期的零点终
In [155]: index=pd.date_range(start='4/1/2018 08:04:22',end='4/10/2018 12:12:21'
     ...: ,periods=5)

In [156]: index=pd.date_range(start='4/1/2018',end='4/10/2018',periods=5,normali
     ...: ze=True)

In [157]: s=pd.Series(np.random.randn(5),index=index)

In [158]: s
Out[158]: 
2018-04-01 00:00:00    1.209585
2018-04-03 06:00:00   -1.129351
2018-04-05 12:00:00   -1.488379
2018-04-07 18:00:00   -0.882576
2018-04-10 00:00:00   -0.152073
dtype: float64

频率和基础偏移量

pandas中的频率是由一个基础频率(hbase frequency)和一个乘数组成的,基础频率通常以一个字符串别名表示,比如'M'表示月,'H'表示小时,对于每个基础频率,都有一个被称为日期偏移量(date offset)的对象与之对应

In [159]: from pandas.tseries.offsets import Hour,Minute
In [160]: hour=Hour()#基础频率
In [161]: hour
Out[161]: 

In [162]: four_hours=Hour(4)#导入一个整数即可定义偏移量的倍数

In [163]: four_hours
Out[163]: <4 * Hours>

#一般无需显式创建这样的对象,在date_range函数前传入参数freq,参数值使用诸如'h','4h'这样的字符串别名的整数倍
In [164]: dates=pd.date_range('20181211','20181220',freq='2d')#字符串别名不区分大小写

In [165]: s=pd.Series(np.random.randn(5),index=dates)

In [166]: s
Out[166]: 
2018-12-11   -0.031886
2018-12-13    0.827174
2018-12-15    0.317349
2018-12-17   -1.938784
2018-12-19   -0.410817
Freq: 2D, dtype: float64

#也可以传入频率字符串:如'4h30min',这种字符串可以被高效的解析为等效的表达式

In [168]: dates=pd.date_range('20181211','20181220',freq='2d48h')
In [170]: s=pd.Series(np.random.randn(3),index=dates)

In [171]: s
Out[171]: 
2018-12-11   -1.380608
2018-12-15   -0.012547
2018-12-19   -0.858346
Freq: 4D, dtype: float64

#大部分偏移量对象都可以用'+'加号连接
In [167]: Hour(2)+Minute(30)
Out[167]: <150 * Minutes>


时间序列的常见基础频率表

注意:有些频率描述的时间点不是均匀分隔的,如‘M’日里月末和’BM’每月最后一个工作日,对于前者,就取决于每月的天数,对于后者,还要考虑月末是不是周末,我们称这些为锚点偏移量

别名 偏移量类型 说明
D Day 每日历日
B BussinessDay 每工作日
H Hour 每小时
T/min Minute 每分
S Second 每秒
L/ms Milli 每毫秒(即千分之一秒)
U Micro 每微秒(即每百万分之一秒)
M MonthEnd 每月最后一个日历日
BM BusinessMonthEnd 每月最后一个工作日
MS MonthBegin 每月第一个日历日
BMS BusinessMonthBegin 每月第一个工作日
W-MON,W-TUE.... Week 从指定的星期几开始计算
WOM-1MON,WOM-2MON...   从指定的每月第一,第二...周的星期即,例如:WOM-3FRI表示每月的第三隔星期五
A-JAN,A-FEB YearEnd 每年指定月份的最后一个日历日
BA-JAN,BA-FEB BusinessYearEnd 每年指定月份的最后一个工作日
AS-JAN,AS-FEB YearBegin 每年指定月份的第一个日历日
BAS-JAN,BAS-FEB BusinessYearBegin 每年指定月份的第一个工作日

WOM日期

WOM(Week Of Month)是一种非常使用的频率类,它以WOM开头,它使你能获得'每月第3隔星期五'之类的日期

In [172]: rng=pd.date_range('1/1/2018','9/1/2018',freq='WOM-3FRI')

In [173]: list(rng)
Out[173]: 
[Timestamp('2018-01-19 00:00:00', freq='WOM-3FRI'),
 Timestamp('2018-02-16 00:00:00', freq='WOM-3FRI'),
 Timestamp('2018-03-16 00:00:00', freq='WOM-3FRI'),
 Timestamp('2018-04-20 00:00:00', freq='WOM-3FRI'),
 Timestamp('2018-05-18 00:00:00', freq='WOM-3FRI'),
 Timestamp('2018-06-15 00:00:00', freq='WOM-3FRI'),
 Timestamp('2018-07-20 00:00:00', freq='WOM-3FRI'),
 Timestamp('2018-08-17 00:00:00', freq='WOM-3FRI')]

移动(超前或滞后)数据

移动(shifting)指的是沿着时间轴将数据前移或者后移,但保持索引不变

In [4]: ts=pd.Series(np.random.randn(4),index=pd.date_range('1/1/2018',periods=4
   ...: ,freq='M'))
#periods=4表示以1/1/2018为起始日期产生4个时间索引,间隔为月,即freq='M'
In [5]: ts
Out[5]: 
2018-01-31    0.467078
2018-02-28   -0.664430
2018-03-31   -0.823731
2018-04-30    2.407555
Freq: M, dtype: float64

In [14]: ts.shift(2)#正整数向下移
Out[14]: 
2018-01-31         NaN
2018-02-28         NaN
2018-03-31    0.467078
2018-04-30   -0.664430
Freq: M, dtype: float64

In [15]: ts.shift(-2)#负整数向上移动
Out[15]: 
2018-01-31   -0.823731
2018-02-28    2.407555
2018-03-31         NaN
2018-04-30         NaN
Freq: M, dtype: float64

#shift通常用于计算一个时间序列或多个时间序列中的百分比变化,可以这样表达:
ts/ts.shift(1)-1
#由于单纯的位移操作不会修改索引,所以部分数据会被丢弃,因此如果频率已知,则可以将频率freq传给shift以便实现移动时间戳进行位移,而不是移动数据:
In [17]: ts.shift(2,freq='M')
Out[17]: 
2018-03-31    0.467078
2018-04-30   -0.664430
2018-05-31   -0.823731
2018-06-30    2.407555
Freq: M, dtype: float64
#还可以使用其他频率,于是就能非常灵活地对数据进行超前或滞后处理了
In [21]: ts.shift(1,'3d')
Out[21]: 
2018-02-03    0.467078
2018-03-03   -0.664430
2018-04-03   -0.823731
2018-05-03    2.407555
dtype: float64

In [22]: ts.shift(3,'d')
Out[22]: 
2018-02-03    0.467078
2018-03-03   -0.664430
2018-04-03   -0.823731
2018-05-03    2.407555
dtype: float64

通过偏移量对日期进行位移

pandas的日期偏移量还可以用在datetime和Timestamp对象上

In [25]: from pandas.tseries.offsets import Day,MonthEnd
In [26]: now=datetime(2011,11,7)

In [27]: now+3*Day()
Out[27]: Timestamp('2011-11-10 00:00:00')
#如果加的是锚点偏移量,第一次增量会将原日期向前滚动到符合频率规则的下一个日期,比如第一次位移的量可能没有一个月那么长,就在当月
In [26]: now=datetime(2011,11,7)

In [27]: now+3*Day()
Out[27]: Timestamp('2011-11-10 00:00:00')

In [28]:  now+MonthEnd()
Out[28]: Timestamp('2011-11-30 00:00:00')

In [29]: now+2*MonthEnd()
Out[29]: Timestamp('2011-12-31 00:00:00')

In [30]: now+MonthEnd(2)
Out[30]: Timestamp('2011-12-31 00:00:00')
#通过锚点偏移量的rollforward和rollback方法,可以显式的将日期前移或向后滚动
In [31]: offset=MonthEnd()

In [32]: offset.rollforward(now)#向前翻滚就是本月
Out[32]: Timestamp('2011-11-30 00:00:00')
In [33]: offset.rollback(now)#前后翻滚就是上一个月
Out[33]: Timestamp('2011-10-31 00:00:00')
#结合groupby使用前后滚动
In [34]: ts=pd.Series(np.random.randn(20),index=pd.date_range('1/15/2018',period
    ...: s=20,freq='4d'))

In [35]: ts
Out[35]: 
2018-01-15   -1.123869
2018-01-19    0.691454
2018-01-23   -1.492071
2018-01-27    0.047393
2018-01-31    0.190645
2018-02-04   -1.427506
2018-02-08    0.318326
2018-02-12   -0.073011
2018-02-16    0.636296
2018-02-20   -0.570525
2018-02-24   -0.865244
2018-02-28   -0.356154
2018-03-04   -0.247588
2018-03-08    0.589253
2018-03-12    1.113633
2018-03-16    1.722783
2018-03-20   -2.332676
2018-03-24   -0.275168
2018-03-28   -0.171739
2018-04-01   -0.369748
Freq: 4D, dtype: float64

In [36]: ts.groupby(offset.rollforward).mean()#全部转换为几类相同日期,然后分类
Out[36]: 
2018-01-31   -0.337290
2018-02-28   -0.333974
2018-03-31    0.056928
2018-04-30   -0.369748
dtype: float64
#结合groupby函数使用滚动的封装方法:resample
In [38]: ts.resample('M',how='mean')#python2
/usr/bin/ipython3:1: FutureWarning: how in .resample() is deprecated
the new syntax is .resample(...).mean()
  #! /bin/sh
Out[38]: 
2018-01-31   -0.337290
2018-02-28   -0.333974
2018-03-31    0.056928
2018-04-30   -0.369748
Freq: M, dtype: float64
#结合groupby函数使用滚动的封装方法:resample

In [39]: ts.resample('M').mean()#python3
Out[39]: 
2018-01-31   -0.337290
2018-02-28   -0.333974
2018-03-31    0.056928
2018-04-30   -0.369748
Freq: M, dtype: float64


时区处理

时区信息来自python库pytz,它使python可以使用Olso数据库(汇编了世界时区信息),由于pandas包装了pytz的功能,因此可以不用记其API,只要记住时区名即可,时区名可以在文档中找到,也可以通过交互查看

In [42]: import pytz#时区信息库
In [43]: pytz.timezone('US/Eastern')#获取时区信息,
Out[43]: 
In [48]: pytz.common_timezones[1]#时区名称列表
Out[48]: 'Africa/Accra'

本地化和转换

默认情况下,pandas中的时间序列是单纯的(naive)时区,其索引的tz字段为None,在生成日期范围的时候还可以加上一个时区集,从单纯到本地化的转换是通过tz_locallize方法处理的:

In [56]: rng=pd.date_range('3/9/2018 9:30',periods=6,freq='D')

In [57]: ts=pd.Series(np.random.randn(len(rng)),index=rng)

In [58]: ts
Out[58]: 
2018-03-09 09:30:00   -1.470021
2018-03-10 09:30:00   -1.171575
2018-03-11 09:30:00    0.739337
2018-03-12 09:30:00   -0.990569
2018-03-13 09:30:00    0.007370
2018-03-14 09:30:00    0.389544
Freq: D, dtype: float64

In [62]: rng.tz

In [63]: #返回值为空

In [52]: ts1=pd.date_range('3/9/2018 9:30',periods=10,freq='D',tz='UTC')
Out[52]: 
DatetimeIndex(['2018-03-09 09:30:00+00:00', '2018-03-10 09:30:00+00:00',
               '2018-03-11 09:30:00+00:00', '2018-03-12 09:30:00+00:00',
               '2018-03-13 09:30:00+00:00', '2018-03-14 09:30:00+00:00',
               '2018-03-15 09:30:00+00:00', '2018-03-16 09:30:00+00:00',
               '2018-03-17 09:30:00+00:00', '2018-03-18 09:30:00+00:00'],
              dtype='datetime64[ns, UTC]', freq='D')
In [55]: ts1.tz#默认tz属性为None
Out[55]: 
In [65]: ts_utc=ts.tz_localize('UTC')
#使用tz_localize转换为UTC本地时区
In [66]: ts_utc
Out[66]: 
2018-03-09 09:30:00+00:00   -1.470021
2018-03-10 09:30:00+00:00   -1.171575
2018-03-11 09:30:00+00:00    0.739337
2018-03-12 09:30:00+00:00   -0.990569
2018-03-13 09:30:00+00:00    0.007370
2018-03-14 09:30:00+00:00    0.389544
Freq: D, dtype: float64

#当时间序列被本地化到某个特定时区,就可以用tz_convert将其转换为别的时区了

In [67]: ts_utc.tz_convert('US/Eastern')
Out[67]: 
2018-03-09 04:30:00-05:00   -1.470021
2018-03-10 04:30:00-05:00   -1.171575
2018-03-11 05:30:00-04:00    0.739337
2018-03-12 05:30:00-04:00   -0.990569
2018-03-13 05:30:00-04:00    0.007370
2018-03-14 05:30:00-04:00    0.389544
Freq: D, dtype: float64
#tz_localize和tz_convert是DatetimeIndex的实例方法,对于单纯时间戳的本地化操作还会检查夏令时转变期附近容易混淆或不存在的时间

操作时区意识型Timestamp对象

跟时间序列和日期范围差不多,Timestamp对象也能被从单纯型(naive)本地化为时区意识型(time zone-aware),并从一个时区转换为另一个时区

#单纯时区和本地时区之间可以灵活转换
In [70]: stamp=pd.Timestamp('2011-03-12 04:00')#创建Timestamp

In [71]: stamp
Out[71]: Timestamp('2011-03-12 04:00:00')

In [72]: stamp_utc=stamp.tz_localize('utc')

In [73]: stamp_utc
Out[73]: Timestamp('2011-03-12 04:00:00+0000', tz='UTC')

In [75]: stamp_utc.tz_convert('US/Eastern')
Out[75]: Timestamp('2011-03-11 23:00:00-0500', tz='US/Eastern')
#创建Timestamp时,还可以传入一个时区信息
In [76]: stamp_moscow=pd.Timestamp('2011-01-12 04:00',tz='Europe/Moscow')

In [77]: stamp_moscow
Out[77]: Timestamp('2011-01-12 04:00:00+0300', tz='Europe/Moscow')

时区意识型Timestamp对象在内部保存了一个UTC时间戳(自UNIX纪元(1970年1月1日0时)算起的纳秒数)。这个UTC值在时区转换过程中是不会发生变化的

In [76]: stamp_moscow=pd.Timestamp('2011-01-12 04:00',tz='Europe/Moscow')

In [77]: stamp_moscow
Out[77]: Timestamp('2011-01-12 04:00:00+0300', tz='Europe/Moscow')

In [78]: stamp_utc.value
Out[78]: 1299902400000000000

In [79]: stamp_utc.tz_convert('US/Eastern').value
Out[79]: 1299902400000000000

当使用pandas的DateOffset对象执行时间算术运算时,运算过程会自动关注是否存在夏令时转变期

#夏令时转变前30分钟--没有区别
In [80]: from pandas.tseries.offsets import Hour

In [81]: stamp=pd.Timestamp('2012-03-12 01:30',tz='US/Eastern')

In [82]: stamp+Hour()
Out[82]: Timestamp('2012-03-12 02:30:00-0400', tz='US/Eastern')

In [83]: stamp
Out[83]: Timestamp('2012-03-12 01:30:00-0400', tz='US/Eastern')
#夏令时转变前90分钟--少了一个小时,why?
In [84]: stamp=pd.Timestamp('2012-11-04 00:30',tz='US/Eastern')

In [85]: stamp
Out[85]: Timestamp('2012-11-04 00:30:00-0400', tz='US/Eastern')

In [86]: stamp+2*Hour()
Out[86]: Timestamp('2012-11-04 01:30:00-0500', tz='US/Eastern')

不同时区之间的运算

如果两个时间序列的时区不同,在将他们合并到一起时,最终结果就会是UTC,由于时间戳其实是以UTC存储的,所以并不需要发生任何转换

In [87]: rng=pd.date_range('3/7/2012 9:30',periods=10,freq='B')

In [88]: ts=pd.Series(np.random.randn(10),index=rng)

In [89]: ts
Out[89]: 
2012-03-07 09:30:00    1.946924
2012-03-08 09:30:00    0.305003
2012-03-09 09:30:00    0.529779
2012-03-12 09:30:00   -1.501415
2012-03-13 09:30:00   -0.837557
2012-03-14 09:30:00    0.529487
2012-03-15 09:30:00    0.055145
2012-03-16 09:30:00   -0.746819
2012-03-19 09:30:00   -0.824349
2012-03-20 09:30:00    0.627202
Freq: B, dtype: float64

In [90]: ts1=ts[:7].tz_localize('Europe/London')

In [91]: ts2=ts[2:].tz_localize('Europe/Moscow')

In [92]: ts1
Out[92]: 
2012-03-07 09:30:00+00:00    1.946924
2012-03-08 09:30:00+00:00    0.305003
2012-03-09 09:30:00+00:00    0.529779
2012-03-12 09:30:00+00:00   -1.501415
2012-03-13 09:30:00+00:00   -0.837557
2012-03-14 09:30:00+00:00    0.529487
2012-03-15 09:30:00+00:00    0.055145
Freq: B, dtype: float64

In [93]: ts2
Out[93]: 
2012-03-09 09:30:00+04:00    0.529779
2012-03-12 09:30:00+04:00   -1.501415
2012-03-13 09:30:00+04:00   -0.837557
2012-03-14 09:30:00+04:00    0.529487
2012-03-15 09:30:00+04:00    0.055145
2012-03-16 09:30:00+04:00   -0.746819
2012-03-19 09:30:00+04:00   -0.824349
2012-03-20 09:30:00+04:00    0.627202
Freq: B, dtype: float64

In [94]: ts1+ts2#为什么是NaN值?????????
Out[94]: 
2012-03-07 09:30:00+00:00   NaN
2012-03-08 09:30:00+00:00   NaN
2012-03-09 05:30:00+00:00   NaN
2012-03-09 09:30:00+00:00   NaN
2012-03-12 05:30:00+00:00   NaN
2012-03-12 09:30:00+00:00   NaN
2012-03-13 05:30:00+00:00   NaN
2012-03-13 09:30:00+00:00   NaN
2012-03-14 05:30:00+00:00   NaN
2012-03-14 09:30:00+00:00   NaN
2012-03-15 05:30:00+00:00   NaN
2012-03-15 09:30:00+00:00   NaN
2012-03-16 05:30:00+00:00   NaN
2012-03-19 05:30:00+00:00   NaN
2012-03-20 05:30:00+00:00   NaN
dtype: float64

 

 

 

 

 

 

 

三 matplotlib模块

matplotlib是一个用于创建出版质量图表的桌面绘图包(主要是2D方面),其目的是为python构建一个MATLAB式的绘图接口,它不仅支持各种操作系统上许多不同的GUI后端,而且还能将图片导出为各种常见的矢量(vector)和光栅(raster)图

标PDF JPG什么的

先来看一个使用制图小例子

>>> import matplotlib.pyplot as plt
>>> x=np.linspace(0,10,1000)
>>> y=np.sin(x)
>>> y=np.sin(x)+1
>>> z=np.cos(x**2)+1
>>> plt.plot(x,y,label='$\sin x+1$',color='red',linewidth=2)
[]
>>> plt.figure(figsize=(8,4))
>>> plt.plot(x,z,'b--',label='$\cos x^2+1$') [] >>> plt.xlabel('time(s)') Text(0.5, 0, 'time(s)') >>> plt.ylabel('volt') Text(0, 0.5, 'volt') >>> plt.title('example') Text(0.5, 1.0, 'example') >>> plt.ylim(0,2.2) (0, 2.2) >>> plt.legend() >>> plt.show()
'''
安装matplotlib,需要使用apt-get安装依赖包(不是用pip3安装)
sudo apt-get install python3-tk
pip3 install matplotlib
'''

>>> import matplotlib
>>> import matplotlib.pyplot as plt
>>> fg=plt.figure()
>>> ax1=fg.add_subplot(2,2,1)
>>> ax2=fg.add_subplot(2,2,2)
>>> ax3=fg.add_subplot(2,2,3
... )
>>> plt.gcf()
>>> plt.show()

四    scipy模块

Numpy提供了多维数组功能,但它只是一般的数组,并不是矩阵,例如,当两个数组相乘时,只是对应元素相乘,而不是矩阵乘法,scipy提供了真正的矩阵,以及大量基于矩阵预算的对象和函数,但是scipy是依赖于numpy库的,numpy还是基础库

Scipy包含的功能有最优化、线性代数、积分、插值、拟合、特殊函数、快速傅里叶变换、信号处理和图像处理、常微分方程求解和其他科学与工程中常用的计算

#-*- coding:utf-8 -*-
#求解非线性方程组2x1-x2^2=1,x1^2-x2=2
from scipy.optimize import fsolve #导入求解方程组的函数
def f(x):                         #定义需要求解的方程组
    x1=x[0]
    x2=x[1]
    return [2*x1-x2**2-1,x1**2-x2-2]
result=fsolve(f,[1,1])            #输入初值[1,1]并求解
print(result)

#数值积分
from scipy import integrate     #导入积分函数
def g(x):                       #定义被积函数
    return (1-x**2)**0.5
pi_2,err=integrate.quad(g,-1,1) #积分结果和误差
print(pi_2*2)                   #由微积分知识知道积分结果为圆周率pi的一半

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(python,数据分析)