基于区间的Pandas Dataframe 加权均值计算

ekqde3dh  于 2023-01-15  发布在  其他
关注(0)|答案(1)|浏览(115)

我有2个 Dataframe df1, df2
df1由3列start, end, id组成。df2由4列start, end, id, quantity组成。请注意,start < end始终用于两个 Dataframe 。
对于df1,每行的end - start总是15,并且每行的[start, end]对对于每个id是不重叠的和连续的,例如,

df1:

id   start   end
1    0       15
1    15      30
1    30      45
2    0       15
2    15      30
2    30      45

我需要在df1中创建第4列quantity_average,其中每行的quantity_average是所有df2.quantity的 * 加权 * 平均值,以便两个 Dataframe 中对应的id相同,并且两个 Dataframe 中的开始、结束对之间存在完全/部分重叠。
权重被定义为(min(df2.end, df1.end) - max(df2.start, df1.start)) / 15,即,与重叠量成比例。
我将提供一个完整的示例。我们将使用上面的df1,并使用

df2 = 
id   start   end    quantity
1    0       1.1    3.5
1    1.1     11.4   5.5
1    11.4    34     2.5
1    34      46     3
2    0       1.5    2.2
2    1.5     20     1.0
2    20      30     4.5

因此,quantity_average的结果为:

1.1 / 15 * 3.5 + (11.4 - 1.1)/15 * 5.5 + (15 - 11.4) / 15 * 2.5 = 4.63333

(30 - 15) / 15 * 2.5 = 2.5

(34 - 30) / 15 * 2.5 = 0.66666

1.5 / 15 * 2.2 + (15 - 1.5) / 15 * 1.0 = 1.12

(20 - 15) / 15 * 1.0 + (30 - 20) / 15 * 4.5 = 3.33333333333

0

我想知道是否有一种快速的方法可以在Pandas身上做到这一点?

toe95027

toe950271#

这里有一个(不那么简单)的方法。它的速度很快,因为它使用矢量化函数,但它的时间和内存复杂度都是O(len(df1) * len(df2))。根据数据集的规模,内存需求可能会超过计算机硬件。
其思想是使用numpy广播将df1中的每一行与df2中的每一行进行比较,搜索满足以下条件的对:

  • 具有相同的id
  • 具有重叠持续时间(start - end)。

...然后对它们执行计算:

# Extract the columns to numpy array
# For the columns of df1, raise each by one dimension to prepare
# for numpy broadcasting
id1, start1, end1 = [col[:, None] for col in df1.to_numpy().T]
id2, start2, end2, quantity2 = df2.to_numpy().T

# Match each row in df1 to each row in df2
# `is_match` is a matrix where if cell (i, j) is True, row i of
# df1 matches row j of df2
is_match = (id1 == id2) & (start1 <= end2) & (start2 <= end1)

# `start` is a matrix where cell (i, j) is the maximum start time
# between row i of df1 and row j of df2
start = np.maximum(
    np.tile(start1, len(df2)),
    np.tile(start2, (len(df1), 1))
)

# Likewise, `end` is a matrix where cell (i, j) is the minium end
# time between row i of df1 and row j of df2
end = np.minimum(
    np.tile(end1, len(df2)),
    np.tile(end2, (len(df1), 1))
)

# This assumes that every row in df1 has a duration of 15
df1["quantity"] = (is_match * (end - start) * quantity2).sum(axis=1) / 15

# This allow each row in df1 to have a different duration
df1["quantity"] = (is_match * (end - start) * quantity2).sum(axis=1) / (end1 - start1)[:, 0]

相关问题