# 5. Pandas 数据处理
# 1. Pandas 数据结构
# 1.1. 数据结构
pandas 对象
名称 维数 描述 Series 1 可以看作有标签的一维数组 DataFrame 2 一般是二维标签,尺寸可变的表格结构 pandas 数据类型
类型 简介 float64 64 位浮点数 float32 32 位浮点数 int32 32 为整数 datetime64[ns] 日期时间格式 category 分类 object 对象,无明确类型
# 1.2. 生成数据
生成数据
import pandas as pd df = pd.DataFrame({'a': [1.0, 0.53, 0.67, 0.98], 'b': [0.53, 1.0, 0.89, 0.21], 'c': [0.67, 0.89, 1.0, 0.74], 'd': [0.98, 0.21, 0.74, 1.0]}, index=list('abcd')) # 得到的数据表格为: | | a | b | c | d | | --- | ---- | ---- | ---- | ---- | | a | 1 | 0.53 | 0.67 | 0.98 | | b | 0.53 | 1 | 0.89 | 0.21 | | c | 0.67 | 0.89 | 1 | 0.74 | | d | 0.98 | 0.21 | 0.74 | 1 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15生成 DataFrame 空结构
# 创建一个空 DataFrame,默认 utf-8 编码 cols = ['a', 'b', 'c'] # 列名 index = [1, 2, 3, 4] # 行名 df = pd.DataFrame(index=index, columns=cols) # 得到的数据表格为: | | a | b | c | | --- | --- | --- | --- | | 1 | nan | nan | nan | | 2 | nan | nan | nan | | 3 | nan | nan | nan | | 4 | nan | nan | nan | # 先生成空的 df 再赋值行列名是错误的做法 df = pd.DataFrame() df.columns = cols # 错误 df.index = index # 错误
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17date_range:生成等间隔时间序列,官方文档🔗 (opens new window)
函数:
pd.date_range(start, end, pediods, freq)
参数 简介 start 开始日期 end 结束日期 periods 生成日期数量 freq 日期间隔 import pandas as pd start = '2020-01-01' data = pd.date_range(start, periods=3, freq='4H') print(data) # 输出 DatetimeIndex(['2020-01-01 00:00:00', '2020-01-01 04:00:00', '2020-01-01 08:00:00'], dtype='datetime64[ns]', freq='4H') # 也可以通过开始和结束时间生成时间序列,间隔 1day date = pd.date_range('2020-01-01 12:00:00', '2022-12-31 12:00:00', freq='1D')
1
2
3
4
5
6
7
8
9
10
11
12
13
# 1.3. 数据格式转换
astype 转换成其他类型:数据格式不对可能会造成多种问题,比如计算、绘图(这些操作均不会改变原数据)
# 由其他类型转换成 float a = df.iloc[:, 0].astype('float') # 将一列数据格式设为 datetime m_df['tm'] = m_df['tm'].astype('datetime64[ns]')
1
2
3
4
5to_numeric 转换成数字
s = pd.Series(['1.0', '2', -3]) s = pd.to_numeric(s)
1
2to_datetime 转换成日期
tm = pd.to_datetime(df.iloc[:, 0])
1to_timedelta 相对日期
tolist() Series 转 list(DataFrame)
list1 = Series.tolist()
1pandas 的 datetime 格式转 python datetime
# 不能对 Series 操作 moment = data['时间'][0].to_pydatetime()
1
2pandas 转 numpy
np_data = df_data.values
1
# 1.4. 行/列操作
行操作
# 行数 len(df) # index index = data.index # 设置 index 标题 df.index.name = 'index_name' # 添加/替换 index df.index = list('abcd') # 将 index 设置成 [a,b,c,d] # 将现有数据设置成 index data.set_index('a', inplace=True) # 将 a 列设成 index
1
2
3
4
5
6
7
8
9
10
11
12
13列操作
# 列数 len(df.columns) # 列名 columns = df.columns.to_list() # 列重命名 df.columns = ['new_col1', 'new_col2'] # 重命名所有列 df.rename(columns={'a': 'A', 'b': 'B'}, inplace=True) # a 重命名为 A, b 重命名为 B # 调整列的顺序 df[['new_col2', 'new_col1']] # 重置 index,让 index 变成 0,1,2.... # drop=True: 避免在 dataframe 中插入 index 列 # inplace=True: 用修改后的 df 替换掉原本的 df df.reset_index(drop=True, inplace=True)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16index-column 互相转换
# index->column df.reset_index(level=0, inplace=True) # column->index df.set_index('col', inplace=True) # 转置(行列转换) df = df.T
1
2
3
4
5
6
7
8MultiIndex 多索引
# 创建多索引
1
2
# 2. Pandas 数据处理
# 2.1. 增
增加一行、列数据
# 增加一行数据 df.loc['0'] = [1, 2, 3] df.loc[len(df.index)] = [1, 2, 3] # 最后一行增加新数据,推荐用法 df = df.append({'a': 1, 'b': 2, 'c':3}, ignore_index=True) df['col'] = 'abc' # 增加一列完全相同的值 # 增加一列数据 df['d'] = [1, 2, 3]
1
2
3
4
5
6
7
8merge:合并,列增加
# 数据左右合并,合并依据为 key # how = inner, outer, left, right 默认 inner # 需要注意,有时合并数据会造成意外的重复 df3 = pd.merge(df1, df2, how='left', on='key1') # 单个 key df3 = pd.merge(df1, df2, how='left', on=['key1','key2']) # 多 key
1
2
3
4
5concat:拼接
# 数据拼接,列不变,行叠加 df3 = pd.concat([df1, df2]).reset_index(drop=True) # 去掉重复行 df = df.drop_duplicates() # 数据拼接,列增加,行不变 df4 = pd.concat([df1, df2, df3], axis=1)
1
2
3
4
5
6
7copy 拷贝,复制
# 浅拷贝,df1 和 df2 同步改变 df2 = df1 df2 = df1.copy(deep=False) # 深拷贝,deep 默认为 True df2 = df1.copy(deep=True)
1
2
3
4
5
6
# 2.2. 删
清理无效数据
df[df.notnull()] df.dropna()
1
2删除指定的行/列
# 删除行,axis=0 默认删除行 df = df.drop([0, 1]) # 删除第 0,1 行 # 删除列 df = df.drop(['col1'], axis=1) # 删除'col1'列
1
2
3
4
5
6删除重复行
# 去掉重复行 df = df.drop_duplicates(subset=None, keep='first', inplace=False, ignore_index=False) # 或 df.drop_duplicates(subset=['col1'], inplace=True)
1
2
3
4参数 简介 subset 根据列判断重复,默认所有列 keep 'first'保留重复的首行,'last'保留重复的末行,False 删除所有重复行 inplace True 直接修改源数据 ignore_index True 去重后重排 index 删除全 0 行
df = df.loc[~(df == 0).all(axis=1)]
1
# 2.3. 改
排序
# 升序排列,并替换原 Series sr = Series.sort_values(inplace=True) # 降序排列,不替换原 df,替换可以增加 inplace 参数 df = df.sort_values(by=['col1'], ascending=False)
1
2
3
4
5replace: 替换
import numpy as np # 将数据中的 0 替换为 nan, 这样在进行统计计算时会自动忽略这些值 df = df.replace(0, np.NaN) # 或 df.replace(0, np.NaN, inplace=True) # 将 nan 替换为其他值 df = df.replace(np.nan, 0) # 将 na 修改为 0 df = df.fillna(0) # 同上 df.fillna(0, inplace=True) # 同上
1
2
3
4
5
6
7
8
9
10
11
12修改值
df['col1'][i] = 0 # ×, 会出现警告:A value is trying to be set on a copy of a slice from a DataFrame df.loc[i, 'col1'] = 0 # √
1
2保留小数位
# round-四舍五入,decimals 保留小数位 m_df = m_df.round(decimals=3)
1
2
# 2.4. 查
检测数据是否有空值 (Nan)
# 含空数据返回 true,不含空数据返回 false df.isnull().any() # 判断数据是否为 nan,不能用== if df[] is np.nan # 查询 nan 所在位置 na_idx = np.where(np.isnan(df)) for i, j in zip(na_idx[0], na_idx[1]): print(i, j) # 行,列
1
2
3
4
5
6
7
8
9查看数据
df.head() # 顶部数据,个数可选,默认 5 行 df.tail() # 尾部数据,个数可选 df.index # 显示索引、列和底层 numpy 数据 df.info() # 显示数据的类型 df.describe() # 显示数据的快速统计摘要 df.T # 转置数据 new_df = df.sort_index(axis=1, ascending=False) # 按轴排序,降序 # 按值排序,注意这个函数不操作自身 new_df = df.sort_values(by='B') # 除非使用 inplace df.sort_values(by='B', inplace=True)
1
2
3
4
5
6
7
8
9
10
11
12
13
14选择/查询数据
# 获取 df['A'] # 获取 A 列数据 df.A # 同上 df[0] # 若 A 为第 1 列,则同上 df['20130102':'20130104'] # 通过 [] 选择,对行切片 # 按位置索引 df.iloc[3] # 显示第四行数据 df.iloc[0:3, 1:] # 类似 numpy df.iloc[[1, 2, 4], [0, 2]] # 类似 numpy # loc 通过标签访问,iloc 通过行列号访问 # 获取 a,b 列的数据 new_df = df.loc[:, ['a', 'b']] # DataFrame, 注意两层方括号 # 获取第 1 列的数 new_df = df.iloc[:, 1] # Series # 还可以直接访问列标签 new_df = df['a'][0:3000] # Series
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19条件筛选数据
# 布尔索引 df[df.A > 0] new_df = df[df['tm1'] >= '2018-01-01 00:00:00'] # 选择并选取指定行 new_df = df.loc[df['tm1'] >= '2019-01-01 00:00:00', ['tm1', 'hv']] new_df = df[df.tm1 >= '2019-01-01 00:00:00'][['tm1', 'hv']] # 列号单引号出错的话可以用双引号 # 根据具体时间筛选数据 new_df = df[df['time'].dt.hour == 10] # 筛选所有 10 点钟的数据 new_df = df[df['time'].dt.date == tm.date()] # 按照日期筛选数据(年月日) new_df = df[df['time'].dt.time == tm.time()] # 按照时间筛选数据(时分秒) # 多条件筛选,小括号不能缺 df[(df['a'] >= 10) & (df['b'] >= 10)] # 与 df[(df.a == 10) | (df.b > 20)] # 或 # 还可以进行范围筛选 new_df = df[df['date'].dt.date.between(start.date(), end.date())] # 日期范围 new_df = df[df['date'].dt.time.between(start.time(), end.time())] # 时间范围 new_df = df[(df['tm'] >= start) & (df['tm'] < end)] # 日期时间范围 new_df = df[df.a.between(10, 20)] # 值范围 # 根据条件筛选多行数据 keys = ['a', 'b', 'c'] df = df[df['name'].isin(keys)] # 选择 name 列=a,b,c 的数据 df = df[~df['name'].isin(keys)] # 选择 name 列非 a,b,c 的数据 # 筛选含有指定字段的数据,支持正则(模糊匹配) df = df[df['name'].str.contains('a')] # 选择 name 列包含字符 a 的数据 df = df[df['name'].str.contains('a|b')] # 选择 name 列包含字符 a 或 b 的数据 df = df[df['name'].str.contains('a') | df['name'].str.contains('b')] # 等同于上面 # 根据值查询元素所在位置 x = 1 df_x = df[df[0] == x] # 查找 df 第 0 列值为 x 的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35查询列名中包含特定字符的列
df = df.filter(regex='a') # 查询列名包含 a 的列
1查询索引
# 获取所在行数 idx = df[df['name'].str.contains('a')].index.to_list() # 如果 df 中有空行,需要指定空行的返回值 idx = df[df['name'].str.contains('a', na=False)].index.to_list() # 根据值查询元素所在位置 x = 1 x_idx = df[df[0] == x].index.to_list() # 查找 df 第 0 列值为 x 的元素位置
1
2
3
4
5
6
7
# 2.5. 数据计算
求和
# 行求和 df['row_sum'] = df.apply(lambda x: x.sum(), axis=1) # 部分行求和(前两列) df['row_sum'] = df.iloc[:, :2].apply(lambda x: x.sum(), axis=1) # 也可以直接用列名 df['row_sum'] = df['a'] + df['b'] # 列求和 df.sum() # 所有列求和 df['col_sum'] = df.apply(lambda x: x.sum(), axis=0)
1
2
3
4
5
6
7
8
9
10乘除
df['val'] = df['val'].map(lambda x: x / num) # 除法
1最大最小值
# 最小值 min_val = df[0].min() # 第 1 列最小值 min_val = df.iloc[:, 0].min() # 同上 # 最大值 max_val = df.iloc[0, :].max() # 第 1 行最大值
1
2
3
4
5
6求平均值
df = pd.read_csv(path, dtype=float) # 求列平均值 df['A'].mean() # 求 A 列平均值 df.mean() # 求每一列的平均值 # 求行平均值 df.mean(1)
1
2
3
4
5
6求标准差
# 计算每列数据标准差 df.std() # 计算单列数据标准差 df['a'].std()
1
2
3
4
# 2.6. 分组统计
分组 groupby
groupby 基础应用
import pandas as pd df = pd.DataFrame({ '姓名': ['小明', '小红', '小刚', '小强', '张三', '李四'], '年级': [1, 1, 1, 1, 2, 2], '班级': ['a', 'a', 'b', 'b', 'a', 'b'], '语文': [85, 93, 79, 97, 80, 75], '数学': [94, 92, 84, 84, 90, 66], '英语': [90, 91, 90, 96, 80, 69] }) # 分类统计,单个 key group1 = df.groupby('年级') # 分类统计,多个 key group1 = df.groupby(['年级', '班级']) # 统计 group1.max() group1.mean()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19根据 group 将一个 dataframe 分成多个 df
groups = df.groupby('年级') for name, group in groups: print(name) # 组名 print(group) # 组内数据
1
2
3
4时间分组
如果有一列数据为 datetime 格式,可以按照时间进行分组
# 比如按照小时分组 groups = df.groupby(df['time'].dt.hour)
1
2
聚合 aggregation
可以使用聚合函数:mean/sum/size/count/std/var/sem/describe/first/last/nth/min/max 也可以使用自定义函数
print(group1['语文'].agg(['mean', 'max'])) # mean max # 年级 班级 # 1 a 89 93 # b 88 97 # 2 a 80 80 # b 75 75 print(group1.agg({'数学': ['mean', 'max']})) # 数学 # mean max # 年级 班级 # 1 a 93 94 # b 84 84 # 2 a 90 90 # b 66 66
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16过滤 filteration
1变换 transformation
综合 apply
保存结果
df = pd.DataFrame(group1.agg({'数学': ['mean', 'max']})) df.to_csv(path)
1
2
# 2.7. 日期时间处理
读取数据
import pandas as pd df = pd.read_csv(path, parse_dates=['time']) # time 列数据读取成 Timestep 格式
1
2
3根据具体时间筛选数据
from datetime import datetime, timedelta # 筛选 1 天的数据 date = datetime(2020, 1, 1) tomorrow = date + timedelta(days=1) data = df[(df['time'] >= date.date()) & (df['time'] < tomorrow)] # 方法 2 data = df[(df['time'].dt.month == date.month) & (df['time'].dt.day == date.day)]
1
2
3
4
5
6
7
8
# 2.8. 画图
df.plot
import pandas as pd import matplotlib.pyplot as plt df.plot() plt.show()
1
2
3
4
5其他线形:bar, hist, box, kde, area, scatter, hexbin
# 比如散点图 df.plot.scatter(x='a', y='b', title='scatter') # 或 df.plot(x='tm', y='p', kind='scatter', title='scatter')
1
2
3
4大部分 matplotlib 的设置都可以做为 df.plot() 的参数
# 3. pandas 读写文件
# 3.1. 读文件
read_csv & read_excel
# header: 告诉 pandas 那些是数据的列名,没有则设为 None # encoding='gbk'防止出现乱码 # 读取 csv 文件,表头为第 0 行,文件 gbk 编码,指定字段的数据类型 df = pd.read_csv('filename.csv', header=0, encoding='gbk', dtype={'id': int, 'name': str}) # 读取 excel 文件,表头第 0 行,表 sheet1,选择第 0,1 列数据 # 依赖 openpyxl, pip install openpyxl # 读取 sheet1 用 sheet_name=0 也可 df1 = pd.read_excel('filename.xlsx', header=0, sheet_name='Sheet1', usecols=[0, 1]) # 读取 excel 还有一种方法 data = pd.ExcelFile('filename.xlsx') df2 = pd.read_excel(data, sheet_name=0, parse_cols=[0], skiprows=[0], names=['Country'])
1
2
3
4
5
6
7
8
9
10
11
12
13
14-
常用关键字 功能 chunksize=4 每 4 行数据为一组 comment='<' 如果行首字符为 <
, 则跳过该行delim_whitespacebool=True 将单个或多个空格视为分隔符,等同于 sep='\s+'
, 默认 Falsedtype = {'col' : str} 修改 col 类型到 str engine='python' 默认'c',c 更快,python 功能更完善 false_value=["no"] no 被认为 False hearder=0 默认第 0 行为 header, 没有则设为 None index_col=False 目录列,'False'表示没有目录 MultiIndex 支持双列目录 na_values=[5] 5 和 5.0 会被认为是 NaN na_valuede=["Na","0"] Na 和 0 会被认为是 NaN nrows 读取的行数 parse_dates=['tm'] 将'tm'列读取成 datetime 格式,也可以用列号 sep=':' 分隔符,支持 ':'
等符号,默认为','
,多空格或 Tab 用'\\s+'
, 多种分隔符用|
隔开skiprows=[0,3] 跳过第 0 行和第 3 行 skipfooter=2 跳过最后两行,只支持 int 型,默认 0 skip_blank_lines=True 是否跳过空行,默认 True true_values=["yes"] yes 被认为 True usecols=['col1', 'col2'] 选择要读取的列,列名列号都可以 使用设置
# 跳过多行,但保留第一行表头 df = pd.read_csv(file, skiprows=range(1, 100)) # 多空格分割:去除字符串数据中两侧的空格 df = pd.read_csv('filename.csv', sep='\\s+')
1
2
3
4
5
6
# 3.2. 写文件
to_csv 用法
# 将 df 存储为 csv,index 表示是否显示行名 df.to_csv('name.csv', index=False, sep=',', float_format='%.3f', columns=['col1', 'col2']) # 会给数据添加引号,尽量不要用 df.to_csv('name.csv', index=False, delimiter=',') # 遇到 float_format 不起作用是因为数据格式问题,转换方法如下: df = pd.DataFrame(df, dtype='float') # 数据格式转换成 float
1
2
3
4
5
6
7to_csv 部分配置
关键字 功能 sep 分隔符,默认',' na_rep 缺失数据表示,默认空,比如:na_rep='0' float_format 浮点格式,保留小数位,比如:float_format='%.3f' columns 选择写入的列,比如:columns=['col1', 'col2'] header 是否写入列名,默认'True' index 是否写入目录,默认'True' mode 写入模式,'a'追加,'w'写入,默认'w' encoding 文件编码,默认'utf-8' to_excel 用法
df.to_excel('name.excel', sheet_name='Sheet1')
1
# 4. PandasGUI
简介:Pandasgui 是一个开源的 python 模块,它为 pandas 创建了一个 GUI 界面,我们可以在其中使用 pandas 的功能分析数据和使用不同的功能,以便可视化和分析数据,并执行探索性数据分析。
安装
pip install pandasgui
1使用
import pandas as pd from pandasgui import show df = pd.read_csv('') # 打开一个数据集 show(df) # 打开 pandasGUI 界面
1
2
3
4
5
# 5. Pandas 错误处理
read_csv mixed types
问题🔗 (opens new window)cannot convert the series to <class 'float'>
问题- 原因:可能是某处变量调用忘了加限定,比如 a[i] 写成了 a