熊猫根据组min max动态交叉连接



这是我的数据

data = [
{'shape': 'circle', 'height': 5},
{'shape': 'circle', 'height': 2},
{'shape': 'square', 'height': 6}
]

我想为每个形状填充范围内每个形状的缺失高度,在上面的例子中,
表示'circle'的范围是2-5,
表示'square'的范围是6,

结果将是:

data = [
{'shape': 'circle', 'height': 2},
{'shape': 'circle', 'height': 3},
{'shape': 'circle', 'height': 4},
{'shape': 'circle', 'height': 5},
{'shape': 'square', 'height': 6}
]

是否有一种方法可以使用pandas来实现这一点,使用交叉连接,而不用在数据帧上使用for循环??
这是我尝试的代码,它有一个问题-(见最后)

from itertools import product
df = pd.DataFrame(data)
# get missing values
min_height = df['height'].min()
max_height = df['height'].max()
all_heights = list(range(min_height, max_height + 1))
# create full values df
full_shape_list_df = pd.DataFrame(
list(product(list(df['shape'].unique()), all_heights)),
columns=['shape', 'height']
)
# merge with existing df
df = pd.merge(
df,
full_shape_list_df,
how='outer',
on=['shape', 'height']
).drop_duplicates().sort_values(['shape', 'height'])

这个解决方案的问题是所有的范围是2-6形状,结果是:

[{'shape': 'circle', 'height': 2},
{'shape': 'circle', 'height': 3},
{'shape': 'circle', 'height': 4},
{'shape': 'circle', 'height': 5},
{'shape': 'circle', 'height': 6},
{'shape': 'square', 'height': 2},
{'shape': 'square', 'height': 3},
{'shape': 'square', 'height': 4},
{'shape': 'square', 'height': 5},
{'shape': 'square', 'height': 6}]

也许我可以尝试按形状聚合-然后做交叉连接?

shape_height_min_max_df = df.groupby('shape').height.agg(['min', 'max'])
# now do here some cross join (avoid for loops) - how?

我们可以使用非常类似的方法,除了groupby aggregatelist的范围值从minmax的每组,然后DataFrame.explode返回到行:

df = df.groupby('shape', as_index=False)['height'].agg(
lambda x: np.arange(x.min(), x.max() + 1).tolist()
).explode('height', ignore_index=True)

df:

shape height
0  circle      2
1  circle      3
2  circle      4
3  circle      5
4  square      6

DataFrame和imports:

import numpy as np
import pandas as pd
df = pd.DataFrame({'shape': ['circle', 'circle', 'square'],
'height': [5, 2, 6]})

编辑包括width column:

我们也可以创建一个MultiIndex.from_frame,并使用它来reindex数据帧:

midx = pd.MultiIndex.from_frame(
df.groupby('shape', as_index=False)['height'].agg(
lambda x: np.arange(x.min(), x.max() + 1).tolist()
).explode('height', ignore_index=True)
)
df = df.set_index(['shape', 'height']).reindex(midx, fill_value=0).reset_index()
shape  height  width
0  circle       2      3
1  circle       3      0
2  circle       4      0
3  circle       5      4
4  square       6      2

DataFrame和imports:

import numpy as np
import pandas as pd
df = pd.DataFrame({'shape': ['circle', 'circle', 'square'],
'height': [5, 2, 6],
'width': [4, 3, 2]})

解释:

  1. minmax(+ 1,因为上界不包含)的值聚合到列表中:
df.groupby('shape', as_index=False)['height'].agg(
lambda x: np.arange(x.min(), x.max() + 1).tolist()
)
shape        height
0  circle  [2, 3, 4, 5]
1  square           [6]
  1. explode列表值:
df.groupby('shape', as_index=False)['height'].agg(
lambda x: np.arange(x.min(), x.max() + 1).tolist()
).explode('height', ignore_index=True)
shape height
0  circle      2
1  circle      3
2  circle      4
3  circle      5
4  square      6

您可以设置高度作为索引,并使用最小和最大之间的范围重新索引:

def reindex_fill(d):
return (d.set_index('height')
.reindex(range(d['height'].min(),
d['height'].max()+1)
)
.ffill()
.reset_index()
)

df.groupby('shape', as_index=False).apply(reindex_fill).droplevel(0)

输出:

height   shape
0       2  circle
1       3  circle
2       4  circle
3       5  circle
0       6  square

NB。这将移动height作为第一列,但如果出现

问题,则可以修复此问题。

最新更新