如何使用字段名称的变量访问namedtuple的字段?

时间:2017-06-19 15:52:30

标签: python namedtuple

我可以通过名称访问命名元组的元素,如下所示(*):

from collections import namedtuple
Car = namedtuple('Car', 'color mileage')
my_car = Car('red', 100)
print my_car.color

但是如何使用变量来指定我想要访问的字段的名称? E.g。

field = 'color'
my_car[field] # doesn't work
my_car.field # doesn't work

我的实际用例是我用for row in data.itertuples()迭代pandas数据帧。我正在对特定列的值进行操作,我希望能够指定要按名称使用的列作为包含此循环的方法的参数。

(*)example taken from here。我使用的是Python 2.7。

4 个答案:

答案 0 :(得分:40)

您可以使用getattr

getattr(my_car, field)

答案 1 :(得分:1)

访问它们的另一种方法可以是:

field_idx = my_car._fields.index(field)
my_car[field_idx]

提取该字段的索引,然后用它来索引namedtuple。

答案 2 :(得分:0)

“ getattr”答案有效,但是还有另一个选项,它稍快一些。

idx = {name: i for i, name in enumerate(list(df), start=1)}
for row in df.itertuples(name=None):
   example_value = row[idx['product_price']]

说明

制作一个字典,将列名映射到行位置。用“ name = None”调用“ itertuples”。然后使用来访问每个元组中的所需值 使用字典中的列名获得的索引。

  1. 制作字典以查找索引。

idx = {name: i for i, name in enumerate(list(df), start=1)}

  1. 使用字典按行元组中的名称访问所需的值
for row in df.itertuples(name=None):
   example_value = row[idx['product_price']]

注意:如果您用start=0来调用itupuples,请在enumerate中使用index=False

这是一个工作示例,展示了这两种方法以及这两种方法的时间。

import numpy as np
import pandas as pd
import timeit

data_length = 3 * 10**5
fake_data = {
    "id_code": list(range(data_length)),
    "letter_code": np.random.choice(list('abcdefgz'), size=data_length),
    "pine_cones": np.random.randint(low=1, high=100, size=data_length),
    "area": np.random.randint(low=1, high=100, size=data_length),
    "temperature": np.random.randint(low=1, high=100, size=data_length),
    "elevation": np.random.randint(low=1, high=100, size=data_length),
}
df = pd.DataFrame(fake_data)


def iter_with_idx():
    result_data = []
    
    idx = {name: i for i, name in enumerate(list(df), start=1)}
    
    for row in df.itertuples(name=None):
        
        row_calc = row[idx['pine_cones']] / row[idx['area']]
        result_data.append(row_calc)
        
    return result_data

      
def iter_with_getaatr():
    
    result_data = []
    for row in df.itertuples():
        row_calc = getattr(row, 'pine_cones') / getattr(row, 'area')
        result_data.append(row_calc)
        
    return result_data
    

dict_idx_method = timeit.timeit(iter_with_idx, number=100)
get_attr_method = timeit.timeit(iter_with_getaatr, number=100)

print(f'Dictionary index Method {dict_idx_method:0.4f} seconds')
print(f'Get attribute method {get_attr_method:0.4f} seconds')

结果:

Dictionary index Method 49.1814 seconds
Get attribute method 80.1912 seconds

我认为差异是由于创建元组和命名元组的开销较低,以及通过索引而不是getattr访问它的开销较低,但这两者都是猜测。如果有人知道更好,请发表评论。

我还没有探索列数与行数如何影响时序结果。

答案 3 :(得分:0)

因为python 3.6版可以从typing.NamedTuple继承

class HistoryItem(tp.NamedTuple):
    inp: str
    tsb: float
    rtn: int
    frequency: int = None

    def __getitem__(self, item):
        if isinstance(item, str):
            return getattr(self, item)
        return tp.NamedTuple.__getitem__(self, item)
        # return super().__getitem__(item)

    def get(self, item, default=None):
        try:
            return self[item]
        except (KeyError, AttributeError):
            return default

然后item[num]item["fld_name"]都可以工作