【问题标题】:python list average over bin sizespython列表平均超过bin大小
【发布时间】:2012-10-16 17:40:26
【问题描述】:

我有 N 个不同长度的大型列表,其中列表中的每个值代表长度为 25 的固定窗口上的信号。即,我每 25 秒/基数/等取信号的平均值,然后存储列表中的那个值。

我为运行不同时间长度(所有 25 btw 的倍数)的不同实验/设备执行此操作。

即,列表 1 是 1000 次运行,列表 1 中有 1000/25=40 个值,列表 2 是 1025 分钟运行,1025/25 = 列表 2 中的 41 个值,列表 3 是 2525 次运行,2525/ list3 中的 25 = 101 个值等...

现在,为了比较,我想将每个列表重新缩放到相同数量的 bin,假设为 40 个 bin。

事实上,list1resized 的长度将是 40,并且它的值不会改变,因为 1000/40 = 25 正好。 list2resized 将从 41 个值的长度变为 40 个值的长度,而 list3 将从 101 个值的长度变为 40 个值的长度(也就是所有列表现在都具有相同的大小)。

问题来了。如何通过对适当的 bin 进行加权平均来将每个列表的大小调整为 40 的固定长度?

举个例子来说明问题。

list1 = [4.8, 6.9, ...]  #40 values for the 1000 run
list2 = [5.6, 7.8, 8.9, 13.4, ...] #41 values for the 1025 run
list3 = [4.1, 5.6, 10.3, 9.8, 40, 30, 21.4, 3, 2,...] #101 values for the 2525 run

现在,调整大小的列表应如下所示:

list1resized = [4.8*25/25, 6.9*25/25,...] #40 values for the 1000 run
list2resized = [(5.6*25+7.8*0.625)/25.625, (7.8*24.375+8.9*1.275)/25.625, (23.725*8.9+1.9*13.4)/25.625,...] # 40 values, averaged accordingly, for the 1025 run
list3resized = [(4.1*25+5.6*25+10.3*13.125)/(63.125), (10.3*11.875+9.8*25+40*25+30*1.25)/(63.125),...] # 40 values, averaged accordingly, for the 2525 run

为了获得调整后列表中每个元素的平均值,我们对新调整大小的 bin 进行加权平均(即,对于 list1,平均超过 1000/40=25,对于 list2,平均超过 1025/40=25.625 ,平均超过 2525/40=63.125 对于 list3 等)。即,相同但使用我用于加权平均值的公式:

list1resized = [4.8*25/25, 6.9*25/25,...] #40 values for the 1000 run
list2resized = [(5.6*25+7.8*0.625)/25.625, (7.8*24.375+8.9*(25.65-24.375))/(25.625), (23.725*8.9+(25.625-23.725)*13.4)/(25.625),...] # 40 values, averaged accordingly, for the 1025 run
list3resized = [(4.1*25+5.6*25+10.3*13.125)/(63.125), (10.3*(25-13.125)+9.8*25+40*25+30*(63.125-25*3+13.125)))/(63.125),...] # 40 values, averaged accordingly, for the 2525 run

如您所见,它可能会变得混乱且难以处理,但我正在寻找一种 Python 风格、优雅且快速的解决方案。

我必须多次对许多列表执行此操作,因此考虑运行时间会很好。

不确定您是否有任何想法,但我们将不胜感激。

谢谢。

【问题讨论】:

  • 我实际上对如何在 python 中实际编写这样的框架一无所知。我一直在苦苦挣扎,因为我每次都必须采用不同的子列表,而且它们的长度会改变……不知道实际上该怎么做。
  • 如果您希望您的问题集中在执行此操作的最佳数学算法上,Computational Science 实际上可能是提问的地方。然后你可以在这里询问如何在 Python 中实现该算法。
  • 我很高兴其他人似乎理解了你,并且你显然得到了你需要的帮助。对我来说,这就像泥泞一样清晰。我可以看到您是如何到达 25、25.625 和 63.125 的;但除此之外,我完全迷失了。

标签: python list math average


【解决方案1】:

这个时髦的 [也许] 解决方案怎么样?

首先是测量列表...

l = [5.6, 7.8, 8.9, 13.4]

将每个测量值复制 25 次(每秒一次...)

l1 = [item for sublist in [list(itertools.repeat(k,25)) for k in l] for item in sublist]

每秒标准化:

l2 = map(lambda x: x / 25., l1)

有关将列表分成 n 个几乎相等的分区的函数(复制如下),请参阅此 SO 帖子:

Python: Slicing a list into n nearly-equal-length partitions

def partition(lst, n):
    division = len(lst) / float(n)
    return [ lst[int(round(division * i)): int(round(division * (i + 1)))] for i in xrange(n) ]

定义列表的新长度

new_len = 2

将你的每秒列表分成你想要的部分数:

l3 = partition(l2, new_len)

对每个分区中每一秒的值求和

l4 = map(sum, l3)

标准化列表大小的差异

l5 = map(lambda x: x * new_len / float(len(l)), l4)

欣赏结果:

print l5

【讨论】:

  • 或者简而言之,[sum(t) * new_len / float(len(l)) for t in partition(itertools.chain(itertools.repeat(k/25., 25) for k in l), new_len)](嗯,类似的东西)
  • 我曾想过将事情分成几秒,但坦率地说,使用秒作为时间单位在 imo 中是完全没有必要的。完全忽略“秒”参数的基于比率的解决方案(因为秒首先是列表长度的函数)更具有数学意义。
  • @kreativitea:你可能是对的,但像 David Zaslavsky 提议的那样,塞进一个单线可能会更难。
  • @juniper 我认为找到一个好的解决方案的关键是找到一种方法来生成子列表的系数。每个子列表的系数之和等于加权平均比率--[(4.1*25+5.6*25+10.3*13.125)/(63.125),:在此,25+25+13.125 = 63.125。我不太擅长使用模算术即时生成这类数字,但我确信有一种很好的方法可以从这些数字中推导出系列 1,1,2.525
  • @DavidZaslavsky 谢谢。我尝试了您的解决方案,但给了我一个错误:TypeError:'itertools.chain' 类型的对象没有 len()。不知道这意味着什么。瞻博网络,谢谢,很棒的解决方案,对于很长的数据集来说有点慢,但它可以工作。
【解决方案2】:

这是一个相当困难的问题,但我认为你让它变得比实际更复杂。我将从一些观察开始。

观察 1. 您可以将很多事情考虑在内,以减少涉及的编码。与其除以 25(这会很快变得非常复杂),不如将该操作保存到最后。

list2resized = [i/25.625 for i in [(5.6*25+7.8*0.625), 
                                   (7.8*24.375+8.9*(25.65-24.375)), 
                                   (23.725*8.9+(25.625-23.725)*13.4),...]]

# consider using ratios, rather than division
list2resized = [i * 1.025 for i in [(5.6 * 1 + 7.8 * .025), 
                                    (7.8 * .975 + 8.9 * .050), 
                                    (8.9 * .95 + 13.4 * .075),...]]

观察 2。因此,每个进行项的系数是 25 的递增步长。将除法除以 1000 直到后记——如果您选择将整个方程乘以 1000 并使用模运算符...

 list2resized = [i * 1025/1000 for i in [(5.6 * 1000 + 7.8 * 25), # 1025 steps in
                                          (7.8 * 975 + 8.9 * 50), # 2050 steps in
                                          (8.9 * 950 + 13.4 * 75) # 3075 steps in

观察3。

最终调整大小中的每个“bin”需要为 1.025 长(给定 41 个起始 bin,但最终取决于要调整的列表的长度)。 1.0 * list[0] + .025 * list[1]考虑观察2,可以把整个方程改写成一个级数--

# the sum of the coefficients is always equal to the resize ratio
(1 * n1) + (.025 * n2)
(.975 * n2) + (.050 * n3) 
(.950 * n3) + (.075 * n4)
...

等等

现在,你可以生成这些系数了——

a = [i/40.0 for i in range(0, 40)][1:]
b = [1 - i/40.0 for i in range(0, 40)]

但是这些都很容易,因为“旋转”永远不会赶上它自己。您所要做的就是为方程的每个相应部分迭代每个 bin 中的系数,然后将它们压缩在一起,并对它们求和。这只会将列表最大压缩到其原始大小的一半。在这种情况下,您应该使用上述算法,它会比您可以使用的任何其他算法快得多,因为它只是创建一个数字列表,然后在列表推导上进行乘法运算。

但是,复杂的情况是您有 101 个数字的示例,其中出现了多个术语(有时是第四个!)...

101/40.0 = 2.525 
# your bins need to be 2.525 units long.  

data = [4.1, 5.6, 10.3, 9.8, 40, 30, 21.4, 3, 2,...]

# calculated by hand
(1 * n1) + (1 * n2) + (.525 * n3) 
(.475 * n3) + (1 * n4) + (1 * n5) + (.05 * n6)
(.95 * n6) + (1 * n7) + (.575 * n8)
(.425 * n8) + (1 * n9) + (1 * n10) + (.100 * n11)

因此,我们需要一种更好的方法来生成系数。如前所述(3),最后一项中的系数之和是旧项目与新项目的比率。

101:40 = 2.525:1
41:40 = 1.025:1

接下来是生成系数。我们将使用 list-in-list 数据结构,它遍历子列表直到什么都没有。

[(1, 1, .525), (.475, 1, 1, .05) ...]

第一个子列表映射到新列表中的第 1 项。第二个子列表,到第 2 项,依此类推,一直到最后。所有子列表中所有项目的总和应等于原始列表中的项目 n(在本例中为 101)。

我现在要继续发布这个,因为我必须实际工作。我会尝试稍后再回来处理。

/编辑

这是一个生成系数的函数。

n = 1000
d = 2525
items = 101
def coefficients(n, d, items):
    start = [n for i in xrange(items)]
    result = []
    p = []
    while True:
        while sum(p) < d:
            try:
                p.append(start.pop())
            except IndexError:
                return result
        extra = sum(p) % d
        p[-1] = n - extra
        result.append(p)
        p = [extra]

遍历系数以返回 40 个最终列表。如果您需要更多帮助,请告诉我。

【讨论】:

  • 非常感谢,这里有很棒的想法。如果您有时间,我仍然希望收到您的更多信息,但到目前为止,这是一个非常好的想法。这绝对是最快的实现,因为我的列表很长,所以有一个快速的选项会很好。
  • 我自己也在考虑这些思路,但我没有时间去研究细节。不错:-)
  • @Dnaiel 我添加了一个函数来生成系数,这应该是一个完整的答案。
【解决方案3】:

我对 Python 还是很陌生,所以你需要其他人来评价它的 Python 性、优雅性和速度。

class StretchableList(list):
    def stretch(self, newlen):
        old = [ (i * (newlen-1), self[i]) for i in range(len(self)) ]
        new = [ i * (len(self)-1) for i in range(newlen) ]
        self[:] = []
        for n in new:
            while len(old) > 1 and n >= old[1][0]:
                old.pop(0)
            if old[0][0] == n:
                self.append(old[0][1])
            else:
                self.append( old[0][1] + \
                             float((n-old[0][0]))/(old[1][0]-old[0][0]) * \
                             (old[1][1]-old[0][1]) )
        return self

基本上,这定义了list 的子类,它只是添加了一个名为stretch 的方法。用所需的新长度调用它,它将拉伸或压缩到新长度。我执行的加权平均值与您执行的略有不同……它可能相等也可能不相等,但我假设您可以根据需要修改数学部分。

【讨论】:

  • @glidbud。感谢您的回答,尚未测试,但似乎是一个很好的。出于速度目的,我会将它与其他的进行比较。
猜你喜欢
  • 1970-01-01
  • 2017-12-15
  • 1970-01-01
  • 2017-08-13
  • 2023-04-07
  • 2019-02-03
  • 2016-06-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多