10分钟搞定Pandas

通常,我们按如下方式导入库:

import numpy as np
import pandas as pd

Pandas的核心数据结构

Pandas提供了两种主要的类来处理数据:

  • Series(序列):一种一维的带标签数组,可以存储任意类型的数据(如整数、字符串、Python对象等)。
  • DataFrame(数据框):一种二维的数据结构,类似于二维数组或带有行标签和列标签的表格。

创建对象

创建Series

通过传递一个值列表来创建Series,Pandas会自动生成默认的整数索引(RangeIndex)。

s = pd.Series([1, 3, 5, np.nan, 6, 8])

s
Out[4]: 
0    1.0
1    3.0
2    5.0
3    NaN
4    6.0
5    8.0
dtype: float64

创建DataFrame

通过传递一个NumPy数组,并使用date_range()生成日期时间索引,以及带标签的列,来创建DataFrame:

dates = pd.date_range("20130101", periods=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[ns]', freq='D')

df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list("ABCD"))

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,字典的键为列标签,值为列数据:

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",
    }
)

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

生成的DataFrame各列的数据类型(dtype)各不相同:

df2.dtypes
Out[11]: 
A           float64
B    datetime64[ns]
C           float32
D             int32
E          category
F            object
dtype: object

如果你使用IPython,列名(以及公共属性)的Tab补全功能会自动启用。以下是部分可以补全的属性:

df2.<TAB>  # 按Tab键会显示以下属性
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

如你所见,列ABCD会自动补全。EF也同样存在;为了简洁,其余属性已被截断。

查看数据

使用DataFrame.head()DataFrame.tail()分别查看DataFrame的前几行和后几行:

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

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(列名):

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[ns]', freq='D')

df.columns
Out[16]: Index(['A', 'B', 'C', 'D'], dtype='object')

使用DataFrame.to_numpy()返回底层数据的NumPy表示形式(不含索引和列标签):

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中所有dtype的NumPy dtype。如果共同的数据类型是objectDataFrame.to_numpy()将需要复制数据。

df2.dtypes
Out[18]: 
A           float64
B    datetime64[ns]
C           float32
D             int32
E          category
F            object
dtype: object

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()显示数据的快速统计摘要:

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

转置数据(行变列,列变行):

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()按轴排序:

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()按值排序:

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()

使用方括号 [] 选择

对于DataFrame,传入单个标签会选择一列并返回一个Series:

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

如果列名只包含字母、数字和下划线,你也可以使用点号访问(属性形式):

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

传入列标签列表可以选择多列,这对于获取子集或重新排序列很有用:

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,传入一个切片(slice)会选择匹配的行:

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

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

按标签选择(.loc

使用.loc进行纯标签索引。

选择与标签匹配的一行:

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

选择所有行(:)和部分列标签:

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

对于标签切片,两端都包含在内:

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

选择单个行和列标签会返回一个标量:

df.loc[dates[0], "A"]
Out[32]: 0.4691122999071863

快速访问标量(等同于上面的方法,但速度更快):

df.at[dates[0], "A"]
Out[33]: 0.4691122999071863

按位置选择(.iloc

使用.iloc进行纯整数位置索引。

通过传入整数的位置进行选择:

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:

df.iloc[3:5, 0:2]
Out[35]: 
                   A         B
2013-01-04  0.721555 -0.706771
2013-01-05 -0.424972  0.567020

整数位置列表:

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

显式地对行进行切片(取所有列):

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

显式地对列进行切片(取所有行):

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

显式地获取单个值:

df.iloc[1, 1]
Out[39]: -0.17321464905330858

快速访问标量(等同于上面的方法,但速度更快):

df.iat[1, 1]
Out[40]: -0.17321464905330858

布尔索引

选择df.A大于0的行:

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中选择满足布尔条件的值(不满足的设为NaN):

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()方法进行过滤:

df2 = df.copy()

df2["E"] = ["one", "one", "two", "three", "four", "three"]

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

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

设置数据

设置新列时,数据会根据索引自动对齐:

s1 = pd.Series(
    [1, 2, 3, 4, 5, 6],
    index=pd.date_range("20130102", periods=6)
)

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

df["F"] = s1

按标签设置值:

df.at[dates[0], "A"] = 0

按位置设置值:

df.iat[0, 1] = 0

通过NumPy数组赋值:

df.loc[:, "D"] = np.array([5] * len(df))

上述设置操作的结果:

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操作进行设置(将满足条件的值替换为相反数):

df2 = df.copy()

df2[df2 > 0] = -df2

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表示缺失数据。默认情况下,它不参与计算。

使用reindex(重新索引)可以在指定轴上更改/添加/删除索引。它会返回数据的副本:

df1 = df.reindex(index=dates[0:4], columns=list(df.columns) + ["E"])

df1.loc[dates[0] : dates[1], "E"] = 1

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()删除任何包含缺失数据的行:

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()填充缺失数据:

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的布尔掩码:

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

运算操作

统计

操作通常会排除缺失数据。

计算每列的均值:

df.mean()
Out[63]: 
A   -0.004474
B   -0.383981
C   -0.687758
D    5.000000
F    3.000000
dtype: float64

计算每行的均值:

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

s = pd.Series([1, 3, 5, np.nan, 6, 8], index=dates).shift(2)

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

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()分别应用用户自定义函数,这些函数会缩减(aggregate)或广播(transform)其结果。

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

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

值计数

s = pd.Series(np.random.randint(0, 7, size=10))

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

s.value_counts()
Out[72]: 
4    5
2    2
6    2
1    1
Name: count, dtype: int64

字符串方法

Series在str属性中配备了一组字符串处理方法,可以方便地对数组中的每个元素进行操作。

s = pd.Series(["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"])

s.str.lower()
Out[74]: 
0       a
1       b
2       c
3    aaba
4    baca
5     NaN
6    caba
7     dog
8     cat
dtype: object

合并

拼接(concat)

Pandas提供了多种功能,可以轻松地将Series和DataFrame对象组合在一起。

使用concat()按行拼接Pandas对象:

df = pd.DataFrame(np.random.randn(10, 4))

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

# 将其分成几块
pieces = [df[:3], df[3:7], df[7:]]

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

注意:向DataFrame添加列相对较快。但是,添加行需要复制数据,可能代价较高。我们建议将预先构建的记录列表传递给DataFrame构造函数,而不是通过迭代追加记录来构建DataFrame。

连接(join/merge)

merge()支持沿特定列的SQL风格连接类型。

left = pd.DataFrame({"key": ["foo", "foo"], "lval": [1, 2]})

right = pd.DataFrame({"key": ["foo", "foo"], "rval": [4, 5]})

left
Out[81]: 
   key  lval
0  foo     1
1  foo     2

right
Out[82]: 
   key  rval
0  foo     4
1  foo     5

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()

left = pd.DataFrame({"key": ["foo", "bar"], "lval": [1, 2]})

right = pd.DataFrame({"key": ["foo", "bar"], "rval": [4, 5]})

left
Out[86]: 
   key  lval
0  foo     1
1  bar     2

right
Out[87]: 
   key  rval
0  foo     4
1  bar     5

pd.merge(left, right, on="key")
Out[88]: 
   key  lval  rval
0  foo     1     4
1  bar     2     5

分组(grouping)

“分组”(group by)指的是一个涉及以下一个或多个步骤的过程:

  • 拆分:根据某些标准将数据分成组
  • 应用:对每个组独立地应用一个函数
  • 组合:将结果合并到一个数据结构中
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),
    }
)

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

按列标签分组,选择列标签,然后对结果组应用sum()函数:

df.groupby("A")[["C", "D"]].sum()
Out[91]: 
            C         D
A                      
bar  1.732707  1.073134
foo  2.824590 -0.574779

按多个列标签分组会形成多层索引(MultiIndex)。

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)

堆叠(stack)与解堆叠(unstack)

arrays = [
    ["bar", "bar", "baz", "baz", "foo", "foo", "qux", "qux"],
    ["one", "two", "one", "two", "one", "two", "one", "two"],
]

index = pd.MultiIndex.from_arrays(arrays, names=["first", "second"])

df = pd.DataFrame(np.random.randn(8, 2), index=index, columns=["A", "B"])

df2 = df[:4]

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列的一个层级,将列标签转移到行索引,形成多层索引的Series:

stacked = df2.stack()

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(其index为MultiIndex),stack()的逆操作是unstack(),默认情况下会解叠最后一层(将行索引的最后一层转移回列):

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

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

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

数据透视表(pivot table)

pivot_table()通过指定index(行索引)、columns(列索引)和values(值)来对DataFrame进行数据透视:

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),
    }
)

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

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分钟级数据)。

rng = pd.date_range("1/1/2012", periods=100, freq="s")

ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng)

ts.resample("5Min").sum()
Out[108]: 
2012-01-01    24182
Freq: 5min, dtype: int64

Series.tz_localize()将时间序列本地化到一个时区:

rng = pd.date_range("3/6/2012 00:00", periods=5, freq="D")

ts = pd.Series(np.random.randn(len(rng)), rng)

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

ts_utc = ts.tz_localize("UTC")

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()将已有时区信息的时间序列转换为另一个时区:

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
Freq: D, dtype: float64

向时间序列添加非固定持续时间(例如工作日):

rng
Out[115]: 
DatetimeIndex(['2012-03-06', '2012-03-07', '2012-03-08', '2012-03-09',
               '2012-03-10'],
              dtype='datetime64[ns]', freq='D')

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[ns]', freq=None)

分类数据(Categoricals)

Pandas可以在DataFrame中包含分类数据。

df = pd.DataFrame(
    {"id": [1, 2, 3, 4, 5, 6], "raw_grade": ["a", "b", "b", "a", "a", "e"]}
)

将原始成绩转换为分类数据类型:

df["grade"] = df["raw_grade"].astype("category")

df["grade"]
Out[119]: 
0    a
1    b
2    b
3    a
4    a
5    e
Name: grade, dtype: category
Categories (3, object): ['a', 'b', 'e']

将类别重命名为更有意义的名称:

new_categories = ["very good", "good", "very bad"]

df["grade"] = df["grade"].cat.rename_categories(new_categories)

重新排序类别并同时添加缺失的类别(Series.cat下的方法默认返回一个新的Series):

df["grade"] = df["grade"].cat.set_categories(
    ["very bad", "bad", "medium", "good", "very good"]
)

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, object): ['very bad', 'bad', 'medium', 'good', 'very good']

排序是按照类别在categories中的顺序,而不是字典顺序:

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也会显示空类别:

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:

import matplotlib.pyplot as plt

plt.close("all")

plt.close方法用于关闭图形窗口。

ts = pd.Series(np.random.randn(1000), index=pd.date_range("1/1/2000", periods=1000))

ts = ts.cumsum()

ts.plot()

series_plot_basic

注意:在Jupyter中使用时,plot()会直接显示图形。否则,使用matplotlib.pyplot.show来显示,或matplotlib.pyplot.savefig将其保存到文件。

plot()会绘制所有列:

df = pd.DataFrame(
    np.random.randn(1000, 4), index=ts.index, columns=["A", "B", "C", "D"]
)

df = df.cumsum()

plt.figure()

df.plot()

plt.legend(loc='best')

frame_plot_basic

数据导入导出

CSV

写入CSV文件:使用DataFrame.to_csv()

df = pd.DataFrame(np.random.randint(0, 5, (10, 5)))

df.to_csv("foo.csv")

从CSV文件读取:使用read_csv()

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文件:

df.to_parquet("foo.parquet")

从Parquet文件读取:使用read_parquet()

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()

df.to_excel("foo.xlsx", sheet_name="Sheet1")

从Excel文件读取:使用read_excel()

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执行布尔操作,可能会看到类似这样的异常:

if pd.Series([False, True, False]):
    print("I was true")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-143-b27eb9c1dfc0> in <module>
----> 1 if pd.Series([False, True, False]):
      2     print("I was true")

~/work/pandas/pandas/pandas/core/generic.py in __bool__(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().

出现这个错误是因为Pandas希望明确你是在检查整个Series是否为空、是否所有元素都为真,还是至少有一个元素为真。正确的做法是使用.empty.bool().item().any().all()方法。


下面这份 Pandas 常用函数速查表 按场景分类,每个函数都配有简洁示例

1. 数据结构与创建

函数/语法 说明 示例
pd.Series(data, index) 创建一维带标签数组 pd.Series([1,2,3], index=['a','b','c'])
pd.DataFrame(data) 创建二维表格 pd.DataFrame({'A':[1,2], 'B':[3,4]})
pd.read_csv('file.csv') 从 CSV 文件读取 最常用的读取函数
pd.read_excel('file.xlsx') 从 Excel 读取 需安装 openpyxl
pd.read_sql(query, conn) 从 SQL 数据库读取

2. 数据查看与概览

函数/属性 说明 示例
df.head(n) 查看前 n 行(默认5) df.head(10)
df.tail(n) 查看后 n 行
df.info() 查看列名、非空计数、数据类型 快速了解数据概貌
df.describe() 数值列统计描述(均值、分位数等) 默认只统计数值列
df.shape 返回(行数, 列数)
df.columns 查看所有列名
df.dtypes 查看每列数据类型
df.index 查看索引

3. 数据选择与过滤

方式 说明 示例
df['col'] 选择单列,返回 Series df['age']
df[['col1','col2']] 选择多列,返回 DataFrame df[['name','age']]
df.iloc[row, col] 按整数位置选择(行/列序号) df.iloc[0, 1] 第0行第1列
df.iloc[:3, :2] 前3行前2列
df.loc[row_label, col_label] 按标签选择 df.loc[0, 'name'] 索引为0的行,'name’列
df.loc[df['age']>30, ['name','age']]
df[df['age'] > 30] 布尔索引(行过滤) 选取年龄大于30的所有行
df.query('age > 30 and gender == "M"') 使用字符串表达式过滤 简洁,适合复杂条件
df.isin([list]) 判断元素是否在列表中 df['city'].isin(['北京','上海'])

4. 数据清洗与处理

缺失值处理

函数 说明 示例
df.isnull().sum() 查看每列缺失值数量 df.isna().sum() 同义
df.dropna(axis=0, how='any') 删除缺失行/列 axis=0 删行,how='all' 全缺失才删
df.fillna(value) 填充缺失值 df.fillna(0) 填充0
df.fillna(method='ffill') 前向填充
df.interpolate() 插值填充(线性等) 适用于时间序列

重复值处理

函数 说明 示例
df.duplicated(subset=['col']) 标记重复行(返回布尔Series) subset指定列,keep='first'
df.drop_duplicates() 删除重复行 同上参数

类型转换与重命名

函数 说明 示例
df.astype({'col':'int'}) 转换列数据类型 df['age'].astype(float)
df.rename(columns={'old':'new'}) 重命名列 也可用 index 参数重命名行
df.set_index('col') 将某列设为索引 df.reset_index() 恢复
df.reset_index(drop=True) 重置索引为0,1,2… drop=True 丢弃原索引

排序

函数 说明 示例
df.sort_values(by='col', ascending=False) 按列排序 by可传入列表多列排序
df.sort_index() 按索引排序

列操作

操作 说明 示例
df['new_col'] = values 新增列 df['new'] = df['a'] + df['b']
df.drop('col', axis=1) 删除列 axis=1 表示列
df.pop('col') 弹出列(删除并返回)
df.insert(loc, 'col', value) 在指定位置插入列

5. 数据变换与计算

元素级操作

函数 说明 示例
df['col'].apply(func) 对 Series 每个元素应用函数 df['age'].apply(lambda x: x*2)
df.apply(func, axis=1) 对 DataFrame 的行或列应用 axis=0 对列,axis=1 对行
df['col'].map(dict) 根据映射字典替换值 也可用于 Series 元素转换
df.replace(to_replace, value) 替换值(全局或指定列)

字符串处理(通过 .str 访问器)

函数 说明 示例
df['col'].str.lower() 转小写 还有 upper, title
df['col'].str.contains('pattern') 是否包含子串 返回布尔 Series
df['col'].str.split(',') 分割字符串 可结合 expand=True 生成多列
df['col'].str.strip() 去除首尾空格
df['col'].str.len() 计算字符串长度

数值与统计

函数 说明 示例
df['col'].value_counts() 统计频次(降序) normalize=True 返回比例
df['col'].nunique() 唯一值个数
df.corr() 计算数值列之间的相关系数矩阵
df.cov() 协方差矩阵
df['col'].clip(lower, upper) 将值截断到指定范围

6. 分组与聚合

函数 说明 示例
df.groupby('col') 按列分组,返回 GroupBy 对象 df.groupby('gender')['age'].mean()
df.groupby(['col1','col2']).agg({'age':'mean', 'salary':'sum'}) 对不同列应用不同聚合函数 agg 可传入函数列表
df.pivot_table(values='age', index='gender', columns='city', aggfunc='mean') 创建数据透视表 类似 Excel 透视表
df.crosstab(index, columns) 交叉表(频次统计) pd.crosstab(df['gender'], df['city'])

7. 数据合并

函数 说明 示例
pd.concat([df1, df2], axis=0) 沿行或列拼接 axis=0 追加行,axis=1 追加列
df1.merge(df2, on='key', how='inner') SQL 风格的合并 how 可选 innerleftrightouter
df1.join(df2, lsuffix='_left', rsuffix='_right') 基于索引的连接 通常用于按索引合并

8. 时间序列基础

函数 说明 示例
pd.to_datetime(df['date']) 将列转换为时间类型 返回 DatetimeIndex
df.set_index('date').resample('M').mean() 重采样(按月平均) 常用时间频率:D天,W周,M月,Q
df['date'].dt.year 提取年份 通过 .dt 访问器获取年、月、日、星期等
pd.date_range('2023-01-01', periods=10, freq='D') 生成日期范围

9. 输入/输出

函数 说明 示例
df.to_csv('file.csv', index=False) 保存到 CSV index=False 不保存行索引
df.to_excel('file.xlsx', sheet_name='Sheet1') 保存到 Excel
df.to_sql('table', conn, if_exists='replace') 写入 SQL 数据库
pd.read_html(url) 从 HTML 页面读取表格 返回 DataFrame 列表
pd.read_json('file.json') 读取 JSON

10. 常用参数与技巧

  • inplace=True:直接在原对象上修改(如 df.dropna(inplace=True)),但建议慎用,先用默认返回新对象的方式调试。
  • axis:0 表示行(index),1 表示列(columns)。例如 dropna(axis=0) 删除行,drop(axis=1) 删除列。
  • 如何查看帮助:在 Jupyter 中输入 df.groupby?help(df.groupby) 即可查看详细文档。
  • 链式操作:Pandas 很多方法返回新对象,可以连续调用,如 df.dropna().groupby('A').mean()

初学者的两个提醒

  1. 慎用链式赋值:如 df[df.a>0]['b'] = 5 可能不生效,应改用 df.loc[df.a>0, 'b'] = 5
  2. 注意视图与副本:某些操作返回原数据的视图,修改可能影响原数据,使用 .copy() 可创建独立副本。

Pandas 进阶知识点速查表(面试/实战补充)

1. 窗口函数与扩展
函数/语法 说明 示例
df['col'].rolling(window=n).mean() 计算滚动窗口均值(window为窗口大小) df['销售额'].rolling(window=3).mean() 计算3期移动平均
df['col'].expanding().sum() 计算扩展窗口累计和(从第一行累计到当前行) df['成交量'].expanding().sum() 计算累计成交量
df['col'].ewm(span=n).mean() 指数加权移动平均 df['股价'].ewm(span=12, adjust=False).mean() 计算12日EWMA
df['col'].pct_change(periods=n) 计算当前值相对于前n个值的百分比变化 df['价格'].pct_change() 计算每日收益率
2. 数据重塑与透视
函数/语法 说明 示例
df.pivot(index, columns, values) 将长格式数据重塑为宽格式(要求数据无重复索引) df.pivot(index='日期', columns='产品', values='销量')
df.melt(id_vars, value_vars) 将宽格式数据逆透视(熔化)为长格式 pd.melt(df, id_vars=['姓名'], value_vars=['数学','英语'], var_name='科目', value_name='分数')
df.stack(level=-1) 将指定列层级旋转为行层级(列索引变行索引) df.stack() 将最内层列索引变为行索引
df.unstack(level=-1) 将指定行层级旋转为列层级(行索引变列索引) df.unstack() 将最内层行索引变为列索引
3. 分类数据处理
函数/语法 说明 示例
df['col'].astype('category') 将列转换为分类类型以节省内存 df['性别'] = df['性别'].astype('category')
df['col'].cat.categories 获取分类的所有类别 df['质量等级'].cat.categories
df['col'].cat.rename_categories(new_cats) 重命名类别 df['grade'].cat.rename_categories(['优','良','中'])
df['col'].cat.set_categories(new_cats) 设置新的类别顺序(可用于排序) df['grade'].cat.set_categories(['低','中','高'], ordered=True)
df['col'].cat.codes 获取类别编码(整数) df['性别编码'] = df['性别'].cat.codes
4. 时间序列进阶
函数/语法 说明 示例
df.set_index('date').resample('M').agg(...) 重采样后应用多个聚合函数 df.resample('M').agg({'销量':'sum', '利润':'mean'})
df['date'].dt.to_period('M') 将时间戳转换为周期(如按月) df['月份'] = df['日期'].dt.to_period('M')
pd.date_range(start, periods, freq) 生成日期范围 pd.date_range('2023-01-01', periods=10, freq='B') 生成10个工作日
ts.shift(periods, freq) 将时间序列向前或向后平移 df['昨日收盘'] = df['收盘价'].shift(1)
ts.diff(periods) 计算与前一期的差分 df['价格变化'] = df['价格'].diff()
ts.tz_localize(tz) / ts.tz_convert(tz) 时区本地化与转换 df.index.tz_localize('UTC').tz_convert('Asia/Shanghai')
5. 性能优化与高级技巧
函数/语法 说明 示例
pd.eval(expression) 使用字符串表达式高效计算 pd.eval('df.A + df.B') 适用于大型DataFrame
df.query(expression, engine='numexpr') 高效的条件查询,可使用局部变量 df.query('A > @threshold & B < 10')
df.eval('new_col = A + B', inplace=True) 原地创建新列的高效方法 df.eval('总和 = 数学 + 英语', inplace=True)
pd.cut(x, bins, labels) 将连续变量分箱(离散化) pd.cut(df['年龄'], bins=[0,18,60,100], labels=['少年','青年','老年'])
pd.qcut(x, q, labels) 基于分位数的分箱 pd.qcut(df['收入'], q=4, labels=['Q1','Q2','Q3','Q4'])
df.memory_usage(deep=True) 查看每列内存占用 df.memory_usage(deep=True).sum() / 1024**2 转为MB
6. 高级索引与选择
函数/语法 说明 示例
df.xs(key, axis, level) 从多层索引中选取指定级别的数据 df.xs('A', level='产品', axis=0) 选取产品A的所有行
df.filter(items, like, regex) 根据列名筛选 `df.filter(regex='价格
df.where(cond, other) 条件筛选,不满足条件的替换为other df.where(df > 0, -df) 将非正数替换为绝对值
df.mask(cond, other) where的反向操作 df.mask(df < 0, 0) 将负数替换为0
7. 函数应用与映射
函数/语法 说明 示例
df.transform(func) 对DataFrame应用函数并返回相同形状的结果 df.transform(['sqrt', 'exp']) 同时对多列应用多个函数
df.pipe(func, *args, **kwargs) 将整个DataFrame传递给自定义函数,支持链式调用 df.pipe(clean_data).pipe(transform_data)
df['col'].agg(['sum', 'mean', lambda x: x.max()-x.min()]) 对列应用多个聚合函数 df['销售额'].agg(['sum', 'mean', 'range'])
8. 数据校验与处理
函数/语法 说明 示例
df.nunique() 统计每列唯一值的个数 df.nunique() 快速发现分类变量
df['col'].value_counts(normalize=True) 统计频次及比例 df['区域'].value_counts(normalize=True) 看各区域占比
df['col'].between(left, right) 判断是否在区间内 df[df['年龄'].between(18, 35)] 选取18-35岁
df['col'].clip(lower, upper) 将超出边界的值截断 df['异常值'].clip(lower=-3, upper=3) 处理离群点
9. 综合技巧与注意事项
技巧/概念 说明 示例
链式赋值警告 避免df[df.a>0]['b'] = 5,应使用df.loc[df.a>0, 'b'] = 5 确保修改生效,避免视图与副本混淆
pd.options.display 调整显示选项 pd.set_option('display.max_columns', None) 显示所有列
df.copy(deep=True) 创建深拷贝,避免修改原数据 df_clean = df.copy() 在清洗前备份
pd.testing.assert_frame_equal(df1, df2) 测试两个DataFrame是否相等 用于单元测试或验证数据一致性

总结说明

这份补充速查表聚焦于:

  1. 时间序列分析(窗口函数、重采样、时区)
  2. 数据重塑(pivot/melt、stack/unstack)
  3. 分类数据(优化内存和有序分析)
  4. 性能优化(eval/query、分箱)
  5. 高级选择(多层索引、条件过滤)

这些都是面试中容易考察的进阶内容,也是实际工作中处理复杂数据的常用工具。建议结合官方文档和实战练习来加深理解。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐