以大约相等的计算成本将不等大小的 pandas/numpy 数组分箱
Posted
技术标签:
【中文标题】以大约相等的计算成本将不等大小的 pandas/numpy 数组分箱【英文标题】:Binning pandas/numpy array in unequal sizes with approx equal computational cost 【发布时间】:2021-07-13 10:29:47 【问题描述】:我遇到了一个问题,即必须跨多个内核处理数据。让 df 成为 Pandas DataFrameGroupBy (size()
) 对象。每个值代表每个 GroupBy 对核心的计算“成本”。如何将 df 划分为 大小不等 且计算成本相同 (近似)的 n 箱?
import pandas as pd
import numpy as np
size = 50
rng = np.random.default_rng(2021)
df = pd.DataFrame(
"one": np.linspace(0, 10, size, dtype=np.uint8),
"two": np.linspace(0, 5, size, dtype=np.uint8),
"data": rng.integers(0, 100, size)
)
groups = df.groupby(["one", "two"]).sum()
df
one two data
0 0 0 75
1 0 0 75
2 0 0 49
3 0 0 94
4 0 0 66
...
45 9 4 12
46 9 4 97
47 9 4 12
48 9 4 32
49 10 5 45
人们通常将数据集拆分为 n 个箱,例如下面的代码。但是,将数据集分成 n 等份是不可取的,因为核心接收非常不平衡的工作负载,例如205 与 788。
n = 4
bins = np.array_split(groups, n) # undesired
[b.sum() for b in bins] #undesired
[data 788
dtype: int64, data 558
dtype: int64, data 768
dtype: int64, data 205
dtype: int64]
理想的解决方案是将数据拆分为大小不等且总和值大致相等的 bin。 IE。 abs(743-548) = 195
之间的差异小于之前的方法abs(205-788) = 583
。差异应尽可能小。一个简单的列表示例,说明它应该如何实现:
# only an example to demonstrate desired functionality
example = [[[10, 5], 45], [[2, 1], 187], [[3, 1], 249], [[6, 3], 262]], [[[9, 4], 153], [[4, 2], 248], [[1, 0], 264]], [[[8, 4], 245], [[7, 3], 326]], [[[5, 2], 189], [[0, 0], 359]]
[sum([size for (group, size) in test]) for test in t] # [743, 665, 571, 548]
在 pandas 或 numpy 中是否有更有效的方法将数据集拆分为 bin?
拆分/装箱 GroupBy 对象很重要,以与np.array_split()
返回的类似方式访问数据。
【问题讨论】:
【参考方案1】:我认为已经找到了一个好的方法。向同事致谢。
这个想法是对组大小进行排序(按降序排列),并以“向后 S”模式将组放入箱中。让我用一个例子来说明。假设n = 3
(箱数)和以下数据:
groups
data
0 359
1 326
2 264
3 262
4 249
5 248
6 245
7 189
8 187
9 153
10 45
这个想法是把一组放在一个垃圾箱中,在垃圾箱之间以“向后 S”模式“从右到左”(反之亦然)。 bin 0 中的第一个元素,bin 1 中的第二个元素等。然后在到达最后一个 bin 时向后退:bin 2 中的第四个元素,bin 1 中的第五个元素等。请参阅下面如何将元素按组号放入 bin在括号中。这些值是组大小。
Bins: | 0 | 1 | 2 |
| 359 (0)| 326 (1)| 264 (2)|
| 248 (5)| 249 (4)| 262 (3)|
| 245 (6)| 189 (7)| 187 (8)|
| | 45(10)| 153 (9)|
这些 bin 将具有大致相同数量的值,因此,计算“成本”也大致相同。垃圾箱大小为:[852, 809, 866]
任何感兴趣的人。我已经尝试过真实世界的数据集,并且这些垃圾箱的大小相似。不能保证所有数据集的 bin 大小相似。
代码可以变得更高效,但这足以让想法变得清晰:
n = 3
size = 50
rng = np.random.default_rng(2021)
df = pd.DataFrame(
"one": np.linspace(0, 10, size, dtype=np.uint8),
"two": np.linspace(0, 5, size, dtype=np.uint8),
"data": rng.integers(0, 100, size)
)
groups = df.groupby(["one", "two"]).sum()
groups = groups.sort_values("data", ascending=False).reset_index(drop=True)
bins = [[] for i in range(n)]
backward = False
i = 0
for group in groups.iterrows():
bins[i].append(group)
i = i + 1 if not backward else i - 1
if i == n:
backward = True
i -= 1
if i == -1 and backward:
backward = False
i += 1
[sum([size[0] for (group, size) in bin]) for bin in bins]
【讨论】:
以上是关于以大约相等的计算成本将不等大小的 pandas/numpy 数组分箱的主要内容,如果未能解决你的问题,请参考以下文章