如何使用字段名称变量访问 namedtuple 的字段?
How to access a field of a namedtuple using a variable for the field name?
我可以按如下方式按名称访问命名元组的元素(*):
from collections import namedtuple
Car = namedtuple('Car', 'color mileage')
my_car = Car('red', 100)
print my_car.color
但是如何使用变量来指定我要访问的字段的名称呢?例如
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.
您可以使用getattr
getattr(my_car, field)
'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'。然后使用访问每个元组中的所需值
使用字典中的列名获得的索引。
- 制作字典以查找索引。
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']]
注意:如果使用 index=False
调用 itertuples,请在 enumerate
中使用 start=0
这是一个工作示例,显示了这两种方法和两种方法的时间。
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 访问元组的开销也较低,但这两者都只是猜测。如果有人知道更好,请评论。
我还没有探索列数与行数如何影响计时结果。
访问它们的另一种方法是:
field_idx = my_car._fields.index(field)
my_car[field_idx]
提取字段的索引,然后用它来索引namedtuple。
因为 python 版本 3.6 可以继承自 typing.NamedTuple
import typing as tp
class HistoryItem(tp.NamedTuple):
inp: str
tsb: float
rtn: int
frequency: int = None
def __getitem__(self, item):
if isinstance(item, int):
item = self._fields[item]
return getattr(self, item)
def get(self, item, default=None):
try:
return self[item]
except (KeyError, AttributeError, IndexError):
return default
item = HistoryItem("inp", 10, 10, 10)
print(item[0]) # 'inp'
print(item["inp"]) # 'inp'
使用以下代码
for i,x in enumerate(my_car._fields):
print(x, my_car[i])
我可以按如下方式按名称访问命名元组的元素(*):
from collections import namedtuple
Car = namedtuple('Car', 'color mileage')
my_car = Car('red', 100)
print my_car.color
但是如何使用变量来指定我要访问的字段的名称呢?例如
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.
您可以使用getattr
getattr(my_car, field)
'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'。然后使用访问每个元组中的所需值 使用字典中的列名获得的索引。
- 制作字典以查找索引。
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']]
注意:如果使用 index=False
enumerate
中使用 start=0
这是一个工作示例,显示了这两种方法和两种方法的时间。
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 访问元组的开销也较低,但这两者都只是猜测。如果有人知道更好,请评论。
我还没有探索列数与行数如何影响计时结果。
访问它们的另一种方法是:
field_idx = my_car._fields.index(field)
my_car[field_idx]
提取字段的索引,然后用它来索引namedtuple。
因为 python 版本 3.6 可以继承自 typing.NamedTuple
import typing as tp
class HistoryItem(tp.NamedTuple):
inp: str
tsb: float
rtn: int
frequency: int = None
def __getitem__(self, item):
if isinstance(item, int):
item = self._fields[item]
return getattr(self, item)
def get(self, item, default=None):
try:
return self[item]
except (KeyError, AttributeError, IndexError):
return default
item = HistoryItem("inp", 10, 10, 10)
print(item[0]) # 'inp'
print(item["inp"]) # 'inp'
使用以下代码
for i,x in enumerate(my_car._fields):
print(x, my_car[i])