【Python】数据处理工具:Pandas详细指南_pandas python
文章目录
- 一、前言
-
- 1.1 什么是Pandas?
- 1.2 安装Pandas
- 二、Pandas 数据相关
-
- 2.1 Pandas核心数据结构
-
- 1. Series
- 2. DataFrame
- 2.2 数据读取与写入
- 2.3 数据探索与清洗
-
- 查看数据
- 处理缺失值
- 数据筛选
- 2.4 数据操作
-
- 1. 排序:
-
- df.sort_values
- 代码示例
- 2. 分组
-
- df.groupby
- 代码示例
- 3. 合并数据
-
- pd.concat
- 使用示例:按行合并 DataFrame
- pd.merge
- 使用示例:SQL风格的合并
- 4. 应用函数
-
- series.apply
- 使用示例:将 `Age` 列的值乘以 2
- df.apply
- 使用示例:按行计算 `Age` 和 `Weight` 的乘积
- 2.5 时间序列处理
- 2.6 数据可视化
- 2.7 性能优化技巧
- 三、代码示例
-
- 示例1:销售数据分析
- 示例2:客户细分
一、前言
1.1 什么是Pandas?
Pandas
是Python中最流行的数据处理和分析库之一,它提供了高效、灵活且易于使用的数据结构,使得数据清洗、分析和处理变得简单直观。Pandas
的名字来源于\"Panel Data\"(面板数据)和\"Python Data Analysis\"(Python数据分析)的组合。
Pandas最初由Wes McKinney于2008年开发,现已成为数据科学领域的标准工具之一,广泛应用于金融、统计、社会科学和许多工程领域。
下面是Pandas的使用与相关接口进行介绍,很多接口的参数较多与复杂,这里只对部分进行解释,感兴趣的话可以去看官方文档:
pandas documentation
1.2 安装Pandas
在开始使用Pandas之前,需要先安装它。直接通过pip安装即可:
pip install pandas# 如果使用Anaconda发行版conda install pandas
二、Pandas 数据相关
2.1 Pandas核心数据结构
Pandas有两个主要的数据结构:Series
和DataFrame
。
1. Series
Series是一维的标签化数组,可以保存任何数据类型(整数、字符串、浮点数、Python对象等)。
import pandas as pd# 创建一个Seriess = pd.Series([1, 3, 5, 7, 9])print(s)
输出结果:
0 11 32 53 74 9dtype: int64
2. DataFrame
DataFrame是一个二维的标签化数据结构,可以看作是由多个Series组成的字典。
# 创建一个DataFramedata = { \'Name\': [\'Alice\', \'Bob\', \'Charlie\', \'David\'], \'Age\': [25, 30, 35, 40], \'City\': [\'New York\', \'Paris\', \'London\', \'Tokyo\']}df = pd.DataFrame(data)print(df)
输出结果:
Name Age City0 Alice 25 New York1 Bob 30 Paris2 Charlie 35 London3 David 40 Tokyo
2.2 数据读取与写入
Pandas支持多种数据格式的读写操作:
# 读取CSV文件df = pd.read_csv(\'data.csv\')# 读取Excel文件df = pd.read_excel(\'data.xlsx\')# 读取JSON文件df = pd.read_json(\'data.json\')# 写入CSV文件df.to_csv(\'output.csv\', index=False)# 写入Excel文件df.to_excel(\'output.xlsx\', index=False)
2.3 数据探索与清洗
查看数据
-
df.head()
df.head(n=5) # n: 要显示的前n行,默认为5
-
df.tail()
df.tail(n=5) # n: 要显示的后n行,默认为5
-
df.info()
df.info(verbose=None, buf=None, max_cols=None, memory_usage=None, show_counts=None) # 查看数据的基本信息
- 参数
verbose
:如果为True
,则打印完整的列信息。如果为False
,则打印简要的列信息。默认为None
,即自动选择。 - 参数
buf
:输出的目标,可以是None
(默认),表示输出到控制台;也可以指定一个文件对象,将输出写入该文件。 - 参数
max_cols
:指定显示的最大列数,默认为None
,即显示所有列。 - 参数
memory_usage
:是否显示内存使用情况。默认为None
,即自动选择。 - 参数
show_counts
:是否显示每列非空值的数量,默认为None
,即自动选择。
- 参数
-
df.describe()
df.describe(percentiles=None, include=None, exclude=None) # 查看数据的统计摘要
- 参数
percentiles
:指定需要计算的百分位数。它是一个列表,默认计算常见的25%
、50%
(中位数)和75%
百分位数。 - 参数
include
:指定要计算统计摘要的列类型。可以是None
(默认),表示所有列,或者指定如[\'object\']
(仅包括对象类型列)等类型。 - 参数
exclude
:指定不包含的列类型。默认是None
,表示不排除任何列。
- 参数
处理缺失值
# 检查缺失值df.isnull().sum()# 删除包含缺失值的行df.dropna()# 填充缺失值df.fillna(value)
数据筛选
# 选择单列df[\'column_name\']# 选择多列df[[\'col1\', \'col2\']]# 条件筛选df[df[\'Age\'] > 30]# 使用loc和ilocdf.loc[row_indexer, column_indexer]df.iloc[row_position, column_position]
2.4 数据操作
1. 排序:
df.sort_values
df.sort_values(by, axis=0, ascending=True, inplace=False, kind=\'quicksort\', na_position=\'last\', ignore_index=False, key=None) # 对数据进行排序
- 参数
by
:指定排序的列或列的列表。可以是单个列名(如\'Age\'
)或者多个列名组成的列表(如[\'Age\', \'Name\']
)。- 参数
axis
:指定排序的轴。默认值为0
,表示对行进行排序;如果设置为1
,则表示对列进行排序。- 参数
ascending
:控制排序顺序。默认为True
,表示升序排列;False
则表示降序排列。如果by
为多个列名,可以传入一个布尔列表,指定每列的排序顺序。- 参数
inplace
:是否在原 DataFrame 上进行修改。默认为False
,返回排序后的新 DataFrame;如果设置为True
,则直接在原 DataFrame 上进行排序。- 参数
kind
:指定排序算法。默认为\'quicksort\'
,还可以选择\'mergesort\'
或\'heapsort\'
等。- 参数
na_position
:指定缺失值的位置。默认值为\'last\'
,表示缺失值排在最后;可以设置为\'first\'
,表示缺失值排在最前面。- 参数
ignore_index
:是否重新生成索引。默认为False
,保留原有索引;设置为True
时,会重新生成连续的整数索引。- 参数
key
:传入一个函数,用于对排序列进行转换。例如,key=str.lower
会先将字符串列转换为小写字母后再排序。
代码示例
假设有一个名为 df
的 DataFrame,其中包含了员工的姓名、年龄和薪水信息:
import pandas as pd# 创建示例 DataFramedata = { \'Name\': [\'Alice\', \'Bob\', \'Charlie\', \'David\', \'Eve\'], \'Age\': [25, 30, 35, 40, 28], \'Salary\': [50000, 60000, 55000, 80000, 75000]}df = pd.DataFrame(data)print(\"原始 DataFrame:\")print(df)\'\'\'输出: Name Age Salary0 Alice 25 500001 Bob 30 600002 Charlie 35 550003 David 40 800004 Eve 28 75000\'\'\'
示例 1:按 Age
列升序排序
df_sorted = df.sort_values(by=\'Age\', ascending=True)print(\"\\n按 Age 升序排序:\")print(df_sorted)
输出:
Name Age Salary0 Alice 25 500004 Eve 28 750001 Bob 30 600002 Charlie 35 550003 David 40 80000
2. 分组
df.groupby
df.groupby(by, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, observed=False, dropna=True) # 对数据进行分组
- 参数
by
:指定用于分组的列名、列的列表或者字典。可以是单个列(如\'Age\'
),多个列(如[\'Age\', \'Gender\']
)或列名称到列值的映射(如{\'Region\': \'Country\'}
)。- 参数
axis
:指定要分组的轴,默认为0
,表示对行进行分组;如果为1
,则表示对列进行分组。- 参数
level
:针对层次化索引(MultiIndex)进行分组,指定要按哪个层级进行分组,默认为None
,表示按所有列进行分组。- 参数
as_index
:控制是否将分组的列作为返回 DataFrame 的索引。默认为True
,即分组列成为索引;如果为False
,分组列会作为普通列返回。- 参数
sort
:是否按分组键进行排序。默认为True
,表示按分组键排序;如果为False
,则按原始顺序返回分组。- 参数
group_keys
:是否保留分组键。默认为True
,即会保留分组键作为结果的一部分;如果为False
,则只返回结果。- 参数
squeeze
:如果分组结果是单列或单行,是否返回一个 Series 而不是 DataFrame。默认为False
。- 参数
observed
:在分组时,是否仅考虑实际出现的类别。默认为False
,即包括所有类别;如果为True
,则仅包括在数据中实际出现的类别。- 参数
dropna
:是否排除缺失值。默认为True
,即排除缺失值所在的组;如果为False
,则保留缺失值所在的组。
groupby
允许我们根据某些列的值对数据进行分组,然后对每个组进行聚合操作(如求平均值、求和等)。可以对分组后的数据执行多种统计操作,非常灵活。
代码示例
假设有一个名为 df
的 DataFrame,包含员工的部门和薪水信息,想要按部门计算薪水的平均值:
import pandas as pd# 创建示例 DataFramedata = { \'Department\': [\'HR\', \'Engineering\', \'Engineering\', \'HR\', \'Sales\'], \'Salary\': [50000, 70000, 75000, 52000, 60000]}df = pd.DataFrame(data)# 按部门进行分组,并计算薪水的平均值grouped = df.groupby(\'Department\')[\'Salary\'].mean()print(grouped)
输出:
DepartmentEngineering 72500.0HR 51000.0Sales 60000.0Name: Salary, dtype: float64
在这个示例中,我们使用 groupby
按 Department
列进行分组,并计算每个部门的平均薪水。
3. 合并数据
pd.concat
pd.concat(objs, axis=0, join=\'outer\', ignore_index=False, keys=None, levels=None, names=None, verify_integrity=False, sort=False, copy=True)
- 参数
objs
:一个可迭代的对象(如列表、元组等),包含多个 DataFrame 或 Series 要合并的对象。- 参数
axis
:指定沿着哪个轴进行合并。默认为0
,表示按行合并(垂直拼接);如果为1
,表示按列合并(水平拼接)。- 参数
join
:指定合并时的连接方式。\'outer\'
(默认)表示并集;\'inner\'
表示交集,只保留两个 DataFrame 都有的索引/列。- 参数
ignore_index
:是否重置索引。默认为False
,即保留原索引;如果为True
,则会重新生成索引。- 参数
keys
:用于在合并的结果中创建层次化索引,可以传入一个序列来为每个 DataFrame 指定一个键。- 参数
levels
:指定层次化索引的级别。- 参数
names
:指定层次化索引的名称。- 参数
verify_integrity
:是否检查合并后的数据是否存在重复的索引。默认为False
,即不检查;如果为True
,会抛出错误。- 参数
sort
:合并时是否对列进行排序。默认为False
,即不排序;如果为True
,则会按字母顺序排序。- 参数
copy
:是否复制数据。默认为True
,即复制数据;如果为False
,则会尽量避免复制数据。
使用示例:按行合并 DataFrame
import pandas as pd# 创建示例 DataFramedata1 = { \'Name\': [\'Alice\', \'Bob\'], \'Age\': [25, 30]}data2 = { \'Name\': [\'Charlie\', \'David\'], \'Age\': [35, 40]}df1 = pd.DataFrame(data1)df2 = pd.DataFrame(data2)# 合并两个 DataFrame(按行合并)result = pd.concat([df1, df2], ignore_index=True)print(result)
输出:
Name Age0 Alice 251 Bob 302 Charlie 353 David 40
在此示例中,df1
和 df2
被按行合并,且重置了索引。
pd.merge
pd.merge(left, right, how=\'inner\', on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=True, suffixes=(\'_x\', \'_y\'), copy=True, indicator=False, validate=None)
- 参数
left
和right
:要合并的两个 DataFrame。- 参数
how
:指定合并的方式。常见的选项有:
\'inner\'
(默认):取交集,只保留在两个 DataFrame 中都存在的行。\'outer\'
:取并集,保留两个 DataFrame 中所有的行。\'left\'
:只保留左侧 DataFrame 中的行。\'right\'
:只保留右侧 DataFrame 中的行。
- 参数
on
:指定合并时的列名,默认为None
,表示在两个 DataFrame 中都有的列名上进行合并。- 参数
left_on
和right_on
:指定分别用于左侧和右侧 DataFrame 合并的列名。如果on
为None
,可以通过这两个参数单独指定。- 参数
left_index
和right_index
:是否使用索引进行合并。默认为False
,如果为True
,则使用索引进行合并。- 参数
sort
:是否对合并后的数据按键进行排序。默认为True
,即按键排序;如果为False
,则不排序。- 参数
suffixes
:为重复列添加后缀。默认为(\'_x\', \'_y\')
。- 参数
indicator
:是否添加一列指示信息,显示每行数据来自哪个 DataFrame。默认为False
,如果为True
,会添加一个名为_merge
的列。- 参数
validate
:用于数据完整性检查的参数,常见选项有\'one_to_one\'
,\'one_to_many\'
,\'many_to_one\'
,\'many_to_many\'
。
使用示例:SQL风格的合并
import pandas as pd# 创建示例 DataFramedata1 = { \'ID\': [1, 2, 3], \'Name\': [\'Alice\', \'Bob\', \'Charlie\']}data2 = { \'ID\': [2, 3, 4], \'Salary\': [50000, 60000, 70000]}df1 = pd.DataFrame(data1)df2 = pd.DataFrame(data2)# 按 \'ID\' 列合并两个 DataFrame(内连接)result = pd.merge(df1, df2, on=\'ID\', how=\'inner\')print(result)
输出:
ID Name Salary0 2 Bob 500001 3 Charlie 60000
在这个例子中,df1
和 df2
按照 ID
列进行 SQL 风格的合并,只保留在两个 DataFrame 中都存在的 ID
值。
4. 应用函数
series.apply
Series.apply(func, convert_dtype=True, args=(), **kwds)
- 参数
func
:要应用到每个元素的函数(或函数名)。在这个例子中,lambda x: x * 2
是将每个Age
列的元素都乘以 2。- 参数
convert_dtype
:是否转换返回的结果类型。默认为True
,即转换为适当的类型;如果为False
,则保留原始类型。- 参数
args
:传递给func
的额外位置参数。- 参数
**kwds
:传递给func
的额外关键字参数。
使用示例:将 Age
列的值乘以 2
import pandas as pd# 创建示例 DataFramedata = { \'Name\': [\'Alice\', \'Bob\', \'Charlie\'], \'Age\': [25, 30, 35], \'Weight\': [55, 65, 75]}df = pd.DataFrame(data)# 使用 apply 将 Age 列的每个元素乘以 2df[\'Age_doubled\'] = df[\'Age\'].apply(lambda x: x * 2)print(df)
输出:
Name Age Weight Age_doubled0 Alice 25 55 501 Bob 30 65 602 Charlie 35 75 70
在这个例子中,我们使用 apply
对 Age
列的每个元素应用了 lambda x: x * 2
的函数,使得每个 Age
值都被乘以了 2。
df.apply
DataFrame.apply(func, axis=0, raw=False, result_type=None, args=(), **kwds)
- 参数
func
:要应用的函数(或函数名)。在这个例子中,lambda row: row[\'Age\'] * row[\'Weight\']
是对每一行执行计算,即将Age
和Weight
两列相乘。- 参数
axis
:指定操作的轴。axis=0
表示按列应用函数(默认);axis=1
表示按行应用函数。在这个例子中,axis=1
使得我们在每行上应用函数。- 参数
raw
:是否传递原始数据给函数。如果为True
,则传递原始数组给函数,而不是 Series。- 参数
result_type
:指定返回结果的类型。可以是expand
(展开为 DataFrame),reduce
(返回 Series),或者broadcast
(广播到原始 DataFrame 形状)。- 参数
args
:传递给func
的额外位置参数。- 参数
**kwds
:传递给func
的额外关键字参数。
使用示例:按行计算 Age
和 Weight
的乘积
import pandas as pd# 创建示例 DataFramedata = { \'Name\': [\'Alice\', \'Bob\', \'Charlie\'], \'Age\': [25, 30, 35], \'Weight\': [55, 65, 75]}df = pd.DataFrame(data)# 使用 apply 对每行进行计算,得到 Age 和 Weight 的乘积df[\'Age_Weight\'] = df.apply(lambda row: row[\'Age\'] * row[\'Weight\'], axis=1)print(df)
输出:
Name Age Weight Age_Weight0 Alice 25 55 13751 Bob 30 65 19502 Charlie 35 75 2625
在这个例子中,我们使用 apply
对每一行应用了 lambda row: row[\'Age\'] * row[\'Weight\']
的函数,使得每行的 Age
和 Weight
列的乘积被计算并添加为新的一列 Age_Weight
。
2.5 时间序列处理
Pandas对时间序列数据有出色的支持:
# 创建时间序列date_rng = pd.date_range(start=\'1/1/2020\', end=\'1/08/2020\', freq=\'D\')# 设置索引为日期df.set_index(\'date_column\', inplace=True)# 重采样df.resample(\'M\').mean()
2.6 数据可视化
Pandas集成了Matplotlib,可以轻松绘制图表:
# 线图df.plot()# 柱状图df.plot.bar()# 直方图df[\'Age\'].plot.hist()# 箱线图df.plot.box()
2.7 性能优化技巧
- 使用适当的数据类型(如category类型用于分类变量)
- 避免循环,使用向量化操作
- 使用query()方法进行高效查询
- 对于大型数据集,考虑使用Dask或Modin等扩展库
三、代码示例
示例1:销售数据分析
# 读取销售数据sales = pd.read_csv(\'sales_data.csv\')# 计算每月销售额sales[\'date\'] = pd.to_datetime(sales[\'date\'])monthly_sales = sales.resample(\'M\', on=\'date\')[\'amount\'].sum()# 可视化monthly_sales.plot(title=\'Monthly Sales\')
示例2:客户细分
# 创建RFM指标snapshot_date = max(df[\'InvoiceDate\']) + pd.Timedelta(days=1)rfm = df.groupby(\'CustomerID\').agg({ \'InvoiceDate\': lambda x: (snapshot_date - x.max()).days, \'InvoiceNo\': \'count\', \'TotalPrice\': \'sum\'})# 重命名列rfm.rename(columns={ \'InvoiceDate\': \'Recency\', \'InvoiceNo\': \'Frequency\', \'TotalPrice\': \'MonetaryValue\'}, inplace=True)