Python 将DataFrame列展开为多行
如果我有一个Python 将DataFrame列展开为多行,python,pandas,Python,Pandas,如果我有一个数据帧,这样: pd.DataFrame( {"name" : "John", "days" : [[1, 3, 5, 7]] }) 给出了以下结构: days name 0 [1, 3, 5, 7] John 如何将其扩展到以下内容 days name 0 1 John 1 3 John 2 5 John 3 7 John 您可以使用df.i
数据帧
,这样:
pd.DataFrame( {"name" : "John",
"days" : [[1, 3, 5, 7]]
})
给出了以下结构:
days name
0 [1, 3, 5, 7] John
如何将其扩展到以下内容
days name
0 1 John
1 3 John
2 5 John
3 7 John
您可以使用
df.itertuples
遍历每一行,并使用列表理解将数据重塑为所需的形式:
import pandas as pd
df = pd.DataFrame( {"name" : ["John", "Eric"],
"days" : [[1, 3, 5, 7], [2,4]]})
result = pd.DataFrame([(d, tup.name) for tup in df.itertuples() for d in tup.days])
print(result)
屈服
0 1
0 1 John
1 3 John
2 5 John
3 7 John
4 2 Eric
5 4 Eric
,使用_repeat,速度最快:
In [48]: %timeit using_repeat(df)
1000 loops, best of 3: 834 µs per loop
In [5]: %timeit using_itertuples(df)
100 loops, best of 3: 3.43 ms per loop
In [7]: %timeit using_apply(df)
1 loop, best of 3: 379 ms per loop
In [8]: %timeit using_append(df)
1 loop, best of 3: 3.59 s per loop
以下是用于上述基准的设置:
import numpy as np
import pandas as pd
N = 10**3
df = pd.DataFrame( {"name" : np.random.choice(list('ABCD'), size=N),
"days" : [np.random.randint(10, size=np.random.randint(5))
for i in range(N)]})
def using_itertuples(df):
return pd.DataFrame([(d, tup.name) for tup in df.itertuples() for d in tup.days])
def using_repeat(df):
lens = [len(item) for item in df['days']]
return pd.DataFrame( {"name" : np.repeat(df['name'].values,lens),
"days" : np.concatenate(df['days'].values)})
def using_apply(df):
return (df.apply(lambda x: pd.Series(x.days), axis=1)
.stack()
.reset_index(level=1, drop=1)
.to_frame('day')
.join(df['name']))
def using_append(df):
df2 = pd.DataFrame(columns = df.columns)
for i,r in df.iterrows():
for e in r.days:
new_r = r.copy()
new_r.days = e
df2 = df2.append(new_r)
return df2
另一个解决方案:
In [139]: (df.apply(lambda x: pd.Series(x.days), axis=1)
.....: .stack()
.....: .reset_index(level=1, drop=1)
.....: .to_frame('day')
.....: .join(df['name'])
.....: )
Out[139]:
day name
0 1 John
0 3 John
0 5 John
0 7 John
这是关于NumPy的东西-
lens = [len(item) for item in df['days']]
df_out = pd.DataFrame( {"name" : np.repeat(df['name'].values,lens),
"days" : np.hstack(df['days'])
})
正如np.concatenate(df['days'].values)
比np.hstack(df['days'])更快
它使用循环理解来提取每个'days'
元素的长度,该长度必须是最小的运行时长度
样本运行-
>>> df
days name
0 [1, 3, 5, 7] John
1 [2, 4] Eric
>>> lens = [len(item) for item in df['days']]
>>> pd.DataFrame( {"name" : np.repeat(df['name'].values,lens),
... "days" : np.hstack(df['days'])
... })
days name
0 1 John
1 3 John
2 5 John
3 7 John
4 2 Eric
5 4 Eric
可能是这样的:
df2 = pd.DataFrame(columns = df.columns)
for i,r in df.iterrows():
for e in r.days:
new_r = r.copy()
new_r.days = e
df2 = df2.append(new_r)
df2
“本地”熊猫解决方案—我们将列拆分为一个系列,然后根据索引重新连接:
import pandas as pd #import
x2 = x.days.apply(lambda x: pd.Series(x)).unstack() #make an unstackeded series, x2
x.drop('days', axis = 1).join(pd.DataFrame(x2.reset_index(level=0, drop=True))) #drop the days column, join to the x2 series
多亏了,我们将其作为包装函数编写,用于展平一列,处理np.nan
和具有多列的数据帧
def flatten_column(df, column_name):
repeat_lens = [len(item) if item is not np.nan else 1 for item in df[column_name]]
df_columns = list(df.columns)
df_columns.remove(column_name)
expanded_df = pd.DataFrame(np.repeat(df.drop(column_name, axis=1).values, repeat_lens, axis=0), columns=df_columns)
flat_column_values = np.hstack(df[column_name].values)
expanded_df[column_name] = flat_column_values
expanded_df[column_name].replace('nan', np.nan, inplace=True)
return expanded_df
从0.25开始新增,您可以使用函数explode()
印刷品
name days
0 John 1
0 John 3
0 John 5
0 John 7
打扰你了,我刚刚修改了我的np.concatenate
替换为np.hstack
。看起来要快一点。你介意用它更新时间吗?:)@迪瓦卡:我很惊讶。这是相当大的进步!感谢更新!我很惊讶看到np.hstack比np.concatenate快,因为我觉得所有这些hstack和vstack都是从np.concatenate派生出来的。也许有熊猫,它在做优化?不确定np.concatenate(df['days'].values)
似乎比np.hstack(df['days'])更快。
。啊,我想这很有趣,也有点道理。因此,通过obj数据类型的NumPy数组的路径更好!我不太明白你为什么要这么做?是不是因为你有一个这样的字典,你想把它变成一个数据帧?而且,在name
列中,您希望始终具有相同的值吗?答案太棒了!稍微不同的需求:当数组类似于[val,None,None,val2,None…]
并且它们的大小不同时,可以修改什么来将NaN值保留到帧中?自我回复:首先用一些占位符替换None
,这样熊猫就不会抓狂了。然后移除reset\u索引
。在堆栈之前移动join,并可以选择在堆栈之前将name字段设置为索引,以避免重复。然后用NaN替换占位符。太棒了!这真是太棒了。
name days
0 John 1
0 John 3
0 John 5
0 John 7