将pandas列值转换为行值

4

我有一个数据集(171列),当我将其放入到数据框中时,它看起来像这样 -

ANO MNO UJ2010  DJ2010   UF2010 DF2010   UM2010 DM2010    UA2010    DA2010 ...
1   A   113   06/01/2010    129 06/02/2010  143 06/03/2010  209 05/04/2010 ...
2   B   218   06/01/2010    211 06/02/2010  244 06/03/2010  348 05/04/2010 ...
3   C   22    06/01/2010    114 06/02/2010  100 06/03/2010  151 05/04/2010 ...

现在我想以这种方式更改我的数据框 -
    ANO MNO Time        Unit
    1   A   06/01/2010  113
    1   A   06/02/2010  129
    1   A   06/03/2010  143
    2   B   06/01/2010  218
    2   B   06/02/2010  211
    2   B   06/03/2010  244
    3   C   06/01/2010  22
    3   C   06/02/2010  114
    3   C   06/03/2010  100
....
.....

我尝试使用pd.melt,但我认为它无法满足我的目的。我该怎么办?

3个回答

4
使用过滤后的列作为分组的独立标题, pd.lreshape 可作为 pd.melt 的近似替代方法。
通过使用 pd.lreshape,当将一个字典对象注入为它的groups参数时,键将采用新的标题名称,而将作为值提供给此dict的所有列名列表均会被转换为该单个标题下。因此,在转换后它会生成一个长格式的DF
最后根据未使用的列排序DF,以相应地对齐这些列。
然后,在末尾进行reset_index(drop=True),以通过删除中间索引来重新标记索引轴为默认整数值。
d = pd.lreshape(df, {"Time": df.filter(regex=r'^D').columns, 
                     "Unit": df.filter(regex=r'^U').columns})

d.sort_values(['ANO', 'MNO']).reset_index(drop=True)

在此输入图片描述


如果分组列的长度不匹配,则:

from itertools import groupby, chain

unused_cols = ['ANO', 'MNO']
cols = df.columns.difference(unused_cols)

# filter based on the common strings starting from the first slice upto end.
fnc = lambda x: x[1:] 
pref1, pref2 = "D", "U"

# Obtain groups based on a common interval of slices.
groups = [list(g) for n, g in groupby(sorted(cols, key=fnc), key=fnc)]

# Fill single length list with it's other char counterpart.
fill_missing = [i if len(i)==2 else i + 
                [pref1 + i[0][1:] if i[0][0] == pref2 else pref2 + i[0][1:]]
                for i in groups]

# Reindex based on newly obtained column names.
df = df.reindex(columns=unused_cols + list(chain(*fill_missing)))

按照上述步骤继续使用pd.lreshape,但这次包含dropna=False参数。


2
pd.lreshape很棒 +1 :-) - pansen
我可能错了,但是找不到关于pd.lreshape的任何文档。你能否请稍微解释一下代码?还有你能否给我提供一下文档的参考资料? - pd farhad
我已更新了我的帖子。如果你正在使用Jupyter, pd.lreshape ??应该会在弹出窗口中显示其底层代码。据我所知,目前它仍处于实验阶段,因此尚未包含在文档页面中。 - Nickil Maveli
当我尝试在我的数据集中运行您的代码片段时,它显示“ValueError: 所有列列表必须具有相同的长度”。我需要填充所有的NaN吗?因为有一些列根本没有account的值。 - pd farhad
也许尝试保留缺失值。只需在函数调用中添加 dropna=Falsepd.lreshape 中,以匹配传递的两个列表的长度。 - Nickil Maveli
显示剩余3条评论

1
你可以通过 stack 进行重塑,但首先需要在列中创建带有 %//MultiIndexMultiIndex 值将 TimeUnit 映射到第二级 MultiIndex,方法是通过 2 的整数除法(//)进行向下取整,每个对的差异则通过模除(%)创建。
然后,stack 使用由 // 创建的最后一级,并在 index 中创建新的 MultiIndex 级别,这是不必要的,因此可以使用 reset_index(level=2, drop=True) 将其删除。
最后,使用 reset_index 将第一级和第二级转换为 columns[[1,0]] 用于交换列以更改顺序。
df = df.set_index(['ANO','MNO'])
cols = np.arange(len(df.columns))
df.columns = [cols % 2, cols // 2]

print (df)
           0           1    0           1    0           1    0           1
           0           0    1           1    2           2    3           3
ANO MNO                                                                    
1   A    113  06/01/2010  129  06/02/2010  143  06/03/2010  209  05/04/2010
2   B    218  06/01/2010  211  06/02/2010  244  06/03/2010  348  05/04/2010
3   C     22  06/01/2010  114  06/02/2010  100  06/03/2010  151  05/04/2010

df = df.stack()[[1,0]].reset_index(level=2, drop=True).reset_index()
df.columns = ['ANO','MNO','Time','Unit']
print (df)
    ANO MNO        Time  Unit
0     1   A  06/01/2010   113
1     1   A  06/02/2010   129
2     1   A  06/03/2010   143
3     1   A  05/04/2010   209
4     2   B  06/01/2010   218
5     2   B  06/02/2010   211
6     2   B  06/03/2010   244
7     2   B  05/04/2010   348
8     3   C  06/01/2010    22
9     3   C  06/02/2010   114
10    3   C  06/03/2010   100
11    3   C  05/04/2010   151

编辑:
#last column is missing 
print (df)
   ANO MNO  UJ2010      DJ2010  UF2010      DF2010  UM2010      DM2010  UA2010
0    1   A     113  06/01/2010     129  06/02/2010     143  06/03/2010     209
1    2   B     218  06/01/2010     211  06/02/2010     244  06/03/2010     348
2    3   C      22  06/01/2010     114  06/02/2010     100  06/03/2010     151

df = df.set_index(['ANO','MNO'])
#MultiIndex is created by first character of column names with all another
df.columns = [df.columns.str[0], df.columns.str[1:]]
print (df)
            U           D     U           D     U           D     U
        J2010       J2010 F2010       F2010 M2010       M2010 A2010
ANO MNO                                                            
1   A     113  06/01/2010   129  06/02/2010   143  06/03/2010   209
2   B     218  06/01/2010   211  06/02/2010   244  06/03/2010   348
3   C      22  06/01/2010   114  06/02/2010   100  06/03/2010   151


#stack add missing values, replace them by NaN
df = df.stack().reset_index(level=2, drop=True).reset_index()
df.columns = ['ANO','MNO','Time','Unit']
print (df)
    ANO MNO        Time  Unit
0     1   A         NaN   209
1     1   A  06/02/2010   129
2     1   A  06/01/2010   113
3     1   A  06/03/2010   143
4     2   B         NaN   348
5     2   B  06/02/2010   211
6     2   B  06/01/2010   218
7     2   B  06/03/2010   244
8     3   C         NaN   151
9     3   C  06/02/2010   114
10    3   C  06/01/2010    22
11    3   C  06/03/2010   100

当然,给我一秒钟。 - jezrael
根据行列转换,你的代码可以工作,但现在的问题是,我丢失了一个特定列中的值。实际上,它有点像将它们的值向左移动到了另一列。我能让你理解这种情况吗? - pd farhad
是的,确实这是问题所在,有一些列缺少值。现在我该怎么办? - pd farhad
左列的数值没有移动。对我来说不起作用。 - pd farhad
我认为列名可能存在问题,有些不对齐成对 :( - jezrael
显示剩余2条评论

0
你可以使用 ilocpd.concat 来实现这个功能。解决方案很简单 - 只需将所有相关列(通过 iloc 选择)垂直堆叠在一起,然后将它们连接起来:
def rename(sub_df):
    sub_df.columns = ["ANO", "MNO", "Time", "Unit"]
    return sub_df

pd.concat([rename(df.iloc[:, [0, 1, x+1, x]])
           for x in range(2, df.shape[1], 2)])

    ANO     MNO     Time    Unit
0   1       A   06/01/2010  113
1   2       B   06/01/2010  218
2   3       C   06/01/2010  22
0   1       A   06/02/2010  129
1   2       B   06/02/2010  211
2   3       C   06/02/2010  114
0   1       A   06/03/2010  143
1   2       B   06/03/2010  244
2   3       C   06/03/2010  100
0   1       A   05/04/2010  209
1   2       B   05/04/2010  348
2   3       C   05/04/2010  151

正如我所说,我有171列,所以我需要更改concat方法内的参数吗? - pd farhad
@pdfarhad 不需要,因为它已经通过 df.shape[1] 包含在内了。范围会自动调整到您的输入数据框。 - pansen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接