从多个字典列表中有条件地提取数据的大多数 Python 方法

Most Pythonic method of conditionally extracting data from multiple lists of dictionaries

提问人:tautology 提问时间:9/21/2023 更新时间:9/22/2023 访问量:65

问:

我正在尝试使用两个字典列表来构建一个对象,这些字典是从与两个不同数据库的TAP连接构建的。由于数据源的原因,我不能保证任何词典都会包含我需要的信息,所以我选择了一本主要词典,如果信息不在那里,那么我就从第二本词典中提取它。

由于我从两个不同的数据源中提取数据,因此两个源的 TAP 中的字段名称不同,因此我不能只对字典进行交集。

目前我可以让它工作,但我对解决方案不满意:

for result in eeuresults:
    name=result['target_name']
    axes=result['semi_major_axis']
    period=result['period']
    radius=result['radius']

    if math.isnan(period):
        for r in eparesults:
            if r['pl_name'] == name: period=r['pl_orbper']
    if math.isnan(axes):
        for r in eparesults:
            if r['pl_name'] == name: axes=r['pl_orbsmax']        
    if math.isnan(radius):
        for r in eparesults:
            if r['pl_name'] == name: radius=r['pl_radj']

我尝试使用 dictionary.get() 来简化它,但如果该值不在第二个字典列表中,它就会下降。

axes=result.get('semi_major_axis',[r['pl_orbper'] for r in eparesults if r['pl_name']==name][0])
Python 字典

评论

1赞 tnknepp 9/21/2023
原版有什么问题?我实际上可以理解这意味着什么。
0赞 rioV8 9/21/2023
@topsail 它很好 python,1 行 if 语句,内联 if-else 也是 1 行
1赞 rioV8 9/21/2023
@topsail有人写了一个网页并调用了 pythonic 并不意味着它是 pythonic

答:

1赞 rioV8 9/21/2023 #1

删除代码重复

def find_in_eparesults(name, value, epa_key):
  if math.isnan(value):
    for r in eparesults:
      if r['pl_name'] == name:
        value = r[epa_key]
        break
  return value

for result in eeuresults:
    name=result['target_name']
    axes=find_in_eparesults(name, result['semi_major_axis'], 'pl_orbper')
    period=find_in_eparesults(name, result['period'], 'pl_orbsmax')
    radius=find_in_eparesults(name, result['radius'], 'pl_radj')

评论

0赞 Swifty 9/21/2023
我会通过使用字典 {result_key: epa_key} 来进一步推动它
0赞 rioV8 9/21/2023
@Swifty如果他不在其他任何地方使用映射,那么字典就矫枉过正了
2赞 Andrej Kesely 9/21/2023 #2

我建议将 转换为 a 以使搜索更容易:eparesultsdict

eeuresults = [
    {"target_name": "A", "semi_major_axis": 1, "period": 2},
    {"target_name": "B", "period": 3, "radius": 4},
]


eparesults = [
    {"pl_name": "A", "pl_orbper": 1, "pl_orbsmax": 2, "pl_radj": 3},
    {"pl_name": "B", "pl_orbper": 4, "pl_orbsmax": 5, "pl_radj": 6},
]

# This is the important part, convert `eparesults` to a dict:
eparesults_dict = {d["pl_name"]: d for d in eparesults}

for result in eeuresults:
    name = result["target_name"]
    axes = result.get("semi_major_axis", eparesults_dict.get(name, {}).get("pl_orbper"))
    period = result.get("period", eparesults_dict.get(name, {}).get("pl_orbsmax"))
    radius = result.get("radius", eparesults_dict.get(name, {}).get("pl_radj"))

    print(f"{name=} {axes=} {period=} {radius=}")

指纹:

name='A' axes=1 period=2 radius=3
name='B' axes=4 period=3 radius=4

评论

1赞 tautology 9/22/2023
这似乎是一个比我所拥有的更干净的解决方案 - 我喜欢双重使用 .get() 来减少循环和条件!
0赞 tautology 9/22/2023
将此转录到我的脚本中,我还注意到我pl_orbper并pl_orbsmax错误的方式,所以你不小心发现我可能使用了错误的数据(它们两个字段是相关的,所以还不错)!
1赞 Marcin Tamiński 9/22/2023 #3

这样的事情怎么样?

from collections import defaultdict
from math import nan, isnan

class Result(object):
    def __getattr__(self, name):
        return nan
    def __setattr__(self, name, value):
        if isnan(value):
            return
        super().__setattr__(name, value)
    def __iter__(self):
        return iter((self.axes, self.period, self.radius))
    
results = defaultdict(Result)

for d in eeuresults:
    r = results[d['target_name']]
    r.axes   = d['semi_major_axis']
    r.period = d['period']
    r.radius = d['radius']
    
for d in eparesults:
    r = results[d['pl_name']]
    r.axes   = d['pl_orbsmax']
    r.period = d['pl_orbper']
    r.radius = d['pl_radj']

# In case you would need to lookup keys yourself, conversion to dict is needed.
# results = dict(results)
    
for name, (axes, period, radius) in results.items():
    print(f"{name=} {axes=} {period=} {radius=}")

只要您知道它们的密钥,这可以包含任意数量的列表。

此外,如果您想要除 .__setattr__isnan

如果您预计任何字典中缺少密钥,请将 d['key'] 替换为 d.get('key', nan)

评论

0赞 tautology 9/22/2023
这绝对是一种有趣而优雅的解决方式;尽管使用新对象有点矫枉过正,然后我需要从字典中获取我的数据到我当前使用的对象属性。我很喜欢它,但对于这个项目来说有点太多了。
0赞 rioV8 9/22/2023
您的逻辑与 OP 代码不同