将 Pandas DataFrame 从宽格式转换为长格式,通过拆分。

3

我正在尝试将以下数据从宽格式转换为长格式

df = pd.DataFrame(
    {
        "size_Ent": {
            pd.Timestamp("2021-01-01 00:00:00"): 600,
            pd.Timestamp("2021-01-02 00:00:00"): 930,
        },
        "size_Baci": {
            pd.Timestamp("2021-01-01 00:00:00"): 700,
            pd.Timestamp("2021-01-02 00:00:00"): 460,
        },
        "min_area_Ent": {
            pd.Timestamp("2021-01-01 00:00:00"): 1240,
            pd.Timestamp("2021-01-02 00:00:00"): 1503,
        },
        "min_area_Baci": {
            pd.Timestamp("2021-01-01 00:00:00"): 1285,
            pd.Timestamp("2021-01-02 00:00:00"): 953,
        },
    }
)

            size_Ent  size_Baci  min_area_Ent  min_area_Baci
2021-01-01       600        700          1240           1285
2021-01-02       930        460          1503            953

问题在于列名包含由下划线分隔的两个不同信息:

  1. 测量的属性/变量(例如大小或最小面积)。我希望这些保留为列名(无重复)。
  2. 被测量物品的标签(例如,Ent 或 Baci)。我希望这些标签成为一个名为“细菌”的新列的值。

此外,我希望行索引保持为时间戳。

它应该像这样:

           bacterium  min_area  size
2021-01-01      Baci      1285   700
2021-01-01       Ent      1240   600
2021-01-02      Baci       953   460
2021-01-02       Ent      1503   930

我尝试使用 df.T 转置数据框,但这并没有给出我想要的结果。


尝试使用 pivot 方法 https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.pivot.html - MrE
你是否正在使用pandasspark或其他类型的数据框架? - Sarah Messer
我使用pandas来处理我的数据框。 - Data_interested
发布预期结果 - RomanPerekhrest
刚刚添加了它,您可以在“目标”下看到它。 - Data_interested
2个回答

3

这个问题可以通过三个简单的步骤来解决:

首先,请注意您的列名实际上编码了一个2x2 MultiIndex,因此让我们从创建元组中的MultiIndex开始。为此,我们需要先将现有的列名转换为元组。这很容易,因为我们知道它们应该在最后一个下划线处拆分。

# Convert column names into MultiIndex, giving an informative name to the level with label data
column_tuples = df.columns.str.rsplit("_", n=1)
column_tuples = [tuple(c) for c in column_tuples]
df.columns = pd.MultiIndex.from_tuples(column_tuples,names=[None,'bacterium'])

接下来,使用df.stack()将列MultiIndex中的“bacterium”级别提取出来,并将其移动到行MultiIndex中。这与您尝试的转置操作不完全相同。
df = df.stack('bacterium')

最后,使用带有“level”参数的df.reset_index()将细菌水平从行MultiIndex中取出并将其变为适当的列。
df = df.reset_index('bacterium')

结果:

           bacterium  min_area  size
2021-01-01      Baci      1285   700
2021-01-01       Ent      1240   600
2021-01-02      Baci       953   460
2021-01-02       Ent      1503   930

你如何将其扩展到更多的变量? - Data_interested
我已经更新了我的答案,加入了一个循环来处理所有变量,只要它们遵循相同的命名约定(例如,size_X,weight_X,freq_X,area_X)。 - evces
当变量被命名为size_Bacilus、size_Enterobacter、min_area_Bacilus、min_area_Enterobacter等时,您会如何更改它?因为在这里我遇到了一个问题,即“Bacilus”和“Enterobacter”没有被识别为细菌名称。我尝试更改df [“variable”]的lambda函数。 - Data_interested
是的,您需要修改这两个lambda函数。对于bacterium,使用lambda x: x[(x.rfind("_")+1):](最后一个下划线之后的所有内容)。对于variable,使用lambda x: x[:x.rfind("_")](最后一个下划线之前的所有内容)。 - evces
嘿@Data_interested,我再想了想,发现使用带有标准pandas重塑操作的MultiIndex有一个更好的解决方案。请查看我的更新答案。如果您对此满意,请接受它。感谢这个有趣的问题! - evces

2

一种选择是使用pd.wide_to_long函数:

(pd
.wide_to_long(
    df.reset_index(), 
    stubnames=['min_area', 'size'], 
    i = 'index', 
    j = 'bacterium', 
    sep='_', 
    suffix='.+')
.reset_index('bacterium')
)
           bacterium  min_area  size
index                               
2021-01-01       Ent      1240   600
2021-01-02       Ent      1503   930
2021-01-01      Baci      1285   700
2021-01-02      Baci       953   460

使用pivot_longerpyjanitor中的另一种选项-对于这种情况,我们在names_pattern中使用正则表达式:

# pip install pyjanitor
import janitor
(df
.pivot_longer(
    index=None, 
    names_to = ('.value', 'bacterium'), 
    names_pattern=r'(size|min_area)_(.+)', 
    ignore_index=False)
)
           bacterium  size  min_area
2021-01-01       Ent   600      1240
2021-01-02       Ent   930      1503
2021-01-01      Baci   700      1285
2021-01-02      Baci   460       953

另一种选择是使用 stack

temp = df.copy()
temp.columns = temp.columns.str.rsplit("_", n = 1, expand=True)
temp.columns.names = [None, 'bacterium']
temp.stack('bacterium').reset_index('bacterium')

           bacterium  min_area  size
2021-01-01      Baci      1285   700
2021-01-01       Ent      1240   600
2021-01-02      Baci       953   460
2021-01-02       Ent      1503   930

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