【问题标题】:how to shorthand a dictionary based search如何简写基于字典的搜索
【发布时间】:2021-10-23 03:04:58
【问题描述】:

在 pandas Dataframe 中,我正在检查 a 变量是否在字典的值中,如果是则返回键。否则返回值。

def getModelType(row):
    for key, value in models.items():
        if row.MODEL in value:
            return key
    return row.MODEL

我正在尝试清理代码并用简写形式编写表达式:

return key for (key,value) in models.items() if row.MODEL in value else row.MODEL

我不断收到语法错误。 可以缩短吗?

【问题讨论】:

  • 你需要一个next 来从迭代器中获取第一个值。
  • 听起来像XY problem。你实际上想做什么(在更大的背景下)?请提供完整的minimal reproducible example
  • 您可以修复您的“速记”,但它不会“清理代码”。恰恰相反。
  • @PierreD 我正在尝试检查数据框中的值是否是一种产品。表中产品由型号描述。我创建了一个包含所有产品类型及其型号的字典,例如:{'Dog': ['Labrador', 'German Sheppard', 'Poodle'], 'Cat':['Scottish fold', 'Persian ']} 如果 row.MODEL 中的值是“拉布拉多”,我希望它返回“狗”。我的问题纯粹是关于我的函数的速记。
  • 你的“速记”很难修复,在任何情况下都不会清理代码也不会加快速度。我正在考虑的一种可能性是return next(key for key, value in models.items() if row.MODEL in value) or row.MODEL,但是一个空迭代器的next()(如果没有匹配项,就是你得到的)引发StopIteration。另一种可能性是return ([key for key, value in models.items() if row.MODEL in value] + [row.MODEL])[0]。它是单行的,但它很丑陋,而且几乎不是“速记”。而且它并不能解决整体解决方案比它需要的速度慢的问题。

标签: python python-3.x conditional-operator shorthand


【解决方案1】:

else 语句必须放在开头。另外,您不想返回生成器,而只返回生成器的第一个元素,因此您需要在其上调用函数next

这应该可行:

return next(key if row.MODEL in value else row.MODEL for key, value in models.items())

【讨论】:

  • 这太急于返回默认值了。
【解决方案2】:

您可能想要进行“反向查找”dict

编辑:加上来自 OP 的额外 cmets,看起来 models dict 具有 {key: list, ...} 结构。

rmodels = {v: k for k, a in models.items() for v in a}

然后,您对 Pandas DataFrame 的操作可以变成例如:

df['MODEL'].apply(lambda v: rmodels.get(v, v))

这将大大加快转换速度。

注意:如果models 中有重复值,您的原始代码将选择值匹配的第一个键(按插入顺序)。相比之下,上面的代码会保留最后一个。

附录:速度

这里是一个快速设置,用于研究这个提议的解决方案的加速。

# 1. setup

n = 10_000
m = 50
c = 20
oovprop = 0.2
models = {model: [f'{model}_{j}' for j in range(c)] for model in [f'model_{i}' for i in range(m)]}
oov = [f'foo_{j}' for j in range(int(m * c * oovprop))]  # oov: "out-of-vocabulary"
alltypes = [v for a in models.values() for v in a] + oov
df = pd.DataFrame({
    'MODEL': np.random.choice(alltypes, size=n),
    'x': np.random.randint(0, 100, n),
})

然后:

# 2. measurements

a = %timeit -o df.apply(getModelType, axis=1)
# 1.53 s ± 3.91 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

b = %timeit -o rmodels = {v: k for k, a in models.items() for v in a}; df['MODEL'].apply(lambda v: rmodels.get(v, v))
# 1.98 ms ± 2.08 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

>>> a.average / b.average
771.67966

有 50 个模型,每个模型 20 种类型,10K 行,包括 20% 的“词汇表外”类型(即,不在任何模型中),应用 getModelType 的速度提升超过 750 倍。

【讨论】:

  • 很好——如果没有看到一些实际示例,很难判断这是错误还是故意。
  • 好的,在阅读OP的说明后修复。
  • 大声笑,在第一杯咖啡之前...已修复。谢谢。
【解决方案3】:

您可以使用 next 的默认值:

return next((k for k, v in models.items() if row.MODEL in v), row.MODEL)

【讨论】:

    猜你喜欢
    • 2017-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-06
    • 2020-10-28
    • 2015-03-29
    • 1970-01-01
    相关资源
    最近更新 更多