Pandas Dataframe GroupBy Agg - LAMBDA - 单个值转到预先存在的或新的列表和预先存在的列表融合
Pandas Dataframe GroupBy Agg - LAMBDA - single values go to preexisting or new lists and preexisting lists fusion
我有这个要按键分组的 DataFrame:
df = pd.DataFrame({
'key': ['1', '1', '1', '2', '2', '3', '3', '4', '4', '5'],
'data1': [['A', 'B', 'C'], 'D', 'P', 'E', ['F', 'G', 'H'], ['I', 'J'], ['K', 'L'], 'M', 'N', 'O']
'data2': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
})
df
我想做groupby key和sum data2,这部分没问题。
但是关于data1,我想:
- 如果列表还不存在:
- 当键没有被复制时,单个值不会改变
- 分配给一个键的单个值被合并到一个新列表中
- 如果列表已经存在:
- 附加其他单个值
- 其他列表值附加到它
生成的 DataFrame 应该是:
dfgood = pd.DataFrame({
'key': ['1', '2', '3', '4', '5'],
'data1': [['A', 'B', 'C', 'D', 'P'], ['F', 'G', 'H', 'E'], ['I', 'J', 'K', 'L'], ['M', 'N'], 'O']
'data2': [6, 9, 13, 17, 10]
})
dfgood
事实上,我并不真正关心 data1 值在列表中的顺序,它也可以是将它们放在一起的任何结构,甚至是带分隔符的字符串或集合,如果它更容易制作的话走你认为最好的路。
我想到了两个解决方案:
- 往那边走:
dfgood = df.groupby('key', as_index=False).agg({
'data1' : lambda x: x.iloc[0].append(x.iloc[1]) if type(x.iloc[0])==list else list(x),
'data2' : sum,
})
dfgood
由于 x.iloc[1]
中的 index out of range
,它不起作用。
我也尝试过,因为 data1 在 :
问题的另一个组中是这样组织的
dfgood = df.groupby('key', as_index=False).agg({
'data1' : lambda g: g.iloc[0] if len(g) == 1 else list(g)),
'data2' : sum,
})
dfgood
但它是根据预先存在的列表或值创建新列表,而不是将数据附加到现有列表。
- 另一种方法,但我认为它更复杂,应该有更好或更快的解决方案:
- 使用
apply
、 将 data1 列表和单个值转换为单独的系列
- 使用
wide_to_long
为每个键保留单个值,
- 然后分组应用:
dfgood = df.groupby('key', as_index=False).agg({
'data1' : lambda g: g.iloc[0] if len(g) == 1 else list(g)),
'data2' : sum,
})
dfgood
我认为我的问题是我不知道如何正确使用 lambda,所以我尝试了一些愚蠢的事情,例如上一个示例中的 x.iloc[1]
。看了很多关于lambdas的教程,但脑子里还是很模糊
您可以 explode
获取单独的行,然后在注意屏蔽 data2 中的重复值(以避免对重复值求和)后再次与 groupby
+agg
聚合:
(df.explode('data1')
.assign(data2=lambda d: d['data2'].mask(d.duplicated(['key', 'data2']), 0))
.groupby('key')
.agg({'data1': list, 'data2': 'sum'})
)
输出:
data1 data2
key
1 [A, B, C, D, P] 6
2 [E, F, G, H] 9
3 [I, J, K, L] 13
4 [M, N] 17
5 [O] 10
列表与标量存在问题组合,可能的解决方案是先创建标量列表,然后在 groupby.agg
中将它们展平:
dfgood = (df.assign(data1 = df['data1'].apply(lambda y: y if isinstance(y, list) else [y]))
.groupby('key', as_index=False).agg({
'data1' : lambda x: [z for y in x for z in y],
'data2' : sum,
})
)
print (dfgood)
key data1 data2
0 1 [A, B, C, D, P] 6
1 2 [E, F, G, H] 9
2 3 [I, J, K, L] 13
3 4 [M, N] 17
4 5 [O] 10
另一个想法是使用 flatten
函数来展平列表,而不是字符串:
#
def flatten(foo):
for x in foo:
if hasattr(x, '__iter__') and not isinstance(x, str):
for y in flatten(x):
yield y
else:
yield x
dfgood = (df.groupby('key', as_index=False).agg({
'data1' : lambda x: list(flatten(x)),
'data2' : sum}))
我有这个要按键分组的 DataFrame:
df = pd.DataFrame({
'key': ['1', '1', '1', '2', '2', '3', '3', '4', '4', '5'],
'data1': [['A', 'B', 'C'], 'D', 'P', 'E', ['F', 'G', 'H'], ['I', 'J'], ['K', 'L'], 'M', 'N', 'O']
'data2': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
})
df
我想做groupby key和sum data2,这部分没问题。 但是关于data1,我想:
- 如果列表还不存在:
- 当键没有被复制时,单个值不会改变
- 分配给一个键的单个值被合并到一个新列表中
- 如果列表已经存在:
- 附加其他单个值
- 其他列表值附加到它
生成的 DataFrame 应该是:
dfgood = pd.DataFrame({
'key': ['1', '2', '3', '4', '5'],
'data1': [['A', 'B', 'C', 'D', 'P'], ['F', 'G', 'H', 'E'], ['I', 'J', 'K', 'L'], ['M', 'N'], 'O']
'data2': [6, 9, 13, 17, 10]
})
dfgood
事实上,我并不真正关心 data1 值在列表中的顺序,它也可以是将它们放在一起的任何结构,甚至是带分隔符的字符串或集合,如果它更容易制作的话走你认为最好的路。
我想到了两个解决方案:
- 往那边走:
dfgood = df.groupby('key', as_index=False).agg({
'data1' : lambda x: x.iloc[0].append(x.iloc[1]) if type(x.iloc[0])==list else list(x),
'data2' : sum,
})
dfgood
由于 x.iloc[1]
中的 index out of range
,它不起作用。
我也尝试过,因为 data1 在
dfgood = df.groupby('key', as_index=False).agg({
'data1' : lambda g: g.iloc[0] if len(g) == 1 else list(g)),
'data2' : sum,
})
dfgood
但它是根据预先存在的列表或值创建新列表,而不是将数据附加到现有列表。
- 另一种方法,但我认为它更复杂,应该有更好或更快的解决方案:
- 使用
apply
、 将 data1 列表和单个值转换为单独的系列
- 使用
wide_to_long
为每个键保留单个值, - 然后分组应用:
- 使用
dfgood = df.groupby('key', as_index=False).agg({
'data1' : lambda g: g.iloc[0] if len(g) == 1 else list(g)),
'data2' : sum,
})
dfgood
我认为我的问题是我不知道如何正确使用 lambda,所以我尝试了一些愚蠢的事情,例如上一个示例中的 x.iloc[1]
。看了很多关于lambdas的教程,但脑子里还是很模糊
您可以 explode
获取单独的行,然后在注意屏蔽 data2 中的重复值(以避免对重复值求和)后再次与 groupby
+agg
聚合:
(df.explode('data1')
.assign(data2=lambda d: d['data2'].mask(d.duplicated(['key', 'data2']), 0))
.groupby('key')
.agg({'data1': list, 'data2': 'sum'})
)
输出:
data1 data2
key
1 [A, B, C, D, P] 6
2 [E, F, G, H] 9
3 [I, J, K, L] 13
4 [M, N] 17
5 [O] 10
列表与标量存在问题组合,可能的解决方案是先创建标量列表,然后在 groupby.agg
中将它们展平:
dfgood = (df.assign(data1 = df['data1'].apply(lambda y: y if isinstance(y, list) else [y]))
.groupby('key', as_index=False).agg({
'data1' : lambda x: [z for y in x for z in y],
'data2' : sum,
})
)
print (dfgood)
key data1 data2
0 1 [A, B, C, D, P] 6
1 2 [E, F, G, H] 9
2 3 [I, J, K, L] 13
3 4 [M, N] 17
4 5 [O] 10
另一个想法是使用 flatten
函数来展平列表,而不是字符串:
#
def flatten(foo):
for x in foo:
if hasattr(x, '__iter__') and not isinstance(x, str):
for y in flatten(x):
yield y
else:
yield x
dfgood = (df.groupby('key', as_index=False).agg({
'data1' : lambda x: list(flatten(x)),
'data2' : sum}))