Лучший способ создать массив NumPy из словаря?

StackOverflow https://stackoverflow.com/questions/601477

  •  03-07-2019
  •  | 
  •  

Вопрос

Я только начинаю изучать 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 предложил интересный трюк с группировкой записей одинакового размера.Проблема с ним (при условии, что под рукой нет никаких априорных данных о длинах записей) заключается в том, что оно требует даже большего количества вычислений, чем простое решение:

  1. по меньшей мере О(Н*логН) Вызовы и сравнения 'len' для сортировки с помощью эффективного алгоритма
  2. НА) проверяет второй путь по списку для получения групп (их индексы начала и конца на «вертикальной» оси)

Использование 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.])
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top