基于行输入的Python条件求和

4
我将尝试在Python中进行条件求和乘积。简化的想法如下:
A = [1 1 2 3 3 3]
B = [0.50 0.25 0.99 0.80 0.70 0.20]

我希望您能提供输出的内容。

Total1 = 0.50*1 + 0.25*1
Total2 = 0.99*2
Total3 = 0.80*3 + 0.70*3 + 0.20*3

我想使用FOR ... IF ...结构,指定对于A中的一个给定值,应该对所有对应的B值进行求和。

实际上这是一个巨大的数据集,所以我将必须使脚本能够循环遍历所有类别?

目前我正在努力将这个想法翻译成适当的Python脚本。 有人可以指导我正确的方向吗?


你能澄清一下A和B之间的关系吗? - jeremye
当然,这里有一些背景信息:我有一个公司名称的Excel文件(行),其中包含以下: 1° 'type' = A 2° 要应用的百分比 = B 我需要获取每种类型公司的百分比总和。希望这可以帮到你!如果还有问题,请随时提出! - Sibren De Preter
1
在这种情况下,产品是什么?看起来你只想要一个总和。 - jeremye
4个回答

3

假设 A 中的值已经排序,那么似乎 itertools.groupby 是一个很好的选择(如果 A=[1,1,2,2,1] ,则可能不会正常工作):

from itertools import groupby
A = [1, 1, 2, 3, 3, 3]
B = [0.50, 0.25, 0.99, 0.80, 0.70, 0.20]

for key, grp in groupby(zip(A, B), key=lambda x: x[0]):
    grp = [i[1] for i in grp]
    print(key, key * sum(grp))

它会打印:

1 0.75
2 1.98
3 5.1

您可以将它存储在列表中,而不是打印值:
res = []
for key, grp in groupby(zip(A, B), key=lambda x: x[0]):
    grp = [i[1] for i in grp]
    res.append(key*sum(grp))
print(res)
# [0.75, 1.98, 5.1]

如果第三方包对你来说是一个选项,你也可以使用iteration_utilities.groupedby

>>> from iteration_utilities import groupedby
>>> from operator import itemgetter, add

>>> {key: key*sum(value) for key, value in groupedby(zip(A, B), key=itemgetter(0), keep=itemgetter(1)).items()}
{1: 0.75, 2: 1.98, 3: 5.1}

或者直接使用groupedbyreduce参数:
>>> groupedby(zip(A, B), key=itemgetter(0), keep=lambda x: x[0]*x[1], reduce=add)
{1: 0.75, 2: 1.98, 3: 5.1}

免责声明:我是iteration_utilities包的作者。


非常感谢您的回复!! :) 还有一个问题,A 中的值没有排序。例如可能是: A = [1 1 1 2 3 2 1 4 2 1] B 中的值与 A 中的值按照这个顺序匹配。因此,如果我对 A 进行排序,那么 B 就不再匹配了吗? - Sibren De Preter
在这种情况下,您可以对zip进行排序。不仅使用zip(A,B),而是使用sorted(zip(A,B),key = lambda x:x [0])(基于A进行排序,而不会丢失来自B的相应值)。但这仅适用于itertools.groupby - 对于iteration_utilities.groupedby,它不需要排序。 - MSeifert
谢谢,完美运作!有没有选项可以针对10个不同的B列进行操作?还是最好运行10次程序呢? - Sibren De Preter
这取决于不同的 B 列是如何存储的。通常你可以在一个循环中处理所有不同的 B 列,例如 for B in all_different_B_columns:。然而,你也可以就此提出另一个问题,并包括 B 是如何存储的(在哪个容器中)。 :) 在评论中回答这个问题有点困难。 - MSeifert
好的,谢谢!https://stackoverflow.com/questions/45635983/itertools-groupby-looping-over-different-columns - Sibren De Preter

2
我想到了以下这个方案。有一个边界情况我不知道该怎么处理,希望能够消除它:
In [1]: sums = {}
In [2]: A = [1, 1, 2, 3, 3, 3]
   ...: B = [0.50, 0.25, 0.99, 0.80, 0.70, 0.20]
In [3]: for count, item in zip(A, B):
    ...:     try:
    ...:         sums[count] += item * count
    ...:     except KeyError:
    ...:         sums[count] = item * count
    ...:         

In [4]: sums
Out[5]: {1: 0.75, 2: 1.98, 3: 5.1}

编辑:

如评论中建议,可以使用 defaultdict 来摆脱这个丑陋的 try-except 块:

In [2]: from collections import defaultdict

In [3]: sum = defaultdict(lambda: 0)

In [4]: sum[1]
Out[4]: 0

In [5]: sum
Out[5]: defaultdict(<function __main__.<lambda>>, {1: 0})

编辑2:

今天我学到了一些东西。在更多的评论之后:

In [6]: sums = defaultdict(int)

In [7]: A = [1, 1, 2, 3, 3, 3]
   ...: B = [0.50, 0.25, 0.99, 0.80, 0.70, 0.20]

In [8]: for count, item in zip(A, B):
   ...:     sums[count] += count * item
   ...:     

In [9]: sums
Out[9]: defaultdict(int, {1: 0.75, 2: 1.98, 3: 5.1})

2
你可以使用初始化为0的defaultdict来避免try-except块。 - KGS
不要使用 lambda:0,直接使用 int。这是因为 int()(无参数)返回 0 :) - MSeifert
谢谢,现在看起来很舒适。 - gonczor

0

我认为你可以使用 itertools.groupby 来解决这个问题:

import itertools
from operator import itemgetter

results = [group * sum(v[1] for v in values)
           for group, values in itertools.groupby(zip(A, B), itemgetter(0))]

这假设所有相等的数字在A中都是相邻的。如果它们可能不是相邻的,您需要对它们进行排序或使用不同的算法。


0

如果您不介意使用numpy,并且假设组已经排序,您可以通过以下方式完成:

A = [1, 1, 2, 3, 3, 3]
B = [0.50, 0.25, 0.99, 0.80, 0.70, 0.20]
A = np.asarray([1, 1, 2, 3, 3, 3])
B = np.asarray([0.50, 0.25, 0.99, 0.80, 0.70, 0.20])
index = np.full(len(A),True)
index[:-1] = A[1:] != A[:-1]
prods = A*B

#result
res = np.add.reduceat(prods, np.append([0], (np.where(index)[0]+1)[:-1]))

此外,如果您拥有大型列表,这将极大地加快操作速度。

仅为完整起见:本答案提到的方法部分基于此答案 - MSeifert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接