Pandas中的轴/翻转表(但不完全)

2024-03-02 20:16:57 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个问题,我设法写了一些工作代码,但我想看看这里是否有人可以有一个更简单/更有组织/不那么难看/更内置的解决方案。很抱歉,标题非常模糊,但我不能用一句话概括这个问题。你知道吗

问题

基本上我有这样一个数据帧:

  id  foo_col  A  B  C  D
0  x  nothing  2  0  1  1
1  y       to  0  0  3  2
2  z      see  1  3  2  2

现在我想将列['A', 'B', 'C', 'D']转换成['W1', 'W2', 'W3'],这将是使用每行中的数字排序的前3个列名(每行)。你知道吗

这样,id为x的行将具有A(带2)、C(带1)、D(带1)、B(带0),从而得到'W1' = 'A''W2' = 'C''W3' = 'D'。你知道吗

目标数据帧将如下所示:

  id  foo_col W1 W2    W3
0  x  nothing  A  C     D
1  y       to  C  D  None
2  z      see  B  C     D

规则

  1. 只需按字母顺序(rowx)就可以打破关系
  2. 如果少于3个非零W,则丢失的将得到None(行y
  3. 如果有3个以上的非零W,那么多余的一个将不会出现在最终的数据帧(行z)中。你知道吗

解决方案

import pandas as pd
import operator
import more_itertools as mit

# Define starting DataFrame
df = pd.DataFrame(data={'id': ['x', 'y', 'z'],
                        'foo_col': ['nothing', 'to', 'see'],
                        'A': [2, 0, 1],
                        'B': [0, 0, 3],
                        'C': [1, 3, 2],
                        'D': [1, 2, 2]})

print('Original DataFrame')
print(df.to_string())
print()

# Define 'source' and 'target' columns
w_columns = ['A', 'B', 'C', 'D']
w_labels = ['W1', 'W2', 'W3']

# Define function to do this pivoting
def pivot_w(row, columns=w_columns, labels=w_labels):
    # Convert relevant columns of DF to dictionary
    row_dict = row[columns].to_dict()
    # Convert dictionary to list of tuples
    row_tuples = [tuple(d) for d in row_dict.items()]
    # Sort list of tuples based on the second item (the value in the cell)
    row_tuples.sort(key=operator.itemgetter(1), reverse=True)
    # Get the sorted 'column' labels
    row_list = [x[0] for x in row_tuples if x[1] != 0]
    # Enforce rules 2 and 3
    if len(row_list) < 3:
        row_list = list(mit.take(3, mit.padnone(row_list)))
    else:
        row_list = row_list[:3]

    # Create a dictionary using the W lables
    output = {i: j for i, j in zip(labels, row_list)}

    return output

# Get DataFrame with W columns and index
df_w = pd.DataFrame(list(df.apply(pivot_w, axis=1)))
# Merge DataFrames on index
df = df.merge(df_w, how='inner', left_index=True, right_index=True)
# Drop A, B, C, D columns
df.drop(columns=w_columns, inplace=True)

print('Final DataFrame')
print(df.to_string())

除了可能重新使用同一个变量在函数中存储中间结果之外,还有什么更聪明的方法吗?你知道吗

p.S.如果你们中有人有更好/更清晰标题的想法,请随时编辑!你知道吗


Tags: columnsthetoiniddataframedflabels
3条回答
(df[['A','B','C','D']]
 .stack()
 .loc[lambda x:x!=0]
 .reset_index()
 .sort_values(by=['level_0',0], ascending=False)
 .groupby('level_0').apply(lambda x:x.reset_index())['level_1']
 .reindex([0,1,2],level=1)
 .rename(lambda x:'W'+str(x+1),level=1)
 .unstack())

有一种方法:

l=['W1', 'W2', 'W3']

m=df.set_index(['id','foo_col'])

m=(m.replace(0,np.nan).apply(lambda x: x.nlargest(3),axis=1).notna().dot(m.columns+',')
 .str[:-1].str.split(',',expand=True))

m.columns=l
m.reset_index()

  id  foo_col W1 W2    W3
0  x  nothing  A  C     D
1  y       to  C  D  None
2  z      see  B  C     D

您可以使用argsort作为get top3列名,但是有必要将0值中的位置替换为排序和np.where

w_columns = ['A', 'B', 'C', 'D']
w_labels = ['W1', 'W2', 'W3']

#sorting columns names by values, last are 0 values (because minimal)
arr = np.array(w_columns)[np.argsort(-a, axis=1)]
print (arr)
[['A' 'C' 'D' 'B']
 ['C' 'D' 'A' 'B']
 ['B' 'C' 'D' 'A']]

#sorting values for 0 to last positions and compare by 0
mask = -np.sort(-df[w_columns], axis=1) == 0
print (mask)
[[False False False  True]
 [False False  True  True]
 [False False False False]]

#replace first 3 'columns' by mask to None
out = np.where(mask[:, :3], None, arr[:, :3])
print (out)
[['A' 'C' 'D']
 ['C' 'D' None]
 ['B' 'C' 'D']]

df1 = pd.DataFrame(out, columns=w_labels, index=df.index)
print (df1)
  W1 W2    W3
0  A  C     D
1  C  D  None
2  B  C     D

df = df.drop(w_columns, 1).join(df1)
print (df)
  id  foo_col W1 W2    W3
0  x  nothing  A  C     D
1  y       to  C  D  None
2  z      see  B  C     D

如果可能需要排除在所有seelcted值中不是最小值的另一个值,可以将其重新计算到NaN并用于测试使用np.isnan

a = np.where(df[w_columns] != 0, df[w_columns], np.nan)
print (a)
[[ 2. nan  1.  1.]
 [nan nan  3.  2.]
 [ 1.  3.  2.  2.]]

arr = np.array(w_columns)[np.argsort(-a, axis=1)]
mask = np.isnan(np.sort(a, axis=1))

out = np.where(mask[:, :3], None, arr[:, :3])
print (out)

[['A' 'C' 'D']
 ['C' 'D' None]
 ['B' 'C' 'D']]

相关问题 更多 >