Python数据分析学习(二):Pandas
和分别应用用户自定义函数,这些函数会缩减(aggregate)或广播(transform)其结果。
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
如你所见,列A、B、C、D会自动补全。E和F也同样存在;为了简洁,其余属性已被截断。
查看数据
使用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的NumPydtype。如果共同的数据类型是object,DataFrame.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()

注意:在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')

数据导入导出
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) 填充0df.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 可选 inner、left、right、outer |
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()。
初学者的两个提醒:
- 慎用链式赋值:如
df[df.a>0]['b'] = 5可能不生效,应改用df.loc[df.a>0, 'b'] = 5。- 注意视图与副本:某些操作返回原数据的视图,修改可能影响原数据,使用
.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是否相等 | 用于单元测试或验证数据一致性 |
总结说明
这份补充速查表聚焦于:
- 时间序列分析(窗口函数、重采样、时区)
- 数据重塑(pivot/melt、stack/unstack)
- 分类数据(优化内存和有序分析)
- 性能优化(eval/query、分箱)
- 高级选择(多层索引、条件过滤)
这些都是面试中容易考察的进阶内容,也是实际工作中处理复杂数据的常用工具。建议结合官方文档和实战练习来加深理解。
更多推荐
所有评论(0)