按列对pandas DataFrame进行分组,并从列组中生成列表

4
假设我们有以下输入的数据框(DataFrame):
In [80]: %paste
data = {
 'Item_2_id': {0: 24, 1: 41, 2: 34},
 'Item_2_quantity': {0: 4, 1: 1, 2: 4},
 'Item_3_id': {0: 16, 1: 33, 2: 8},
 'Item_3_quantity': {0: 1, 1: 1, 2: 2},
 'customer_name': {0: 'John', 1: 'Paul', 2: 'Andrew'},
 'item_1_id': {0: 4, 1: 8, 2: 1},
 'item_1_quantity': {0: 1, 1: 3, 2: 1},
 'order_id': {0: 1, 1: 2, 2: 3}
}
cols = 'order_id customer_name  item_1_id  item_1_quantity  Item_2_id  Item_2_quantity  Item_3_id  Item_3_quantity'.split()

df = pd.DataFrame(data)[cols]

df
## -- End pasted text --
Out[80]:
   order_id customer_name  item_1_id  item_1_quantity  Item_2_id  Item_2_quantity  Item_3_id  Item_3_quantity
0         1          John          4                1         24                4         16                1
1         2          Paul          8                3         41                1         33                1
2         3        Andrew          1                1         34                4          8                2

我们如何对所有的idquantity列进行分组,以便得到以下所需的DataFrame:
In [85]: result
Out[85]:
   order_id customer_name           id   quantity
0         1          John  [4, 24, 16]  [1, 4, 1]
1         2          Paul  [8, 41, 33]  [3, 1, 1]
2         3        Andrew   [1, 34, 8]  [1, 4, 2]

我的尝试:

In [191]: id_vars = ['order_id','customer_name']

In [192]: df.set_index(id_vars) \
            .groupby(lambda x: x.split('_')[-1], axis=1) \
            .agg(lambda x: x.tolist())
Out[192]:
                                                 id                                       quantity
order_id customer_name
1        John           (i, t, e, m, _, 1, _, i, d)  (i, t, e, m, _, 1, _, q, u, a, n, t, i, t, y)
2        Paul           (I, t, e, m, _, 2, _, i, d)  (I, t, e, m, _, 2, _, q, u, a, n, t, i, t, y)
3        Andrew         (I, t, e, m, _, 3, _, i, d)  (I, t, e, m, _, 3, _, q, u, a, n, t, i, t, y)

如果我只是打印它 - 它可以正常工作:

In [193]: df.set_index(id_vars) \
            .groupby(lambda x: x.split('_')[-1], axis=1) \
            .agg(lambda x: print(x.tolist()))
[4, 24, 16]
[8, 41, 33]
[1, 34, 8]
[1, 4, 1]
[3, 1, 1]
[1, 4, 2]
Out[193]:
                          id quantity
order_id customer_name
1        John           None     None
2        Paul           None     None
3        Andrew         None     None

实际上,这个问题是我在尝试回答另一个问题时遇到的。我找到了一种解决方法,但我觉得肯定有更加优雅的解决方案,比如使用以下代码:

df.groupby(..., axis=1).agg(...)

或者

df.groupby(..., axis=1).apply(...)

1
我之前也看到过这个问题。我认为首先要做的是从列名中删除任何“item_”子字符串。它们是无关紧要的,使解析更加麻烦。然后根据下划线左侧的数字将列配对。 - elPastor
@DSM,就是这样!你能把它发布为答案吗?我会很乐意接受它。 - MaxU - stand with Ukraine
你需要自己发布 -- 我不确定它在更复杂的情况下会表现如何,所以我不想担负责任。 :-) - DSM
@DSM,但它完美地回答了这个特定的问题。 - MaxU - stand with Ukraine
@pshep123,感谢您的提示!lambda x: x.split('_')[-1] - 正是这样做的。 - MaxU - stand with Ukraine
3个回答

3

这里是来自@DSM的一个非常好的解决方案:

In [123]: df.set_index(['order_id','customer_name']) \
     ...:   .groupby(lambda x: x.split('_')[-1], axis=1) \
     ...:   .agg(lambda x: x.values.tolist())
     ...:
Out[123]:
                                 id   quantity
order_id customer_name
1        John           [4, 24, 16]  [1, 4, 1]
2        Paul           [8, 41, 33]  [3, 1, 1]
3        Andrew          [1, 34, 8]  [1, 4, 2]

groupby真的很好 +1 - pansen

3

using filter

df[['order_id', 'customer_name']].assign(
    id=df.filter(regex='[Ii]tem_\d+_id').values.tolist(),
    quantity=df.filter(regex='[Ii]tem_\d+_quantity').values.tolist()
)

   order_id customer_name           id   quantity
0         1          John  [4, 24, 16]  [1, 4, 1]
1         2          Paul  [8, 41, 33]  [3, 1, 1]
2         3        Andrew   [1, 34, 8]  [1, 4, 2]

enter image description here


1
不如 @MaxU 好:
id_cols = [col for col in list(df) if 'id' in col and col not in 'order_id']
quant_cols = [col for col in list(df) if 'quantity' in col]

df2 = df[['order_id', 'customer_name']].copy()
df2['quantity'] = list(df[quant_cols].values)
df2['id'] = list(df[id_cols].values)

df2:
   order_id customer_name           id   quantity
0         1          John  [4, 24, 16]  [1, 4, 1]
1         2          Paul  [8, 41, 33]  [3, 1, 1]
2         3        Andrew   [1, 34, 8]  [1, 4, 2]

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