如何对复杂的字典键进行排序

时间:2011-10-14 01:03:41

标签: python sorting dictionary

我已经处理了这些非常复杂的数据文件,并且在处理每个文件时,我使用了orderedDictionary来捕获键和值。每个orderedDictionary都附加到一个列表,所以我的最终结果是一个字典列表。由于这些文件中捕获的数据的多样性,它们有许多共同的键,但是有足够的不常见键将数据导出到Excel比我希望的更复杂,因为我真的需要以一致的方式推出数据结构

每个键都具有类似

的结构
Q_#_SUB_A_COLUMN_#_NUMB_#

所以例如我有

 Q_123_SUB_D_COLUMN_C_NUMB_17

我们可以按如下方式翻译密钥

 Question 123
 SubItem D
 Column C
 Instance 17

因为存在SubItem D,C列和实例17,所以必须有SubItemA,B列和实例16

但是,其中一个源文件可能填充了数据值(以及上述示例范围内的键,其他一些源文件可能会以

结尾)
Q_123_SUB_D_COLUMN_C_NUMB_13

所以,当我遍历字典列表以拉出所有唯一的键实例时,我可以在csv.dictwriter中使用它们作为列标题,我的计划是对唯一列标题的结果列表进行排序,但我不能似乎使排序工作

具体而言,我需要对其进行排序,以使结果看起来像

 Q_122_SUB_A_COLUMN_C_NUMB_1
 Q_122_SUB_B_COLUMN_C_NUMB_1
 Q_123_SUB_A_COLUMN_C_NUMB_1
 Q_123_SUB_B_COLUMN_C_NUMB_1
 Q_123_SUB_C_COLUMN_C_NUMB_1
 Q_123_SUB_D_COLUMN_C_NUMB_1
 dot
 dot
 dot
 Q_123_SUB_A_COLUMN_C_NUMB_17
 Q_123_SUB_B_COLUMN_C_NUMB_17
 Q_123_SUB_C_COLUMN_C_NUMB_17
 Q_123_SUB_D_COLUMN_C_NUMB_17

最大的问题是,在我打开这些文件的任何特定集合之前,我不知道答复了多少问题,回答了多少个子问题,每个问题或子问题关联了多少列或者多少实例存在任何特定的问题,子问题或列的组合,我不想这样做。使用Python我能够将超过1,200行的SAS代码减少到95,但在我开始将其写入CSV文件之前,这一点我似乎无法弄明白。

任何意见都将受到赞赏。

我的计划是通过遍历字典列表找到所有唯一键,然后正确排序这些键,然后我可以使用键作为列标题创建一个csv文件。我知道我可以找到推出的唯一键并手动对其进行排序然后再读取已排序的文件,但这看起来很笨拙。

3 个答案:

答案 0 :(得分:4)

在排序时只需提供一个足够聪明的功能作为键。

>>> (lambda x: tuple(y(z) for (y, z) 
                     in zip((int, str, str, int), 
                            x.split('_')[1::2])))('Q_122_SUB_A_COLUMN_C_NUMB_1')
(122, 'A', 'C', 1)

答案 1 :(得分:2)

您可以使用正则表达式来提取密钥的不同部分,并使用它们进行排序。

如,

import re

names = '''Q_122_SUB_A_COLUMN_C_NUMB_1
Q_122_SUB_B_COLUMN_C_NUMB_1
Q_123_SUB_B_COLUMN_C_NUMB_1
Q_123_SUB_A_COLUMN_C_NUMB_17
Q_123_SUB_D_COLUMN_C_NUMB_1
Q_123_SUB_B_COLUMN_C_NUMB_17
Q_123_SUB_C_COLUMN_C_NUMB_1
Q_123_SUB_C_COLUMN_C_NUMB_17
Q_123_SUB_A_COLUMN_C_NUMB_1
Q_123_SUB_D_COLUMN_C_NUMB_17'''.split()

def key(name, match=re.compile(r'Q_(\d+)_SUB_(\w+)_COLUMN_(\w+)_NUMB_(\d+)').match):
    # not sure what the actual order is, adjust the priorities accordingly
    return tuple(f(value) for f, value in zip((str, int, int, str), match(name).group(3, 4, 1, 2)))

for name in names:
    print name

names.sort(key=key)

print

for name in names:
    print name

为了解释密钥提取过程,我们知道密钥具有某种模式。正则表达式在这里很有用。

r'Q_(\d+)_SUB_(\w+)_COLUMN_(\w+)_NUMB_(\d+)'
#     ^         ^            ^          ^
#     digits    letters      letters    digits
#     group 1   group 2      group 3    group 4

在正则表达式中,包含在parens中的字符串部分是组。 \d表示任何十进制数字。 +表示应该有一个或多个前一个字符。所以\d+表示一个或多个十进制数字。 \w对应一封信。

如果字符串与此模式匹配,我们可以使用group方法轻松访问该字符串中的每个分组。您也可以通过包含更多组号来访问多个组

如,

m = match('Q_122_SUB_B_COLUMN_C_NUMB_1')
# m.group(1) == '122'
# m.group(2) == 'B'
# m.group(3, 4) == ('C', '1')

这类似于Ignacio的方法,对模式只有更严格的要求。一旦你可以解决这个问题,创建适当的排序键应该很简单。

答案 2 :(得分:0)

假设密钥包含在列表中,请说出keyList

list_to_sort=[]

for key in keyList:
    sortKeys=key.split('_')
    keyTuple=(sortKeys[1],sortKeys[-1],sortKeys[3],sortKeys[5],key)
    list_to_sort.append(keyTuple)

在此之后,列表中的项目是看起来像

的元组
 (123,17,D,C,Q_123_SUB_D_COLUMN_C_NUMB_17)


from operator import itemgetter

list_to_sort.sort(key=itemgetter(0,1,2,3)

我不确定itemgetter究竟做了什么,但这样做起来似乎更简单,但不如其他两种解决方案更优雅。

请注意,我安排了元组中的键,以便按照与键出现的方式不同的顺序进行排序。那是我没必要做的

for key in keyList:
    sortKeys=key.split('_')
    keyTuple=(sortKeys[1],sortKeys[3],sortKeys[5],sortKeys[7],key)
    list_to_sort.append(keyTuple)

然后完成了这样的排序

list_to_sort.sort(key=itemgetter(0,3,1,2)

我更容易通过

跟踪第一个