pandas文档0.24.1第三章 马上开始(二)
目录:
第一章 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)。
- shape:给出对象的轴维度,与NumPy的n维数组一致
- 轴标签
- Series:索引(index是Series唯一的轴)
- DataFrame:索引(行)和列
- Panel:条目(items)、主轴(major_axis)和次轴(minor_axis)
提示:可以大胆地给这些属性赋值。
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.values或DataFrame.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数据结构之间的二进制操作,有两个关键点:
- 在高维对象(如DataFrame)和低维对象(如Series)之间进行行为推广(broadcasting behavior)
- 计算中的缺失数据
虽然这两个问题能被同时解决,但是我们将分别说明如何处理这些问题。
行为推广(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 布尔值约简
你可能会使用empty、any()、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(轴)参数,可以通过整数或者名字指定:
- Series:不需要axis参数
- DataFrame:"index"(axis=0,默认值),"columns"(axis=1)
- Panel:"items"(axis=0),"major"(axis=1,默认值),"minor"(axis=2)
For example:
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]]