python:什么是以灵活的方式处理深度嵌套数据的有效技术?

python:什么是以灵活的方式处理深度嵌套数据的有效技术?,python,dictionary,nested,Python,Dictionary,Nested,我的问题不是关于某个特定的代码片段,而是更一般的问题,所以请耐心听我说: 我应该如何组织我正在分析的数据,以及应该使用哪些工具来管理它 我正在使用python和numpy分析数据。因为python文档表明python中的字典非常优化,而且由于数据本身非常结构化,所以我将其存储在一个深度嵌套的字典中 这是字典的一个框架:层次结构中的位置定义了元素的性质,每一新行定义了前一级中键的内容: [AS091209M02] [AS091209M01] [AS090901M06] ... [100113]

我的问题不是关于某个特定的代码片段,而是更一般的问题,所以请耐心听我说:

我应该如何组织我正在分析的数据,以及应该使用哪些工具来管理它

我正在使用python和numpy分析数据。因为python文档表明python中的字典非常优化,而且由于数据本身非常结构化,所以我将其存储在一个深度嵌套的字典中

这是字典的一个框架:层次结构中的位置定义了元素的性质,每一新行定义了前一级中键的内容:

[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]
编辑:为了更好地解释我的数据集:

[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]
例如,我执行的操作类型是计算数组的属性(列在Ch1、Ch2下),拾取数组以创建新集合,例如分析给定个体在不同时间点的区域16(R16)的N01响应,等等

这个结构对我来说很好,而且非常快,就像承诺的那样。我可以很快地分析完整的数据集(而且字典太小,无法填满我计算机的内存:半个gig)

我的问题来自于我需要对字典的操作进行编程的繁琐方式。我经常有这样的代码:

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
这是丑陋的,笨重的,不可重用的,脆弱的(需要为字典的任何变体重新编码)

我尝试过使用递归函数,但除了最简单的应用程序外,我还遇到了一些非常讨厌的bug和奇怪的行为,这些行为造成了极大的时间浪费(当我处理嵌套较深的递归函数时,我没有在ipython中使用pdb进行调试是没有帮助的)。最后,我经常使用的唯一递归函数如下:

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)
for element in elementsAt(dic, 4):
    # Do something with element
我知道我做错了,因为我的代码很长,很粗糙,不可重用。我需要使用更好的技术来灵活地操作字典,或者将数据放在某种数据库格式(sqlite?)中。我的问题是,由于我(糟糕地)自学编程,我缺乏实践经验和背景知识来欣赏可用的选项。我已经准备好学习新的工具(SQL,面向对象编程),无论完成工作需要什么,但是我不愿意把时间和精力投入到那些对我的需求来说是死胡同的东西上

那么,您有什么建议来解决这个问题,并能够以更简单、灵活和可重用的方式编写我的工具

附录:除了使用数据字典的特定子字典外,以下是我为数据集dic或其子字典实现的一些操作示例:

实际上,我有一些递归函数工作得很好:

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
对于某些操作,我没有找到其他方法,只能将字典展平:

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

通过替换以下内容,可以使循环看起来更好:

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

因此,您可以使用相对简洁的代码访问所有值。如果您还需要一些钥匙,可以执行以下操作:

for (mk, mv) in dic.items():
    # etc.

根据您的需要,您也可以考虑创建并使用带有元组键的单个字典:

dic[(mk, rgk, nv, ik, ek)]
你会问:我应该如何组织我正在分析的数据,我应该使用哪些工具来管理它

我怀疑字典,尽管经过了优化,并不是这个问题的正确答案。我认为最好使用XML,如果有Python绑定的话,最好使用HDF5,甚至NetCDF。或者,正如你自己所建议的,一个数据库


如果您的项目有足够的持续时间和有用性来保证学习如何使用这些技术,那么我认为您会发现,现在就学习这些技术并获得正确的数据结构比在整个项目中使用错误的数据结构更好。学习XML、HDF5、SQL或您选择的任何东西,都将增强您的一般专业技能,使您能够更好地处理下一个项目。坚持使用笨拙、特定于问题和特殊的数据结构会导致下一次出现同样的问题。

您可以编写一个生成器函数,允许您迭代某一级别的所有元素:

def elementsAt(dic, level):
    if not hasattr(dic, 'itervalues'):
        return
    for element in dic.itervalues():
        if level == 0:
            yield element
        else:
            for subelement in elementsAt(element, level - 1):
                yield subelement
然后可按如下方式使用:

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)
for element in elementsAt(dic, 4):
    # Do something with element
如果您还需要过滤元素,可以首先获得所有需要过滤的元素(例如“rgk”级别):

至少这会使使用字典的层次结构变得更容易一些。使用更具描述性的名称也会有所帮助

“我把它存储在一个嵌套很深的字典中”

而且,正如你所看到的,结果并不好

替代方案是什么

  • 复合键和浅字典。您有一个由8部分组成的密钥: (个人、成像会话、成像区域、文件时间戳、文件属性、图像感兴趣区域、数据格式、采集通道)映射 指向一个值数组

    { ('AS091209M02', '100113', 'R16', '1263399103', 'Responses', 'N01', 'Sequential', 'Ch1' ): array, 
    ...
    
    这方面的问题是搜索

  • 适当的班级结构。实际上,一个完整的类定义可能有点过头了

  • “例如,我执行的操作类型是计算数组的属性 (列在Ch1、Ch2下),拾取数组以创建新集合,例如analyze 特定个体在不同时间点的区域16(R16)的N01响应等。”

    建议

    首先,使用一个名为tuple的
    作为最终对象

    Array = namedtuple( 'Array', 'individual, session, region, timestamp, properties, roi, format, channel, data' )
    
    或者类似的。构建这些命名元组对象的简单列表。然后,您可以简单地对它们进行迭代

    其次,在数组对象的主列表上使用许多简单的map reduce操作

    筛选:

    for a in theMasterArrrayList:
        if a.region = 'R16' and interest = 'N01':
            # do something on these items only.
    
    通过公用键减少:

    individual_dict = defaultdict(list)
    for a in theMasterArrayList:
        individual_dict[ a.individual ].append( a )
    
    这将在地图中创建一个子集,该子集正好包含您想要的项目

    然后,您可以进行单独记录['AS091209M02',并获得他们的所有数据。您可以为任何(或
    region_dict = defaultdict(list)
    for a in theMasterArrayList:
        region_dict[ a.region ].append( a )
    
    for a in theMasterArrayList:
        someTransformationFunction( a.data )
    
    def region_filter( array_list, region_set ):
        for a in array_list:
            if a.region in region_set:
                yield a
    
    def array_map( array_list, someConstant ):
        for a in array_list:
            yield Array( *(a[:8] + (someTranformation( a.data, someConstant ),) )
    
    def some_result( array_list, region, someConstant ):
        for a in array_map( region_filter( array_list, region ), someConstant ):
            yield a
    
    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
    
    for ek in deep_loop(dic):
        do_something
    
    def deep_loop(dic):
        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():
                            yield ek
    
    def deep_loop(dic, depth):
        if depth == 0:
            yield (), dic
        for subkey, subval in dic.items():
            for ktuple, value in deep_loop(subval, depth-1):
                yield (subkey,)+ktuple, value
    
    for (k1,k2,k3,k4), value in deep_loop(dic, 4):
        # do something