本文的使用场景相对常见,例如需要解析一份含几千上万行Excel表(或者长报告里需要插入表格数据),需要将某列的一个单元格长字符串对应展开为多行且索引号不变,或者groupby后将同组的多行值拼接为单个长字符串,使用pandas可以快速完成。(文章由jupyter notebook导出的Markdown文件生成)
groupby后将同组的多行值连接为一个长字符串
使用df.groupby、apply、pd.merge
(在mysql中可以直接使用GROUP_CONCAT(字段 SEPARATOR ‘、’)方法完成)
1 2 3 4 5 6 7
| import pandas as pd data={ '省份':['上海','广东','上海','广东','上海'], '大学名称':['复旦大学','中山大学','同济大学','华南理工大学','上海交通大学'] } df=pd.DataFrame(data) df
|
|
省份 |
大学名称 |
0 |
上海 |
复旦大学 |
1 |
广东 |
中山大学 |
2 |
上海 |
同济大学 |
3 |
广东 |
华南理工大学 |
4 |
上海 |
上海交通大学 |
1 2 3 4
| flatten_rows=lambda s:'、'.join(s)
df1=df.groupby('省份',as_index=False)['大学名称'].apply(flatten_rows) df1
|
|
省份 |
大学名称 |
0 |
上海 |
复旦大学、同济大学、上海交通大学 |
1 |
广东 |
中山大学、华南理工大学 |
1 2
| df2=df.groupby('省份',as_index=False)['大学名称'].agg('count') df2
|
1 2
| df1.merge(df2,on='省份',suffixes=['_df1','_df2'])
|
|
省份 |
大学名称_df1 |
大学名称_df2 |
0 |
上海 |
复旦大学、同济大学、上海交通大学 |
3 |
1 |
广东 |
中山大学、华南理工大学 |
2 |
1 2
| merged_df=pd.merge(df1,df2,on='省份',suffixes=['_df1','_df2']) merged_df
|
|
省份 |
大学名称_df1 |
大学名称_df2 |
0 |
上海 |
复旦大学、同济大学、上海交通大学 |
3 |
1 |
广东 |
中山大学、华南理工大学 |
2 |
1 2
| merged_df.columns=['省份','大学列表','数量'] merged_df
|
|
省份 |
大学列表 |
数量 |
0 |
上海 |
复旦大学、同济大学、上海交通大学 |
3 |
1 |
广东 |
中山大学、华南理工大学 |
2 |
df.groupby和agg方法
1 2
| groupby_df=df.groupby('省份',as_index=False).agg({'大学名称': [flatten_rows,'count']}) groupby_df
|
|
省份 |
大学名称 |
|
|
<lambda_0> |
count |
0 |
上海 |
复旦大学、同济大学、上海交通大学 |
3 |
1 |
广东 |
中山大学、华南理工大学 |
2 |
1 2
| groupby_df.columns=['省份','大学列表','数量'] groupby_df
|
|
省份 |
大学列表 |
数量 |
0 |
上海 |
复旦大学、同济大学、上海交通大学 |
3 |
1 |
广东 |
中山大学、华南理工大学 |
2 |
同组一行长字符串展开为多行
此内容为前面的逆向处理(若使用mysql,需较为复杂的sql语句实现)
1 2 3 4 5 6
| data2={ '省':pd.Series(['上海','广东','广西']), '大学列表':pd.Series(['复旦大学、同济大学、上海交通大学','中山大学、华南理工大学',None]) } df=pd.DataFrame(data2) df
|
|
省 |
大学列表 |
0 |
上海 |
复旦大学、同济大学、上海交通大学 |
1 |
广东 |
中山大学、华南理工大学 |
2 |
广西 |
None |
1 2 3
| df21=df.copy()
df21['大学列表'][0]
|
使用pd.explode方法快速将一行展开为多行
1 2 3 4
| def split_func(cell): if not cell: return cell return cell.split('、')
|
1 2
| df21['大学列表']=df21['大学列表'].apply(split_func) df21['大学列表'][0]
|
['复旦大学', '同济大学', '上海交通大学']
1
| df21.explode('大学列表',ignore_index=False)
|
|
省 |
大学列表 |
0 |
上海 |
复旦大学 |
0 |
上海 |
同济大学 |
0 |
上海 |
上海交通大学 |
1 |
广东 |
中山大学 |
1 |
广东 |
华南理工大学 |
2 |
广西 |
None |
以上重点解析:对cell单元格的长字符串进行分割为一个列表,需自行设计一个分割函数
例如字符串:’复旦大学、同济大学、上海交通大学’
同理其他更为复杂的字符串:”黄小明201、李小明202|黄小明203%…”,采用正则分割即可
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
| """ pandas.DataFrame.explode()内部调用pandas.Series.explode(): result = df[column].explode() # 删除原df指定展开列后,再与该列单独使用pandas.Series.explode得到的结果进行索引关联合并 result = df.drop([column], axis=1).join(result)
pandas.Series.explode()内部使用reshape.explode values, counts = reshape.explode(np.asarray(self.array)) # 该方法来pandas._libs基础库目录的reshape.cpython-37m-darwin.so*,这里已经被编译为动态链接库
# pandas.Series.explode()方法表示将一列中单元格为列表值展开为多行
>>> s = pd.Series([[1, 2, 3], 'foo', [], [3, 4]]) >>> s 0 [1, 2, 3] 1 foo 2 [] 3 [3, 4] dtype: object
>>> s.explode() 0 1 0 2 0 3 1 foo 2 NaN 3 3 3 4 dtype: object
# 可以同一单元格展开多行后,索引号跟原单元格索引号相同,因此可用原df通过join展开多行的Series,就能快速完成全表展开。
"""
|
使用stack+join方法快速将一行展开为多行
|
省 |
大学列表 |
0 |
上海 |
复旦大学、同济大学、上海交通大学 |
1 |
广东 |
中山大学、华南理工大学 |
2 |
广西 |
None |
1 2
| df22_1=df22['大学列表'].str.split('、',expand=True) df22_1
|
|
0 |
1 |
2 |
0 |
复旦大学 |
同济大学 |
上海交通大学 |
1 |
中山大学 |
华南理工大学 |
None |
2 |
None |
None |
None |
1 2 3
| df22_2=df22_1.stack(dropna=True) df22_2
|
0 0 复旦大学
1 同济大学
2 上海交通大学
1 0 中山大学
1 华南理工大学
dtype: object
MultiIndex([(0, 0),
(0, 1),
(0, 2),
(1, 0),
(1, 1)],
)
1 2
| df22_2.reset_index(level=1,drop=True,inplace=True) df22_2
|
0 复旦大学
0 同济大学
0 上海交通大学
1 中山大学
1 华南理工大学
dtype: object
1 2
| df22_2.name='new_col' df22_2
|
0 复旦大学
0 同济大学
0 上海交通大学
1 中山大学
1 华南理工大学
Name: new_col, dtype: object
1 2
| df22_3=df22.join(df22_2) df22_3
|
|
省 |
new_col |
0 |
上海 |
复旦大学 |
0 |
上海 |
同济大学 |
0 |
上海 |
上海交通大学 |
1 |
广东 |
中山大学 |
1 |
广东 |
华南理工大学 |
2 |
广西 |
NaN |
1 2 3
| df22_3=pd.concat([df22['省'],df22_2],join='inner',axis=1) df22_3
|
|
省 |
new_col |
0 |
上海 |
复旦大学 |
0 |
上海 |
同济大学 |
0 |
上海 |
上海交通大学 |
1 |
广东 |
中山大学 |
1 |
广东 |
华南理工大学 |
1 2
| df22_3.columns=['省','大学名称'] df22_3
|
|
省 |
大学名称 |
0 |
上海 |
复旦大学 |
0 |
上海 |
同济大学 |
0 |
上海 |
上海交通大学 |
1 |
广东 |
中山大学 |
1 |
广东 |
华南理工大学 |
自行设计合并逻辑
将以下两行数据行
1 2
| 上海 复旦大学、同济大学、上海交通大学 广东 中山大学、华南理工大学
|
对应生成两个Series列数据
1 2
| ['上海','上海','上海','广东','广东'] ['复旦大学','同济大学','上海交通大学','中山大学','华南理工大学']
|
再将这两个Series生成Dataframe即可
|
省 |
大学列表 |
0 |
上海 |
复旦大学、同济大学、上海交通大学 |
1 |
广东 |
中山大学、华南理工大学 |
2 |
广西 |
None |
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| province_list=[] college_list=[] for row in df23.itertuples(): prov_cell=row[1] split_cell=row[2] if split_cell: split_cell_list=split_cell.split('、') for item in split_cell_list: province_list.append(prov_cell) college_list.append(item) else: province_list.append(prov_cell) college_list.append(split_cell)
|
['上海', '上海', '上海', '广东', '广东', '广西']
['复旦大学', '同济大学', '上海交通大学', '中山大学', '华南理工大学', None]
1 2 3 4 5
| data23={ '省':province_list, '大学名称':college_list } pd.DataFrame(data23)
|
|
省 |
大学名称 |
0 |
上海 |
复旦大学 |
1 |
上海 |
同济大学 |
2 |
上海 |
上海交通大学 |
3 |
广东 |
中山大学 |
4 |
广东 |
华南理工大学 |
5 |
广西 |
None |