问题的设置
假设有多个列,其中包含不同长度的对象
df = pd.DataFrame({
'A': [1, 2],
'B': [[1, 2], [3, 4]],
'C': [[1, 2], [3, 4, 5]]
})
df
A B C
0 1 [1, 2] [1, 2]
1 2 [3, 4] [3, 4, 5]
当长度相同时,我们很容易假设不同的元素重合,并且应该“压缩”在一起。
A B C
0 1 [1, 2] [1, 2] # Typical to assume these should be zipped [(1, 1), (2, 2)]
1 2 [3, 4] [3, 4, 5]
然而,当我们看到不同长度的对象时,这个假设就会受到挑战,我们应该“压缩”吗?如果是的话,我们如何处理其中一个对象中的多余部分呢?或者,也许我们想要所有物体的乘积。这将迅速扩大规模,但可能正是人们想要的。
A B C
0 1 [1, 2] [1, 2]
1 2 [3, 4] [3, 4, 5] # is this [(3, 3), (4, 4), (None, 5)]?
OR
A B C
0 1 [1, 2] [1, 2]
1 2 [3, 4] [3, 4, 5] # is this [(3, 3), (3, 4), (3, 5), (4, 3), (4, 4), (4, 5)]
这个函数
该函数基于一个参数优雅地处理zip或product,并假设根据最长的zip_longest对象的长度进行压缩
from itertools import zip_longest, product
def xplode(df, explode, zipped=True):
method = zip_longest if zipped else product
rest = {*df} - {*explode}
zipped = zip(zip(*map(df.get, rest)), zip(*map(df.get, explode)))
tups = [tup + exploded
for tup, pre in zipped
for exploded in method(*pre)]
return pd.DataFrame(tups, columns=[*rest, *explode])[[*df]]
压缩
xplode(df, ['B', 'C'])
A B C
0 1 1.0 1
1 1 2.0 2
2 2 3.0 3
3 2 4.0 4
4 2 NaN 5
产品
xplode(df, ['B', 'C'], zipped=False)
A B C
0 1 1 1
1 1 1 2
2 1 2 1
3 1 2 2
4 2 3 3
5 2 3 4
6 2 3 5
7 2 4 3
8 2 4 4
9 2 4 5
新设置
稍微改变一下这个例子
df = pd.DataFrame({
'A': [1, 2],
'B': [[1, 2], [3, 4]],
'C': 'C',
'D': [[1, 2], [3, 4, 5]],
'E': [('X', 'Y', 'Z'), ('W',)]
})
df
A B C D E
0 1 [1, 2] C [1, 2] (X, Y, Z)
1 2 [3, 4] C [3, 4, 5] (W,)
压缩
xplode(df, ['B', 'D', 'E'])
A B C D E
0 1 1.0 C 1.0 X
1 1 2.0 C 2.0 Y
2 1 NaN C NaN Z
3 2 3.0 C 3.0 W
4 2 4.0 C 4.0 None
5 2 NaN C 5.0 None
产品
xplode(df, ['B', 'D', 'E'], zipped=False)
A B C D E
0 1 1 C 1 X
1 1 1 C 1 Y
2 1 1 C 1 Z
3 1 1 C 2 X
4 1 1 C 2 Y
5 1 1 C 2 Z
6 1 2 C 1 X
7 1 2 C 1 Y
8 1 2 C 1 Z
9 1 2 C 2 X
10 1 2 C 2 Y
11 1 2 C 2 Z
12 2 3 C 3 W
13 2 3 C 4 W
14 2 3 C 5 W
15 2 4 C 3 W
16 2 4 C 4 W
17 2 4 C 5 W