数据蛙数据分析每周作业小秩学数据分析

pandas文档0.24.1第三章 马上开始(二)

2019-03-18  本文已影响0人  Lykit01

目录:
第一章 0.24.1版本新特性
第二章 安装
第三章 马上开始(一)快速上手
第三章 马上开始(二)基础功能

[toc]

3.3 重要基础功能

现在我们讨论一下pandas数据结构共有的一些基础功能。在前文的例子中我们已经创建过一些pandas数据结构,比如:

In [1]: index = pd.date_range('1/1/2000', periods=8)

In [2]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])

In [3]: df = pd.DataFrame(np.random.randn(8, 3), index=index,
   ...:                   columns=['A', 'B', 'C'])

In [4]: wp = pd.Panel(np.random.randn(2, 5, 4), items=['Item1', 'Item2'],
   ...:               major_axis=pd.date_range('1/1/2000', periods=5),
   ...:               minor_axis=['A', 'B', 'C', 'D'])

3.3.1 头和尾

在pandas中我们用head()和tail()方法来观察Series和DataFrame的一小部分样本。预览操作默认能看到的样本个数是5个,读者可以按需求更改。

In [5]: long_series = pd.Series(np.random.randn(1000))

In [6]: long_series.head()
Out[6]: 
0   -2.211372
1    0.974466
2   -2.006747
3   -0.410001
4   -0.078638
dtype: float64

In [7]: long_series.tail(3)
Out[7]: 
997   -0.196166
998    0.380733
999   -0.275874
dtype: float64

3.3.2 属性和底层数据

pandas对象有很多属性,能够让用户直接访问到元数据(metadata)。

In [8]: df[:2]
Out[8]: 
                   A         B         C
2000-01-01 -0.173215  0.119209 -1.044236
2000-01-02 -0.861849 -2.104569 -0.494929

In [9]: df.columns = [x.lower() for x in df.columns]

In [10]: df
Out[10]: 
                   a         b         c
2000-01-01 -0.173215  0.119209 -1.044236
2000-01-02 -0.861849 -2.104569 -0.494929
2000-01-03  1.071804  0.721555 -0.706771
2000-01-04 -1.039575  0.271860 -0.424972
2000-01-05  0.567020  0.276232 -1.087401
2000-01-06 -0.673690  0.113648 -1.478427
2000-01-07  0.524988  0.404705  0.577046
2000-01-08 -1.715002 -1.039268 -0.370647

pandas对象(Index、Series和DataFrame)可以看做数组的容器,它们储存数据并且参与实际的计算。对于许多类型来说,它们底层的数组都是numpy.ndarray。然而,为了支持自定义数组,pandas和其他第三方库扩展了NumPy的类型系统。(请看dtypes
如果要取出Index或Series中的数据,可以使用.array特性。

In [11]: s.array
Out[11]: 
<PandasArray>
[ 0.46911229990718628, -0.28286334432866328,  -1.5090585031735124,
  -1.1356323710171934,   1.2121120250208506]
Length: 5, dtype: float64

In [12]: s.index.array
Out[12]: 
<PandasArray>
['a', 'b', 'c', 'd', 'e']
Length: 5, dtype: object

取出的数组将是一个扩展数组(ExtensionArray)类型。那么扩展数组是什么呢?为什么pandas要使用扩展数组?这两个问题超过了我们这章的“简介”的范围,详情请看dtypes
如果你需要将Series转换为NumPy数组,请用to_numpy()或者numpy.asarray()。

In [13]: s.to_numpy()
Out[13]: array([ 0.4691, -0.2829, -1.5091, -1.1356,  1.2121])

In [14]: np.asarray(s)
Out[14]: array([ 0.4691, -0.2829, -1.5091, -1.1356,  1.2121])

当Series或Index由扩展数组(ExtensionArray)支持时,to_numpy()操作困难会涉及复制数据和支配值(coercing values)。详情请看dtypes
to_numpy()操作会对生成的numpy.ndarray有一些控制。拿时区举个例子。NumPy并没有一个数据类型来表示带有时区的时间,困难有两种补偿办法:
1.创建一个带有时间戳(Timestamp)的numpy.ndarray对象,每个对象都附带正确的时区(tz)
2.一个datetime64[ns]类型的numpy.ndarray,里面的数据值已经被转换到了UTC,同时时区信息被抹去了

UTC(协调世界时),详情请看UTC

可以用dtype=object来保留时区。(即第一种补方法)

In [15]: ser = pd.Series(pd.date_range('2000', periods=2, tz="CET"))

In [16]: ser.to_numpy(dtype=object)
Out[16]: 
array([Timestamp('2000-01-01 00:00:00+0100', tz='CET', freq='D'),
       Timestamp('2000-01-02 00:00:00+0100', tz='CET', freq='D')], dtype=object)

或者抛弃时区,改用dtype='datetime64[ns]'

In [17]: ser.to_numpy(dtype="datetime64[ns]")
Out[17]: array(['1999-12-31T23:00:00.000000000', '2000-01-01T23:00:00.000000000'], dtype='datetime64[ns]')

从DataFrame中取出原始数据可能有点复杂。如果DataFrame中所有的列都只有一种数据类型,那么DataFrame.to_numpy()将会返回底层数据。

In [18]: df.to_numpy()
Out[18]: 
array([[-0.1732,  0.1192, -1.0442],
       [-0.8618, -2.1046, -0.4949],
       [ 1.0718,  0.7216, -0.7068],
       [-1.0396,  0.2719, -0.425 ],
       [ 0.567 ,  0.2762, -1.0874],
       [-0.6737,  0.1136, -1.4784],
       [ 0.525 ,  0.4047,  0.577 ],
       [-1.715 , -1.0393, -0.3706]])

如果DataFrame或Panel包含同种类型的数据,n维数组(ndarray)能马上转换,这种变化将体现在数据结构中。对于不同类型的数据(比如DataFrame中列的数据类型并不都是一样的),事情就不一样了。值本身的属性将不会保留,这点和轴标签不太一样。
注意: 对于不同类型的数据,生成的n维数组的数据类型将会改变来满足原数据的所有数据。比如,如果数据中有字符串,那么生成的数据类型将会是对象类型(object dtype)。如果只有浮点数和整数,生成的数组将会是浮点数类型(float dtype)。
过去pandas推荐使用Series.valuesDataFrame.values来从Series或DataFrame中提取数据。老的代码库或者网上可能还能找到相关的链接。现在,我们推荐使用.array.to_numpy,避免使用.values。.values有以下缺点:
1.如果你的Series中有扩展类型(extension type),很难辨别.values返回的是NumPy数组还是扩展数组。Series.array返回的总是ExtensionArray,并且不会复制数据。Series.to_numpy()返回的总是NumPy数组,可以进行开销很大的复制/支配值操作。
2.如果你的DataFrame中含有多种数据类型,DataFrame.values操作可能会进行复制数据和支配值的操作,这会带来比较大的开销。
DataFrame.to_numpy()作为一种方法,返回值更明确,返回的NumPy数组可能不是DataFrame同一数据上的视图。

3.3.3 加速操作

pandas支持使用numexpr库和瓶颈库加速某些类型的二进制数值和布尔运算。
这些库在处理大数据集时非常有用,能提供大幅度的加速。numexpr使用智能分区、缓存和多核。bottleneck是一套特定的cython程序,在处理带有空值的数组时非常快。
举个100列X10000行的DataFrame的例子:

操作 0.11.0版本(ms) 先前的版本(ms) 比率
df1>df2 13.32 125.35 0.1063
df1*df2 21.71 36.63 0.5928
df1+df2 22.04 36.50 0.6039

这里0.11.0版本的含义待考证

强烈建议安装这两个库。更多安装的信息请看推荐的依赖库
这两个库都是默认安装并开启的,通过下面的设置可以控制:
此操作是pandas0.20.0版本的新特性

pd.set_option('compute.use_bottleneck', False)
pd.set_option('compute.use_numexpr', False)

3.3.4 灵活的二进制操作

对于pandas数据结构之间的二进制操作,有两个关键点:

行为推广(broadcasting behavior):指将某个方法运用到Series、DataFrame等数据结构的某几行或某几列的所有数据中,即操作沿着一条轴进行/推广。

3.3.4.1 匹配/传播行为

对于二进制操作,DataFrame有add()sub()mul()div()等方法和相关函数如radd()rsub()等。对于行为推广来说,Series的输入是首要关注点。用这些函数时,通过axis关键字能够选择在行或列上推广。

In [19]: df = pd.DataFrame({
   ....:     'one': pd.Series(np.random.randn(3), index=['a', 'b', 'c']),
   ....:     'two': pd.Series(np.random.randn(4), index=['a', 'b', 'c', 'd']),
   ....:     'three': pd.Series(np.random.randn(3), index=['b', 'c', 'd'])})
   ....: 

In [20]: df
Out[20]: 
        one       two     three
a  1.400810 -1.643041       NaN
b -0.356470  1.045911  0.395023
c  0.797268  0.924515 -0.007090
d       NaN  1.553693 -1.670830

In [21]: row = df.iloc[1]

In [22]: column = df['two']

In [23]: df.sub(row, axis='columns')
Out[23]: 
        one       two     three
a  1.757280 -2.688953       NaN
b  0.000000  0.000000  0.000000
c  1.153738 -0.121396 -0.402113
d       NaN  0.507782 -2.065853

In [24]: df.sub(row, axis=1)
Out[24]: 
        one       two     three
a  1.757280 -2.688953       NaN
b  0.000000  0.000000  0.000000
c  1.153738 -0.121396 -0.402113
d       NaN  0.507782 -2.065853

In [25]: df.sub(column, axis='index')
Out[25]: 
        one  two     three
a  3.043851  0.0       NaN
b -1.402381  0.0 -0.650888
c -0.127247  0.0 -0.931605
d       NaN  0.0 -3.224524

In [26]: df.sub(column, axis=0)
Out[26]: 
        one  two     three
a  3.043851  0.0       NaN
b -1.402381  0.0 -0.650888
c -0.127247  0.0 -0.931605
d       NaN  0.0 -3.224524

sub()是做减法,axis='columns'或1是沿着列方向减去b行的每个值,axis='index'或0是沿着列方向减去df['two']的每个值。

此外,还可以将多索引DataFrame的索引层与Series对齐。

In [27]: dfmi = df.copy()

In [28]: dfmi.index = pd.MultiIndex.from_tuples([(1, 'a'), (1, 'b'),
   ....:                                         (1, 'c'), (2, 'a')],
   ....:                                        names=['first', 'second'])
   ....: 

In [29]: dfmi.sub(column, axis=0, level='second')
Out[29]: 
                   one       two     three
first second                              
1     a       3.043851  0.000000       NaN
      b      -1.402381  0.000000 -0.650888
      c      -0.127247  0.000000 -0.931605
2     a            NaN  3.196734 -0.027789

注意这里只有first=1、second=['a','b','c']与Series(即上面的要减的coLumn)对齐了,所以在结果中的first=2、second='a'没有变化。

在Panel中,描述行为推广有一点困难,我们提供了算术方法(可能有点令人困惑)来供你指定推广轴(broadcast axis)。举个例子,假设我们希望沿着一条特定的轴对数据进行降级(demean)。这可以通过在轴上取平均值并在同一轴上广播来实现。

In [30]: major_mean = wp.mean(axis='major')

In [31]: major_mean
Out[31]: 
      Item1     Item2
A -0.378069  0.675929
B -0.241429 -0.018080
C -0.597702  0.129006
D  0.204005  0.245570

In [32]: wp.sub(major_mean, axis='major')
Out[32]: 
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 5 (major_axis) x 4 (minor_axis)
Items axis: Item1 to Item2
Major_axis axis: 2000-01-01 00:00:00 to 2000-01-05 00:00:00
Minor_axis axis: A to D

对于axis="items"和axis="minor"也一样。
注意: 我(指pandas作者)本可以让DataFrame和Panel的行为推广的 axis 参数设置成一样的。虽然这需要一段转换期,以便于用户能够更改他们的代码。

这里举个的例子的代码不全,应该是从文档的某个地方摘出来的,后续译者再补充进去。

Series和Index也支持python内置的divmod()函数。这个函数同时进行整除除和取余操作,返回一个包含商和余数的元组(a // b, a % b),元组的类型和左边的a参数一致。例如:

In [33]: s = pd.Series(np.arange(10))

In [34]: s
Out[34]: 
0    0
1    1
2    2
3    3
4    4
5    5
6    6
7    7
8    8
9    9
dtype: int64

In [35]: div, rem = divmod(s, 3)

In [36]: div
Out[36]: 
0    0
1    0
2    0
3    1
4    1
5    1
6    2
7    2
8    2
9    3
dtype: int64

In [37]: rem
Out[37]: 
0    0
1    1
2    2
3    0
4    1
5    2
6    0
7    1
8    2
9    0
dtype: int64

In [38]: idx = pd.Index(np.arange(10))

In [39]: idx
Out[39]: Int64Index([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='int64')

In [40]: div, rem = divmod(idx, 3)

In [41]: div
Out[41]: Int64Index([0, 0, 0, 1, 1, 1, 2, 2, 2, 3], dtype='int64')

In [42]: rem
Out[42]: Int64Index([0, 1, 2, 0, 1, 2, 0, 1, 2, 0], dtype='int64')

也可以进行元素级别的divmod()

In [43]: div, rem = divmod(s, [2, 2, 3, 3, 4, 4, 5, 5, 6, 6])

In [44]: div
Out[44]: 
0    0
1    0
2    0
3    1
4    1
5    1
6    1
7    1
8    1
9    1
dtype: int64

In [45]: rem
Out[45]: 
0    0
1    1
2    2
3    0
4    0
5    1
6    1
7    2
8    2
9    3
dtype: int64

这里不是对Series的每个元素分别除以同样的除数,而是除以相对应的除数。

3.3.4.2 缺失数据的填充

在Series和DataFrame中,算术函数有一个可选的输入参数fill_value,即当数据结构中至多只有少数缺失值时可以用一个值来代替。例如,当把两个DataFrame相加时,你可能想把NaN当作0来处理,除非两个DataFrame的这个值都是NaN。但是相加的实际情况是只要两个DataFrame中有一个为NaN,相加的结果就为NaN。(不过如果你愿意,你之后也可以用fillna()函数来替换掉那些NaN值。)

In [46]: df
Out[46]: 
        one       two     three
a  1.400810 -1.643041       NaN
b -0.356470  1.045911  0.395023
c  0.797268  0.924515 -0.007090
d       NaN  1.553693 -1.670830

In [47]: df2
Out[47]: 
        one       two     three
a  1.400810 -1.643041  1.000000
b -0.356470  1.045911  0.395023
c  0.797268  0.924515 -0.007090
d       NaN  1.553693 -1.670830

In [48]: df + df2
Out[48]: 
        one       two     three
a  2.801620 -3.286083       NaN
b -0.712940  2.091822  0.790046
c  1.594536  1.849030 -0.014180
d       NaN  3.107386 -3.341661

In [49]: df.add(df2, fill_value=0)
Out[49]: 
        one       two     three
a  2.801620 -3.286083  1.000000
b -0.712940  2.091822  0.790046
c  1.594536  1.849030 -0.014180
d       NaN  3.107386 -3.341661
3.3.4.3 灵活的比较

Series和DataFrame有二进制的比较方法eq、ne、lt、gt、le和ge,操作和上面描述的二进制算术操作类似。

In [50]: df.gt(df2)
Out[50]: 
     one    two  three
a  False  False  False
b  False  False  False
c  False  False  False
d  False  False  False

In [51]: df2.ne(df)
Out[51]: 
     one    two  three
a  False  False   True
b  False  False  False
c  False  False  False
d   True  False  False

这些操作会创建一个和左边输入的对象同样结构的布尔类型的pandas对象。新产生的布尔值对象能用在索引操作中,详情请看布尔索引部分

3.3.4.4 布尔值约简

你可能会使用emptyany()all()bool()等约简方式来汇总得到的布尔值。

In [52]: (df > 0).all()
Out[52]: 
one      False
two      False
three    False
dtype: bool

In [53]: (df > 0).any()
Out[53]: 
one      True
two      True
three    True
dtype: bool

你能约简到最终的结果:

In [54]: (df > 0).any().any()
Out[54]: True

只有组内有一个值是true,any()就会返回true

通过empty特性,能够测试pandas对象是否为空。

In [55]: df.empty
Out[55]: False

In [56]: pd.DataFrame(columns=list('ABC')).empty
Out[56]: True

判断单个元素的pandas对象的真假,可以用bool()方法:

In [57]: pd.Series([True]).bool()
Out[57]: True

In [58]: pd.Series([False]).bool()
Out[58]: False

In [59]: pd.DataFrame([[True]]).bool()
Out[59]: True

In [60]: pd.DataFrame([[False]]).bool()
Out[60]: False

<u></u><font color=red>警告:</font>你可能会尝试以下操作:

>>> if df:
...     pass

或者

>>> df and df2

这两种操作都会导致错误,因为你在试图比较多个值:

ValueError: The truth value of an array is ambiguous. Use a.empty, a.any() or a.all().
#值错误:数组的真值是含混的。请使用a.empty,a.any()或者a.all()。

详情请看gotchas

3.3.4.5 比较对象是否相等

你经常会发现计算出同样结果的方式不止一种。举个简单的例子,比如df+df和df*2。要测试这两个计算是否产生同样的结果,考虑到上面讲的工具,你可能会想用(df+df==df*2).all()。但是事实上,这个表达是错的。

In [61]: df + df == df * 2
Out[61]: 
     one   two  three
a   True  True  False
b   True  True   True
c   True  True   True
d  False  True   True

In [62]: (df + df == df * 2).all()
Out[62]: 
one      False
two       True
three    False
dtype: bool

注意布尔值的DataFrame df+df==df*2包含一些False值。这是因为NaNs并不被视为相等:

In [63]: np.nan == np.nan
Out[63]: False

因此,n维结构(比如Series、DataFrame和Panel)有equals()方法来比较是否相等,equals()方法会把对应位置的NaN值视为相等。

In [64]: (df + df).equals(df * 2)
Out[64]: True

注意Series和DataFrame的索引要是同样的顺序,这样才会被识别为相等。

In [65]: df1 = pd.DataFrame({'col': ['foo', 0, np.nan]})

In [66]: df2 = pd.DataFrame({'col': [np.nan, 0, 'foo']}, index=[2, 1, 0])

In [67]: df1.equals(df2)
Out[67]: False

In [68]: df1.equals(df2.sort_index())
Out[68]: True
3.3.4.6 比较类似数组的对象

当把pandas数据结构同一个标量值比较时,很容易就能进行元素级别的比较:

In [69]: pd.Series(['foo', 'bar', 'baz']) == 'foo'
Out[69]: 
0     True
1    False
2    False
dtype: bool

In [70]: pd.Index(['foo', 'bar', 'baz']) == 'foo'
Out[70]: array([ True, False, False], dtype=bool)

pandas也支持不同的数组类(比如Series、Index、np.array)之间的元素级别的比较:

In [71]: pd.Series(['foo', 'bar', 'baz']) == pd.Index(['foo', 'bar', 'qux'])
Out[71]: 
0     True
1     True
2    False
dtype: bool

In [72]: pd.Series(['foo', 'bar', 'baz']) == np.array(['foo', 'bar', 'qux'])
Out[72]: 
0     True
1     True
2    False
dtype: bool

但是比较不同长度的Series或Index对象时会报错:

In [55]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo', 'bar'])
ValueError: Series lengths must match to compare

In [56]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo'])
ValueError: Series lengths must match to compare

注意,这和NumPy不同,在后者中比较是可以传递的:

In [73]: np.array([1, 2, 3]) == np.array([2])
Out[73]: array([False,  True, False], dtype=bool)

如果比较不能传递,会返回False:

In [74]: np.array([1, 2, 3]) == np.array([1, 2])
Out[74]: False
3.3.4.7 组合有重叠部分的数据集

在组合两个相似的并且其中一个优先级更高的数据集时,有时会产生一个问题,举个例子,这里有两个Series同时呈现一个特定的经济指标,但是其中的质量更高。但是,低质量的Series可能能回溯更多的历史数据和保留更多的原始数据。正因为如此,在组合两个DataFrame时,我们更愿意把更好的数据集中的缺失值有条件地用其他DataFrame中的相似标签的值来代替。执行这个操作的函数是combine_first(),具体请看下面的解释:

In [75]: df1 = pd.DataFrame({'A': [1., np.nan, 3., 5., np.nan],
   ....:                     'B': [np.nan, 2., 3., np.nan, 6.]})
   ....: 

In [76]: df2 = pd.DataFrame({'A': [5., 2., 4., np.nan, 3., 7.],
   ....:                     'B': [np.nan, np.nan, 3., 4., 6., 8.]})
   ....: 

In [77]: df1
Out[77]: 
     A    B
0  1.0  NaN
1  NaN  2.0
2  3.0  3.0
3  5.0  NaN
4  NaN  6.0

In [78]: df2
Out[78]: 
     A    B
0  5.0  NaN
1  2.0  NaN
2  4.0  3.0
3  NaN  4.0
4  3.0  6.0
5  7.0  8.0

In [79]: df1.combine_first(df2)
Out[79]: 
     A    B
0  1.0  NaN
1  2.0  2.0
2  3.0  3.0
3  5.0  4.0
4  3.0  6.0
5  7.0  8.0

A中的NaN值用B中的值代替了。A中缺少index=5的值,也用B中的值补全了。

3.3.4.8 一般的DataFrame组合

上面的combine_first()方法调用更通用的dataframe.combine(),此方法采用另一个DataFrame和合并器函数,对齐输入的DataFrame,然后传递序列的合并器函数对(即名称相同的列)。
例如,现在要复现上面的combine_first():

In [80]: def combiner(x, y):
   ....:     np.where(pd.isna(x), y, x)
   ....: df1.combine(df2, combiner)
   ....: 
Out[80]: 
     A    B
0  NaN  NaN
1  NaN  NaN
2  NaN  NaN
3  NaN  NaN
4  NaN  NaN
5  NaN  NaN

注意这里的文档有误,combiner函数里应该加个return关键字,正是因为缺少返回值,所以最后输出的结果全为空值。下面请看改正后运行的结果:

In[22]:def combiner(x, y):
    return np.where(pd.isna(x), y, x)
df1.combine(df2, combiner)

Out[22]:
     A    B   
0   1.0 NaN
1   2.0 2.0
2   3.0 3.0
3   5.0 4.0
4   3.0 6.0
5   7.0 8.0

3.3.5 统计描述

pandas对于Series、DataFrame和Panel的统计描述计算和相关操作有很多方法。其中大多数都是聚合函数(会产生一个比原结构维度低的结果),比如sum()mean()quantile(),但是也有一些别的函数,会返回和原结构同样维度大小的结果,比如cumsum()cumprod()
通常来讲,ndarray.{sum,std,...}这些方法需要一个axis(轴)参数,可以通过整数或者名字指定:

In [81]: df
Out[81]: 
        one       two     three
a  1.400810 -1.643041       NaN
b -0.356470  1.045911  0.395023
c  0.797268  0.924515 -0.007090
d       NaN  1.553693 -1.670830

In [82]: df.mean(0)
Out[82]: 
one      0.613869
two      0.470270
three   -0.427633
dtype: float64

In [83]: df.mean(1)
Out[83]: 
a   -0.121116
b    0.361488
c    0.571564
d   -0.058569
dtype: float64

所有方法都有一个可选参数skipna,可以决定是否去除缺失值(skipna默认是True):

In [84]: df.sum(0, skipna=False)
Out[84]: 
one           NaN
two      1.881078
three         NaN
dtype: float64

In [85]: df.sum(axis=1, skipna=True)
Out[85]: 
a   -0.242232
b    1.084464
c    1.714693
d   -0.117137
dtype: float64

不去除缺失值的话,只要有NaN,就会被计算为NaN。

结合推广/算术行为,可以非常简洁地描述各种统计过程,如标准化(standardization)(呈现数据零均值化(zero-mean)和标准差):

In [86]: ts_stand = (df - df.mean()) / df.std()

In [87]: ts_stand.std()
Out[87]: 
one      1.0
two      1.0
three    1.0
dtype: float64

In [88]: xs_stand = df.sub(df.mean(1), axis=0).div(df.std(1), axis=0)

In [89]: xs_stand.std(1)
Out[89]: 
a    1.0
b    1.0
c    1.0
d    1.0
dtype: float64

注意cumsum()和cumprod()方法保存了NaN值的位置。这与expanding()rolling()有点不同。详情请看注释

In [90]: df.cumsum()
Out[90]: 
        one       two     three
a  1.400810 -1.643041       NaN
b  1.044340 -0.597130  0.395023
c  1.841608  0.327385  0.387933
d       NaN  1.881078 -1.282898

下面是常用函数的快速参考摘要表。如果目标对象有多重索引,每个函数还可以传入可选的level参数。

函数 描述
count 非空的观察值的数量
sum 求和
mean 平均值
mad 平均绝对偏差
median 算术中值
min 最小值
max 最大值
mode 模式
abs 绝对值
prod 乘积
std Bessel校正样品标准差
var 无偏方差
sem 平均值的标准误差
skew 样品偏度(第三力矩)
kurt 样品峰度(第四力矩)
quantile 样品分位数(值为百分数)
cumsum 累积和
cumprod 累积积
cummax 累积最大值
cummin 累积最小值

请注意一些NumPy方法比如mean、std和sum,可能会默认排除Series中的空值(NA)。

In [91]: np.mean(df['one'])
Out[91]: 0.6138692844180106

In [92]: np.mean(df['one'].to_numpy())
Out[92]: nan

series.nunique()将返回序列中所有唯一的非NA值的数目:

In [93]: series = pd.Series(np.random.randn(500))

In [94]: series[20:500] = np.nan

In [95]: series[10:20] = 5

In [96]: series.nunique()
Out[96]: 11
3.3.5.1 数据摘要:describe函数

describe()这个函数很方便,它能能计算Series或DataFrame的各列的各种统计描述值(当然会排除空值):

In [97]: series = pd.Series(np.random.randn(1000))

In [98]: series[::2] = np.nan

In [99]: series.describe()
Out[99]: 
count    500.000000
mean      -0.020695
std        1.011840
min       -2.683763
25%       -0.709297
50%       -0.070211
75%        0.712856
max        3.160915
dtype: float64

In [100]: frame = pd.DataFrame(np.random.randn(1000, 5),
   .....:                      columns=['a', 'b', 'c', 'd', 'e'])
   .....: 

In [101]: frame.iloc[::2] = np.nan

In [102]: frame.describe()
Out[102]: 
                a           b           c           d           e
count  500.000000  500.000000  500.000000  500.000000  500.000000
mean     0.026515    0.022952   -0.047307   -0.052551    0.011210
std      1.016752    0.980046    1.020837    1.008271    1.006726
min     -3.000951   -2.637901   -3.303099   -3.159200   -3.188821
25%     -0.647623   -0.593587   -0.709906   -0.691338   -0.689176
50%      0.047578   -0.026675   -0.029655   -0.032769   -0.015775
75%      0.723946    0.771931    0.603753    0.667044    0.652221
max      2.740139    2.752332    3.004229    2.728702    3.240991

而且你能自定义要在结果中展现的具体百分数值:

In [103]: series.describe(percentiles=[.05, .25, .75, .95])
Out[103]: 
count    500.000000
mean      -0.020695
std        1.011840
min       -2.683763
5%        -1.641337
25%       -0.709297
50%       -0.070211
75%        0.712856
95%        1.699176
max        3.160915
dtype: float64

结果会默认包含中位数。
对于非数值类的Series对象,describe()函数只会给出一个简单的统计,包括唯一值的数目和出现最频繁的值。

In [104]: s = pd.Series(['a', 'a', 'b', 'b', 'a', 'a', np.nan, 'c', 'd', 'a'])

In [105]: s.describe()
Out[105]: 
count     9
unique    4
top       a
freq      5
dtype: object

另外对于混合类型的DataFrame对象,describe()将会只给出数值型列的摘要,如果没有数值型的列,那么只会给出category类的列的摘要:

In [106]: frame = pd.DataFrame({'a': ['Yes', 'Yes', 'No', 'No'], 'b': range(4)})

In [107]: frame.describe()
Out[107]: 
              b
count  4.000000
mean   1.500000
std    1.290994
min    0.000000
25%    0.750000
50%    1.500000
75%    2.250000
max    3.000000

这个函数也能自定义,只需要传入include/exclude参数就行。特殊值也可以使用:

In [108]: frame.describe(include=['object'])
Out[108]: 
          a
count     4
unique    2
top     Yes
freq      2

In [109]: frame.describe(include=['number'])
Out[109]: 
              b
count  4.000000
mean   1.500000
std    1.290994
min    0.000000
25%    0.750000
50%    1.500000
75%    2.250000
max    3.000000

In [110]: frame.describe(include='all')
Out[110]: 
          a         b
count     4  4.000000
unique    2       NaN
top     Yes       NaN
freq      2       NaN
mean    NaN  1.500000
std     NaN  1.290994
min     NaN  0.000000
25%     NaN  0.750000
50%     NaN  1.500000
75%     NaN  2.250000
max     NaN  3.000000

这个功能依赖于选择数据类型。有关可行的输入的详细信息,请参阅此处。

3.3.5.2 最小/大值索引

idxmin()idxmax()函数能计算出Series和DataFrame中最小/大值的索引标签。

In [111]: s1 = pd.Series(np.random.randn(5))

In [112]: s1
Out[112]: 
0   -0.068822
1   -1.129788
2   -0.269798
3   -0.375580
4    0.513381
dtype: float64

In [113]: s1.idxmin(), s1.idxmax()
Out[113]: (1, 4)

In [114]: df1 = pd.DataFrame(np.random.randn(5, 3), columns=['A', 'B', 'C'])

In [115]: df1
Out[115]: 
          A         B         C
0  0.333329 -0.910090 -1.321220
1  2.111424  1.701169  0.858336
2 -0.608055 -2.082155 -0.069618
3  1.412817 -0.562658  0.770042
4  0.373294 -0.965381 -1.607840

In [116]: df1.idxmin(axis=0)
Out[116]: 
A    2
B    2
C    4
dtype: int64

In [117]: df1.idxmax(axis=1)
Out[117]: 
0    A
1    A
2    C
3    A
4    A
dtype: object

如果有多个行或列符合最小/大值,idxmin()和idxmax()会返回第一个匹配的索引。

In [118]: df3 = pd.DataFrame([2, 1, 1, 3, np.nan], columns=['A'], index=list('edcba'))

In [119]: df3
Out[119]: 
     A
e  2.0
d  1.0
c  1.0
b  3.0
a  NaN

In [120]: df3['A'].idxmin()
Out[120]: 'd'

注意: idxmin和idxmax在NumPy中被叫做argmin和argmax。

3.3.5.3 计数(histogramming)/模式

Series的value_counts()方法和顶级函数会计算出一维数组的柱状图。对于常规的数组也可以用这个方法:

In [121]: data = np.random.randint(0, 7, size=50)

In [122]: data
Out[122]: 
array([6, 4, 1, 3, 4, 4, 4, 6, 5, 2, 6, 1, 0, 4, 3, 2, 5, 3, 4, 0, 5, 3, 0,
       1, 5, 0, 1, 5, 3, 4, 1, 2, 3, 2, 4, 6, 1, 4, 3, 5, 2, 1, 2, 4, 1, 6,
       3, 6, 3, 3])

In [123]: s = pd.Series(data)

In [124]: s.value_counts()
Out[124]: 
4    10
3    10
1     8
6     6
5     6
2     6
0     4
dtype: int64

In [125]: pd.value_counts(data)
Out[125]: 
4    10
3    10
1     8
6     6
5     6
2     6
0     4
dtype: int64

类似的,在Series和DataFrame中,你也能获取最常出现的值或模式:

In [126]: s5 = pd.Series([1, 1, 3, 3, 3, 5, 5, 7, 7, 7])

In [127]: s5.mode()
Out[127]: 
0    3
1    7
dtype: int64

In [128]: df5 = pd.DataFrame({"A": np.random.randint(0, 7, size=50),
   .....:                     "B": np.random.randint(-10, 15, size=50)})
   .....: 

In [129]: df5.mode()
Out[129]: 
   A  B
0  0 -9
3.3.5.4 离散化和分位数

连续值能够通过cut()(容器基于值)和qcut()(容器基于样品分位数)函数进行离散化。(结果会得到很多容器)

In [130]: arr = np.random.randn(20)

In [131]: factor = pd.cut(arr, 4)

In [132]: factor
Out[132]: 
[(1.27, 2.31], (0.231, 1.27], (-0.809, 0.231], (-1.853, -0.809], (1.27, 2.31], ..., (0.231, 1.27], (-0.809, 0.231], (-1.853, -0.809], (1.27, 2.31], (0.231, 1.27]]
Length: 20
Categories (4, interval[float64]): [(-1.853, -0.809] < (-0.809, 0.231] < (0.231, 1.27] < (1.27, 2.31]]

In [133]: factor = pd.cut(arr, [-5, -1, 0, 1, 5])

In [134]: factor
Out[134]: 
[(1, 5], (0, 1], (-1, 0], (-5, -1], (1, 5], ..., (1, 5], (-1, 0], (-5, -1], (1, 5], (0, 1]]
Length: 20
Categories (4, interval[int64]): [(-5, -1] < (-1, 0] < (0, 1] < (1, 5]]

qcut()计算出样品分位数。例如,我们能够把一些常规分布的数据切分成同样大小的分段:

In [135]: arr = np.random.randn(30)

In [136]: factor = pd.qcut(arr, [0, .25, .5, .75, 1])

In [137]: factor
Out[137]: 
[(-2.219, -0.669], (-0.669, 0.00453], (0.367, 2.369], (0.00453, 0.367], (0.367, 2.369], ..., (0.00453, 0.367], (0.367, 2.369], (0.00453, 0.367], (-0.669, 0.00453], (0.367, 2.369]]
Length: 30
Categories (4, interval[float64]): [(-2.219, -0.669] < (-0.669, 0.00453] < (0.00453, 0.367] <
                                    (0.367, 2.369]]

In [138]: pd.value_counts(factor)
Out[138]: 
(0.367, 2.369]       8
(-2.219, -0.669]     8
(0.00453, 0.367]     7
(-0.669, 0.00453]    7
dtype: int64

我们也能传递无限制来定义容器:

In [139]: arr = np.random.randn(20)

In [140]: factor = pd.cut(arr, [-np.inf, 0, np.inf])

In [141]: factor
Out[141]: 
[(0.0, inf], (-inf, 0.0], (-inf, 0.0], (-inf, 0.0], (-inf, 0.0], ..., (-inf, 0.0], (-inf, 0.0], (0.0, inf], (-inf, 0.0], (-inf, 0.0]]
Length: 20
Categories (2, interval[float64]): [(-inf, 0.0] < (0.0, inf]]
上一篇下一篇

猜你喜欢

热点阅读