簡體   English   中英

將 pandas dataframe 從行擴展到列

[英]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 個替代解決方案可能如下所示:

  • Groupby 解決方案 1
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)]
  • Groupby 解決方案 2
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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM