两个字典列表的键值对交集

3

I have two lists of dictionaries in the format:

systolic_sex = [
        {'attribute': u'bp', 'value_d': 133.0, 'value_s': u'133', 'sid': 6}, 
        {'attribute': u'bp', 'value_d': 127.0, 'value_s': u'127', 'sid': 17}, 
        {'attribute': u'bp', 'value_d': 121.0, 'value_s': u'121', 'sid': 18}, 
        {'attribute': u'bp', 'value_d': 127.0, 'value_s': u'127', 'sid': 27}, 
        {'attribute': u'bp', 'value_d': 120.0, 'value_s': u'120', 'sid': 42},
        {'attribute': u'SEX', 'value_d': 0.0, 'value_s': u'M', 'sid': 6},      
        {'attribute': u'SEX', 'value_d': 0.0, 'value_s': u'M', 'sid': 17},   
        {'attribute': u'SEX', 'value_d': 0.0, 'value_s': u'M', 'sid': 18},
        {'attribute': u'SEX', 'value_d': 0.0, 'value_s': u'M', 'sid': 27},   
        {'attribute': u'SEX', 'value_d': 0.0, 'value_s': u'M', 'sid': 42}
    ]



sex = [
        {'attribute': u'SEX', 'value_d': 0.0, 'value_s': u'M', 'sid': 6},      
        {'attribute': u'SEX', 'value_d': 0.0, 'value_s': u'M', 'sid': 17},   
        {'attribute': u'SEX', 'value_d': 0.0, 'value_s': u'M', 'sid': 42}
    ]

我希望通过匹配“sid”键的值来将这些列表进行匹配,因此如果两个列表中都有相同的“sid”值,则表示匹配成功,否则不匹配。 如果匹配成功,我将按照以下方式根据“sid”从两个集合中附加匹配字典到新列表中

new_set = [
        {'attribute': u'bp', 'value_d': 133.0, 'value_s': u'133', 'sid': 6}, 
        {'attribute': u'SEX', 'value_d': 0.0, 'value_s': u'M', 'sid': 6},
        {'attribute': u'bp', 'value_d': 127.0, 'value_s': u'127', 'sid': 17}, 
        {'attribute': u'SEX', 'value_d': 0.0, 'value_s': u'M', 'sid': 17},
        {'attribute': u'bp', 'value_d': 120.0, 'value_s': u'120', 'sid': 42},
        {'attribute': u'SEX', 'value_d': 0.0, 'value_s': u'M', 'sid': 42}
    ]

我尝试过各种交集方法,包括修改匹配字典集的答案,但我想创建一个新的字典列表,其中包含匹配的sids,而不是在两个列表之间替换值。


你能展示一下在这种情况下期望的输出是什么样子吗?听起来你想要通过 sid 列有效地将这两个“表格”连接起来,对吗? - Amit Kumar Gupta
2
哈哈,输出结果和我想象的完全不一样,不知道为什么。删掉我的回答了。 - woot
这是数据的一个不错的重新映射!对未来的努力很有用。 - horcle_buzz
注意:set sex 可以是 systolic_sex 的形式,也就是说,在 'sid' 键的单个值上,'attribute' 键可能有多个不同值的集合。例如,我可以有两个集合,systolic_sex 和 diastolic_sex,然后通过 'sid' 匹配所有键。如果原帖中没有说明清楚,那么很抱歉。无论如何,@dawg 的解决方案对此起作用,即使我没有明确这个要求。 - horcle_buzz
4个回答

3

如果你经常处理这样的数据,你可能会对使用pandas感兴趣。 你的字典已经是pandas喜欢的形式了,所以你可以这样做:

import pandas

systolic_sex = pandas.DataFrame(systolic_sex)
sex = pandas.DataFrame(sex)

matches = systolic_sex[systolic_sex.sid.isin(sex.sid)]

如果您希望以与提供的格式相同的形式获取数据,则可以进行以下操作:
output = matches.to_dict(orient='records')

我实际上使用了Pandas来操作数据,如果不能使用纯Python方法,我也会使用它。实际上,我更喜欢这个解决方案,因为它更加简洁,除了额外的导入之外。 - horcle_buzz
1
是的,我发现无论我去哪里,Pandas都变得非常标准化,因此我认为使用它并不会增加太多负担,而且可以更好地处理这些数据。 - chthonicdaemon
对于我的需求来说,这个事情变得有点复杂。我会再给它一次机会。 - horcle_buzz
1
哦,我必须评论一下,你是我见过的少数程序员/数据人员实际上使用正确的主谓一致性来描述数据的品种!(data are/datum is - horcle_buzz
谢谢。我是一名讲师,追求严谨是我的职业特点。请注意,如果您的数据最初是以json文件的形式存在的(看起来是这样),您可以使用pandas.read_json直接将其读入数据框中,最后转换的步骤更加简便,只需使用matches.to_dict(orient='records')即可。 - chthonicdaemon
显示剩余2条评论

1
在你链接的帖子中,根据答案所述:
systolic_sex = dict((e['sid'], e) for e in systolic_sex)
sex = set(e['sid'] for e in sex)

matches = []
for sid,v in systolic_sex.items():
    if sid not in sex: continue
    matches.append(v)

我在进行编辑之前,基于先前的尝试了一下,并且对于所述的问题起到了作用。然而,我意识到我的第一组应该被合并为systolic_sex。我已相应地编辑了原始帖子。 - horcle_buzz
明白了,有点儿模糊,但我知道该怎么做。感谢让我重新回到正轨,并教授一些新的Python技巧!(^_^) - horcle_buzz

1
>>> uniq=set(e['sid'] for e in sex) 
>>> filter(lambda d: d['sid'] in uniq, systolic_sex)
[{'attribute': u'bp', 'sid': 6L, 'value_s': u'133', 'value_d': 133.0},        
 {'attribute': u'bp', 'sid': 17L, 'value_s': u'127', 'value_d': 127.0},  
 {'attribute': u'bp', 'sid': 42L, 'value_s': u'120', 'value_d': 120.0}, 
 {'attribute': u'SEX', 'sid': 6L, 'value_s': u'M', 'value_d': 0.0}, 
 {'attribute': u'SEX', 'sid': 17L, 'value_s': u'M', 'value_d': 0.0}, 
 {'attribute': u'SEX', 'sid': 42L, 'value_s': u'M', 'value_d': 0.0}]

1
这是其中一种情况,列表推导式既短又漂亮:[d for d in systolic_sex if d['sid'] in uniq] - chthonicdaemon
我刚刚在比我在示例中提供的更一般化的数据集上进行了测试(具有关键字“sid”的任意重复值,但具有两个以上非重复值的关键字“attribute”),并且结果符合预期。Pandas解决方案很可能需要进行一些微调,因为它给出了与@inspectorG4dget的解决方案相同的结果,而后者对于更一般的情况需要进行一些微调。 - horcle_buzz
为了达到一般情况,Pandas 是使用建议中最容易的。 - horcle_buzz

1
我最终采用了以下方式(根据@chtohnicdaemon的建议):
import pandas
#-----> code snipped here
#----->
# iterate over record sets returned by SQLAlchemy to populate list
    for result in query_right:
        data = {'sid': result.patient_sid,
                'value_s': result.string_value,
                'value_d': result.double_value,
                'attribute': result.attribute_value}

                result_right.append(data)

    for result in left_child:
        data = {'sid': result.patient_sid,
                'value_s': result.string_value,
                'value_d': result.double_value,
                'attribute': result.attribute_value}

                result_left.append(data)

# convert list of dictionaries to data frames
right = pandas.DataFrame(right_result)
left = pandas.DataFrame(left_result)

# get matches
matches_right  = right[right.sid.isin(left.sid)]
matches_left  = left[left.sid.isin(right.sid)]

# combine matched sets into single set
frames = [matches_right,matches_left]

# concatenate data, drop duplicates and convert back to a list of dictionaries
result = pd.concat(frames).drop_duplicates().to_dict(orient='records')

运行得非常好!


1
drop_duplicates 不支持原地操作,所以该行代码在这里实际上并没有起到任何作用。在“获取匹配项”区域中也不需要额外调用 pandas.Dataframe。你可以使用pandas.concat(frames).drop_duplicates().to_dict(orient='records')来代替从“连接数据集”到后面的所有部分。 - chthonicdaemon
去重函数在Python解释器中运行正常(我还没有在我的代码中测试过)。除此之外,我想多余的数据框确实是冗余的,否则我很喜欢这一行代码。谢谢! - horcle_buzz
1
你可能已经看到了结果,但它并没有被存储。Pandas 的一些函数可以直接在原数据上进行操作。 - chthonicdaemon
1
我看到你正在从数据库中读取数据。你可以直接使用pandas.read_sql来完成。 - chthonicdaemon
太棒了!看起来我可以按照读取 pandas.read_sql(query_right.statement, query_right.session.bind) 到数据框的方式做我需要的事情... 这将减少一些处理时间,特别是当返回的记录集很大时,因为我不需要遍历它来构建列表。 - horcle_buzz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接