3

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

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

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

现在,为了比较,我想将每个列表重新缩放到相同数量的箱,让我们说 40 个箱。

事实上,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 for 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 风格、优雅且快速的解决方案。

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

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

谢谢。

4

3 回答 3

3

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

首先是测量列表...

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)

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

Python:将列表切成 n 个几乎等长的分区

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
于 2012-10-16T20:18:13.800 回答
3

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

观察 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 的最终列表。如果您需要更多帮助,请告诉我。

于 2012-10-16T21:23:51.147 回答
2

我对 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. 使用所需的新长度调用它,它将拉伸或压缩到新长度。我执行的加权平均值与您所做的略有不同……它可能等效也可能不等效,但我假设您可以根据需要修改数学部分。

于 2012-10-16T19:41:36.590 回答