在Python中计算列表中相同条目的长度

3

我有一个时间序列,代表了对系统功能的常规查询,其中 1 = 正常工作0 = 不正常工作。例如,将时间序列表示为列表:

U = [0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,0,0,1,1,0]

我对计算类似于故障平均时间(系统运行时间)和修复平均时间(系统停机时间)以及其他类似统计数据感兴趣,所以我想做的是计算连续的10条目。我想剪去开头和结尾的组合,因为对于以上示例,我不知道系统最初何时停机,也不知道未来它何时会重新启动。因此,在这种情况下,我要生成的输出将是:

uptime = [6, 4, 9, 2] # 6 ones followed by zeros, then 4 ones followed by zeros, etc.
downtime = [3, 3, 2] # like uptime but ignoring zeros at indices [0,1] and [-1] 

我写了一个脚本来做这个,但它似乎有点笨拙,我想知道是否有更好的、更符合 Python 风格的方法来完成。以下是我的代码。
def count_times(U, down=False):
    if down:
        U = [1 - u for u in U]
    T = [] 
    # Skip the first entry as you don't know when it started
    m = U.index(0)
    m += U[m:].index(1)
    while m < len(U):
        try:
            T.append(U[m:].index(0))
            m += U[m:].index(0)
            m += U[m:].index(1)
        except ValueError:
            # skip the last entry as you don't know when it will end
            return T

yielding:

print count_times(U)
# [6, 4, 9, 2]
print count_times(U, down = True)
# [3, 3, 2]

这个方法可以运行,但我不禁想知道是否有更简洁的方式?
4个回答

2

我的方法与Ruben的类似,但在应用groupby后,它首先将上升时间和下降时间保存在同一个列表中,因此更容易修剪开始和结束集合。

import itertools
U = [0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,0,0,1,1,0]
run_lengths = [(value, len(list(group))) for value, group in itertools.groupby(U)]

#discard first and last runs
run_lengths = run_lengths[1:-1]

#split runs into separate up and down time lists
uptime = [length for value, length in run_lengths if value == 1]
downtime = [length for value, length in run_lengths if value == 0]

print uptime
print downtime

结果:

[6, 4, 9, 2]
[3, 3, 2]

1

你可以使用 itertools 模块中的 groupby

from itertools import groupby

testvalue = [0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,0,0,1,1,0]

def count_times(U, down=False):
    if down:
        return [len(list(group)) for key, group in groupby(U) if key == 0]
    else:
        return [len(list(group)) for key, group in groupby(U) if key == 1]

print count_times(testvalues, True) # [2, 3, 3, 2, 1]
print count_times(testvalues, False) # [6, 4, 9, 2]

它没有返回OP所要求的内容。他说:“我想要去掉开头和结尾的集合,因为对于上面的示例,我不知道系统最初何时崩溃,也不知道将来何时恢复。因此,在这种情况下,我要生成的输出将是” - Raul Guiu

1
使用 reduce
def groups(U,i):
    a = reduce(lambda u,v: (u[0],u[1]+1) if v==i else (u[0] + [u[1]], 0) if u[1]>0 else u, U,([],0))[0]
    if U[0]== i: a=a[1:]   # truncate  begining
    if U[-1]==i: a=a[:-1]  # truncate end
    return a


U = [0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,0,0,1,1,0]

uptime = groups(U,1)
downtime = groups(U,0)

1
有时被称为“行程长度编码”。R有一个很好的内置函数rle()可以完成这个任务。无论如何,以下是我的方法,最初考虑使用takewhile(),但这是我能想到的最简洁的方式:
from itertools import chain

def rle(x):
    x = chain(x)
    last = x.next()
    i = 1
    for item in x:
        if item != last:
            yield (last, i)
            i = 1
        else:
            i += 1
        last = item
    yield (last, i)

然后你可以这样获取停机时间或正常运行时间:
[L for v,L in rle(U) if v == 1]
[L for v,L in rle(U) if v == 0]

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