这是我的数据
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 aggregate
到list
的范围值从min
到max
的每组,然后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]})
解释:
- 将
min
到max
(+ 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]
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
作为第一列,但如果出现