10分钟上手pandas#
这是pandas的简短介绍,主要面向新用户。您可以在Cookbook中找到更复杂的示例。
通常,我们按如下方式导入:
In [1]: import numpy as np
In [2]: import pandas as pd
pandas 的基本数据结构#
pandas 提供了两种数据处理类:
对象创建#
请参阅数据结构入门部分。
通过传入值列表创建Series,让 pandas 创建默认的RangeIndex。
In [3]: s = pd.Series([1, 3, 5, np.nan, 6, 8])
In [4]: s
Out[4]:
0 1.0
1 3.0
2 5.0
3 NaN
4 6.0
5 8.0
dtype: float64
通过传入一个具有日期时间索引和已标记列的 NumPy 数组来创建DataFrame,该索引使用date_range()。
In [5]: dates = pd.date_range("20130101", periods=6)
In [6]: dates
Out[6]:
DatetimeIndex(['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04',
'2013-01-05', '2013-01-06'],
dtype='datetime64[us]', freq='D')
In [7]: df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list("ABCD"))
In [8]: df
Out[8]:
A B C D
2013-01-01 0.469112 -0.282863 -1.509059 -1.135632
2013-01-02 1.212112 -0.173215 0.119209 -1.044236
2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
2013-01-04 0.721555 -0.706771 -1.039575 0.271860
2013-01-05 -0.424972 0.567020 0.276232 -1.087401
2013-01-06 -0.673690 0.113648 -1.478427 0.524988
通过传入一个对象字典来创建DataFrame,其中键是列标签,值是列值。
In [9]: df2 = pd.DataFrame(
...: {
...: "A": 1.0,
...: "B": pd.Timestamp("20130102"),
...: "C": pd.Series(1, index=list(range(4)), dtype="float32"),
...: "D": np.array([3] * 4, dtype="int32"),
...: "E": pd.Categorical(["test", "train", "test", "train"]),
...: "F": "foo",
...: }
...: )
...:
In [10]: df2
Out[10]:
A B C D E F
0 1.0 2013-01-02 1.0 3 test foo
1 1.0 2013-01-02 1.0 3 train foo
2 1.0 2013-01-02 1.0 3 test foo
3 1.0 2013-01-02 1.0 3 train foo
In [11]: df2.dtypes
Out[11]:
A float64
B datetime64[us]
C float32
D int32
E category
F str
dtype: object
如果您使用的是 IPython,则会自动启用列名(以及公共属性)的选项卡补全。以下是会自动补全的属性子集:
In [12]: df2.<TAB> # noqa: E225, E999
df2.A df2.bool
df2.abs df2.boxplot
df2.add df2.C
df2.add_prefix df2.clip
df2.add_suffix df2.columns
df2.align df2.copy
df2.all df2.count
df2.any df2.combine
df2.append df2.D
df2.apply df2.describe
df2.B df2.duplicated
df2.diff
如您所见,列 A、B、C 和 D 会自动进行选项卡补全。E 和 F 也在其中;为简洁起见,其余属性已被截断。
查看数据#
请参阅基本功能部分。
使用 DataFrame.head() 和 DataFrame.tail() 分别查看 DataFrame 的顶部和底部行。
In [13]: df.head()
Out[13]:
A B C D
2013-01-01 0.469112 -0.282863 -1.509059 -1.135632
2013-01-02 1.212112 -0.173215 0.119209 -1.044236
2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
2013-01-04 0.721555 -0.706771 -1.039575 0.271860
2013-01-05 -0.424972 0.567020 0.276232 -1.087401
In [14]: df.tail(3)
Out[14]:
A B C D
2013-01-04 0.721555 -0.706771 -1.039575 0.271860
2013-01-05 -0.424972 0.567020 0.276232 -1.087401
2013-01-06 -0.673690 0.113648 -1.478427 0.524988
显示 DataFrame.index 或 DataFrame.columns。
In [15]: df.index
Out[15]:
DatetimeIndex(['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04',
'2013-01-05', '2013-01-06'],
dtype='datetime64[us]', freq='D')
In [16]: df.columns
Out[16]: Index(['A', 'B', 'C', 'D'], dtype='str')
使用 DataFrame.to_numpy() 返回底层数据的 NumPy 表示,不包含索引或列标签。
In [17]: df.to_numpy()
Out[17]:
array([[ 0.4691, -0.2829, -1.5091, -1.1356],
[ 1.2121, -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 ]])
注意
NumPy 数组对整个数组有一个 dtype,而 pandas DataFrame 对每列有一个 dtype。调用 DataFrame.to_numpy() 时,pandas 会找到能够容纳 DataFrame 中 *所有* dtypes 的 NumPy dtype。如果通用数据类型是 object,DataFrame.to_numpy() 将需要复制数据。
In [18]: df2.dtypes
Out[18]:
A float64
B datetime64[us]
C float32
D int32
E category
F str
dtype: object
In [19]: df2.to_numpy()
Out[19]:
array([[1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'test', 'foo'],
[1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'train', 'foo'],
[1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'test', 'foo'],
[1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'train', 'foo']],
dtype=object)
describe() 显示数据快速的统计摘要。
In [20]: df.describe()
Out[20]:
A B C D
count 6.000000 6.000000 6.000000 6.000000
mean 0.073711 -0.431125 -0.687758 -0.233103
std 0.843157 0.922818 0.779887 0.973118
min -0.861849 -2.104569 -1.509059 -1.135632
25% -0.611510 -0.600794 -1.368714 -1.076610
50% 0.022070 -0.228039 -0.767252 -0.386188
75% 0.658444 0.041933 -0.034326 0.461706
max 1.212112 0.567020 0.276232 1.071804
转置数据
In [21]: df.T
Out[21]:
2013-01-01 2013-01-02 2013-01-03 2013-01-04 2013-01-05 2013-01-06
A 0.469112 1.212112 -0.861849 0.721555 -0.424972 -0.673690
B -0.282863 -0.173215 -2.104569 -0.706771 0.567020 0.113648
C -1.509059 0.119209 -0.494929 -1.039575 0.276232 -1.478427
D -1.135632 -1.044236 1.071804 0.271860 -1.087401 0.524988
DataFrame.sort_index() 按轴排序。
In [22]: df.sort_index(axis=1, ascending=False)
Out[22]:
D C B A
2013-01-01 -1.135632 -1.509059 -0.282863 0.469112
2013-01-02 -1.044236 0.119209 -0.173215 1.212112
2013-01-03 1.071804 -0.494929 -2.104569 -0.861849
2013-01-04 0.271860 -1.039575 -0.706771 0.721555
2013-01-05 -1.087401 0.276232 0.567020 -0.424972
2013-01-06 0.524988 -1.478427 0.113648 -0.673690
DataFrame.sort_values() 按值排序。
In [23]: df.sort_values(by="B")
Out[23]:
A B C D
2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
2013-01-04 0.721555 -0.706771 -1.039575 0.271860
2013-01-01 0.469112 -0.282863 -1.509059 -1.135632
2013-01-02 1.212112 -0.173215 0.119209 -1.044236
2013-01-06 -0.673690 0.113648 -1.478427 0.524988
2013-01-05 -0.424972 0.567020 0.276232 -1.087401
选择#
注意
虽然标准的 Python/NumPy 表达式用于选择和设置直观且在交互式工作中很有用,但对于生产代码,我们推荐使用优化的 pandas 数据访问方法:DataFrame.at()、DataFrame.iat()、DataFrame.loc() 和 DataFrame.iloc()。
请参阅索引和选择数据以及MultiIndex / 高级索引的索引文档。
getitem([])#
对于 DataFrame,传入单个标签会选择一列并返回一个 Series。
In [24]: df["A"]
Out[24]:
2013-01-01 0.469112
2013-01-02 1.212112
2013-01-03 -0.861849
2013-01-04 0.721555
2013-01-05 -0.424972
2013-01-06 -0.673690
Freq: D, Name: A, dtype: float64
如果标签只包含字母、数字和下划线,您也可以使用列名属性。
In [25]: df.A
Out[25]:
2013-01-01 0.469112
2013-01-02 1.212112
2013-01-03 -0.861849
2013-01-04 0.721555
2013-01-05 -0.424972
2013-01-06 -0.673690
Freq: D, Name: A, dtype: float64
传入一个列标签列表会选择多个列,这对于获取子集/重新排列很有用。
In [26]: df[["B", "A"]]
Out[26]:
B A
2013-01-01 -0.282863 0.469112
2013-01-02 -0.173215 1.212112
2013-01-03 -2.104569 -0.861849
2013-01-04 -0.706771 0.721555
2013-01-05 0.567020 -0.424972
2013-01-06 0.113648 -0.673690
对于 DataFrame,传入一个切片 : 会选择匹配的行。
In [27]: df[0:3]
Out[27]:
A B C D
2013-01-01 0.469112 -0.282863 -1.509059 -1.135632
2013-01-02 1.212112 -0.173215 0.119209 -1.044236
2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
In [28]: df["20130102":"20130104"]
Out[28]:
A B C D
2013-01-02 1.212112 -0.173215 0.119209 -1.044236
2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
2013-01-04 0.721555 -0.706771 -1.039575 0.271860
按标签选择#
使用 DataFrame.loc() 或 DataFrame.at(),请参阅按标签选择中的更多内容。
选择匹配标签的行。
In [29]: df.loc[dates[0]]
Out[29]:
A 0.469112
B -0.282863
C -1.509059
D -1.135632
Name: 2013-01-01 00:00:00, dtype: float64
使用选择列标签选择所有行(:)。
In [30]: df.loc[:, ["A", "B"]]
Out[30]:
A B
2013-01-01 0.469112 -0.282863
2013-01-02 1.212112 -0.173215
2013-01-03 -0.861849 -2.104569
2013-01-04 0.721555 -0.706771
2013-01-05 -0.424972 0.567020
2013-01-06 -0.673690 0.113648
对于标签切片,两个端点都*包含*在内。
In [31]: df.loc["20130102":"20130104", ["A", "B"]]
Out[31]:
A B
2013-01-02 1.212112 -0.173215
2013-01-03 -0.861849 -2.104569
2013-01-04 0.721555 -0.706771
选择单个行和列标签会返回一个标量。
In [32]: df.loc[dates[0], "A"]
Out[32]: np.float64(0.4691122999071863)
为了快速访问标量(等同于前一种方法)。
In [33]: df.at[dates[0], "A"]
Out[33]: np.float64(0.4691122999071863)
按位置选择#
使用 DataFrame.iloc() 或 DataFrame.iat(),请参阅按位置选择中的更多内容。
通过传入整数的位置进行选择。
In [34]: df.iloc[3]
Out[34]:
A 0.721555
B -0.706771
C -1.039575
D 0.271860
Name: 2013-01-04 00:00:00, dtype: float64
整数切片与 NumPy/Python 类似。
In [35]: df.iloc[3:5, 0:2]
Out[35]:
A B
2013-01-04 0.721555 -0.706771
2013-01-05 -0.424972 0.567020
整数位置列表。
In [36]: df.iloc[[1, 2, 4], [0, 2]]
Out[36]:
A C
2013-01-02 1.212112 0.119209
2013-01-03 -0.861849 -0.494929
2013-01-05 -0.424972 0.276232
显式切片行。
In [37]: df.iloc[1:3, :]
Out[37]:
A B C D
2013-01-02 1.212112 -0.173215 0.119209 -1.044236
2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
显式切片列。
In [38]: df.iloc[:, 1:3]
Out[38]:
B C
2013-01-01 -0.282863 -1.509059
2013-01-02 -0.173215 0.119209
2013-01-03 -2.104569 -0.494929
2013-01-04 -0.706771 -1.039575
2013-01-05 0.567020 0.276232
2013-01-06 0.113648 -1.478427
显式获取值。
In [39]: df.iloc[1, 1]
Out[39]: np.float64(-0.17321464905330858)
为了快速访问标量(等同于前一种方法)。
In [40]: df.iat[1, 1]
Out[40]: np.float64(-0.17321464905330858)
布尔索引#
选择 df.A 大于 0 的行。
In [41]: df[df["A"] > 0]
Out[41]:
A B C D
2013-01-01 0.469112 -0.282863 -1.509059 -1.135632
2013-01-02 1.212112 -0.173215 0.119209 -1.044236
2013-01-04 0.721555 -0.706771 -1.039575 0.271860
在满足布尔条件的情况下,从 DataFrame 中选择值。
In [42]: df[df > 0]
Out[42]:
A B C D
2013-01-01 0.469112 NaN NaN NaN
2013-01-02 1.212112 NaN 0.119209 NaN
2013-01-03 NaN NaN NaN 1.071804
2013-01-04 0.721555 NaN NaN 0.271860
2013-01-05 NaN 0.567020 0.276232 NaN
2013-01-06 NaN 0.113648 NaN 0.524988
使用 isin() 方法进行过滤。
In [43]: df2 = df.copy()
In [44]: df2["E"] = ["one", "one", "two", "three", "four", "three"]
In [45]: df2
Out[45]:
A B C D E
2013-01-01 0.469112 -0.282863 -1.509059 -1.135632 one
2013-01-02 1.212112 -0.173215 0.119209 -1.044236 one
2013-01-03 -0.861849 -2.104569 -0.494929 1.071804 two
2013-01-04 0.721555 -0.706771 -1.039575 0.271860 three
2013-01-05 -0.424972 0.567020 0.276232 -1.087401 four
2013-01-06 -0.673690 0.113648 -1.478427 0.524988 three
In [46]: df2[df2["E"].isin(["two", "four"])]
Out[46]:
A B C D E
2013-01-03 -0.861849 -2.104569 -0.494929 1.071804 two
2013-01-05 -0.424972 0.567020 0.276232 -1.087401 four
设置#
设置新列会自动按索引对齐数据。
In [47]: s1 = pd.Series(
....: [1, 2, 3, 4, 5, 6],
....: index=pd.date_range("20130102", periods=6))
....:
In [48]: s1
Out[48]:
2013-01-02 1
2013-01-03 2
2013-01-04 3
2013-01-05 4
2013-01-06 5
2013-01-07 6
Freq: D, dtype: int64
In [49]: df["F"] = s1
按标签设置值。
In [50]: df.at[dates[0], "A"] = 0
按位置设置值。
In [51]: df.iat[0, 1] = 0
通过 NumPy 数组赋值进行设置。
In [52]: df.loc[:, "D"] = np.array([5] * len(df))
前述设置操作的结果。
In [53]: df
Out[53]:
A B C D F
2013-01-01 0.000000 0.000000 -1.509059 5.0 NaN
2013-01-02 1.212112 -0.173215 0.119209 5.0 1.0
2013-01-03 -0.861849 -2.104569 -0.494929 5.0 2.0
2013-01-04 0.721555 -0.706771 -1.039575 5.0 3.0
2013-01-05 -0.424972 0.567020 0.276232 5.0 4.0
2013-01-06 -0.673690 0.113648 -1.478427 5.0 5.0
带有设置的 where 操作。
In [54]: df2 = df.copy()
In [55]: df2[df2 > 0] = -df2
In [56]: df2
Out[56]:
A B C D F
2013-01-01 0.000000 0.000000 -1.509059 -5.0 NaN
2013-01-02 -1.212112 -0.173215 -0.119209 -5.0 -1.0
2013-01-03 -0.861849 -2.104569 -0.494929 -5.0 -2.0
2013-01-04 -0.721555 -0.706771 -1.039575 -5.0 -3.0
2013-01-05 -0.424972 -0.567020 -0.276232 -5.0 -4.0
2013-01-06 -0.673690 -0.113648 -1.478427 -5.0 -5.0
缺失数据#
对于 NumPy 数据类型,np.nan 表示缺失数据。默认情况下,它不包含在计算中。请参阅缺失数据部分。
重新索引允许您更改/添加/删除指定轴上的索引。这将返回数据的副本。
In [57]: df1 = df.reindex(index=dates[0:4], columns=list(df.columns) + ["E"])
In [58]: df1.loc[dates[0] : dates[1], "E"] = 1
In [59]: df1
Out[59]:
A B C D F E
2013-01-01 0.000000 0.000000 -1.509059 5.0 NaN 1.0
2013-01-02 1.212112 -0.173215 0.119209 5.0 1.0 1.0
2013-01-03 -0.861849 -2.104569 -0.494929 5.0 2.0 NaN
2013-01-04 0.721555 -0.706771 -1.039575 5.0 3.0 NaN
DataFrame.dropna() 删除所有包含缺失数据的行。
In [60]: df1.dropna(how="any")
Out[60]:
A B C D F E
2013-01-02 1.212112 -0.173215 0.119209 5.0 1.0 1.0
DataFrame.fillna() 填充缺失数据。
In [61]: df1.fillna(value=5)
Out[61]:
A B C D F E
2013-01-01 0.000000 0.000000 -1.509059 5.0 5.0 1.0
2013-01-02 1.212112 -0.173215 0.119209 5.0 1.0 1.0
2013-01-03 -0.861849 -2.104569 -0.494929 5.0 2.0 5.0
2013-01-04 0.721555 -0.706771 -1.039575 5.0 3.0 5.0
isna() 获取值为 nan 的布尔掩码。
In [62]: pd.isna(df1)
Out[62]:
A B C D F E
2013-01-01 False False False False True False
2013-01-02 False False False False False False
2013-01-03 False False False False False True
2013-01-04 False False False False False True
操作#
请参阅二元运算基础部分。
统计#
一般而言,运算会*排除*缺失数据。
计算每列的平均值。
In [63]: df.mean()
Out[63]:
A -0.004474
B -0.383981
C -0.687758
D 5.000000
F 3.000000
dtype: float64
计算每行的平均值。
In [64]: df.mean(axis=1)
Out[64]:
2013-01-01 0.872735
2013-01-02 1.431621
2013-01-03 0.707731
2013-01-04 1.395042
2013-01-05 1.883656
2013-01-06 1.592306
Freq: D, dtype: float64
与具有不同索引或列的另一个 Series 或 DataFrame 进行运算,会按索引或列标签的并集对结果进行对齐。此外,pandas 会自动沿指定维度广播,并用 np.nan 填充未对齐的标签。
In [65]: s = pd.Series([1, 3, 5, np.nan, 6, 8], index=dates).shift(2)
In [66]: s
Out[66]:
2013-01-01 NaN
2013-01-02 NaN
2013-01-03 1.0
2013-01-04 3.0
2013-01-05 5.0
2013-01-06 NaN
Freq: D, dtype: float64
In [67]: df.sub(s, axis="index")
Out[67]:
A B C D F
2013-01-01 NaN NaN NaN NaN NaN
2013-01-02 NaN NaN NaN NaN NaN
2013-01-03 -1.861849 -3.104569 -1.494929 4.0 1.0
2013-01-04 -2.278445 -3.706771 -4.039575 2.0 0.0
2013-01-05 -5.424972 -4.432980 -4.723768 0.0 -1.0
2013-01-06 NaN NaN NaN NaN NaN
用户定义函数#
DataFrame.agg() 和 DataFrame.transform() 应用用户定义的函数,该函数分别进行归约或广播其结果。
In [68]: df.agg(lambda x: np.mean(x) * 5.6)
Out[68]:
A -0.025054
B -2.150294
C -3.851445
D 28.000000
F 16.800000
dtype: float64
In [69]: df.transform(lambda x: x * 101.2)
Out[69]:
A B C D F
2013-01-01 0.000000 0.000000 -152.716721 506.0 NaN
2013-01-02 122.665737 -17.529322 12.063922 506.0 101.2
2013-01-03 -87.219115 -212.982405 -50.086843 506.0 202.4
2013-01-04 73.021382 -71.525239 -105.204988 506.0 303.6
2013-01-05 -43.007200 57.382459 27.954680 506.0 404.8
2013-01-06 -68.177398 11.501219 -149.616767 506.0 506.0
值计数#
更多内容请参阅直方图和离散化。
In [70]: s = pd.Series(np.random.randint(0, 7, size=10))
In [71]: s
Out[71]:
0 4
1 2
2 1
3 2
4 6
5 4
6 4
7 6
8 4
9 4
dtype: int64
In [72]: s.value_counts()
Out[72]:
4 5
2 2
6 2
1 1
Name: count, dtype: int64
字符串方法#
Series 在 str 属性中配备了一系列字符串处理方法,这些方法可以轻松地对数组的每个元素进行操作,如下面的代码片段所示。更多内容请参阅向量化字符串方法。
In [73]: s = pd.Series(["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"])
In [74]: s.str.lower()
Out[74]:
0 a
1 b
2 c
3 aaba
4 baca
5 NaN
6 caba
7 dog
8 cat
dtype: str
合并#
连接#
pandas 提供了各种工具,可以轻松地将 Series 和 DataFrame 对象连接起来,具有各种集合逻辑用于索引,以及在连接/合并类型操作的情况下进行关系代数运算。
请参阅合并部分。
使用 concat() 按行连接 pandas 对象。
In [75]: df = pd.DataFrame(np.random.randn(10, 4))
In [76]: df
Out[76]:
0 1 2 3
0 -0.548702 1.467327 -1.015962 -0.483075
1 1.637550 -1.217659 -0.291519 -1.745505
2 -0.263952 0.991460 -0.919069 0.266046
3 -0.709661 1.669052 1.037882 -1.705775
4 -0.919854 -0.042379 1.247642 -0.009920
5 0.290213 0.495767 0.362949 1.548106
6 -1.131345 -0.089329 0.337863 -0.945867
7 -0.932132 1.956030 0.017587 -0.016692
8 -0.575247 0.254161 -1.143704 0.215897
9 1.193555 -0.077118 -0.408530 -0.862495
# break it into pieces
In [77]: pieces = [df[:3], df[3:7], df[7:]]
In [78]: pd.concat(pieces)
Out[78]:
0 1 2 3
0 -0.548702 1.467327 -1.015962 -0.483075
1 1.637550 -1.217659 -0.291519 -1.745505
2 -0.263952 0.991460 -0.919069 0.266046
3 -0.709661 1.669052 1.037882 -1.705775
4 -0.919854 -0.042379 1.247642 -0.009920
5 0.290213 0.495767 0.362949 1.548106
6 -1.131345 -0.089329 0.337863 -0.945867
7 -0.932132 1.956030 0.017587 -0.016692
8 -0.575247 0.254161 -1.143704 0.215897
9 1.193555 -0.077118 -0.408530 -0.862495
连接#
merge() 支持 SQL 风格的连接类型,沿特定列进行。请参阅数据库风格连接部分。
In [79]: left = pd.DataFrame({"key": ["foo", "foo"], "lval": [1, 2]})
In [80]: right = pd.DataFrame({"key": ["foo", "foo"], "rval": [4, 5]})
In [81]: left
Out[81]:
key lval
0 foo 1
1 foo 2
In [82]: right
Out[82]:
key rval
0 foo 4
1 foo 5
In [83]: pd.merge(left, right, on="key")
Out[83]:
key lval rval
0 foo 1 4
1 foo 1 5
2 foo 2 4
3 foo 2 5
在唯一键上进行merge()。
In [84]: left = pd.DataFrame({"key": ["foo", "bar"], "lval": [1, 2]})
In [85]: right = pd.DataFrame({"key": ["foo", "bar"], "rval": [4, 5]})
In [86]: left
Out[86]:
key lval
0 foo 1
1 bar 2
In [87]: right
Out[87]:
key rval
0 foo 4
1 bar 5
In [88]: pd.merge(left, right, on="key")
Out[88]:
key lval rval
0 foo 1 4
1 bar 2 5
分组#
“分组依据”是指涉及以下一个或多个步骤的过程:
根据某些标准将数据**拆分**成组。
对每个组独立地**应用**一个函数。
**组合**结果到一个数据结构中。
请参阅分组部分。
In [89]: df = pd.DataFrame(
....: {
....: "A": ["foo", "bar", "foo", "bar", "foo", "bar", "foo", "foo"],
....: "B": ["one", "one", "two", "three", "two", "two", "one", "three"],
....: "C": np.random.randn(8),
....: "D": np.random.randn(8),
....: }
....: )
....:
In [90]: df
Out[90]:
A B C D
0 foo one 1.346061 -1.577585
1 bar one 1.511763 0.396823
2 foo two 1.627081 -0.105381
3 bar three -0.990582 -0.532532
4 foo two -0.441652 1.453749
5 bar two 1.211526 1.208843
6 foo one 0.268520 -0.080952
7 foo three 0.024580 -0.264610
按列标签分组,选择列标签,然后对结果组应用 DataFrameGroupBy.sum() 函数。
In [91]: df.groupby("A")[["C", "D"]].sum()
Out[91]:
C D
A
bar 1.732707 1.073134
foo 2.824590 -0.574779
按多个列标签分组会形成 MultiIndex。
In [92]: df.groupby(["A", "B"]).sum()
Out[92]:
C D
A B
bar one 1.511763 0.396823
three -0.990582 -0.532532
two 1.211526 1.208843
foo one 1.614581 -1.658537
three 0.024580 -0.264610
two 1.185429 1.348368
Reshaping#
堆叠#
In [93]: arrays = [
....: ["bar", "bar", "baz", "baz", "foo", "foo", "qux", "qux"],
....: ["one", "two", "one", "two", "one", "two", "one", "two"],
....: ]
....:
In [94]: index = pd.MultiIndex.from_arrays(arrays, names=["first", "second"])
In [95]: df = pd.DataFrame(np.random.randn(8, 2), index=index, columns=["A", "B"])
In [96]: df2 = df[:4]
In [97]: df2
Out[97]:
A B
first second
bar one -0.727965 -0.589346
two 0.339969 -0.693205
baz one -0.339355 0.593616
two 0.884345 1.591431
stack() 方法“压缩”DataFrame 列中的一个级别。
In [98]: stacked = df2.stack()
In [99]: stacked
Out[99]:
first second
bar one A -0.727965
B -0.589346
two A 0.339969
B -0.693205
baz one A -0.339355
B 0.593616
two A 0.884345
B 1.591431
dtype: float64
对于“堆叠”的 DataFrame 或 Series(其索引为 MultiIndex),stack() 的逆操作是 unstack(),它默认会展开**最后一个级别**。
In [100]: stacked.unstack()
Out[100]:
A B
first second
bar one -0.727965 -0.589346
two 0.339969 -0.693205
baz one -0.339355 0.593616
two 0.884345 1.591431
In [101]: stacked.unstack(1)
Out[101]:
second one two
first
bar A -0.727965 0.339969
B -0.589346 -0.693205
baz A -0.339355 0.884345
B 0.593616 1.591431
In [102]: stacked.unstack(0)
Out[102]:
first bar baz
second
one A -0.727965 -0.339355
B -0.589346 0.593616
two A 0.339969 0.884345
B -0.693205 1.591431
透视表#
请参阅透视表部分。
In [103]: df = pd.DataFrame(
.....: {
.....: "A": ["one", "one", "two", "three"] * 3,
.....: "B": ["A", "B", "C"] * 4,
.....: "C": ["foo", "foo", "foo", "bar", "bar", "bar"] * 2,
.....: "D": np.random.randn(12),
.....: "E": np.random.randn(12),
.....: }
.....: )
.....:
In [104]: df
Out[104]:
A B C D E
0 one A foo -1.202872 0.047609
1 one B foo -1.814470 -0.136473
2 two C foo 1.018601 -0.561757
3 three A bar -0.595447 -1.623033
4 one B bar 1.395433 0.029399
5 one C bar -0.392670 -0.542108
6 two A foo 0.007207 0.282696
7 three B foo 1.928123 -0.087302
8 one C foo -0.055224 -1.575170
9 one A bar 2.395985 1.771208
10 two B bar 1.552825 0.816482
11 three C bar 0.166599 1.100230
pivot_table() 会根据指定的 values、index 和 columns 来透视 DataFrame。
In [105]: pd.pivot_table(df, values="D", index=["A", "B"], columns=["C"])
Out[105]:
C bar foo
A B
one A 2.395985 -1.202872
B 1.395433 -1.814470
C -0.392670 -0.055224
three A -0.595447 NaN
B NaN 1.928123
C 0.166599 NaN
two A NaN 0.007207
B 1.552825 NaN
C NaN 1.018601
时间序列#
pandas 提供了简单、强大且高效的功能,用于在频率转换(例如,将秒级数据转换为 5 分钟数据)期间执行重采样操作。这在金融应用中非常常见,但不限于此。请参阅时间序列部分。
In [106]: rng = pd.date_range("1/1/2012", periods=100, freq="s")
In [107]: ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng)
In [108]: ts.resample("5Min").sum()
Out[108]:
2012-01-01 24182
Freq: 5min, dtype: int64
Series.tz_localize() 将时间序列本地化到时区。
In [109]: rng = pd.date_range("3/6/2012 00:00", periods=5, freq="D")
In [110]: ts = pd.Series(np.random.randn(len(rng)), rng)
In [111]: ts
Out[111]:
2012-03-06 1.857704
2012-03-07 -1.193545
2012-03-08 0.677510
2012-03-09 -0.153931
2012-03-10 0.520091
Freq: D, dtype: float64
In [112]: ts_utc = ts.tz_localize("UTC")
In [113]: ts_utc
Out[113]:
2012-03-06 00:00:00+00:00 1.857704
2012-03-07 00:00:00+00:00 -1.193545
2012-03-08 00:00:00+00:00 0.677510
2012-03-09 00:00:00+00:00 -0.153931
2012-03-10 00:00:00+00:00 0.520091
Freq: D, dtype: float64
Series.tz_convert() 将有 timezone 感知的时间序列转换为另一个时区。
In [114]: ts_utc.tz_convert("US/Eastern")
Out[114]:
2012-03-05 19:00:00-05:00 1.857704
2012-03-06 19:00:00-05:00 -1.193545
2012-03-07 19:00:00-05:00 0.677510
2012-03-08 19:00:00-05:00 -0.153931
2012-03-09 19:00:00-05:00 0.520091
dtype: float64
向时间序列添加非固定持续时间(BusinessDay)。
In [115]: rng
Out[115]:
DatetimeIndex(['2012-03-06', '2012-03-07', '2012-03-08', '2012-03-09',
'2012-03-10'],
dtype='datetime64[us]', freq='D')
In [116]: rng + pd.offsets.BusinessDay(5)
Out[116]:
DatetimeIndex(['2012-03-13', '2012-03-14', '2012-03-15', '2012-03-16',
'2012-03-16'],
dtype='datetime64[us]', freq=None)
分类数据#
pandas 可以在 DataFrame 中包含分类数据。有关完整文档,请参阅分类数据介绍和API 文档。
In [117]: df = pd.DataFrame(
.....: {"id": [1, 2, 3, 4, 5, 6], "raw_grade": ["a", "b", "b", "a", "a", "e"]}
.....: )
.....:
将原始分数转换为分类数据类型。
In [118]: df["grade"] = df["raw_grade"].astype("category")
In [119]: df["grade"]
Out[119]:
0 a
1 b
2 b
3 a
4 a
5 e
Name: grade, dtype: category
Categories (3, str): ['a', 'b', 'e']
将类别重命名为更有意义的名称。
In [120]: new_categories = ["very good", "good", "very bad"]
In [121]: df["grade"] = df["grade"].cat.rename_categories(new_categories)
重新排序类别并同时添加缺失的类别(Series.cat() 下的方法默认返回一个新的 Series)。
In [122]: df["grade"] = df["grade"].cat.set_categories(
.....: ["very bad", "bad", "medium", "good", "very good"]
.....: )
.....:
In [123]: df["grade"]
Out[123]:
0 very good
1 good
2 good
3 very good
4 very good
5 very bad
Name: grade, dtype: category
Categories (5, str): ['very bad', 'bad', 'medium', 'good', 'very good']
排序是根据类别中的顺序,而不是字典顺序。
In [124]: df.sort_values(by="grade")
Out[124]:
id raw_grade grade
5 6 e very bad
1 2 b good
2 3 b good
0 1 a very good
3 4 a very good
4 5 a very good
使用 observed=False 按分类列分组也会显示空类别。
In [125]: df.groupby("grade", observed=False).size()
Out[125]:
grade
very bad 1
bad 0
medium 0
good 2
very good 3
dtype: int64
绘图#
请参阅绘图文档。
我们使用标准约定来引用 matplotlib API。
In [126]: import matplotlib.pyplot as plt
In [127]: plt.close("all")
plt.close 方法用于关闭图形窗口。
In [128]: ts = pd.Series(np.random.randn(1000), index=pd.date_range("1/1/2000", periods=1000))
In [129]: ts = ts.cumsum()
In [130]: ts.plot();
注意
当使用 Jupyter 时,绘图会使用 plot() 显示。否则,请使用 matplotlib.pyplot.show 显示,或使用 matplotlib.pyplot.savefig 将其保存到文件。
plot() 会绘制所有列。
In [131]: df = pd.DataFrame(
.....: np.random.randn(1000, 4), index=ts.index, columns=["A", "B", "C", "D"]
.....: )
.....:
In [132]: df = df.cumsum()
In [133]: plt.figure();
In [134]: df.plot();
In [135]: plt.legend(loc='best');
导入和导出数据#
请参阅IO 工具部分。
CSV#
使用 DataFrame.to_csv()写入 CSV 文件:。
In [136]: df = pd.DataFrame(np.random.randint(0, 5, (10, 5)))
In [137]: df.to_csv("foo.csv")
In [138]: pd.read_csv("foo.csv")
Out[138]:
Unnamed: 0 0 1 2 3 4
0 0 4 3 1 1 2
1 1 1 0 2 3 2
2 2 1 4 2 1 2
3 3 0 4 0 2 2
4 4 4 2 2 3 4
5 5 4 0 4 3 1
6 6 2 1 2 0 3
7 7 4 0 4 4 4
8 8 4 4 1 0 1
9 9 0 4 3 0 3
Parquet#
写入 Parquet 文件。
In [139]: df.to_parquet("foo.parquet")
使用 read_parquet() 从 Parquet 文件存储读取。
In [140]: pd.read_parquet("foo.parquet")
Out[140]:
0 1 2 3 4
0 4 3 1 1 2
1 1 0 2 3 2
2 1 4 2 1 2
3 0 4 0 2 2
4 4 2 2 3 4
5 4 0 4 3 1
6 2 1 2 0 3
7 4 0 4 4 4
8 4 4 1 0 1
9 0 4 3 0 3
Excel#
读写 Excel。
使用 DataFrame.to_excel() 写入 Excel 文件。
In [141]: df.to_excel("foo.xlsx", sheet_name="Sheet1")
使用 read_excel() 从 Excel 文件读取。
In [142]: pd.read_excel("foo.xlsx", "Sheet1", index_col=None, na_values=["NA"])
Out[142]:
Unnamed: 0 0 1 2 3 4
0 0 4 3 1 1 2
1 1 1 0 2 3 2
2 2 1 4 2 1 2
3 3 0 4 0 2 2
4 4 4 2 2 3 4
5 5 4 0 4 3 1
6 6 2 1 2 0 3
7 7 4 0 4 4 4
8 8 4 4 1 0 1
9 9 0 4 3 0 3
注意事项#
如果您尝试对 Series 或 DataFrame 执行布尔运算,您可能会看到如下异常:
In [143]: if pd.Series([False, True, False]):
.....: print("I was true")
.....:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-143-b27eb9c1dfc0> in ?()
----> 1 if pd.Series([False, True, False]):
2 print("I was true")
~/work/pandas/pandas/pandas/core/generic.py in ?(self)
1511 @final
1512 def __bool__(self) -> NoReturn:
-> 1513 raise ValueError(
1514 f"The truth value of a {type(self).__name__} is ambiguous. "
1515 "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
1516 )
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().