问题来了
又是下午,昏昏欲睡。翻起了之前遇到的一个奇怪的需求,具体细节记不得了,只记得小姐姐当时一脸严肃的说,我这儿有一堆数据,某一列可能是列表,你把他们展开,一会儿给我。当时我还一脸懵逼,回味了很久才大致知道她啥意思。
大概就是这么一个dataframe:
把他进化成:
当时想了想,这需求很简单啊,应该五分钟吧。于是,我又熟练的打开CSDN,在饱受一大群装逼犯和复制粘贴党的精神摧残后,还是决定关掉浏览器,先暴力解决一下(因为我很怕小姐姐口中的一会儿就是10分钟或者15分钟)。
导入一下,建造一下:
import pandas as pd
import numpy as np
dd = pd.DataFrame(
{
"A": [1, 2, 3, 4, 5],
"B": ["a", "b", "c", "d", "e"],
"C": [[1, 2], [3], np.nan, [], ["a", "b", "c"]],
}
)
dd
这就是上面那个变身前的鬼数据。
- 方法一
所以,咋暴力呢,大概就是对dataframe的每一行遍历一下,遇到list就for循环拼接一下:
def explode1(df, col):
dftem1 = pd.DataFrame()
for i in range(df.shape[0]):
dftem = df.iloc[i].to_frame().T
if isinstance(df.iloc[i][col], list):
if len(df.iloc[i][col]) > 0:
for j in range(len(df.iloc[i][col])):
dftem[col + "_"] = df.iloc[i][col][j]
dftem1 = pd.concat([dftem1, dftem])
else:
dftem1 = pd.concat([dftem1, dftem])
else:
dftem1 = pd.concat([dftem1, dftem])
del dftem1[col]
dftem1.rename(columns={col + "_": col}, inplace=True)
return dftem1
在沉浸其中许久,写出了这么一坨,运行一下:
explode1(dd, "C")
破费,交任务去了。
- 方法二:
任务交上去了,我再回首我之前的写法,发现我不是很想看它。能不能优化一下呢?于是我找到了玩弄pandas多年的何同学,并向他展示了我刚刚写的高级写法。
“你先import this ”
“为啥?你就说能不能解决吧?”
“你这if套着for,for套着if,必需得学习一下python精神 ”
在我学习精神后,何同学给我发来这么一段:
def explode2(df, col):
df[col] = df[col].apply(lambda x: [x] if not isinstance(x, list) else x)
return df.drop(col, axis=1).join(
pd.DataFrame(list(df[col])).stack().reset_index(level=1, drop=True).rename(col)
)
这么简洁,这么优美,运行一下:
explode2(dd, "C")
任重道远啊。
- 方法三:
欣赏完何同学的写法,我陷入了沉思,在借鉴何同学和CSDN上的一个写法后,憋出了方法三:
def explode3(df, col):
def cc(x):
if isinstance(x, list):
if len(x) >= 1:
return x
else:
return np.nan
else:
return np.nan
df[col] = dd[col].apply(cc)
dt = df[pd.notnull(df.C)]
return df.drop(columns=col).join(
pd.DataFrame(
{
dt.columns[0]: dt[dt.columns[0]].repeat(dt[col].str.len()),
col: np.concatenate(dt[col].values),
}
)[[col]]
)
运行一下:
explode3(dd, "C")
破费。
那么看一下三个方法的运行效率吧:
%timeit explode1(dd, "C")
%timeit explode2(dd, "C")
%timeit explode3(dd, "C")
何同学还是高啊:
14.7 ms ± 742 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.87 ms ± 52.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
3.58 ms ± 148 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
收拾收拾,睡午觉了。
- 方法四
其实,官方有函数,刚在评论区看到:
dd.explode("C")
以上。