2016-10-24 3 views
3

Рассмотрим следующую переменную длину 2D массивпитон: среднее переменной длины 2 матрицы

[ 
[1, 2, 3], 
[4, 5], 
[6, 7, 8, 9] 
]  

Как я могу найти среднее значение переменных вдоль столбца?

Я хочу что-то вроде [(1+4+6)/3,(2+5+7)/3, (3+8)/2, 9/1]

Таким образом, конечный результат будет [3.667, 4.667, 5.5, 9]

Возможно ли это с помощью NumPy?

Я пробовал np.mean(x, axis=0), но numpy ожидает массивы того же размера.

Прямо сейчас, я вывожу элементы каждого столбца и нахожу среднее. Есть ли лучший способ добиться результата?

ответ

5

Вы могли бы использовать панды:

import pandas as pd 

a = [[1, 2, 3], 
    [4, 5], 
    [6, 7, 8, 9]] 

df = pd.DataFrame(a) 
# 0 1 2 3 
# 0 1 2 3 NaN 
# 1 4 5 NaN NaN 
# 2 6 7 8 9 

df.mean() 
# 0 3.666667 
# 1 4.666667 
# 2 5.500000 
# 3 9.000000 
# dtype: float64 

Вот еще одно решение, которое использует только NumPy:

import numpy as np 
nrows = len(a) 
ncols = max(len(row) for row in a) 
arr = np.zeros((nrows, ncols)) 
arr.fill(np.nan) 
for jrow, row in enumerate(a): 
    for jcol, col in enumerate(row): 
     arr[jrow, jcol] = col 
print np.nanmean(arr, axis=0) 
# array([ 3.66666667, 4.66666667, 5.5  , 9.  ]) 
0

Если вы хотите сделать это вручную, что я хотел бы сделать:

max_length = 0 

Рисунок максимальной длины массива:

for array in arrays: 
    if len(array) > max: 
     max = len(array) 

Pad всех массивы на эту длину с 'None'

for array in arrays: 
    while len(array) < max: 
     array.append(None) 

Zip сгруппирует колонну

columns = zip(*arrays) 

columns == [(1, 4, 6), (2, 5, 7), (3, 'None', 8), ('None', 'None', 9)]

Вычислить среднее значение, как для любого списка:

for col in columns: 
    count = 0 
    sum = 0.0 
    for num in col: 
     if num is not None: 
      count += 1 
      sum += float(num) 
    print "%s: Avg %s" % (col, sum/count) 

Или как список понимание после того, как обивка массивы:

[sum(filter(None, col))/float(len(filter(None, col))) for col in zip(*arrays)] 

Выход:

(1, 4, 6): Avg 3.66666666667 
(2, 5, 7): Avg 4.66666666667 
(3, 'None', 8): Avg 5.5 
('None', 'None', 9): Avg 9.0 
2

Перечислен в этой должности почти Векторизованный подход с использованием NumPy. Мы попытались бы присвоить каждому элементу в элементе списка идентификатор на основе их позиций. Эти идентификаторы затем могут быть отправлены в np.bincount, поскольку они будут выполнять суммирование по идентификатору. Наконец, мы разделили бы суммы соответственно на длины каждого ID, чтобы получить окончательные средние значения.

Таким образом, мы имели бы реализацию как так -

def variable_mean(a): 
    vals = np.concatenate(a) 
    lens = np.array(map(len,a)) 
    id_arr = np.ones(vals.size,dtype=int) 
    id_arr[0] = 0 
    id_arr[lens.cumsum()[:-1]] = -lens[:-1] + 1 
    IDs = id_arr.cumsum() 
    return np.bincount(IDs,vals)/np.bincount(IDs) 

выполнения тест -

In [298]: # Setup input 
    ...: N = 1000 # number of elems in input list 
    ...: minL = 3 # min len of an element (list) in input list 
    ...: maxL = 10 # max len of an element (list) in input list 
    ...: a = [list(np.random.randint(0,9,(i))) \ 
    ...:  for i in np.random.randint(minL,maxL,(N))] 
    ...: 

In [299]: %timeit pd.DataFrame(a).mean() #@Julien Spronck's pandas soln 
100 loops, best of 3: 3.33 ms per loop 

In [300]: %timeit variable_mean(a) 
100 loops, best of 3: 2.36 ms per loop 

In [301]: # Setup input 
    ...: N = 1000 # number of elems in input list 
    ...: minL = 3 # min len of an element (list) in input list 
    ...: maxL = 100 # max len of an element (list) in input list 
    ...: a = [list(np.random.randint(0,9,(i))) \ 
    ...:  for i in np.random.randint(minL,maxL,(N))] 
    ...: 

In [302]: %timeit pd.DataFrame(a).mean() #@Julien Spronck's pandas soln 
10 loops, best of 3: 27.1 ms per loop 

In [303]: %timeit variable_mean(a) 
100 loops, best of 3: 9.58 ms per loop 
+0

Это должно называться Numpythonic ;-). – Kasramvd

+0

@ Kasramvd Ха-ха хорошо это то, что я делаю в основном;) – Divakar

2

Очень простой альтернативный подход с использованием itertools.izip_longest() как:

>>> mean_list = [] 
>>> for sub_list in izip_longest(*my_list): 
...  filtered_list = filter(None, sub_list) 
...  mean_list.append(sum(filtered_list)/(len(filtered_list)*1.0)) 
... 
>>> mean_list 
[3.6666666666666665, 4.666666666666667, 5.5, 9.0] 

где my_list равна :

[ 
[1, 2, 3], 
[4, 5], 
[6, 7, 8, 9] 
] 
+0

Использование 0 в качестве значения заполнения разрушает среднее значение. – TemporalWolf

+0

@TemporalWolf Ohh Да. Обновлен ответ –

0

В PY3, zip_longest принимает fillvalue параметр:

In [1208]: ll=[ 
     ...: [1, 2, 3], 
     ...: [4, 5], 
     ...: [6, 7, 8, 9] 
     ...: ] 
In [1209]: list(itertools.zip_longest(*ll, fillvalue=np.nan)) 
Out[1209]: [(1, 4, 6), (2, 5, 7), (3, nan, 8), (nan, nan, 9)] 

Заполнив с nan, я могу использовать np.nanmean взять среднее игнорирование nan. nanmean поворачивает вход (здесь _ из предыдущей строки) в массив:

In [1210]: np.nanmean(_, axis=1) 
Out[1210]: array([ 3.66666667, 4.66666667, 5.5  , 9.  ])