Mi pregunta no es sobre un fragmento de código específico sino más general, así que tenga paciencia:python: ¿cuáles son las técnicas eficientes para tratar datos profundamente anidados de manera flexible?
¿Cómo debo organizar los datos que estoy analizando y qué herramientas debo usar para gestionarlo?
Estoy usando python y numpy para analizar datos. Debido a que la documentación de Python indica que los diccionarios están muy optimizados en Python, y también debido a que los datos en sí son muy estructurados, los almacené en un diccionario profundamente anidado.
Aquí es un esqueleto del diccionario: la posición en la jerarquía define la naturaleza del elemento, y cada nueva línea define el contenido de una clave en el nivel precedente:
[AS091209M02] [AS091209M01] [AS090901M06] ...
[100113] [100211] [100128] [100121]
[R16] [R17] [R03] [R15] [R05] [R04] [R07] ...
[1263399103] ...
[ImageSize] [FilePath] [Trials] [Depth] [Frames] [Responses] ...
[N01] [N04] ...
[Sequential] [Randomized]
[Ch1] [Ch2]
Editar: Para explicar un poco mejor fijan mis datos:
[individual] ex: [AS091209M02]
[imaging session (date string)] ex: [100113]
[Region imaged] ex: [R16]
[timestamp of file] ex [1263399103]
[properties of file] ex: [Responses]
[regions of interest in image ] ex [N01]
[format of data] ex [Sequential]
[channel of acquisition: this key indexes an array of values] ex [Ch1]
El tipo de operaciones que realizo es, por ejemplo, para calcular las propiedades de las matrices (enumerados en c1, c2), recoger las matrices para hacer una nueva colección, por ejemplo, analizar las respuestas de N01 de la región 16 (R16) de un indi dado vidual en diferentes puntos de tiempo, etc.
Esta estructura funciona bien para mí y es muy rápida, como se prometió. Puedo analizar el conjunto completo de datos con bastante rapidez (y el diccionario es demasiado pequeño para llenar el RAM de mi computadora: medio concierto).
Mi problema proviene de la manera engorrosa en la que necesito programar las operaciones del diccionario. A menudo tengo tramos de código que algo así:
for mk in dic.keys():
for rgk in dic[mk].keys():
for nk in dic[mk][rgk].keys():
for ik in dic[mk][rgk][nk].keys():
for ek in dic[mk][rgk][nk][ik].keys():
#do something
que es feo, engorroso no reutilizable, y quebradiza (necesidad de recodificar para cualquier variante del diccionario).
Traté de usar funciones recursivas, pero aparte de las aplicaciones más simples, me encontré con algunos errores muy desagradables y comportamientos extraños que causaron una gran pérdida de tiempo (no ayuda que no logré depurar con pdb en ipython cuando estoy tratando con funciones recursivas profundamente anidadas). Al final, la única función recursiva que utilizo con regularidad es la siguiente:
def dicExplorer(dic, depth = -1, stp = 0):
'''prints the hierarchy of a dictionary.
if depth not specified, will explore all the dictionary
'''
if depth - stp == 0: return
try : list_keys = dic.keys()
except AttributeError: return
stp += 1
for key in list_keys:
else: print '+%s> [\'%s\']' %(stp * '---', key)
dicExplorer(dic[key], depth, stp)
Yo sé que estoy haciendo esto mal, porque mi código es largo, noodly y no reutilizable. Necesito utilizar mejores técnicas para manipular de forma flexible los diccionarios o poner los datos en algún formato de base de datos (sqlite?). Mi problema es que dado que soy (mal) autodidacta en lo que respecta a la programación, carezco de experiencia práctica y conocimiento de fondo para apreciar las opciones disponibles. Estoy listo para aprender nuevas herramientas (SQL, programación orientada a objetos), lo que sea necesario para hacer el trabajo, pero soy reacio a invertir mi tiempo y esfuerzos en algo que será un callejón sin salida para mis necesidades.
¿Cuáles son sus sugerencias para abordar este problema y ser capaz de codificar mis herramientas de una manera más breve, flexible y reutilizable?
Adición: aparte de hacer algo con un subdiccionario particular del diccionario de datos, estos son algunos ejemplos de las operaciones que he implementado para la DIC conjunto de datos, o un diccionario secundario de la misma:
en realidad tienen algún recursiva funciones que han funcionado bien:
def normalizeSeqDic(dic, norm_dic = {}, legend =()):
'''returns a normalized dictionary from a seq_amp_dic. Normalization is performed using the first time point as reference
'''
try :
list_keys = dic.keys()
for key in list_keys:
next_legend = legend + (key,)
normalizeSeqDic(dic[key], norm_dic, next_legend)
except AttributeError:
# normalization
# unpack list
mk, ek, nk, tpk = legend
#assign values to amplitude dict
if mk not in norm_dic: norm_dic[mk] = {}
if ek not in norm_dic[mk]: norm_dic[mk][ek] = {}
if nk not in norm_dic[mk][ek]: norm_dic[mk][ek][nk] = {}
if tpk not in norm_dic[mk][ek][nk]: norm_dic[mk][ek][nk][tpk] = {}
new_array = []
for x in range(dic.shape[0]):
new_array.append(dic[x][1:]/dic[x][0])
new_array = asarray(new_array)
norm_dic[mk][ek][nk][tpk] = new_array
return norm_dic
def poolDic(dic):
'''returns a dic in which all the values are pooled, and root (mk) keys are fused
these pooled dics can later be combined into another dic
'''
pooled_dic = {}
for mk in dic.keys():
for ek in dic[mk].keys():
for nk in dic[mk][ek].keys():
for tpk in dic[mk][ek][nk].keys():
#assign values to amplitude dict
if ek not in pooled_dic: pooled_dic[ek] = {}
if nk not in pooled_dic[ek]: pooled_dic[ek][nk] = {}
if tpk not in pooled_dic[ek][nk]:
pooled_dic[ek][nk][tpk] = dic[mk][ek][nk][tpk]
else: pooled_dic[ek][nk][tpk]= vstack((pooled_dic[ek][nk][tpk], dic[mk][ek][nk][tpk]))
return pooled_dic
def timePointsDic(dic):
'''Determines the timepoints for each individual key at root
'''
tp_dic = {}
for mk in dic.keys():
tp_list = []
for rgk in dic[mk].keys():
tp_list.extend(dic[mk][rgk]['Neuropil'].keys())
tp_dic[mk]=tuple(sorted(list(set(tp_list))))
return tp_dic
para algunas operaciones no he encontrado ninguna otra manera que para aplanar el diccionario:
def flattenDic(dic, label):
'''flattens a dic to produce a list of of tuples containing keys and 'label' values
'''
flat_list = []
for mk in dic.keys():
for rgk in dic[mk].keys():
for nk in dic[mk][rgk].keys():
for ik in dic[mk][rgk][nk].keys():
for ek in dic[mk][rgk][nk][ik].keys():
flat_list.append((mk, rgk, nk, ik, ek, dic[mk][rgk][nk][ik][ek][label])
return flat_list
def extractDataSequencePoints(flat_list, mk, nk, tp_list):
'''produces a list containing arrays of time point values
time_points is a list of the time points wished (can have 2 or 3 elements)
'''
nb_tp = len(tp_list)
# build tp_seq list
tp_seq = []
tp1, tp2, tp3 = [], [], []
if nk == 'Neuropil':
tp1.extend(x for x in flat_list if x[0]==mk and x[2] == 'Neuropil' and x[3] == tp_list[0])
tp2.extend(x for x in flat_list if x[0]==mk and x[2] == 'Neuropil'and x[3] == tp_list[1])
else:
tp1.extend(x for x in flat_list if x[0]==mk and x[2] != 'Neuropil'and x[3] == tp_list[0])
tp2.extend(x for x in flat_list if x[0]==mk and x[2] != 'Neuropil'and x[3] == tp_list[1])
if nb_tp == 3:
if nk == 'Neuropil':
tp3.extend(x for x in flat_list if x[0]==mk and x[2] == 'Neuropil'and x[3] == tp_list[2])
else:
tp3.extend(x for x in flat_list if x[0]==mk and x[2] != 'Neuropil'and x[3] == tp_list[2])
for x in tp1:
for y in tp2:
if x[0:3] == y[0:3] :
if nb_tp == 3:
for z in tp3:
if x[0:3] == z[0:3] :
tp_seq.append(asarray([x[4],y[4],z[4]]))
else:
tp_seq.append(asarray([x[4],y[4]]))
return tp_seq
@AlexandreS: me temo que realmente no entiendo lo suficiente acerca de sus datos de muestra para poder dar muchos consejos. ¿Podrían ampliar los datos que están analizando y qué análisis están realizando? – MattH
@MattH: edité la pregunta para proporcionar más detalles. Avíseme si no es suficiente – AlexandreS
@AlexandrS: gracias por la aclaración. Puede ayudar aún más si pudiera explicar cómo se adquieren/almacenan/derivan estos datos en este momento. Creo que el camino a seguir sería hacer un diagrama abstracto de su estructura como objetos con propiedades y cómo los objetos/propiedades se relacionan entre sí. Cuando estoy decidiendo cómo codificar una estructura de datos, a menudo esbozaré estas cosas. – MattH