使用 Pandas 根据可能部分重叠的范围组合两个数据帧并跟踪多个值
Combine two dataframes based on ranges which may partially overlap using Pandas and track multiple values
我有两个大数据帧(100K 行),一个有 'values',一个有 'types'。我想根据深度将 df2 中的 'type' 分配给 df1 中的列。深度以深度 'From' 和深度 'To' 列给出。 'types' 也由深度 'From' 和 'To' 定义。但它们不是相同的间隔。 df1 深度可能跨越多个 df2 类型。
我想将 df2 'types' 分配给 df1,如果有多种类型,也尝试捕获该信息。示例如下。
import pandas as pd
import numpy as np
df1=pd.DataFrame(np.array([[1,3,0.001],[3,5,0.005],[5,7,0.002],[7,10,0.001]]),columns=['From', 'To', 'val'])
df2=pd.DataFrame(np.array([[0.0,4,'A'],[4,5,'B'],[5,6,'C'],[6,8,'D'],[8,10,'E']]),columns=['From', 'To', 'Type'])
df1
Out[1]:
From To val
0 1.0 3.0 0.001
1 3.0 5.0 0.005
2 5.0 7.0 0.002
3 7.0 10.0 0.001
df2
Out[2]:
From To Type
0 0 4 A
1 4 5 B
2 5 6 C
3 6 8 D
4 8 10 E
可能的可接受输出:
Out[4]:
From To val Type
0 1 3 0.001 A
1 3 5 0.005 1 unit A,2 units B
2 5 7 0.002 1 unit C,1 unit D
3 7 10 0.001 1 unit D, 3 units E
类型的百分比在类型中也是一个很好的输出。
一个解决方案可能是创建一个新的高分辨率数据框 'depths' 并向前填充类型,并对“收件人”和“发件人”进行某种 VLOOKUP。
我还考虑过在每个 df 中创建一个基于来回列的 'set' 列的可能性。
可能加入或合并,但需要先让数据兼容。
不知道从哪里开始。希望有解决这个问题的巧妙方法,我的情况基本上与 this guy 完全相同,但我不会说 'R' 并且可能会报告多种类型的信息。
从df2创建一个辅助系列,标记每个"starting point"
单位(长度范围1):
units = df2.set_index('Type').apply(lambda row: pd.Series(
range(row.From, row.To)), axis=1).stack()\
.reset_index(level=1, drop=True)
结果是:
Type
A 0.0
A 1.0
A 2.0
A 3.0
B 4.0
C 5.0
D 6.0
D 7.0
E 8.0
E 9.0
dtype: float64
然后定义一个为当前行生成类型的函数:
def getType(row):
gr = units[units.ge(row.From) & units.lt(row.To)].groupby(level=0)
if gr.ngroups == 1:
return gr.ngroup().index[0]
txt = []
for key, grp in gr:
siz = grp.size
un = 'unit' if siz == 1 else 'units'
txt.append(f'{siz} {un} {key}')
return ','.join(txt)
并生成 Type 列,将其应用于每一行:
df1['Type'] = df1.apply(getType, axis=1)
结果是:
From To val Type
0 1.0 3.0 0.001 A
1 3.0 5.0 0.005 1 unit A,1 unit B
2 5.0 7.0 0.002 1 unit C,1 unit D
3 7.0 10.0 0.001 1 unit D,2 units E
这个结果和你预期的有点不一样,不过我觉得
你创建它的方式有点不合逻辑。
我认为我的解决方案是正确的(至少更重要),因为:
- 行1.0 - 3.0完全在0 4 A的范围内,所以
结果只是 A(就像你的 post)。
- 行3.0 - 5.0可以"divided"变成:
- 3.0 - 4.0 在 0 4 A(1 个单位)、
的限制范围内
- 4.0 - 5.0 在 4 5 B 的范围内(也 1 个单位,
但你想要 2 个单位)。
- 行5.0 - 7.0可以再"divided"改成:
- 5.0 - 6.0在5 6 C范围内(1单位) ,
- 6.0 - 7.0在6 8 D范围内(1单位,就像你一样)。
- 行7.0 - 10.0可以"divided"变成:
- 7.0 - 8.0在6 8 D的范围内(1单位,就像你一样),
- 8.0 - 10.0 在 8 10 E (2 单位的范围内,不是你写的3。
我有两个大数据帧(100K 行),一个有 'values',一个有 'types'。我想根据深度将 df2 中的 'type' 分配给 df1 中的列。深度以深度 'From' 和深度 'To' 列给出。 'types' 也由深度 'From' 和 'To' 定义。但它们不是相同的间隔。 df1 深度可能跨越多个 df2 类型。
我想将 df2 'types' 分配给 df1,如果有多种类型,也尝试捕获该信息。示例如下。
import pandas as pd
import numpy as np
df1=pd.DataFrame(np.array([[1,3,0.001],[3,5,0.005],[5,7,0.002],[7,10,0.001]]),columns=['From', 'To', 'val'])
df2=pd.DataFrame(np.array([[0.0,4,'A'],[4,5,'B'],[5,6,'C'],[6,8,'D'],[8,10,'E']]),columns=['From', 'To', 'Type'])
df1
Out[1]:
From To val
0 1.0 3.0 0.001
1 3.0 5.0 0.005
2 5.0 7.0 0.002
3 7.0 10.0 0.001
df2
Out[2]:
From To Type
0 0 4 A
1 4 5 B
2 5 6 C
3 6 8 D
4 8 10 E
可能的可接受输出:
Out[4]:
From To val Type
0 1 3 0.001 A
1 3 5 0.005 1 unit A,2 units B
2 5 7 0.002 1 unit C,1 unit D
3 7 10 0.001 1 unit D, 3 units E
类型的百分比在类型中也是一个很好的输出。
一个解决方案可能是创建一个新的高分辨率数据框 'depths' 并向前填充类型,并对“收件人”和“发件人”进行某种 VLOOKUP。
我还考虑过在每个 df 中创建一个基于来回列的 'set' 列的可能性。
可能加入或合并,但需要先让数据兼容。
不知道从哪里开始。希望有解决这个问题的巧妙方法,我的情况基本上与 this guy 完全相同,但我不会说 'R' 并且可能会报告多种类型的信息。
从df2创建一个辅助系列,标记每个"starting point" 单位(长度范围1):
units = df2.set_index('Type').apply(lambda row: pd.Series(
range(row.From, row.To)), axis=1).stack()\
.reset_index(level=1, drop=True)
结果是:
Type
A 0.0
A 1.0
A 2.0
A 3.0
B 4.0
C 5.0
D 6.0
D 7.0
E 8.0
E 9.0
dtype: float64
然后定义一个为当前行生成类型的函数:
def getType(row):
gr = units[units.ge(row.From) & units.lt(row.To)].groupby(level=0)
if gr.ngroups == 1:
return gr.ngroup().index[0]
txt = []
for key, grp in gr:
siz = grp.size
un = 'unit' if siz == 1 else 'units'
txt.append(f'{siz} {un} {key}')
return ','.join(txt)
并生成 Type 列,将其应用于每一行:
df1['Type'] = df1.apply(getType, axis=1)
结果是:
From To val Type
0 1.0 3.0 0.001 A
1 3.0 5.0 0.005 1 unit A,1 unit B
2 5.0 7.0 0.002 1 unit C,1 unit D
3 7.0 10.0 0.001 1 unit D,2 units E
这个结果和你预期的有点不一样,不过我觉得 你创建它的方式有点不合逻辑。
我认为我的解决方案是正确的(至少更重要),因为:
- 行1.0 - 3.0完全在0 4 A的范围内,所以 结果只是 A(就像你的 post)。
- 行3.0 - 5.0可以"divided"变成:
- 3.0 - 4.0 在 0 4 A(1 个单位)、 的限制范围内
- 4.0 - 5.0 在 4 5 B 的范围内(也 1 个单位, 但你想要 2 个单位)。
- 行5.0 - 7.0可以再"divided"改成:
- 5.0 - 6.0在5 6 C范围内(1单位) ,
- 6.0 - 7.0在6 8 D范围内(1单位,就像你一样)。
- 行7.0 - 10.0可以"divided"变成:
- 7.0 - 8.0在6 8 D的范围内(1单位,就像你一样),
- 8.0 - 10.0 在 8 10 E (2 单位的范围内,不是你写的3。