【问题标题】:Pandas Dataframe: shift/merge multiple rows sharing the same column values into one rowPandas Dataframe:将共享相同列值的多行移动/合并为一行
【发布时间】:2018-12-01 03:35:17
【问题描述】:

对于可能与标题混淆的情况,我们深表歉意。我会用下面的代码和图片更好地描述我的问题。

现在我有一个包含多列的数据框。前两列,它们被排序,'Route' 和 'ID' (对不起格式,这里所有的行都有 'Route' 值 '100' 和 'ID' 从 1 到 3。

df1.head(9)
  Route ID  Year    Vol Truck_Vol   Truck_%
0   100 1   2017.0  7016    635.0   9.1
1   100 1   2014.0  6835    NaN NaN
2   100 1   2011.0  5959    352.0   5.9
3   100 2   2018.0  15828   NaN NaN
4   100 2   2015.0  13114   2964.0  22.6
5   100 2   2009.0  11844   1280.0  10.8
6   100 3   2016.0  15434   NaN NaN
7   100 3   2013.0  18699   2015.0  10.8
8   100 3   2010.0  15903   NaN NaN

我想要的是

 Route  ID  Year    Vol1    Truck_Vol1  Truck_%1    Year2   Vol2    Truck_Vol2  Truck_%2    Year3   Vol3    Truck_Vol3  Truck_%3
0   100 1   2017    7016    635.0   9.1 2014    6835    NaN NaN 2011    5959    352.0   5.9
1   100 2   2018    15828   NaN NaN 2015    13114   2964.0  22.6    2009    11844   1280.0  10.8
2   100 3   2016    15434   NaN NaN 2013    18699   2015.0  10.8    2010    15903   NaN NaN

再次抱歉,格式混乱。让我试试简化版。

输入:

  Route ID  Year    Vol T_%
0   100 1   2017    100 1.0
1   100 1   2014    200 NaN
2   100 1   2011    300 2.0
3   100 2   2018    400 NaN
4   100 2   2015    500 3.0
5   100 2   2009    600 4.0

期望的输出:

Route   ID  Year    Vol T_% Year.1  Vol.1   T_%.1   Year.2  Vol.2   T_%.2
0   100 1   2017    100 1.0 2014    200     NaN     2011    300      2
1   100 2   2018    400 NaN 2015    500     3.0     2009    600      4

所以基本上只是移动图片中显示的单元格

我被难住了。新生成的列的名称无关紧要。

对于这个当前的数据框,我每个“组”有三行,如代码所示。如果答案可以容纳每组任意数量的行,那就太好了。

感谢您的宝贵时间。

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    groupby + cumcount + set_index + unstack

    df1 = df.assign(cid = df.groupby(['Route', 'ID']).cumcount()).set_index(['Route', 'ID', 'cid']).unstack(-1).sort_index(1,1)
    df1.columns = [f'{x}{y}' for x,y in df1.columns]
    df1 = df1.reset_index()
    

    输出df1:

       Route  ID  T_%0  Vol0  Year0  T_%1  Vol1  Year1  T_%2  Vol2  Year2
    0    100   1   1.0   100   2017   NaN   200   2014   2.0   300   2011
    1    100   2   NaN   400   2018   3.0   500   2015   4.0   600   2009
    

    【讨论】:

    • 感谢您的及时回复。这真太了不起了。但是由于我从未使用过 cumcount 和 unstack,您介意解释一下每个步骤的用途吗?代码[f'{x}{y}' for x,y in df1.columns] 是否在每个列名的末尾添加了0, 1, 2?再次感谢!
    • @BowenLiu 抱歉没有早点回复。 @user3483203 似乎解释了 cumcount 的原因。由于 unstack,我的列留下了 MultiIndex,第一级是原始列名,第二级是来自cumcount 的数字。 [f'{x}{y}' for x,y in df1.columns] 删除列上的 MultiIndex 并将 cumcount 值添加到每个变量名称。
    • @BowenLiu,是的,.sort_index(1,1) 等同于.sort_index(axis=1, level=1)。指定axis=1 表示我想对列进行排序,指定level=1 表示我想仅根据该级别中的值(即cumcount 变量)对列进行排序。在排序之前,第一级的值为 [0,1,2,0,1,2,0,1,2],因此在该级别上排序只会使其变为 [0,0,0,1,1,1,2, 2,2]。除了列出现的顺序之外,这不会更改输出 DataFrame 中的任何内容。
    • 使用索引可能很难直接看到值是什么,所以如果你在第一行代码之后执行df1.columns.get_level_values(1),你可以准确地看到每一列该级别的值是什么
    • @BowenLiu 是的,它首先对level=1 值进行排序,然后对剩余值(level=0)进行排序。您可以通过将参数 sort_remaining=False 添加到 .sort_index 来关闭该功能
    【解决方案2】:

    melt + pivot_table

    v = df.melt(id_vars=['Route', 'ID'])
    v['variable'] += v.groupby(['Route', 'ID', 'variable']).cumcount().astype(str)
    
    res = v.pivot_table(index=['Route', 'ID'], columns='variable', values='value')
    

    variable  T_% 0  T_% 1  T_% 2  Vol 0  Vol 1  Vol 2  Year 0  Year 1  Year 2
    Route ID
    100   1     1.0    NaN    2.0  100.0  200.0  300.0  2017.0  2014.0  2011.0
          2     NaN    3.0    4.0  400.0  500.0  600.0  2018.0  2015.0  2009.0
    

    如果你想对这些进行排序:

    c = res.columns.str.extract(r'(\d+)')[0].values.astype(int)
    res.iloc[:,np.argsort(c)]
    

    variable  T_%0   Vol0   Year0  T_%1   Vol1   Year1  T_%2   Vol2   Year2
    Route ID
    100   1    1.0  100.0  2017.0   NaN  200.0  2014.0   2.0  300.0  2011.0
          2    NaN  400.0  2018.0   3.0  500.0  2015.0   4.0  600.0  2009.0
    

    你问我为什么使用cumcount。解释一下,这是v 从上面看的样子:

        Route  ID variable   value
    0     100   1     Year  2017.0
    1     100   1     Year  2014.0
    2     100   1     Year  2011.0
    3     100   2     Year  2018.0
    4     100   2     Year  2015.0
    5     100   2     Year  2009.0
    6     100   1      Vol   100.0
    7     100   1      Vol   200.0
    8     100   1      Vol   300.0
    9     100   2      Vol   400.0
    10    100   2      Vol   500.0
    11    100   2      Vol   600.0
    12    100   1      T_%     1.0
    13    100   1      T_%     NaN
    14    100   1      T_%     2.0
    15    100   2      T_%     NaN
    16    100   2      T_%     3.0
    17    100   2      T_%     4.0
    

    如果我在这个 DataFrame 上使用 pivot_table,你最终会得到这样的结果:

    variable  T_%    Vol    Year
    Route ID
    100   1   1.5  200.0  2014.0
          2   3.5  500.0  2014.0
    

    显然,您在这里丢失了数据。 cumcount 是解决方案,因为它将variable 系列变成了这样:

        Route  ID variable   value
    0     100   1    Year0  2017.0
    1     100   1    Year1  2014.0
    2     100   1    Year2  2011.0
    3     100   2    Year0  2018.0
    4     100   2    Year1  2015.0
    5     100   2    Year2  2009.0
    6     100   1     Vol0   100.0
    7     100   1     Vol1   200.0
    8     100   1     Vol2   300.0
    9     100   2     Vol0   400.0
    10    100   2     Vol1   500.0
    11    100   2     Vol2   600.0
    12    100   1     T_%0     1.0
    13    100   1     T_%1     NaN
    14    100   1     T_%2     2.0
    15    100   2     T_%0     NaN
    16    100   2     T_%1     3.0
    17    100   2     T_%2     4.0
    

    每个唯一 RouteID 的重复元素计数。

    【讨论】:

    • 非常感谢。我之前从未真正使用过df.melt,因为我对它的作用感到困惑。你和@ALollz 都使用了cumcount,你愿意详细说明它的作用吗?并感谢您对排序的最后润色。
    • @BowenLiu 我加了解释
    • 我现在就开始细读。非常感谢。除了使用正则表达式之外,还有其他方法可以对数据进行排序吗?我问是因为当我应用您的代码对原始列进行排序时,出现了一些非常奇怪的东西。我对正则表达式了解不多,哈哈,但我确实觉得我真的需要学习它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-02
    • 2017-09-02
    相关资源
    最近更新 更多