Структурированный массив Numpy Mean
Предположим, что у меня есть структурированный массив студентов (строки) и результатов тестов (целых), где каждая запись - это результат, полученный конкретным студентом в конкретном тесте. Естественно, у каждого ученика есть несколько записей в этом массиве.
пример
import numpy
grades = numpy.array([('Mary', 96), ('John', 94), ('Mary', 88), ('Edgar', 89), ('John', 84)],
dtype=[('student', 'a50'), ('score', 'i')])
print grades
#[('Mary', 96) ('John', 94) ('Mary', 88) ('Edgar', 89) ('John', 84)]
Как мне легко вычислить средний балл каждого ученика? Другими словами, как мне взять среднее значение массива в измерении "оценка"? Я хотел бы сделать
grades.mean('score')
и вернуть Numpy
[('Mary', 92), ('John', 89), ('Edgar', 89)]
но Numpy жалуется
TypeError: an integer is required
Есть ли Numpy-esque способ сделать это легко? Я думаю, что это может включать просмотр структурированного массива с другим dtype. Любая помощь будет оценена. Благодарю.
редактировать
>>> grades = numpy.zeros(5, dtype=[('student', 'a50'), ('score', 'i'), ('testid', 'i'])
>>> grades[0] = ('Mary', 96, 1)
>>> grades[1] = ('John', 94, 1)
>>> grades[2] = ('Mary', 88, 2)
>>> grades[3] = ('Edgar', 89, 1)
>>> grades[4] = ('John', 84, 2)
>>> np.mean(grades, 'testid')
TypeError: an integer is required
4 ответа
NumPy не предназначен для возможности группировать строки и применять агрегатные функции к этим группам. Вы могли бы:
- использование
itertools.groupby
и восстановить массив; - используйте Pandas, которая основана на NumPy и отлично подходит для группировки; или же
- добавьте еще одно измерение в массив для идентификатора теста (так что в этом случае будет массив 2х3, потому что похоже, что было два теста).
Вот itertools
решение, но, как вы можете видеть, это довольно сложно и неэффективно. Я бы порекомендовал один из двух других методов.
np.array([(k, np.array(list(g), dtype=grades.dtype).view(np.recarray)['score'].mean())
for k, g in groupby(np.sort(grades, order='student').view(np.recarray),
itemgetter('student'))], dtype=grades.dtype)
collapseByField(оценки, "студент") дает то, что вы хотите, после:
def collapseByField(e,collapsefield,keepFields=None,agg=None):
import numpy as np
assert isinstance(e,np.ndarray) # Structured array
if agg is None:
agg=np.mean
if keepFields is None:
newf=[(n,agg,n) for n in e.dtype.names if n not in (collapsefield)]
import matplotlib as mpl
return(mpl.mlab.rec_groupby(e,[collapsefield],newf))
Чуть быстрее и проще решение на основе itertools
без использования view(), есть
[(k,e['score'][list(g)].mean()) for k, g in groupby(argsort(e),e['student'].__getitem__ )]
Это та же идея ecatmur, но она работает в терминах индексов, использующих argsort() вместо sort.