Python pandas 0.19.1 Intro to Data Structures 数据结构介绍 文档翻译
官方文档链接http://pandas.pydata.org/pandas-docs/stable/dsintro.html
数据结构介绍
我们将以一个快速的、非全面的pandas的基础数据结构概述来开始。应用在所有对象的数据类型、索引和轴标签/对齐等的基础操作。首先我们需要向你的命名空间引入numpy和pandas。
In [1]: import numpy as np In [2]: import pandas as pd
有个宗旨需要牢记:数据对齐是内在的。标签和数据间的链接不会被轻易改变,除非你明确地做了相应的操作来让它改变。
我们将会对数据结构进行简要的介绍,然后会考虑到单独部分的功能和方法。
Series
Series
是一个一维的、可容纳任何数据类型(整型,字符串,浮点型数据,Python对象等)的标记数组。它的轴标签统称为索引。最基本的创建一个Series的方法是:
>>> s = pd.Series(data, index=index)
在这里,data可以是多种事物:
- 一个Python字典
- 一个多维数组
- 一个标量值(如5)
传入的index是一个轴标签列表。因此,根据data的不同,有如下几种情况:
基于多维数组
如果data是一个多维数组,index必须和data的长度相同。如果没有传入index,系统将会自动创建一个index,其值为[0, ..., len(data) - 1]
.
In [3]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e']) In [4]: s Out[4]: a 0.2735 b 0.6052 c -0.1692 d 1.8298 e 0.5432 dtype: float64 In [5]: s.index Out[5]: Index([u'a', u'b', u'c', u'd', u'e'], dtype='object') In [6]: pd.Series(np.random.randn(5)) Out[6]: 0 0.3674 1 -0.8230 2 -1.0295 3 -1.0523 4 -0.8502 dtype: float64
注意
从v0.8.0版本开始,pandas开始支持非唯一索引值。如果尝试一个不支持重复索引值的操作,将会引发一个异常。懒惰的原因几乎都是源于性能(有许多计算中的实例,如GroupBy中的一部分就不需要用到index)
基于字典
如果data是一个字典,如果向index 中传入的值是data中的,那么index中对应于标签数据的值将会被移出。否则,如果可能的话,将会构成一个基于排序字典的index。
In [7]: d = {'a' : 0., 'b' : 1., 'c' : 2.} In [8]: pd.Series(d) Out[8]: a 0.0 b 1.0 c 2.0 dtype: float64 In [9]: pd.Series(d, index=['b', 'c', 'd', 'a']) Out[9]: b 1.0 c 2.0 d NaN a 0.0 dtype: float64
注意
NaN在pandas代表缺失值。
基于标量值
如果data是一个标量值,就必须传入一个index。因为这个标量值的数量取决于index的长度。
In [10]: pd.Series(5., index=['a', 'b', 'c', 'd', 'e']) Out[10]: a 5.0 b 5.0 c 5.0 d 5.0 e 5.0 dtype: float64
多维数组型Series
Series
非常像一个多维数组,拥有着numpy的大部分功能。然而,切片操作也会对索引进行切片。
In [11]: s[0] Out[11]: 0.27348116325673794 In [12]: s[:3] Out[12]: a 0.2735 b 0.6052 c -0.1692 dtype: float64 In [13]: s[s > s.median()] Out[13]: b 0.6052 d 1.8298 dtype: float64 In [14]: s[[4, 3, 1]] Out[14]: e 0.5432 d 1.8298 b 0.6052 dtype: float64 In [15]: np.exp(s) Out[15]: a 1.3145 b 1.8317 c 0.8443 d 6.2327 e 1.7215 dtype: float64
我们将在一个单独的 section介绍基于数组的索引。
字典型Series
一个Series就像一个固定大小的字典,你可以通过索引标签来获取和设置Series的值:
In [16]: s['a'] Out[16]: 0.27348116325673794 In [17]: s['e'] = 12. In [18]: s Out[18]: a 0.2735 b 0.6052 c -0.1692 d 1.8298 e 12.0000 dtype: float64 In [19]: 'e' in s Out[19]: True In [20]: 'f' in s Out[20]: False
如果一个标签不包含在其中,将会引发一个异常:
>>> s['f'] KeyError: 'f'
使用get方法,缺失的标签将会返回None或者指定的缺省值:
In [21]: s.get('f') In [22]: s.get('f', np.nan) Out[22]: nan
参见属性访问部分 section on attribute access.
Series的量化操作和标签对齐
当进行数据分析时,通过Series的一个又一个值进行循环通常是没有必要的。Series能够使用大部分的numpy方法。
In [23]: s + s Out[23]: a 0.5470 b 1.2104 c -0.3385 d 3.6596 e 24.0000 dtype: float64 In [24]: s * 2 Out[24]: a 0.5470 b 1.2104 c -0.3385 d 3.6596 e 24.0000 dtype: float64 In [25]: np.exp(s) Out[25]: a 1.3145 b 1.8317 c 0.8443 d 6.2327 e 162754.7914 dtype: float64
Series和多维数组的关键区别在于Series的数据对齐是基于标签的。因此,不管你的Series是不是有相同的标签,你都可以无所顾忌地进行计算。
In [26]: s[1:] + s[:-1] Out[26]: a NaN b 1.2104 c -0.3385 d 3.6596 e NaN dtype: float64
在未对齐的Series进行操作将会涉及到索引的联合。如果一个标签在一个Series或其他中都不存在,在结果中将会被标记为缺失值。能够在未做任何明确的数据对齐的情况下编写代码,为交互式数据分析和研究提供了极大的自由和灵活性。
注意
一般来说,为了避免信息错乱,我们选择在不同的索引对象间的操作的默认结果产生索引的联合。
name属性
Series还有一个name属性:
In [27]: s = pd.Series(np.random.randn(5), name='something') In [28]: s Out[28]: 0 1.5140 1 -1.2345 2 0.5666 3 -1.0184 4 0.1081 Name: something, dtype: float64 In [29]: s.name Out[29]: 'something'
Series的name属性在很多情况下会被自动分配,特别是在你在下面看到的dataframe的一维切片中。
0.18.0.的新版本。
你可以通过 pandas.Series.rename()
方法来对Series重命名。
In [30]: s2 = s.rename("different") In [31]: s2.name Out[31]: 'different'
注意s和s2是不同的对象。
DataFrame
DataFrame 是一个拥有不同类型的列的二维的标记数据结构。你可以把它想成一个电子表格或数据库表,或者一个Seires对象的字典。它是pandas对象中最常用的一种。和Series一样,dataframe能够接受不同类型的输入:
- 一维数组,列表或Seires的字典
- 二维的bumpy数组
- Structured or record结构化或记录数组
- 一个
Series
另外一个DataFrame
对于dataframe中的data, 你可以选择传入index(行标签)和columns(列标签)。如果你传入了一个index和/或columns,你必须保证你传入的生成data frame的index和/或columns。因此,一个Series字典加上一个明确的索引将会丢弃所有不匹配索引的数据。
如果没有传入轴标签,系统将会基于常识规则根据输入的数据构造轴标签。
基于Series字典或字典的data frame
最终index是多种Series的index的联合。如果有任何嵌套字典,将会首先被转换成Series。如果没有传入列,列将会是字典的键的排序列表。
In [32]: d = {'one' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']), ....: 'two' : pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])} ....: In [33]: df = pd.DataFrame(d) In [34]: df Out[34]: one two a 1.0 1.0 b 2.0 2.0 c 3.0 3.0 d NaN 4.0 In [35]: pd.DataFrame(d, index=['d', 'b', 'a']) Out[35]: one two d NaN 4.0 b 2.0 2.0 a 1.0 1.0 In [36]: pd.DataFrame(d, index=['d', 'b', 'a'], columns=['two', 'three']) Out[36]: two three d 4.0 NaN b 2.0 NaN a 1.0 NaN
行和列标签能够分别通过访问index和columns属性进行访问。
注意
当一个特殊的列集合和字典同时被传入时,传入的列将会重写字典中的键。In [37]: df.index Out[37]: Index([u'a', u'b', u'c', u'd'], dtype='object') In [38]: df.columns Out[38]: Index([u'one', u'two'], dtype='object')
基于多维数组/列表的字典的data frame
多维数组必须都是相同长度的,如果传入了一个index,它必须和数组的长度相同。如果没有传入index,那么自动生成的index就是range(n),其中n是数组的长度。
In [39]: d = {'one' : [1., 2., 3., 4.], ....: 'two' : [4., 3., 2., 1.]} ....: In [40]: pd.DataFrame(d) Out[40]: one two 0 1.0 4.0 1 2.0 3.0 2 3.0 2.0 3 4.0 1.0 In [41]: pd.DataFrame(d, index=['a', 'b', 'c', 'd']) Out[41]: one two a 1.0 4.0 b 2.0 3.0 c 3.0 2.0 d 4.0 1.0
基于结构化或记录数组的data frame
这种情况和数组字典的情况相同。
In [42]: data = np.zeros((2,), dtype=[('A', 'i4'),('B', 'f4'),('C', 'a10')]) In [43]: data[:] = [(1,2.,'Hello'), (2,3.,"World")] In [44]: pd.DataFrame(data) Out[44]: A B C 0 1 2.0 Hello 1 2 3.0 World In [45]: pd.DataFrame(data, index=['first', 'second']) Out[45]: A B C first 1 2.0 Hello second 2 3.0 World In [46]: pd.DataFrame(data, columns=['C', 'A', 'B']) Out[46]: C A B 0 Hello 1 2.0 1 World 2 3.0
注意
DataFrame 并不打算向二维bumpy数组那样.
基于字典列表的data frame
In [47]: data2 = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}] In [48]: pd.DataFrame(data2) Out[48]: a b c 0 1 2 NaN 1 5 10 20.0 In [49]: pd.DataFrame(data2, index=['first', 'second']) Out[49]: a b c first 1 2 NaN second 5 10 20.0 In [50]: pd.DataFrame(data2, columns=['a', 'b']) Out[50]: a b 0 1 2 1 5 10
基于集合字典的data frame
你可以通过传入一个集合字典来自动创建一个多索引的frame。
In [51]: pd.DataFrame({('a', 'b'): {('A', 'B'): 1, ('A', 'C'): 2}, ....: ('a', 'a'): {('A', 'C'): 3, ('A', 'B'): 4}, ....: ('a', 'c'): {('A', 'B'): 5, ('A', 'C'): 6}, ....: ('b', 'a'): {('A', 'C'): 7, ('A', 'B'): 8}, ....: ('b', 'b'): {('A', 'D'): 9, ('A', 'B'): 10}}) ....: Out[51]: a b a b c a b A B 4.0 1.0 5.0 8.0 10.0 C 3.0 2.0 6.0 7.0 NaN D NaN NaN NaN NaN 9.0
基于一个Series
创建的结果是一个和输入的Series有着相同index的dataframe ,其中一列的名字是Series的原始名字(如果其他列的名字没有提供的话)
缺失数据
更多详见 Missing data 部分。使用np.nan能够构造一个包含缺失值的data frame。或者,你还可以传入一个numpy.MaskedArray,它的mask项目将被视为缺失值。
交替的构造函数
DataFrame.from_dict
DataFrame.from_dict
接收一个字典或一个数组序列的字典,并返回一个data frame。 It operates like the DataFrame
constructor except for the orient
parameter which is 'columns'
by default, but which can be set to 'index'
in order to use the dict keys as row labels.
DataFrame.from_records
DataFrame.from_records
接收一个集合列表或结构化类型的多维数组。它与正常的dataframe构造器类似,不同的是它的index或许是一个特定的结构化类型。例如:
In [52]: data Out[52]: array([(1, 2.0, 'Hello'), (2, 3.0, 'World')], dtype=[('A', '<i4'), ('B', '<f4'), ('C', 'S10')]) In [53]: pd.DataFrame.from_records(data, index='C') Out[53]: A B C Hello 1 2.0 World 2 3.0
DataFrame.from_items
DataFrame.from_items的形式也与dataframe构造器类似,它接收一个(键,值)序列,其中键是列名(或行名,如果零orient=“index”的话),值是列值(或行值)。这对于构造一个特定顺序列dataframe来说非常有用。
In [54]: pd.DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])]) Out[54]: A B 0 1 4 1 2 5 2 3 6
如果你传入 orient='index’关键字,值就会变成行标签。但是这种情况下你必须接着传入列名:
In [55]: pd.DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])], ....: orient='index', columns=['one', 'two', 'three']) ....: Out[55]: one two three A 1 2 3 B 4 5 6
列的选取,增加和删除
你可以使用一个相同indexed的Series对象的字典的语法对dataframe进行操作。获取,设置和删除列的语法和字典操作类似:
In [56]: df['one'] Out[56]: a 1.0 b 2.0 c 3.0 d NaN Name: one, dtype: float64 In [57]: df['three'] = df['one'] * df['two'] In [58]: df['flag'] = df['one'] > 2 In [59]: df Out[59]: one two three flag a 1.0 1.0 1.0 False b 2.0 2.0 4.0 False c 3.0 3.0 9.0 True d NaN 4.0 NaN False
列可以向一个字典那样被删除或移除:
In [60]: del df['two'] In [61]: three = df.pop('three') In [62]: df Out[62]: one flag a 1.0 False b 2.0 False c 3.0 True d NaN False
当想要插入一个标量值时,它会自动填充整列:
In [63]: df['foo'] = 'bar' In [64]: df Out[64]: one flag foo a 1.0 False bar b 2.0 False bar c 3.0 True bar d NaN False bar
当插入一个与当前dataframe有着不同index的Series时,将会以dataframe的index为基准。
In [65]: df['one_trunc'] = df['one'][:2] In [66]: df Out[66]: one flag foo one_trunc a 1.0 False bar 1.0 b 2.0 False bar 2.0 c 3.0 True bar NaN d NaN False bar NaN
你可以插入原始多维数组,但是它们的长度必须和dataframe的index的长度相匹配。
默认情况下会在末尾插入列。inser功能允许在特定位置插入列:
In [67]: df.insert(1, 'bar', df['one']) In [68]: df Out[68]: one bar flag foo one_trunc a 1.0 1.0 False bar 1.0 b 2.0 2.0 False bar 2.0 c 3.0 3.0 True bar NaN d NaN NaN False bar NaN
在方法链中分配新列
受 dplyr’s 动词的变异, DataFrame 有一个 assign()
方法能够允许你轻易地创建一个从现有列派生出的新列。
In [69]: iris = pd.read_csv('data/iris.data') In [70]: iris.head() Out[70]: SepalLength SepalWidth PetalLength PetalWidth Name 0 5.1 3.5 1.4 0.2 Iris-setosa 1 4.9 3.0 1.4 0.2 Iris-setosa 2 4.7 3.2 1.3 0.2 Iris-setosa 3 4.6 3.1 1.5 0.2 Iris-setosa 4 5.0 3.6 1.4 0.2 Iris-setosa In [71]: (iris.assign(sepal_ratio = iris['SepalWidth'] / iris['SepalLength']) ....: .head()) ....: Out[71]: SepalLength SepalWidth PetalLength PetalWidth Name sepal_ratio 0 5.1 3.5 1.4 0.2 Iris-setosa 0.6863 1 4.9 3.0 1.4 0.2 Iris-setosa 0.6122 2 4.7 3.2 1.3 0.2 Iris-setosa 0.6809 3 4.6 3.1 1.5 0.2 Iris-setosa 0.6739 4 5.0 3.6 1.4 0.2 Iris-setosa 0.7200
以上是插入一个预先计算好的值的例子。我们也可以传入一个单参数的函数.
In [72]: iris.assign(sepal_ratio = lambda x: (x['SepalWidth'] / ....: x['SepalLength'])).head() ....: Out[72]: SepalLength SepalWidth PetalLength PetalWidth Name sepal_ratio 0 5.1 3.5 1.4 0.2 Iris-setosa 0.6863 1 4.9 3.0 1.4 0.2 Iris-setosa 0.6122 2 4.7 3.2 1.3 0.2 Iris-setosa 0.6809 3 4.6 3.1 1.5 0.2 Iris-setosa 0.6739 4 5.0 3.6 1.4 0.2 Iris-setosa 0.7200
assign
总是 returns返回一个数据的副本,而不会改变原data frame.
当你手边没有一个参考的dataframe时,相对于要插入的实际值来说,传入一个可变的值是非常有用的。在链式操作中使用assign是非常普遍的。例如,我们可以限制dataframe只选择那些长度大于5的SepalLength进行计算和作图:
In [73]: (iris.query('SepalLength > 5') ....: .assign(SepalRatio = lambda x: x.SepalWidth / x.SepalLength, ....: PetalRatio = lambda x: x.PetalWidth / x.PetalLength) ....: .plot(kind='scatter', x='SepalRatio', y='PetalRatio')) ....: Out[73]: <matplotlib.axes._subplots.AxesSubplot at 0x7ff286891b50>
由于传入了一个函数,该函数是在被分配到的dataframe上进行计算,重要的是,这个dataframe是那些长度大于5的行的dataframe.首先进行过滤,然后进行计算。在这个例子中,我们没有可过滤的dataframe。
assign的函数签名是简单的**kwargs。键是新字段的列名,值是用来插入的值(例如一个Series或者numpy数组),或者一个单参数的函数。将会返回一个插入了新值的dataframe的副本。
警告
由于assign的函数签名是**kwargs,是个字典,那么在生成的dataframe中插入的新列的顺序不能够保证和你传入的顺序一样。一般是在dataframe的最后按字母顺序插入。
所有的表达式会被首先计算,然后被分配。所以你不能在同一个assign中使用正在分配的列。
In [74]: # 不要这样做 df.assign(C = lambda x: x['A'] + x['B'], D = lambda x: x['A'] + x['C']) In [2]: # 而是需要把它分给两个assign (df.assign(C = lambda x: x['A'] + x['B']) .assign(D = lambda x: x['A'] + x['C']))
索引/选取
基本的索引如下所示:
操作 | 语法 | 结果 |
---|---|---|
选取列 | df[col] |
Series |
通过标签选取行 | df.loc[label] |
Series |
通过整数位置选取行 | df.iloc[loc] |
Series |
行切片 | df[5:10] |
DataFrame |
通过布尔向量选取行 | df[bool_vec] |
DataFrame |
选取行,例如,返回一个Series,其index就是dataframe的列。
In [75]: df.loc['b'] Out[75]: one 2 bar 2 flag False foo bar one_trunc 2 Name: b, dtype: object In [76]: df.iloc[2] Out[76]: one 3 bar 3 flag True foo bar one_trunc NaN Name: c, dtype: object
对于更复杂的基于标签的索引和切片的更详尽的处理,参见 section on indexing.
数据对齐和算法
dataframe对象间的数据对齐会根据索引(行标签)和列自动对齐。同样,产生的结果将是行和列标签的联合。
In [77]: df = pd.DataFrame(np.random.randn(10, 4), columns=['A', 'B', 'C', 'D']) In [78]: df2 = pd.DataFrame(np.random.randn(7, 3), columns=['A', 'B', 'C']) In [79]: df + df2 Out[79]: A B C D 0 0.5222 0.3225 -0.7566 NaN 1 -0.8441 0.2334 0.8818 NaN 2 -2.2079 -0.1572 -0.3875 NaN 3 2.8080 -1.0927 1.0432 NaN 4 -1.7511 -2.0812 2.7477 NaN 5 -3.2473 -1.0850 0.7898 NaN 6 -1.7107 0.0661 0.1294 NaN 7 NaN NaN NaN NaN 8 NaN NaN NaN NaN 9 NaN NaN NaN NaN
当在dataframe和Series间进行操作时,默认操作是将Series的index和dataframe的列对齐,并broadcasting 行。例如:
In [80]: df - df.iloc[0] Out[80]: A B C D 0 0.0000 0.0000 0.0000 0.0000 1 -2.6396 -1.0702 1.7214 -0.7896 2 -2.7662 -1.6918 2.2776 -2.5401 3 0.8679 -3.5247 1.9365 -0.1331 4 -1.9883 -3.2162 2.0464 -1.0700 5 -3.3932 -4.0976 1.6366 -2.1635 6 -1.3668 -1.9572 1.6523 -0.7191 7 -0.7949 -2.1663 0.9706 -2.6297 8 -0.8383 -1.3630 1.6702 -2.0865 9 0.8588 0.0814 3.7305 -1.3737
在对时间序列数据进行操作的特殊情况中,DataFrame 的index包含了日期,因此broadcasting将会对列进行:
In [81]: index = pd.date_range('1/1/2000', periods=8) In [82]: df = pd.DataFrame(np.random.randn(8, 3), index=index, columns=list('ABC')) In [83]: df Out[83]: A B C 2000-01-01 0.2731 0.3604 -1.1515 2000-01-02 1.1577 1.4787 -0.6528 2000-01-03 -0.7712 0.2203 -0.5739 2000-01-04 -0.6356 -1.1703 -0.0789 2000-01-05 -1.4687 0.1705 -1.8796 2000-01-06 -1.2037 0.9568 -1.1383 2000-01-07 -0.6540 -0.2169 0.3843 2000-01-08 -2.1639 -0.8145 -1.2475 In [84]: type(df['A']) Out[84]: pandas.core.series.Series In [85]: df - df['A'] Out[85]: 2000-01-01 00:00:00 2000-01-02 00:00:00 2000-01-03 00:00:00 \ 2000-01-01 NaN NaN NaN 2000-01-02 NaN NaN NaN 2000-01-03 NaN NaN NaN 2000-01-04 NaN NaN NaN 2000-01-05 NaN NaN NaN 2000-01-06 NaN NaN NaN 2000-01-07 NaN NaN NaN 2000-01-08 NaN NaN NaN 2000-01-04 00:00:00 ... 2000-01-08 00:00:00 A B C 2000-01-01 NaN ... NaN NaN NaN NaN 2000-01-02 NaN ... NaN NaN NaN NaN 2000-01-03 NaN ... NaN NaN NaN NaN 2000-01-04 NaN ... NaN NaN NaN NaN 2000-01-05 NaN ... NaN NaN NaN NaN 2000-01-06 NaN ... NaN NaN NaN NaN 2000-01-07 NaN ... NaN NaN NaN NaN 2000-01-08 NaN ... NaN NaN NaN NaN [8 rows x 11 columns]
警告
df - df['A']
现在已不再使用,并将会在以后的版本中移除。该操作正确的方法是:
df.sub(df['A'], axis=0)
对匹配和广播行为的显式控制,参见 flexible binary operations.
你期待的对标量的操作:
In [86]: df * 5 + 2 Out[86]: A B C 2000-01-01 3.3655 3.8018 -3.7575 2000-01-02 7.7885 9.3936 -1.2641 2000-01-03 -1.8558 3.1017 -0.8696 2000-01-04 -1.1781 -3.8513 1.6056 2000-01-05 -5.3437 2.8523 -7.3982 2000-01-06 -4.0186 6.7842 -3.6915 2000-01-07 -1.2699 0.9157 3.9217 2000-01-08 -8.8194 -2.0724 -4.2375 In [87]: 1 / df Out[87]: A B C 2000-01-01 3.6616 2.7751 -0.8684 2000-01-02 0.8638 0.6763 -1.5318 2000-01-03 -1.2967 4.5383 -1.7424 2000-01-04 -1.5733 -0.8545 -12.6759 2000-01-05 -0.6809 5.8662 -0.5320 2000-01-06 -0.8308 1.0451 -0.8785 2000-01-07 -1.5291 -4.6113 2.6019 2000-01-08 -0.4621 -1.2278 -0.8016 In [88]: df ** 4 Out[88]: A B C 2000-01-01 0.0056 0.0169 1.7581e+00 2000-01-02 1.7964 4.7813 1.8162e-01 2000-01-03 0.3537 0.0024 1.0849e-01 2000-01-04 0.1632 1.8755 3.8733e-05 2000-01-05 4.6534 0.0008 1.2482e+01 2000-01-06 2.0995 0.8382 1.6789e+00 2000-01-07 0.1829 0.0022 2.1819e-02 2000-01-08 21.9244 0.4401 2.4219e+00
布尔操作也是如此:
In [89]: df1 = pd.DataFrame({'a' : [1, 0, 1], 'b' : [0, 1, 1] }, dtype=bool) In [90]: df2 = pd.DataFrame({'a' : [0, 1, 1], 'b' : [1, 1, 0] }, dtype=bool) In [91]: df1 & df2 Out[91]: a b 0 False False 1 False True 2 True False In [92]: df1 | df2 Out[92]: a b 0 True True 1 True True 2 True True In [93]: df1 ^ df2 Out[93]: a b 0 True True 1 True False 2 False True In [94]: -df1 Out[94]: a b 0 False True 1 True False 2 False False
转置
转置可以通过T属性进行,和一个多维数组相似:
# 只显示前5行 In [95]: df[:5].T Out[95]: 2000-01-01 2000-01-02 2000-01-03 2000-01-04 2000-01-05 A 0.2731 1.1577 -0.7712 -0.6356 -1.4687 B 0.3604 1.4787 0.2203 -1.1703 0.1705 C -1.1515 -0.6528 -0.5739 -0.0789 -1.8796
DataFrame与 NumPy功能的交互
如果dataframe中的数据是数值型的话,Numpy的功能(如log, exp, sort等)和多种其他的numpy功能可以没有问题地使用在dataframe上。
In [96]: np.exp(df) Out[96]: A B C 2000-01-01 1.3140 1.4338 0.3162 2000-01-02 3.1826 4.3873 0.5206 2000-01-03 0.4625 1.2465 0.5633 2000-01-04 0.5296 0.3103 0.9241 2000-01-05 0.2302 1.1859 0.1526 2000-01-06 0.3001 2.6034 0.3204 2000-01-07 0.5200 0.8050 1.4686 2000-01-08 0.1149 0.4429 0.2872 In [97]: np.asarray(df) Out[97]: array([[ 0.2731, 0.3604, -1.1515], [ 1.1577, 1.4787, -0.6528], [-0.7712, 0.2203, -0.5739], [-0.6356, -1.1703, -0.0789], [-1.4687, 0.1705, -1.8796], [-1.2037, 0.9568, -1.1383], [-0.654 , -0.2169, 0.3843], [-2.1639, -0.8145, -1.2475]])
dataframe中的dot方法能够实现矩阵相乘。
In [98]: df.T.dot(df) Out[98]: A B C A 11.1298 2.8864 6.0015 B 2.8864 5.3895 -1.8913 C 6.0015 -1.8913 8.6204
相似地,Series的dot方法能够执行点积:
In [99]: s1 = pd.Series(np.arange(5,10)) In [100]: s1.dot(s1) Out[100]: 255
dataframe并不旨在做多维数组的替代,因为它的索引语法在矩阵方面和多维数组大不相同。
控制台显示
大规模的dataframe在控制台显示时将会被截断。你可以使用info()来获取一个概括。(下面我读取一个csv试图的baseball数据集)
In [101]: baseball = pd.read_csv('data/baseball.csv') In [102]: print(baseball) id player year stint ... hbp sh sf gidp 0 88641 womacto01 2006 2 ... 0.0 3.0 0.0 0.0 1 88643 schilcu01 2006 1 ... 0.0 0.0 0.0 0.0 .. ... ... ... ... ... ... ... ... ... 98 89533 aloumo01 2007 1 ... 2.0 0.0 3.0 13.0 99 89534 alomasa02 2007 1 ... 0.0 0.0 0.0 0.0 [100 rows x 23 columns] In [103]: baseball.info() <class 'pandas.core.frame.DataFrame'> RangeIndex: 100 entries, 0 to 99 Data columns (total 23 columns): id 100 non-null int64 player 100 non-null object year 100 non-null int64 stint 100 non-null int64 team 100 non-null object lg 100 non-null object g 100 non-null int64 ab 100 non-null int64 r 100 non-null int64 h 100 non-null int64 X2b 100 non-null int64 X3b 100 non-null int64 hr 100 non-null int64 rbi 100 non-null float64 sb 100 non-null float64 cs 100 non-null float64 bb 100 non-null int64 so 100 non-null float64 ibb 100 non-null float64 hbp 100 non-null float64 sh 100 non-null float64 sf 100 non-null float64 gidp 100 non-null float64 dtypes: float64(9), int64(11), object(3) memory usage: 18.0+ KB
然而,使用to_string将会返回一个表格中的dataframe的字符串表示,虽然它不会总是符合控制台的宽度:
In [104]: print(baseball.iloc[-20:, :12].to_string()) id player year stint team lg g ab r h X2b X3b 80 89474 finlest01 2007 1 COL NL 43 94 9 17 3 0 81 89480 embreal01 2007 1 OAK AL 4 0 0 0 0 0 82 89481 edmonji01 2007 1 SLN NL 117 365 39 92 15 2 83 89482 easleda01 2007 1 NYN NL 76 193 24 54 6 0 84 89489 delgaca01 2007 1 NYN NL 139 538 71 139 30 0 85 89493 cormirh01 2007 1 CIN NL 6 0 0 0 0 0 86 89494 coninje01 2007 2 NYN NL 21 41 2 8 2 0 87 89495 coninje01 2007 1 CIN NL 80 215 23 57 11 1 88 89497 clemero02 2007 1 NYA AL 2 2 0 1 0 0 89 89498 claytro01 2007 2 BOS AL 8 6 1 0 0 0 90 89499 claytro01 2007 1 TOR AL 69 189 23 48 14 0 91 89501 cirilje01 2007 2 ARI NL 28 40 6 8 4 0 92 89502 cirilje01 2007 1 MIN AL 50 153 18 40 9 2 93 89521 bondsba01 2007 1 SFN NL 126 340 75 94 14 0 94 89523 biggicr01 2007 1 HOU NL 141 517 68 130 31 3 95 89525 benitar01 2007 2 FLO NL 34 0 0 0 0 0 96 89526 benitar01 2007 1 SFN NL 19 0 0 0 0 0 97 89530 ausmubr01 2007 1 HOU NL 117 349 38 82 16 3 98 89533 aloumo01 2007 1 NYN NL 87 328 51 112 19 1 99 89534 alomasa02 2007 1 NYN NL 8 22 1 3 1 0
从0.10.0版本开始,列数过多的dataframes将默认多行显示:
In [105]: pd.DataFrame(np.random.randn(3, 12)) Out[105]: 0 1 2 3 4 5 6 \ 0 2.173014 1.273573 0.888325 0.631774 0.206584 -1.745845 -0.505310 1 -1.240418 2.177280 -0.082206 0.827373 -0.700792 0.524540 -1.101396 2 0.269598 -0.453050 -1.821539 -0.126332 -0.153257 0.405483 -0.504557 7 8 9 10 11 0 1.376623 0.741168 -0.509153 -2.012112 -1.204418 1 1.115750 0.294139 0.286939 1.709761 -0.212596 2 1.405148 0.778061 -0.799024 -0.670727 0.086877
你可以通过设置display.width选项来改变每一行显示的列数量:
In [106]: pd.set_option('display.width', 40) # 默认为80 In [107]: pd.DataFrame(np.random.randn(3, 12)) Out[107]: 0 1 2 \ 0 1.179465 0.777427 -1.923460 1 0.054928 0.776156 0.372060 2 -0.243404 -1.506557 -1.977226 3 4 5 \ 0 0.782432 0.203446 0.250652 1 0.710963 -0.784859 0.168405 2 -0.226582 -0.777971 0.231309 6 7 8 \ 0 -2.349580 -0.540814 -0.748939 1 0.159230 0.866492 1.266025 2 1.394479 0.723474 -0.097256 9 10 11 0 -0.994345 1.478624 -0.341991 1 0.555240 0.731803 0.219383 2 0.375274 -0.314401 -2.363136
你可以通过设置display.max_colwidth来调整每一列的最大宽度:
In [108]: datafile={'filename': ['filename_01','filename_02'], .....: 'path': ["media/user_name/storage/folder_01/filename_01", .....: "media/user_name/storage/folder_02/filename_02"]} .....: In [109]: pd.set_option('display.max_colwidth',30) In [110]: pd.DataFrame(datafile) Out[110]: filename \ 0 filename_01 1 filename_02 path 0 media/user_name/storage/fo... 1 media/user_name/storage/fo... In [111]: pd.set_option('display.max_colwidth',100) In [112]: pd.DataFrame(datafile) Out[112]: filename \ 0 filename_01 1 filename_02 path 0 media/user_name/storage/folder_01/filename_01 1 media/user_name/storage/folder_02/filename_02
你也可以通过expand_frame_repr 选项来禁用此功能。这将会在一个块中打印表格。
DataFrame 的列属性获取和IPython实现
如果一个dataframe列标签是一个有效的Python变量名,该列就能够通过属性的方式访问:
In [113]: df = pd.DataFrame({'foo1' : np.random.randn(5), .....: 'foo2' : np.random.randn(5)}) .....: In [114]: df Out[114]: foo1 foo2 0 -0.412237 0.213232 1 -0.237644 1.740139 2 1.272869 -0.241491 3 1.220450 -0.868514 4 1.315172 0.407544 In [115]: df.foo1 Out[115]: 0 -0.412237 1 -0.237644 2 1.272869 3 1.220450 4 1.315172 Name: foo1, dtype: float64
列还与 Python 实现机制相连,所以它们能够通过tab键实现自动完成。
In [5]: df.fo<TAB> df.foo1 df.foo2
Panel
Panel是一个用处不多但仍旧很重要的三维数据容器。词条panel data 来自于计量经济学,并且是pandas这个名称的一部分来源( pan(el)-da(ta)-s)。panel的3个轴的名称旨在为包含panel数据的描述操作提供一些语义,尤其是panel数据的计量分析。然而,为了交叉分析dataframe对象集,你可能会发现panel的轴名称有点武断:
- 项: 0轴,每一项对应于一个包含于其中的dataframe
- 主轴: 1轴, 是每个data frame的index (行)
- 次轴: 2轴,是每个data frame的columns
Panels的构成也正如你想象的那样:
基于可选轴标签的三维数组
In [116]: 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']) .....: In [117]: wp Out[117]: <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
基于dataframe对象字典的panel
In [118]: data = {'Item1' : pd.DataFrame(np.random.randn(4, 3)), .....: 'Item2' : pd.DataFrame(np.random.randn(4, 2))} .....: In [119]: pd.Panel(data) Out[119]: <class 'pandas.core.panel.Panel'> Dimensions: 2 (items) x 4 (major_axis) x 3 (minor_axis) Items axis: Item1 to Item2 Major_axis axis: 0 to 3 Minor_axis axis: 0 to 2
注意,字典中的值只需要可转换为dataframe。因此,它们可以是dataframe中任何有效的输入。
一个有用的工厂方法是Panel.from_dict
, 它能够接受一个dataframe字典和下列命名参数:
参数 | 默认 | 描述 |
---|---|---|
intersect | False |
删除指数不对齐的元素 |
orient | items |
使dataframe的列作为panel的项 |
例如,与上面的结构相比:
In [120]: pd.Panel.from_dict(data, orient='minor') Out[120]: <class 'pandas.core.panel.Panel'> Dimensions: 3 (items) x 4 (major_axis) x 2 (minor_axis) Items axis: 0 to 2 Major_axis axis: 0 to 3 Minor_axis axis: Item1 to Item2
Orient 对于混合类型的dataframe特别有用。如果你传入了一个其列是混合类型dataframe对象,除非你传入orient=‘minor
’,否则所有的数据类型都将会向上变为object类型。
In [121]: df = pd.DataFrame({'a': ['foo', 'bar', 'baz'], .....: 'b': np.random.randn(3)}) .....: In [122]: df Out[122]: a b 0 foo -1.142863 1 bar -1.015321 2 baz 0.683625 In [123]: data = {'item1': df, 'item2': df} In [124]: panel = pd.Panel.from_dict(data, orient='minor') In [125]: panel['a'] Out[125]: item1 item2 0 foo foo 1 bar bar 2 baz baz In [126]: panel['b'] Out[126]: item1 item2 0 -1.142863 -1.142863 1 -1.015321 -1.015321 2 0.683625 0.683625 In [127]: panel['b'].dtypes Out[127]: item1 float64 item2 float64 dtype: object
注意
Panel由于不如Series和DataFrame常用,所以它也不像它们那样拥有那么多的好用的功能。dataframe中许多方法和选项在panel中都不能适用。我们将会继续努力。
基于使用to_panel方法的dataframe的panel
在v0.7中介绍了用来代替LongPanel的这个方法,它能够将一个两层索引的DataFrame转换为一个panel。
In [128]: midx = pd.MultiIndex(levels=[['one', 'two'], ['x','y']], labels=[[1,1,0,0],[1,0,1,0]]) In [129]: df = pd.DataFrame({'A' : [1, 2, 3, 4], 'B': [5, 6, 7, 8]}, index=midx) In [130]: df.to_panel() Out[130]: <class 'pandas.core.panel.Panel'> Dimensions: 2 (items) x 2 (major_axis) x 2 (minor_axis) Items axis: A to B Major_axis axis: one to two Minor_axis axis: x to y
项的选取/增加/删除
和dataframe是多个Series组成的字典结构类似,panel就像是一个由多个dataframe组成的字典:
In [131]: wp['Item1'] Out[131]: A B C D 2000-01-01 -0.729430 0.427693 -0.121325 -0.736418 2000-01-02 0.739037 -0.648805 -0.383057 0.385027 2000-01-03 2.321064 -1.290881 0.105458 -1.097035 2000-01-04 0.158759 -1.261191 -0.081710 1.390506 2000-01-05 -1.962031 -0.505580 0.021253 -0.317071 In [132]: wp['Item3'] = wp['Item1'] / wp['Item2']
Panel的插入和删除的接口和dataframe一样。对于dataframe来说,如果项是一个有效的Python识别器,你可以适用Tab自动完成来像访问属性一样访问它。
转置
可以适用转置方法改变一个panel(该方法默认不是生成一个副本,除非数据是异构的)
In [133]: wp.transpose(2, 0, 1) Out[133]: <class 'pandas.core.panel.Panel'> Dimensions: 4 (items) x 3 (major_axis) x 5 (minor_axis) Items axis: A to D Major_axis axis: Item1 to Item3 Minor_axis axis: 2000-01-01 00:00:00 to 2000-01-05 00:00:00
索引/选取
操作 | 语法 | 结果 |
---|---|---|
选取项 | wp[item] |
DataFrame |
在主轴标签上切片 | wp.major_xs(val) |
DataFrame |
在次轴标签上切片 | wp.minor_xs(val) |
DataFrame |
例如,使用之前的示例数据,我们可以:
In [134]: wp['Item1'] Out[134]: A B C D 2000-01-01 -0.729430 0.427693 -0.121325 -0.736418 2000-01-02 0.739037 -0.648805 -0.383057 0.385027 2000-01-03 2.321064 -1.290881 0.105458 -1.097035 2000-01-04 0.158759 -1.261191 -0.081710 1.390506 2000-01-05 -1.962031 -0.505580 0.021253 -0.317071 In [135]: wp.major_xs(wp.major_axis[2]) Out[135]: Item1 Item2 Item3 A 2.321064 -0.538606 -4.309389 B -1.290881 0.791512 -1.630905 C 0.105458 -0.020302 -5.194337 D -1.097035 0.184430 -5.948253 In [136]: wp.minor_axis Out[136]: Index([u'A', u'B', u'C', u'D'], dtype='object') In [137]: wp.minor_xs('C') Out[137]: Item1 Item2 Item3 2000-01-01 -0.121325 1.413524 -0.085832 2000-01-02 -0.383057 1.243178 -0.308127 2000-01-03 0.105458 -0.020302 -5.194337 2000-01-04 -0.081710 -1.811565 0.045105 2000-01-05 0.021253 -1.040542 -0.020425
Squeeze
改变一个对象维度的另一个方法是squeeze一个1-len对象,类似于wp['Item1’]
In [138]: wp.reindex(items=['Item1']).squeeze() Out[138]: A B C D 2000-01-01 -0.729430 0.427693 -0.121325 -0.736418 2000-01-02 0.739037 -0.648805 -0.383057 0.385027 2000-01-03 2.321064 -1.290881 0.105458 -1.097035 2000-01-04 0.158759 -1.261191 -0.081710 1.390506 2000-01-05 -1.962031 -0.505580 0.021253 -0.317071 In [139]: wp.reindex(items=['Item1'], minor=['B']).squeeze() Out[139]: 2000-01-01 0.427693 2000-01-02 -0.648805 2000-01-03 -1.290881 2000-01-04 -1.261191 2000-01-05 -0.505580 Freq: D, Name: B, dtype: float64
转换为DataFrame
一个panel能够被一个分层索引的dataframe二维表格替代。详见hierarchical indexing。使用to_frame方法将一个panel转换为一个dataframe。
In [140]: panel = pd.Panel(np.random.randn(3, 5, 4), items=['one', 'two', 'three'], .....: major_axis=pd.date_range('1/1/2000', periods=5), .....: minor_axis=['a', 'b', 'c', 'd']) .....: In [141]: panel.to_frame() Out[141]: one two three major minor 2000-01-01 a -1.876826 -0.383171 -0.117339 b -1.873827 -0.172217 0.780048 c -0.251457 -1.674685 2.162047 d 0.027599 0.762474 0.874233 2000-01-02 a 1.235291 0.481666 -0.764147 b 0.850574 1.217546 -0.484495 c -1.140302 0.577103 0.298570 d 2.149143 -0.076021 0.825136 2000-01-03 a 0.504452 0.720235 -0.388020 b 0.678026 0.202660 -0.339279 c -0.628443 -0.314950 0.141164 d 1.191156 -0.410852 0.565930 2000-01-04 a -1.145363 0.542758 -1.749969 b -0.523153 1.955407 -1.402941 c -1.299878 -0.940645 0.623222 d -0.110240 0.076257 0.020129 2000-01-05 a -0.333712 -0.897159 -2.858463 b 0.416876 -1.265679 0.885765 c -0.436400 -0.528311 0.158014 d 0.999768 -0.660014 -1.981797
四维Panel 和多维Panel (不推荐使用)
警告
在0.19.0版本中不推荐使用四维Panel和多维panel,它们将会在以后的版本中被逐渐移除。推荐的使用方式是使用 xarray package来代替这种多维数据。Pandas提供了一个to_xarray()
方法来自动完成此转换。