翻译-10分钟入门Pandas
本文是Pandas的简短介绍,主要面向新用户。您可以在代码示例集(Cookbook)中查看更复杂的用法。
通常,我们按如下方式导入:
import numpy as np
import pandas as pd
Pandas 中的基本数据结构
Pandas 提供了两类用于处理数据的基本结构:
Series:一个一维的标签化数组,可保存任何类型的数据(整数、字符串、Python对象等)。DataFrame:一个二维的数据结构,可以像二维数组或包含行和列的表格一样保存数据。
对象创建
创建Series,传入一个值列表,Pandas 会自动创建一个默认的整数索引(RangeIndex)。
s = pd.Series([1, 3, 5, np.nan, 6, 8])
s
# 输出:
# 0 1.0
# 1 3.0
# 2 5.0
# 3 NaN
# 4 6.0
# 5 8.0
# dtype: float64
创建DataFrame,通过传递一个NumPy数组,并指定一个由date_range()生成的日期时间索引和标签化的列名。
dates = pd.date_range("20130101", periods=6)
dates
# 输出:
# 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
# 输出示例:
# 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
# ...
通过传递一个字典对象来创建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
# 输出:
# 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的列具有不同的数据类型(dtypes):
df2.dtypes
# 输出:
# A float64
# B datetime64[s]
# C float32
# D int32
# E category
# F object
# dtype: object
查看数据
使用DataFrame.head()和DataFrame.tail()分别查看框架的顶部和底部行:
df.head() # 默认查看前5行
df.tail(3) # 查看最后3行
显示DataFrame.index或DataFrame.columns:
df.index
df.columns
使用DataFrame.to_numpy()返回底层数据的NumPy表示(不包含索引或列标签):
df.to_numpy()
# 输出示例:
# array([[ 0.4691, -0.2829, -1.5091, -1.1356],
# [ 1.2121, -0.1732, 0.1192, -1.0442],
# ... ])
注意: NumPy数组整个数组只有一个dtype,而pandas DataFrame每列可以有一个dtype。 当你调用
DataFrame.to_numpy()时,Pandas会找到一个能够容纳DataFrame中所有dtype的NumPy数据类型。如果公共数据类型是object,则DataFrame.to_numpy()将需要复制数据。
describe()显示数据的快速统计摘要:
df.describe()
转置数据:
df.T
DataFrame.sort_index()按轴排序:
df.sort_index(axis=1, ascending=False) # 按列名降序排序
DataFrame.sort_values()按值排序:
df.sort_values(by="B") # 按列“B”的值升序排序
数据选择
通过 [] 选择
对于DataFrame,传递单个标签会选择一列,并产生一个Series(等同于df.A):
df["A"]
对于DataFrame,传递一个切片:会选择匹配的行:
df[0:3] # 选择前3行
df["20130102":"20130104"] # 通过标签切片选择行(包含两端)
按标签选择
使用DataFrame.loc()或DataFrame.at()。
选择匹配标签的行:
df.loc[dates[0]]
选择所有行(:)和特定的列标签:
df.loc[:, ["A", "B"]]
对于标签切片,两端都包含:
df.loc["20130102":"20130104", ["A", "B"]]
选择单个行和列标签返回一个标量:
df.loc[dates[0], "A"]
使用at快速访问标量(效果与上述方法相同):
df.at[dates[0], "A"]
按位置选择
使用DataFrame.iloc()或DataFrame.iat()。
通过传递的整数位置选择:
df.iloc[3] # 选择第4行(0基索引)
整数切片的行为类似于NumPy/Python:
df.iloc[3:5, 0:2] # 选择行切片和列切片
通过整数位置列表选择:
df.iloc[[1, 2, 4], [0, 2]] # 选择不连续的行和列
显式地对行进行切片:
df.iloc[1:3, :] # 选择第2到第3行,所有列
显式地对列进行切片:
df.iloc[:, 1:3] # 选择所有行,第2到第3列
显式获取一个值:
df.iloc[1, 1]
使用iat快速访问标量:
df.iat[1, 1]
布尔索引
选择df.A大于0的行:
df[df["A"] > 0]
从DataFrame中选择满足布尔条件的值:
df[df > 0] # 大于0的值保留,其他变为NaN
使用isin()方法进行过滤:
df2 = df.copy()
df2["E"] = ["one", "one", "two", "three", "four", "three"]
df2[df2["E"].isin(["two", "four"])]
数据设置
设置新列会自动按索引对齐数据:
s1 = pd.Series([1, 2, 3, 4, 5, 6], index=pd.date_range("20130102", periods=6))
df["F"] = s1 # 新增列‘F’
按标签设置值:
df.at[dates[0], "A"] = 0
按位置设置值:
df.iat[0, 1] = 0
通过分配NumPy数组进行设置:
df.loc[:, "D"] = np.array([5] * len(df))
带有设置的where操作:
df2 = df.copy()
df2[df2 > 0] = -df2 # 将所有大于0的值替换为其相反数
缺失数据
对于NumPy数据类型,np.nan代表缺失数据。默认情况下,它不参与计算。
重新索引(Reindexing) 允许您更改/添加/删除指定轴上的索引。这会返回一个数据副本:
df1 = df.reindex(index=dates[0:4], columns=list(df.columns) + ["E"])
df1.loc[dates[0] : dates[1], "E"] = 1
df1
DataFrame.dropna()删除任何有缺失数据的行:
df1.dropna(how="any")
DataFrame.fillna()填充缺失数据:
df1.fillna(value=5)
isna()获取值为nan的布尔掩码:
pd.isna(df1)
操作
统计
操作通常排除缺失数据。
计算每列的平均值:
df.mean() # 默认按列计算
计算每行的平均值:
df.mean(axis=1) # 按行计算
用户自定义函数
DataFrame.agg()和DataFrame.transform()分别应用归约或广播结果的用户定义函数。
df.agg(lambda x: np.mean(x) * 5.6) # 聚合,对每列应用函数
df.transform(lambda x: x * 101.2) # 转换,对每个元素应用函数
值计数
s = pd.Series(np.random.randint(0, 7, size=10))
s.value_counts() # 统计每个唯一值出现的次数
字符串方法
Series配备了一组字符串处理方法(在str属性中),可以轻松地对数组的每个元素进行操作。
s = pd.Series(["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"])
s.str.lower() # 将每个字符串转换为小写
合并
连接(Concat)
使用concat()沿行方向连接pandas对象:
df = pd.DataFrame(np.random.randn(10, 4))
pieces = [df[:3], df[3:7], df[7:]]
pd.concat(pieces) # 将拆分的片段重新连接
注意: 向
DataFrame添加列相对较快。然而,添加行需要复制,可能代价高昂。我们建议将预构建的记录列表传递给DataFrame构造函数,而不是通过迭代追加记录来构建DataFrame。
连接(Join)
merge()支持沿特定列的SQL风格连接类型。
left = pd.DataFrame({"key": ["foo", "foo"], "lval": [1, 2]})
right = pd.DataFrame({"key": ["foo", "foo"], "rval": [4, 5]})
pd.merge(left, right, on="key")
在唯一键上进行merge():
left = pd.DataFrame({"key": ["foo", "bar"], "lval": [1, 2]})
right = pd.DataFrame({"key": ["foo", "bar"], "rval": [4, 5]})
pd.merge(left, right, on="key")
分组(Grouping)
“分组”是指涉及以下一个或多个步骤的过程:
- 拆分:根据某些条件将数据拆分为组。
- 应用:将函数独立应用于每个组。
- 合并:将结果组合成数据结构。
按列标签分组,选择列标签,然后对结果组应用sum()函数:
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.groupby("A")[["C", "D"]].sum()
按多个列标签分组会形成MultiIndex(多层索引):
df.groupby(["A", "B"]).sum()
重塑
堆叠(Stack)
stack()方法“压缩”DataFrame列中的一个层级:
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]
stacked = df2.stack(future_stack=True)
stacked
对于“堆叠”后的DataFrame或Series(索引为MultiIndex),stack()的逆操作是unstack(),默认情况下它解堆最后一个层级:
stacked.unstack()
stacked.unstack(1) # 解堆第二层
stacked.unstack(0) # 解堆第一层
数据透视表(Pivot Tables)
pivot_table()通过指定values(值)、index(索引)和columns(列)来透视一个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)
})
pd.pivot_table(df, values="D", index=["A", "B"], columns=["C"])
时间序列
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() # 每5分钟求和
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_utc = ts.tz_localize("UTC") # 本地化为UTC时区
Series.tz_convert()将已有时区信息的时间序列转换为另一个时区:
ts_utc.tz_convert("US/Eastern") # 从UTC转换到美国东部时间
向时间序列添加非固定持续时间(BusinessDay):
rng + pd.offsets.BusinessDay(5) # 增加5个工作日
分类数据
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"]
将类别重命名为更有意义的名称:
new_categories = ["very good", "good", "very bad"]
df["grade"] = df["grade"].cat.rename_categories(new_categories)
对类别重新排序,并同时添加缺失的类别:
df["grade"] = df["grade"].cat.set_categories(["very bad", "bad", "medium", "good", "very good"])
按分类列排序遵循类别中的顺序,而不是词法顺序:
df.sort_values(by="grade")
使用observed=False按分类列分组也会显示空类别:
df.groupby("grade", observed=False).size()
绘图
我们使用引用matplotlib API的标准约定:
import matplotlib.pyplot as plt
plt.close("all") # 关闭图形窗口
ts = pd.Series(np.random.randn(1000), index=pd.date_range("1/1/2000", periods=1000))
ts = ts.cumsum()
ts.plot() # 绘制Series
df = pd.DataFrame(np.random.randn(1000, 4), index=ts.index, columns=["A", "B", "C", "D"])
df = df.cumsum()
df.plot() # 绘制DataFrame,所有列
plt.legend(loc='best')
数据的导入和导出
CSV
写入CSV文件:使用DataFrame.to_csv()
df.to_csv("foo.csv")
从CSV文件读取:使用read_csv()
pd.read_csv("foo.csv")
Parquet
写入Parquet文件:
df.to_parquet("foo.parquet")
从Parquet文件读取:使用read_parquet()
pd.read_parquet("foo.parquet")
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"])
常见陷阱(Gotchas)
如果你尝试对Series或DataFrame执行布尔操作,可能会看到如下异常:
if pd.Series([False, True, False]):
print("I was true")
# 抛出 ValueError: The truth value of a Series is ambiguous...
错误提示很明确:Series的真值是模糊的。应使用a.empty, a.bool(), a.item(), a.any()或a.all()来判断。