【问题标题】:Faster or better way than looping to find data?比循环查找数据更快或更好的方法?
【发布时间】:2017-08-31 19:48:50
【问题描述】:

我有一个 Person 类对象数组,如下所示,thisRate 首先设置为 None:

class Person(object):
    def __init__(self, id, name):
        self.id = id
        self.name = name
        self.thisRate= None

我将大约 21K Person 对象加载到一个数组中,name 未排序。

然后我从包含thisRate 数据的文件中的数据加载了另一个数组,其中大约 13K,name 也没有排序:

person_data = []

# read from file
row['name'] = 'Peter'
row['thisRate'] = '0.12334'

person_data.append(row)

现在有了这两组数组,当name在它们之间匹配时,我会将thisRate从person_data分配到Person.thisRate。

我正在做的是一个循环是这样的:

for person in persons:
    data = None
    try:
        data = next(personData for personData in person_data
                        if personData['name'] == person.name)
    except StopIteration:
        print("No rate for this person: {}".format(person.name))

    if data:
        person.thisRate = float( data['thisRate'] )

这个循环

data = next(personData for personData in person_data
                if personData['name'] == person.name)

运行良好,在我的机器上使用 Python 2.7.13 使用了 21 秒。

我的问题是,有没有更快或更好的方法来用我拥有的 2 个数组实现相同的目标?

【问题讨论】:

  • 当你说数组时,你是指列表吗?
  • 是列表,已在帖子中编辑。

标签: python python-2.7 for-loop


【解决方案1】:

是的。制作从name到thisRate的字典:

nd = {}

with open(<whatever>) as f:
    reader = csv.DictReader(<whatever>):
    for row in reader:
        nd[row['name']] = row['thisRate'] 

现在,使用这本字典对您的 Person 列表进行一次遍历:

for person in persons:
    thisRate = nd.get(person.name, None)
    person.thisRate = thisRate
    if thisRate is None:
        print("No rate for this person: {}".format(person.name))

字典有一个.get 方法,它允许您提供一个默认值,以防密钥不在dict 中。我使用了None(这实际上是默认的默认值),但你可以使用任何你想要的。

这是一个线性时间解决方案。您的解决方案是二次时间,因为您实际上是在做:

for person in persons:
    for data in person_data:
        if data['name'] == person.name:
            person.thisRate = data['thisRate']
            break
    else:
        print("No rate for this person: {}".format(person.name))

只是以一种方式掩盖了生成器表达式中这个基本嵌套的 for 循环(对于生成器表达式来说,这并不是一个很好的用例,你应该只使用一个 for 循环开始,然后你不'不必处理try-catch一个StopIteration

【讨论】:

  • try-catch 是为了让没有数据的人会被报告以供以后采取进一步行动,例如提供此人的缺失率。
  • @StevenYong 可以替换为 else-clause in to your for-loop。
  • 就此而言,next +geneexpr 不需要提高StopIteration。 next 可以通过geneexpr 和一个默认参数,如果没有找到命中:next((personData for personData in person_data if personData['name'] == person.name), None) 这是一个完全有效的用例。
  • @StevenYong 看看我是如何用if thisRate is None 修改for循环的,看看如何处理没有对应名称的情况...
  • @StevenYong:为了记录,next 的使用并没有真正在这里保存任何东西,它不会让你的代码更快。我只是指出,如果不是使用错误算法的情况,next 就可以了;你不需要通过StopIteration来处理它。
猜你喜欢
  • 2022-07-06
  • 2018-02-02
  • 1970-01-01
  • 2019-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-24
  • 1970-01-01
相关资源
最近更新 更多