Лучший способ создать массив NumPy из словаря?
Вопрос
Я только начинаю изучать NumPy, поэтому, возможно, мне не хватает некоторых основных понятий...
Как лучше всего создать массив NumPy из словаря, значения которого представляют собой списки?
Что-то вроде этого:
d = { 1: [10,20,30] , 2: [50,60], 3: [100,200,300,400,500] }
Должно превратиться во что-то вроде:
data = [
[10,20,30,?,?],
[50,60,?,?,?],
[100,200,300,400,500]
]
Я собираюсь сделать некоторую базовую статистику для каждой строки, например:
deviations = numpy.std(data, axis=1)
Вопросы:
Какой лучший/наиболее эффективный способ создать numpy.array из словаря?Словарь большой;пара миллионов ключей, каждый из которых содержит около 20 предметов.
Количество значений для каждой «строки» различно.Если я правильно понимаю, numpy хочет иметь одинаковый размер, так что мне заполнить недостающие элементы, чтобы сделать std() счастливым?
Обновлять:Я забыл упомянуть одну вещь: хотя методы Python разумны (например.цикл по нескольким миллионам элементов выполняется быстро), он ограничен одним процессором.Операции Numpy хорошо масштабируются под оборудование и задействуют все процессоры, поэтому они привлекательны.
Решение
Вам не нужно создавать массивы numpy для вызова numpy.std().Вы можете вызвать numpy.std() в цикле для всех значений вашего словаря.Список будет преобразован в массив numpy на лету для вычисления стандартного варианта.
Недостатком этого метода является то, что основной цикл будет выполняться на Python, а не на C.Но я думаю, это должно быть достаточно быстро:вы по-прежнему будете вычислять std со скоростью C и сэкономите много памяти, поскольку вам не придется хранить значения 0 там, где у вас есть массивы переменного размера.
- Если вы хотите дополнительно оптимизировать это, вы можете сохранить свои значения в списке массивов numpy, чтобы вы выполняли преобразование списка Python -> массив numpy только один раз.
- если вы обнаружите, что это все еще слишком медленно, попробуйте использовать Psycho для оптимизации цикла Python.
- если это все еще слишком медленно, попробуйте использовать Китон вместе с модулем numpy.Этот Руководство заявляет о впечатляющем повышении скорости обработки изображений.Или просто запрограммируйте всю функцию std на Cython (см. этот для тестов и примеров с функцией суммы)
- Альтернативой Cython было бы использование СВИГ с numpy.i.
- если вы хотите использовать только numpy и все вычислять на уровне C, попробуйте сгруппировать все записи одинакового размера в разных массивах и вызвать numpy.std() для каждого из них.Это должно выглядеть так, как показано в следующем примере.
пример со сложностью O(N):
import numpy
list_size_1 = []
list_size_2 = []
for row in data.itervalues():
if len(row) == 1:
list_size_1.append(row)
elif len(row) == 2:
list_size_2.append(row)
list_size_1 = numpy.array(list_size_1)
list_size_2 = numpy.array(list_size_2)
std_1 = numpy.std(list_size_1, axis = 1)
std_2 = numpy.std(list_size_2, axis = 1)
Другие советы
Хотя здесь уже присутствуют некоторые довольно разумные идеи, я считаю, что стоит упомянуть следующее.
Заполнение недостающих данных любым значением по умолчанию испортит статистические характеристики (стандартные и т. д.).Видимо, поэтому Mapad предложил интересный трюк с группировкой записей одинакового размера.Проблема с ним (при условии, что под рукой нет никаких априорных данных о длинах записей) заключается в том, что оно требует даже большего количества вычислений, чем простое решение:
- по меньшей мере О(Н*логН) Вызовы и сравнения 'len' для сортировки с помощью эффективного алгоритма
- НА) проверяет второй путь по списку для получения групп (их индексы начала и конца на «вертикальной» оси)
Использование Psyco — хорошая идея (он поразительно прост в использовании, поэтому обязательно попробуйте).
Кажется, оптимальным способом будет воспользоваться стратегией, описанной Mapad в пункте №1, но с модификацией — не генерировать весь список, а перебирать словарь, конвертируя каждую строку в numpy.array и выполняя необходимые вычисления.Так:
for row in data.itervalues():
np_row = numpy.array(row)
this_row_std = numpy.std(np_row)
# compute any other statistic descriptors needed and then save to some list
В любом случае несколько миллионов циклов в Python не займут столько времени, сколько можно было бы ожидать.Кроме того, это не похоже на рутинные вычисления, так что кого волнует, займет ли это дополнительную секунду/минуту, если оно запускается время от времени или даже только один раз.
Обобщенный вариант того, что предложил Mapad:
from numpy import array, mean, std
def get_statistical_descriptors(a):
if ax = len(shape(a))-1
functions = [mean, std]
return f(a, axis = ax) for f in functions
def process_long_list_stats(data):
import numpy
groups = {}
for key, row in data.iteritems():
size = len(row)
try:
groups[size].append(key)
except KeyError:
groups[size] = ([key])
results = []
for gr_keys in groups.itervalues():
gr_rows = numpy.array([data[k] for k in gr_keys])
stats = get_statistical_descriptors(gr_rows)
results.extend( zip(gr_keys, zip(*stats)) )
return dict(results)
пустой словарь
Вы можете использовать структурированный массив, чтобы сохранить возможность обращаться к numpy-объекту по ключу, например словарю.
import numpy as np
dd = {'a':1,'b':2,'c':3}
dtype = eval('[' + ','.join(["('%s', float)" % key for key in dd.keys()]) + ']')
values = [tuple(dd.values())]
numpy_dict = np.array(values, dtype=dtype)
numpy_dict['c']
сейчас выведу
array([ 3.])