这是一个关于 pandas 从基础到进阶的练习题系列,来源于 github 上的 guipsamora/pandas_exercises 。这个项目从基础到进阶,可以检验你有多么了解 pandas。
我会挑选一些题目,并且提供比原题库更多的解决方法以及更详尽的解析。
计划每天更新一期,希望各位小伙伴先自行思考,再查看答案。如果对你有帮助,记得转发推荐给你的好友!
后台回复"数据",可以下载本题数据集
如下数据:
import pandas as pd
import numpy as np
df = pd.read_csv('chipotle.tsv',
sep='\t',
converters={'item_price': lambda x: float(x[1:-1])})
数据描述:
前面章节讲解过的知识点,本文不再讲解!
这次只需要4个订单的数据即可:
orders = [311, 1828, 328, 1355]
df.query('order_id in @orders')
然后把同一个订单的 item_name 串起来:
orders = [311, 1828, 328, 1355]
df = (
df.query('order_id in @orders')
.groupby('order_id').agg({'item_name':','.join})
.reset_index()
)
需求:
把 item_name 拆开,变回每个 item_name 一行数据
下面是答案了
第一步是把"串起来"的 item_name 拆分:
df.assign(item_name = df.item_name.str.split(','))
现在的 item_name 里面全是 列表对象(list):
(
df.assign(item_name = df.item_name.str.split(','))
['item_name'].apply(type)
)
此时,如果你在使用 pandas 0.25或以上版本,那么可以:
(
df.assign(item_name = df.item_name.str.split(','))
.explode('item_name')
)
点评:
记住次序,先让单元格里面的内容变成列表,然后对列做 explode
注意返回结果的行索引,这能给出另一种解法的提示
很不幸,如果你使用比较旧版本的 pandas,怎么办?
我们使用倒推法找解决思路。
只看 item_name 列,怎么从左边得到右边的结果?
实际就是有一个 列表,里面的元素都是列表,怎么展平成一个列表:
from itertools import chain
list(chain.from_iterable([[1,2,3],[4,5]]))
输出:
[1, 2, 3, 4, 5]
关于 itertools 可以关注我的相关系列教程
也就是:
from itertools import chain
dfx = df.assign(item_name = df.item_name.str.split(','))
names = list(chain.from_iterable(dfx['item_name']))
names
输出:
['Steak Salad',
'Steak Bowl',
'Chips and Guacamole',
'Carnitas Soft Tacos',
'Chicken Bowl',
'Chips and Guacamole',
'Chicken Burrito',
'Chicken Bowl',
'Chips and Guacamole',
'Canned Soft Drink']
接下来,怎么可以从4行,按每一行的 item_name 里面的列表元素数量,拆分成多行?
reindex 可以重复多行数据:
df.reindex([0,0,1,1,2,2,3,3])
怎么知道每个订单需要拆分的行数:
dfx = df.assign(item_name = df.item_name.str.split(','))
dfx['item_name'].str.len()
怎么按这个数量,生成对应的行索引值:
dfx = df.assign(item_name = df.item_name.str.split(','))
lens = dfx['item_name'].str.len()
np.repeat(lens.index,lens)
输出:
Int64Index([0, 0, 0, 0, 1, 1, 2, 3, 3, 3], dtype='int64')
行2也可以使用:
lens = dfx['item_name'].apply(len)
至此,把所有技巧连起来即可:
from itertools import chain
dfx = df.assign(item_name = df.item_name.str.split(','))
# 展开 list 中 list
names = list(chain.from_iterable(dfx['item_name']))
# 展开行
lens = dfx['item_name'].str.len()
idx = np.repeat(lens.index,lens)
dfx = dfx.reindex(idx)
dfx['item_name'] = names
dfx
这就能得到与直接使用 explode 一样的结果
总结:
推荐阅读: