Набор данных с множеством переменных в Python, множеством словарей с отступом?

Я работаю с набором данных, в котором много переменных. В настоящее время я храню данные во многих словарях с отступом следующим образом:

import numpy as np

X_POSITIONS = [0,1.5,1]
Y_POSITIONS = [0,1,2]
CHANNELS = ['left pad', 'right pad', 'top pad', 'bottom pad']

data = {}
for x in X_POSITIONS:
    data[x] = {}
    for y in Y_POSITIONS:
        data[x][y] = {}
        for ch in CHANNELS:
            data[x][y][ch] = np.random.rand() # Here I would place my data.

Это работает нормально, но это неудобно, если по какой-то причине мне нужно изменить порядок клавиш. Рассмотрим следующую функцию:

def do_something_with_single_channel(data_from_one_channel):
    for x in data_from_one_channel:
        for y in data_from_one_channel[x]:
            print(f'x = {x}, y = {y}, data[x][y] = {data_from_one_channel[x][y]}')

Перед вызовом этой функции весь data объект необходимо переставить:

new_data = {}
for ch in CHANNELS:
    new_data[ch] = {}
    for x in X_POSITIONS:
        new_data[ch][x] = {}
        for y in Y_POSITIONS:
            new_data[ch][x][y] = data[x][y][ch]

do_something_with_single_channel(new_data['left pad'])

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

do_something_with_single_channel(data[channel="left pad"])

Что-то подобное существует? Какое имя?

1 ответ
1

Рассмотрим понимание вложенного словаря:

data = {x:{y:{ch: np.random.rand()
          for ch in CHANNELS}
          for y in Y_POSITIONS}
          for x in X_POSITIONS}
pprint.pprint(data)

{0: {0: {'bottom pad': 0.4563182938806024,
         'left pad': 0.7109389987303294,
         'right pad': 0.04972926343584316,
         'top pad': 0.49018200203439044},
     1: {'bottom pad': 0.9197368747212471,
         'left pad': 0.49675239597387033,
         'right pad': 0.8846734838851381,
         'top pad': 0.2536908682927299},
     2: {'bottom pad': 0.19202917332705682,
         'left pad': 0.4680743827356374,
         'right pad': 0.9824888617543756,
         'top pad': 0.7871922090543111}},
 1: {0: {'bottom pad': 0.532524614137474,
         'left pad': 0.5500941768186839,
         'right pad': 0.046363378683273115,
         'top pad': 0.507924966038481},
     1: {'bottom pad': 0.18606527132423667,
         'left pad': 0.2926470569818338,
         'right pad': 0.4542221348696881,
         'top pad': 0.07304292627461106},
     2: {'bottom pad': 0.255962925458759,
         'left pad': 0.8206558157675303,
         'right pad': 0.028156806394849743,
         'top pad': 0.4617476628686388}},
 1.5: {0: {'bottom pad': 0.5537703566924752,
           'left pad': 0.14192043274483335,
           'right pad': 0.04030969407542717,
           'top pad': 0.4145838174513119},
       1: {'bottom pad': 0.10519991606894175,
           'left pad': 0.33471726599841756,
           'right pad': 0.10389744180143101,
           'top pad': 0.3927574328293768},
       2: {'bottom pad': 0.2950323578101469,
           'left pad': 0.9335998766267041,
           'right pad': 0.9337763647877098,
           'top pad': 0.6591832120994695}}}

Однако, чтобы сохранить данные разнородных типов для последующего анализа, используйте pandas Фреймы данных, которые по существу представляют собой серии Pandas равной длины (или одномерные массивы Numpy). И используя itertools.product, вы можете вернуть все возможные комбинации из нескольких итераций:

from itertools import product 

import numpy as np
import pandas as pd

data = list(product(X_POSITIONS, Y_POSITIONS, CHANNELS))

# CAST LIST OF VALUES INTO DATA FRAME AND ASSIGN COLUMN OF RANDOM NUMs
df = (pd.DataFrame(data, columns=['X_POSITIONS', 'Y_POSITIONS', 'CHANNELS'])
        .assign(value = np.random.rand(len(data), 1)))

Вывод (N = 36 для продукта 3 * 3 * 4)

print(df)

#     X_POSITIONS  Y_POSITIONS    CHANNELS     value
# 0           0.0            0    left pad  0.761372
# 1           0.0            0   right pad  0.440973
# 2           0.0            0     top pad  0.182679
# 3           0.0            0  bottom pad  0.564203
# 4           0.0            1    left pad  0.954728
# 5           0.0            1   right pad  0.539686
# 6           0.0            1     top pad  0.957724
# 7           0.0            1  bottom pad  0.232217
# 8           0.0            2    left pad  0.488761
# 9           0.0            2   right pad  0.883579
# 10          0.0            2     top pad  0.010666
# 11          0.0            2  bottom pad  0.022114
# 12          1.5            0    left pad  0.129402
# 13          1.5            0   right pad  0.763472
# 14          1.5            0     top pad  0.475217
# 15          1.5            0  bottom pad  0.160637
# 16          1.5            1    left pad  0.521797
# 17          1.5            1   right pad  0.865391
# 18          1.5            1     top pad  0.263130
# 19          1.5            1  bottom pad  0.576295
# 20          1.5            2    left pad  0.004636
# 21          1.5            2   right pad  0.137856
# 22          1.5            2     top pad  0.156635
# 23          1.5            2  bottom pad  0.198684
# 24          1.0            0    left pad  0.143598
# 25          1.0            0   right pad  0.660144
# 26          1.0            0     top pad  0.588416
# 27          1.0            0  bottom pad  0.294899
# 28          1.0            1    left pad  0.915973
# 29          1.0            1   right pad  0.348533
# 30          1.0            1     top pad  0.391135
# 31          1.0            1  bottom pad  0.951016
# 32          1.0            2    left pad  0.015479
# 33          1.0            2   right pad  0.719314
# 34          1.0            2     top pad  0.976324
# 35          1.0            2  bottom pad  0.191481

Для подмножества фрейма данных по значениям индикатора:

print(df[df['CHANNELS'] == 'right pad'])

#     X_POSITIONS  Y_POSITIONS   CHANNELS     value
# 1           0.0            0  right pad  0.988888
# 5           0.0            1  right pad  0.091176
# 9           0.0            2  right pad  0.334674
# 13          1.5            0  right pad  0.706215
# 17          1.5            1  right pad  0.032422
# 21          1.5            2  right pad  0.024871
# 25          1.0            0  right pad  0.554525
# 29          1.0            1  right pad  0.790112
# 33          1.0            2  right pad  0.650198

  • Спасибо, это выглядит лучше с фреймами данных!

    – user171780

  • Я реализовал свой код с помощью pandas.DataFrame и это работает, но доступ к отдельным элементам невероятно медленный. Мой df имеет около 1,5e6 строк, а позже мне нужно сгруппировать элементы в соответствии со значениями некоторых столбцов, например df[(df['x']=1)&(df['y']=2)&(df['channel']='left pad')]. Мне нужно сделать это для каждого значения x и y и channel и это занимает много времени, поскольку словари с отступом выполняются очень быстро (хотя и слишком жестко).

    – user171780

  • Согласно измерениям времени с использованием time.time() с фреймом данных это занимает около 150 с, со словарями – 20 с.

    – user171780

  • Есть многие pandas методы расчетов. Pandas лучше всего работает с наборами не скалярных значений, особенно с векторизованными вычислениями. Непонятно, что именно нужно делать. Смотреть в groupby проводить расчеты все группы: df.groupby(['x','y','channel'])['value'].agg(['sum','mean','median','min','max']).

    – Отлично

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *