根据值数据类型转置pandas DF

6

我有一个名为pandas的DataFrame A。我正在努力将其转换为我想要的格式,即DataFrame B。我尝试过使用pivotmelt,但我不确定如何使其有条件(将string值转换为FIELD_STR_VALUE,将numeric值转换为FIELD_NUM_VALUE)。我希望您能指导我正确的方向。

A:输入DataFrame

|FIELD_A |FIELD_B |FIELD_C |FIELD_D |
|--------|--------|--------|--------|
|123123  |8       |a       |23423   |
|123124  |7       |c       |6464    |
|123144  |99      |x       |234     |

B:期望的输出 DataFrame

|ID |FIELD_A |FIELD_NAME |FIELD_STR_VALUE |FIELD_NUM_VALUE |
|---|--------|-----------|----------------|----------------|
|1  |123123  |B          |                |8               |
|2  |123123  |C          |a               |                |
|3  |123123  |D          |                |23423           |
|4  |123124  |B          |                |7               |
|5  |123124  |C          |c               |                |
|6  |123124  |D          |                |6464            |
|7  |123144  |B          |                |99              |
|8  |123144  |C          |x               |                |
|9  |123144  |D          |                |234             |
3个回答

5

您可以使用:

# dic = {np.int64: 'NUM', object: 'STR'}

(df.set_index('FIELD_A')
   .pipe(lambda d: d.set_axis(pd.MultiIndex.from_arrays(
          [d.columns, d.dtypes],
         # or for custom NAMES
         #[d.columns, d.dtypes.map(dic)],
                              names=['FIELD_NAME', None]),
                              axis=1)
        )
   .stack(0).add_prefix('FIELD_').add_suffix('_VALUE')
   .reset_index()
)

注意。如果你真的想要STR/NUM,从数据类型中map那些字符串(请参见代码中的注释)。

输出:

   FIELD_A FIELD_NAME  FIELD_int64_VALUE FIELD_object_VALUE
0   123123    FIELD_B                8.0                NaN
1   123123    FIELD_C                NaN                  a
2   123123    FIELD_D            23423.0                NaN
3   123124    FIELD_B                7.0                NaN
4   123124    FIELD_C                NaN                  c
5   123124    FIELD_D             6464.0                NaN
6   123144    FIELD_B               99.0                NaN
7   123144    FIELD_C                NaN                  x
8   123144    FIELD_D              234.0                NaN

哎呀,从来没听说过 pipe 函数... - konichiwa
不是必需的,但它使得可以在链中使用所有内容。 - mozway
2
如果您有任何问题,请告诉我(我可能无法立即回答,可能会离开电脑)。 - mozway
1
是的,我想出了这个,我猜它很相似。你的可能更有效率,因为我调用了一个函数:[ d.columns, [ "numeric" if is_numeric_dtype(x) else "string" for x in d.dtypes ], ] 再次感谢! - konichiwa
如果你只想分割数字和其他类型,那么这很容易,我的方法需要列出预期的类型 ;) - mozway
显示剩余2条评论

4

您也可以尝试这个方法:

(df.melt('FIELD_A')
 .pipe(lambda d: d[['FIELD_A', 'value']].join(d.variable.str.extract('\w+_(?P<FIELD_NAME>\w+)')))
 .pipe(lambda g: g[['FIELD_A', 'FIELD_NAME']].join(g.value.astype(str).str.extract('(?P<FIELD_STR_VALUE>\D+)|(?P<FIELD_NUM_VALUE>\d+)')))
 .sort_values('FIELD_A'))

   FIELD_A FIELD_NAME FIELD_STR_VALUE FIELD_NUM_VALUE
0   123123          B             NaN               8
3   123123          C               a             NaN
6   123123          D             NaN           23423
1   123124          B             NaN               7
4   123124          C               c             NaN
7   123124          D             NaN            6464
2   123144          B             NaN              99
5   123144          C               x             NaN
8   123144          D             NaN             234

2
根据您的要求,这里提供一些批评意见。代码可扩展性不太好,如果有其他类型数据会变得很麻烦。此外需要对变量进行大量硬编码。但是对于所提供的数据集似乎运行良好。你会得到我的赞 ;) - mozway
2
非常感谢。我很欣赏这些评论,并将朝着编写更优雅的代码努力。请随时在您有时间的时候批评我的代码 :) - Anoushiravan R

2
df.melt(id_vars='FIELD_A', var_name='FIELD_NAME', value_name='FIELD_VALUE').sort_values(by='FIELD_A').reset_index(drop=True)

输出

    FIELD_A FIELD_NAME  FIELD_VALUE
0   123123  FIELD_B     8
1   123123  FIELD_C     a
2   123123  FIELD_D     23423
3   123124  FIELD_B     7
4   123124  FIELD_C     c
5   123124  FIELD_D     6464
6   123144  FIELD_B     99
7   123144  FIELD_C     x
8   123144  FIELD_D     234


希望这能帮助您实现所需的输出!
我们可以进一步扩展它:
(df
.melt(
    id_vars='FIELD_A', 
    var_name='FIELD_NAME', 
    value_name='FIELD_VALUE')
.assign(
    FIELD_NAME = lambda df: df.FIELD_NAME.str[-1], 
    FIELD_NUM_VALUE = lambda df: df.FIELD_VALUE.where(df.FIELD_VALUE.map(type) == int),
     FIELD_STR_VALUE = lambda df: np.where(df.FIELD_NUM_VALUE.isna(), df.FIELD_VALUE, np.nan))
    .drop(columns='FIELD_VALUE')
    .sort_values(by='FIELD_A', ignore_index = True)
)

   FIELD_A FIELD_NAME FIELD_NUM_VALUE FIELD_STR_VALUE
0   123123          B               8             NaN
1   123123          C             NaN               a
2   123123          D           23423             NaN
3   123124          B               7             NaN
4   123124          C             NaN               c
5   123124          D            6464             NaN
6   123144          B              99             NaN
7   123144          C             NaN               x
8   123144          D             234             NaN

另一种选项是使用 pd.Series.str.extract
temp = df.melt(id_vars='FIELD_A', var_name='FIELD_NAME', value_name='FIELD_VALUE')
temp = temp.assign(FIELD_NAME = lambda df: df.FIELD_NAME.str[-1])
regex = r"(?P<FIELD_STR_VALUE>\D+)|(?P<FIELD_NUM_VALUE>\d+)"
extract = temp.FIELD_VALUE.astype(str).str.extract(regex)
temp.drop(columns='FIELD_VALUE').assign(**extract)

   FIELD_A FIELD_NAME FIELD_STR_VALUE FIELD_NUM_VALUE
0   123123          B             NaN               8
1   123124          B             NaN               7
2   123144          B             NaN              99
3   123123          C               a             NaN
4   123124          C               c             NaN
5   123144          C               x             NaN
6   123123          D             NaN           23423
7   123124          D             NaN            6464
8   123144          D             NaN             234

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