【问题标题】:Sum value of a column in a pandas dataframe sorted by another column upto a value N熊猫数据框中一列的总和值按另一列排序,最高值为 N
【发布时间】:2022-01-25 22:29:10
【问题描述】:

假设我有一个这样的列(基于对 B 列应用的某种排序):

   A   B
0  2   5   
1  5   4
2  9   3
3  4   3
4  3   1

现在我必须找出不超过 15 的总和,但数据框中行的顺序不能改变(它需要按 B 列值排序)(虽然在计算总和时可以省略一行),所以在这种情况下row sum(0, 1 , 3 , 4)over column A = 14. 基本上是对一列的条件求和。

预期输出:df,其列 A 添加行

注意:

我只能跳过值 A > total_left 的那些行,必须始终仅按该顺序考虑这些行(由 B 优先),例如,数据帧中可能有数百万行,我必须绝对考虑值为

   A  B
0  5  5
1  5  4
3  5  3
4  1  1

我不能先取第 4 行,因为它的价值较小,我需要取 row(0)、row(1)、row(2),因此无法对 column(A) 进行排序。

【问题讨论】:

  • 你的预期输出是什么?
  • @CodeDifferent,A列行(0, 1, 2, 3, 4)之和加起来是23(2+5+9+4+3),没明白吗?
  • @MuhammadHassan,添加了预期的输出

标签: python pandas dataframe


【解决方案1】:

IIUC:

df = df[df['A'].sort_values().cumsum() < 15]

OUTPUT

   A  B
0  2  5
1  5  4
3  4  3
4  3  1

【讨论】:

  • 您已经理解它部分正确,但问题是无法根据 A 列排序,因为 B 列的优先级(排序)需要存在(先决条件),如果我将根据排序在 A 列上,B 列的优先级(排序)受到干扰。
  • 这比我的方法更好:更简洁。但是,您需要使用df.loc[...] 以避免UserWarning。此外,“最多”的意思是“包括”。
  • 感谢您的反馈。
【解决方案2】:

新答案

OP 遗漏了一个关键细节:如果它们对累积总和的贡献使其不超过最大值,则不能跳过按顺序排列的行。这大大改变了问题,无法再使用原始答案。

事实上,在当前的pandasnumpy没有矢量化操作可以在预先未知的位置处实现这样的累积和。

in this SO answer 所示,留给大型数组的最佳方法是使用numba

这里是如何做到这一点,适应这个问题:

from numba import njit
from numba.types import bool_

@njit
def conditional_cumsum(x, skip_if_higher_than):
    total = 0
    result = np.zeros_like(x, dtype=bool_)
    for i, y in enumerate(x):
        if total + y <= skip_if_higher_than:
            total += y
            result[i] = True
    return result

示例

df = pd.DataFrame({'A': [2, 5, 9, 4, 3], 'B': [5, 4, 3, 3, 1]})
mask = conditional_cumsum(df['A'].values, skip_if_higher_than=15)

>>> df.loc[mask]
   A  B
0  2  5
1  5  4
3  4  3
4  3  1


df = pd.DataFrame({'A': [5, 5, 5, 1], 'B': [5, 4, 3, 1]})

>>> conditional_cumsum(df['A'].values, skip_if_higher_than=15)
array([ True,  True,  True, False])

速度

n = 1_000_000
np.random.seed(0)
df = pd.DataFrame({
    'A': np.random.uniform(size=n),
    'B': np.arange(n)
})

%timeit conditional_cumsum(df['A'].values, skip_if_higher_than=1)
460 µs ± 751 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit conditional_cumsum(df['A'].values, skip_if_higher_than=100)
460 µs ± 939 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)

>>> df.loc[conditional_cumsum(df['A'].values, skip_if_higher_than=1)]
                   A       B
0       5.488135e-01       0
4       4.236548e-01       4
16      2.021840e-02      16
99      4.695476e-03      99
757     1.383350e-03     757
821     5.459649e-04     821
1070    6.642186e-04    1070
84341   3.310554e-06   84341
131245  1.989694e-05  131245
661553  7.071203e-07  661553

请注意:任何njit 函数都有一个初始“编译时间”。首先在一个小数组上运行它以让它发生,然后在大数组上运行。

原答案

由于您可以跳过行,因此选择列的顺序并不重要(我们可以在之后恢复它)。正如@MuhammadHassan 所说,最大子集将是A 的部分,已排序,总和为15:

s = df['A'].sort_values().cumsum() <= 15
idx = df.index.intersection(s[s].index)

>>> idx.tolist()
[0, 1, 3, 4]

# and
>>> df.loc[idx]
   A  B
0  2  5
1  5  4
3  4  3
4  3  1

原始答案注释

出于教学目的,我将保留此原始答案,但@MuhammadHassan 的答案是正确且更简洁的。为了防止UserWarning: Boolean Series key will be reindexed to match DataFrame index(并选择最多 15,这意味着最多并包括15):

>>> df.loc[df['A'].sort_values().cumsum() <= 15]
   A  B
0  2  5
1  5  4
3  4  3
4  3  1

【讨论】:

  • 再次编辑了问题,可以跳过行,但我需要检查它是否在每一行都达到所需的总和,如果该行中 A 的值小于 total_sum_left,则始终考虑它们。跨度>
  • 请查看新答案。
猜你喜欢
  • 1970-01-01
  • 2016-03-24
  • 1970-01-01
  • 2022-01-23
  • 2017-11-27
  • 2023-04-04
  • 2016-08-09
  • 1970-01-01
  • 2017-04-14
相关资源
最近更新 更多