Python:计算具有不同长度的列表列表中第n个元素的平均值

时间:2016-04-01 09:09:54

标签: python arrays list numpy nan

假设我有以下列表清单:

a = [ 
      [1, 2, 3],
      [2, 3, 4],
      [3, 4, 5, 6] 
    ]

我希望得到数组中每个第n个元素的平均值。但是,当想要以简单的方式执行此操作时,由于长度不同,Python会生成越界错误。我通过给每个数组赋予最长数组的长度,并用None填充缺失值来解决这个问题。

不幸的是,这样做使得无法计算平均值,因此我将数组转换为蒙版数组。下面显示的代码有效,但看起来相当麻烦。

import numpy as np
import numpy.ma as ma

a = [ [1, 2, 3],
      [2, 3, 4],
      [3, 4, 5, 6] ]

# Determine the length of the longest list
lenlist = []
for i in a:
    lenlist.append(len(i))
max = np.amax(lenlist)

# Fill each list up with None's until required length is reached
for i in a:
    if len(i) <= max:
        for j in range(max - len(i)):
            i.append(None)

# Fill temp_array up with the n-th element
# and add it to temp_array 
temp_list = []
masked_arrays = []
for j in range(max):
    for i in range(len(a)):
        temp_list.append(a[i][j])
    masked_arrays.append(ma.masked_values(temp_list, None))
    del temp_list[:]

# Compute the average of each array 
avg_array = []
for i in masked_arrays:
    avg_array.append(np.ma.average(i))

print avg_array

有没有办法更快地完成这项工作?列表的最终列表将包含600000'行'和最多100'列',因此效率非常重要:-)。

6 个答案:

答案 0 :(得分:4)

这是基于np.bincountnp.cumsum -

的几乎完全向量化的解决方案
# Store lengths of each list and their cumulative and entire summations
lens = np.array([len(i) for i in a]) # Only loop to get lengths
C = lens.cumsum()
N = lens.sum()

# Create ID array such that the first element of each list is 0, 
# the second element as 1 and so on. This is needed in such a format
# for use with bincount later on.
shifts_arr = np.ones(N,dtype=int)
shifts_arr[C[:-1]] = -lens[:-1]+1
id_arr = shifts_arr.cumsum()-1

# Use bincount to get the summations and thus the 
# averages across all lists based on their positions. 
avg_out = np.bincount(id_arr,np.concatenate(a))/np.bincount(id_arr)

- *几乎是因为我们通过循环获得列表的长度,但是在那里涉及的计算量最小,不能大大影响总运行时间。

示例运行 -

In [109]: a = [ [1, 2, 3],
     ...:       [2, 3, 4],
     ...:       [3, 4, 5, 6] ]

In [110]: lens = np.array([len(i) for i in a])
     ...: C = lens.cumsum()
     ...: N = lens.sum()
     ...: 
     ...: shifts_arr = np.ones(N,dtype=int)
     ...: shifts_arr[C[:-1]] = -lens[:-1]+1
     ...: id_arr = shifts_arr.cumsum()-1
     ...: 
     ...: avg_out = np.bincount(id_arr,np.concatenate(a))/np.bincount(id_arr)
     ...: 

In [111]: avg_out
Out[111]: array([ 2.,  3.,  4.,  6.])

答案 1 :(得分:3)

tertools.izip_longest将为您执行所有无填充的填充,以便您的代码可以缩减为:

import numpy as np
import numpy.ma as ma
from itertools import izip_longest

a = [ [1, 2, 3],
      [2, 3, 4],
      [3, 4, 5, 6] ]


averages = [np.ma.average(ma.masked_values(temp_list, None)) for temp_list in izip_longest(*a)]

print(averages)
[2.0, 3.0, 4.0, 6.0]

不知道关于numpy逻辑的最快方法是什么,但这肯定会比你自己的代码更有效率。

如果你想要一个更快的纯python解决方案:

from itertools import izip_longest, imap

a = [[1, 2, 3],
     [2, 3, 4],
     [3, 4, 5, 6]]


def avg(x):
    x = filter(None, x)
    return sum(x, 0.0) / len(x)


filt = imap(avg, izip_longest(*a))

print(list(filt))
[2.0, 3.0, 4.0, 6.0]

如果数组中的0没有工作,0将被视为Falsey,那么在这种情况下你必须使用list comp进行过滤,但它仍然会更快: / p>

def avg(x):
    x = [i for i in x if i is not None]
    return sum(x, 0.0) / len(x)

filt = imap(avg, izip_longest(*a))

答案 2 :(得分:2)

您已经可以清理代码以计算最大长度:此单行完成工作:

len(max(a,key=len))

结合其他答案,你会得到如下结果:

[np.mean([x[i] for x in a if len(x) > i]) for i in range(len(max(a,key=len)))]

答案 3 :(得分:1)

您也可以避免使用masked array并使用np.nan代替:

def replaceNoneTypes(x):
    return tuple(np.nan if isinstance(y, type(None)) else y for y in x)

a = [np.nanmean(replaceNoneTypes(temp_list)) for temp_list in zip_longest(*df[column], fillvalue=np.nan)]

答案 4 :(得分:0)

在您的测试阵列上:

[np.mean([x[i] for x in a if len(x) > i]) for i in range(4)]

返回

[2.0, 3.0, 4.0, 6.0]

答案 5 :(得分:0)

如果您使用的是Python版本&gt; = 3.4,则导入统计模块

from statistics import mean

如果使用较低版本,请创建一个函数来计算平均值

def mean(array):
    sum = 0
    if (not(type(array) == list)):
        print("there is some bad format in your input")
    else:
        for elements in array:
            try:
                sum = sum + float(elements)
            except:
                print("non numerical entry found")
        average = (sum + 0.0) / len(array)
        return average

创建列表列表,例如

myList = [[1,2,3],[4,5,6,7,8],[9,10],[11,12,13,14],[15,16,17,18,19,20,21,22],[23]]

遍历myList

for i, lists in enumerate(myList):
    print(i, mean(lists))

这将打印序列n和第n列表的平均值。

要特别找到第n个列表的平均值,请创建一个函数

def mean_nth(array, n):
    if((type(n) == int) and n >= 1 and type(array) == list):
        return mean(myList[n-1])
    else:
        print("there is some bad format of your input")

请注意,索引从零开始,因此,例如,如果您要查找第5个列表的平均值,它将在索引4处。这解释了代码中的n-1。

然后调用该函数,例如

avg_5thList = mean_nth(myList, 5)
print(avg_5thList)

在myList上运行上面的代码会产生以下结果:

0 2.0
1 6.0
2 9.5
3 12.5
4 18.5
5 23.0
18.5

其中前六行是从迭代循环生成的,并显示第n个列表的索引和列表平均值。最后一行(18.5)显示mean_nth(myList, 5)调用后第五个列表的平均值。

此外,对于像你这样的清单,

a = [ 
      [1, 2, 3],
      [2, 3, 4],
      [3, 4, 5, 6] 
    ]

让我们说你想要平均第一个元素,即(1 + 2 + 3)/ 3 = 2,或第二个元素,即(2 + 3 + 4)/ 3 = 3,或第四个元素,如6 / 1 = 6,您需要找到每个列表的长度,以便您可以在第n个元素中识别是否存在于列表中。为此,您首先需要按列表长度的顺序排列列表。

你可以

1)首先根据成分列表的大小对主列表进行迭代排序,然后通过排序列表来确定成分列表是否足够长

2)或者你可以迭代地查看原始列表中组成列表的长度。

(如果需要的话,我肯定可以回过头来制定更快的递归算法)

计算第二个更有效率,所以假设你的第5个元素在索引(0,1,2,3,4)或第n个元素意味着第(n-1)个元素中意味着第4个,那么就让它去吧创建一个函数

def find_nth_average(array, n):
    if(not(type(n) == int and (int(n) >= 1))):
        return "Bad input format for n"
    else:
        if (not(type(array) == list)):
            return "Bad input format for main list"
        else:           
            total = 0
            count = 0
            for i, elements in enumerate(array):
                if(not(type(elements) == list)):
                    return("non list constituent found at location " + str(i+1))                
                else:
                    listLen = len(elements)
                    if(int(listLen) >= n):
                        try:
                            total = total + elements[n-1]
                            count = count + 1
                        except:
                            return ("non numerical entity found in constituent list " + str(i+1))
            if(int(count) == 0):
                return "No such n-element exists"
            else:
                average = float(total)/float(count)
                return average

现在,我们可以在您的列表a

上调用此函数
print(find_nth_average(a, 0))
print(find_nth_average(a, 1))
print(find_nth_average(a, 2))
print(find_nth_average(a, 3))
print(find_nth_average(a, 4))
print(find_nth_average(a, 5))
print(find_nth_average(a, 'q'))
print(find_nth_average(a, 2.3))
print(find_nth_average(5, 5))

相应的结果是:

Bad input format for n
2.0
3.0
4.0
6.0
No such n-element exists
Bad input format for n
Bad input format for n
Bad input format for main list

如果您有不稳定的列表,例如

a = [[1, 2, 3], 2, [3, 4, 5, 6]]

包含一个非list元素,你得到一个输出:

non list constituent found at location 2 

如果您的成分列表不稳定,例如:

a = [[1, 'p', 3], [2, 3, 4], [3, 4, 5, 6]]

包含列表中的非数字实体,并按print(find_nth_average(a, 2))

查找第2个元素的平均值

你得到一个输出:

non numerical entity found in constituent list 1