[英]Expand pandas dataframe from row-wise to column-wise
我想擴展下面的列(玩具示例)pandas DataFrame,
df = pd.DataFrame({'col1': ["A", "A", "A", "B", "B", "B"],
'col2': [1, 7, 3, 2, 9, 4],
'col3': [3, -1, 0, 5, -2, -3],})
col1 col2 col3
0 A 1 3
1 A 7 -1
2 A 3 0
3 B 2 5
4 B 9 -2
5 B 4 -3
這樣它就會變成逐行的,
col1 col2_1 col2_2 col2_3 col3_1 col3_2 col3_3
0 A 1 7 3 3 -1 0
1 B 2 9 4 5 -2 -3
我知道我將使用groupby('col1')
但不知道如何實現所需的 DataFrame。 注意:當我們執行groupby('col1')
時,每個組中的元素數量都是相等的(在這種情況下,我們有三個 A 和三個 B)
編輯:我設法通過以下代碼做到了,但效率不高,
import pandas as pd
from functools import partial
def func(x, exclude_list):
for col in x.columns:
if col in exclude_list:
continue
for i, value in enumerate(x[col].values):
x[f'{col}_{i+1}'] = value
return x
df = pd.DataFrame({'col1': ["A", "A", "A", "B", "B", "B"],
'col2': [1, 7, 3, 2, 9, 4],
'col3': [3, -1, 0, 5, -2, -3],})
exclude_list = ['col1']
columns_to_expand = ['col2', 'col3']
func2 = partial(func, exclude_list=exclude_list)
df2 = df.groupby(exclude_list).apply(func2)
df2.drop(columns_to_expand, axis=1, inplace=True)
df3 = df2.groupby(exclude_list).tail(1).reset_index()
df3.drop('index', axis=1, inplace=True)
print(df3)
這導致,
col1 col2_1 col2_2 col2_3 col3_1 col3_2 col3_3
0 A 1 7 3 3 -1 0
1 B 2 9 4 5 -2 -3
Edit2 :此代碼基於ouroboros1
答案有效,
df_pivot = None
for col in columns_to_expand:
df['index'] = [f'{col}_{i}' for i in range(1,4)]*len(np.unique(df[exclude_list].values))
if df_pivot is None:
df_pivot = df.pivot(index=exclude_list, values=col, columns='index').reset_index(drop=False)
else:
df_pivot = df_pivot.merge(df.pivot(index=exclude_list, values=col, columns='index').reset_index(drop=False))
更新:問題已更新為逐行擴展多列。 這需要對針對初始問題量身定制的初始答案進行一些重構,這只需要在一個列 ( col2
) 上進行操作。 請注意,當前重構的答案在單個列上也可以正常工作。 但是,由於它們對於這種情況有點冗長,所以我在最后只保留 1 列的原始答案。
您可以為此使用df.pivot
:
import pandas as pd
df = pd.DataFrame({'col1': ["A", "A", "A", "B", "B", "B"],
'col2': [1, 7, 3, 2, 9, 4],
'col3': [3, -1, 0, 5, -2, -3],})
cols = ['col2','col3']
# val count per unique val in col1: N.B. expecting all to have same count!
vals_unique_col1 = df.col1.value_counts()[0]+1 # 3+1 (use in `range()`)
len_unique_col1 = len(df.col1.unique()) # 2
# create temp cols [1,2,3] and store in new col
df['my_index'] = [i for i in range(1,vals_unique_col1)]*len_unique_col1
df_pivot = df.pivot(index='col1',values=cols,columns='my_index')\
.reset_index(drop=False)
# customize df cols by joining MultiIndex columns
df_pivot.columns = ['_'.join(str(i) for i in x) for x in df_pivot.columns]
df_pivot.rename(columns={'col1_':'col1'}, inplace=True)
print(df_pivot)
col1 col2_1 col2_2 col2_3 col3_1 col3_2 col3_3
0 A 1 7 3 3 -1 0
1 B 2 9 4 5 -2 -3
基於df.groupby
的 2 個替代解決方案可能如下所示:
import pandas as pd
df = pd.DataFrame({'col1': ["A", "A", "A", "B", "B", "B"],
'col2': [1, 7, 3, 2, 9, 4],
'col3': [3, -1, 0, 5, -2, -3],})
cols = ['col2','col3']
df_groupby = df.groupby('col1')[cols].agg(list)\
.apply(pd.Series.explode, axis=1).reset_index(drop=False)
# same as in `pivot` method, this will be 3
len_cols = df.col1.value_counts()[0]
# rename cols
df_groupby.columns=[f'{col}_{(idx-1)%len_cols+1}' if col != 'col1' else col
for idx, col in enumerate(df_groupby.columns)]
import pandas as pd
import numpy as np
df = pd.DataFrame({'col1': ["A", "A", "A", "B", "B", "B"],
'col2': [1, 7, 3, 2, 9, 4],
'col3': [3, -1, 0, 5, -2, -3],})
cols = ['col2','col3']
agg_lists = df.groupby('col1')[cols].agg(list)
dfs = [pd.DataFrame(agg_lists[col].tolist(), index=agg_lists.index)
for col in agg_lists.columns]
df_groupby = pd.concat(dfs, axis=1)
len_cols = df.col1.value_counts()[0]
cols_rep = np.repeat(cols,len_cols)
df_groupby.columns = [f'{col}_{str(i+1)}' for col, i
in zip(cols_rep, df_groupby.columns)]
df_groupby.reset_index(drop=False, inplace=True)
您可以為此使用df.pivot
:
import pandas as pd
df = pd.DataFrame({'col1': ["A", "A", "A", "B", "B", "B"],
'col2': [1, 7, 3, 2, 9, 4]})
# add col with prospective col names (`col1_1,*_2,*_3`)
# and multiply by len unique values in `df.col1`
df['index'] = [f'col2_{i}' for i in range(1,4)]*len(df.col1.unique())
df_pivot = df.pivot(index='col1',values='col2',columns='index')\
.reset_index(drop=False)
print(df_pivot)
index col1 col2_1 col2_2 col2_3
0 A 1 7 3
1 B 2 9 4
基於df.groupby
的替代解決方案可能如下所示:
import pandas as pd
df = pd.DataFrame({'col1': ["A", "A", "A", "B", "B", "B"], \
'col2': [1, 7, 3, 2, 9, 4]})
# create lists of values in `col2` per group in `col1`,
# then expand into multiple cols with `apply(pd.Series), finally reset index
df_groupby = df.groupby('col1').agg(list)['col2']\
.apply(pd.Series).reset_index(drop=False)
# overwrite new cols (`0,1,2`) with desired col names `col2_1, etc.`
df_groupby.columns=[f'col2_{col+1}' if col != 'col1' else col
for col in list(df_groupby.columns)]
print(df_groupby)
col1 col2_1 col2_2 col2_3
0 A 1 7 3
1 B 2 9 4
def f(group):
result = (group.set_axis(range(1, len(group) + 1))
.T
.stack())
result.index = (result.index
.to_flat_index()
.map('{0[0]}_{0[1]}'.format))
return result
在此處指定列(作為列表):
chosen_cols = ['col2', 'col3']
df.groupby('col1')[chosen_cols].apply(f).reset_index()
結果:
col1 col2_1 col2_2 col2_3 col3_1 col3_2 col3_3
0 A 1 7 3 3 -1 0
1 B 2 9 4 5 -2 -3
解釋:
f
接受一個子數據框:一個組(其中'col1'
相同),並將該組轉換為一個系列。 返回時,此系列將成為行。
例如:
假設f
接收一個col1
為'A'
的group
:
col2 col3
0 1 3
1 7 -1
2 3 0
它重置索引(因為您希望編號從 1 開始):
group.set_axis(range(1, len(group) + 1))
col2 col3
1 1 3
2 7 -1
3 3 0
在.T.stack()
之后,dataframe 變成了一個帶有 MultiIndex 的系列:
col2 1 1
2 7
3 3
col3 1 3
2 -1
3 0
dtype: int64
這個 MultiIndex 被展平(成元組),元組元素合並在一起,它們之間有一個“_”:
col2_1 1
col2_2 7
col2_3 3
col3_1 3
col3_2 -1
col3_3 0
dtype: int64
就是這樣,之后這個Series從f
返回,變成一行:
col1 col2_1 col2_2 col2_3 col3_1 col3_2 col3_3
0 A 1 7 3 3 -1 0
以前的解決方案(它只適用於 1 列,所以不要使用它):
3 個步驟(使用.groupby
):
temp = df.groupby('col1')['col2'].agg(list)
col1
A [1, 7, 3]
B [2, 9, 4]
Name: col2, dtype: object
result = pd.DataFrame(temp.tolist(), index=temp.index)
0 1 2
col1
A 1 7 3
B 2 9 4
result.columns = range(1, len(result.columns) + 1)
result = result.add_prefix('col2_').reset_index()
col1 col2_1 col2_2 col2_3
0 A 1 7 3
1 B 2 9 4
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.