Я работаю с набором данных, в котором много переменных. В настоящее время я храню данные во многих словарях с отступом следующим образом:
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 ответ
Рассмотрим понимание вложенного словаря:
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'])
.— Отлично