我想将map函数应用于数据帧的列,如下所示:
d = {'one': [1, 2], 'two': [3, 4], 'three':[3,3]}
df = pd.DataFrame(data=d)
recodes = {'one':'A', 'two':'B'}
c = df.columns.map(recodes)
c
#result: Index(['A', 'B', nan], dtype='object')
都很好
现在我想应用另一种字典,值是元组:
recodes2 = {'one':('A','H'), 'two':('AB','HB')}
c = df.columns.map(recodes2)
c
这是行不通的。错误是:
TypeError: Expected tuple, got float
预期输出:
MultiIndex([( 'A', 'H'),
( 'AB', 'HB'),
('_unknown', '_unknown')],
)
问题是蹦跶:
- 一方面为什么会这样? 为什么我没有得到(nan,nan)自动默认值。
- 其次如何避免这个问题
- 以及如何包含一种默认值,例如,对于不属于字典一部分的值,例如元组("_unknown","_unknown")。
- 我寻找一种比获取列的值集更pythonic的答案,并修改字典以包含字典中最初不存在的所有键的默认值。
一个可能的解决方案是:
d = {'one': [1, 2], 'two': [3, 4],'three':[6,7],'four':[8,9]}
df = pd.DataFrame(data=d)
# original recodes dict
recodes3 = {'one':('one','A','H'), 'two':('two','AB','HB')}
# complete recodes dict
missing_values = [col for col in df.columns if col not in recodes3.keys()]
print(missing_values)
recodes_missing = {k:(k,'_unknown','_unknown') for k in missing_values}
#complete the recode dict:
recodes4 = {**recodes3,**recodes_missing}
print(recodes4)
c = df.columns.map(recodes4)
c
但是应该有一种方法可以处理地图熊猫地图函数中的缺失值(我猜)
第一个问题
为什么我没有得到
(nan,nan)
自动默认值。
该函数Index.map(mapper, na_action=None)
。对于值在dict
中不作为键存在时的默认行为,请比较pd.Series.map
的文档:
当
arg
[即Index.map
] 中的== mapper
是一个字典,序列中不在字典中的值(作为键)将转换为NaN
。
因此,这正是该函数的作用:将缺少的键转换为NaN
值。它不"关心"其他键是否转换为tuples
或任何其他类型。随后,当python尝试运行MultiIndex.from_tuples(new_values, names=names)
时,您会遇到更远的错误,而new_values
看起来像[('A','H'), ('AB','HB'), np.nan]
。
第二个和第三个问题
[H]如何避免此问题
[H]如何包含一种默认值
让我们把这两者放在一起,因为避免这个问题的方法确实包括提供一个默认值(各种)。这里有三个选项。第三个是map
的替代品。
- 选项 1
不使用df.columns.map(recodes2)
,创建一个 lambda 函数,并应用dict.get
,如果dict
缺少键,则允许传递默认值:
c = df.columns.map(lambda x: recodes2.get(x,("_unknown","_unknown")))
c
MultiIndex([( 'A', 'H'),
( 'AB', 'HB'),
('_unknown', '_unknown')],
)
- 选项 2
不使用常规dict
,而是使用defaultdict
。这个类似字典的对象将缺少的键添加到dict
(如果缺少默认值),然后该值在map
中使用。例如,我们可以执行以下操作:
from collections import defaultdict
recodes2 = {'one':('A','H'), 'two':('AB','HB')}
def def_value():
return ("_unknown","_unknown")
# create `defaultdict` and add `update` with `recodes2`
my_def_dict = defaultdict(def_value)
my_def_dict.update(recodes2)
print(my_def_dict)
defaultdict(<function def_value at 0x000001F1050F0CA0>,
{'one': ('A', 'H'),
'two': ('AB', 'HB')})
c = df.columns.map(my_def_dict)
print(c)
MultiIndex([( 'A', 'H'),
( 'AB', 'HB'),
('_unknown', '_unknown')],
)
# note that we have now added the key to the dict as well! May be useful, may not be
print(my_def_dict)
defaultdict(<function def_value at 0x000001F105124280>,
{'one': ('A', 'H'),
'two': ('AB', 'HB'),
'three': ('_unknown', '_unknown')})
- 选项 3
除了依赖map
,我们也可以只在tuples
的list
上使用pd.MultiIndex.from_tuples
,用列表推导创建。这样做的好处是我们可以增加准默认值(或:存根名称;例如f'_unknown_{int}'
),以便您最终也会为dict
中不存在的值提供唯一的列名。例如:
# let's add another value, `four` to `df.columns`
d = {'one': [1, 2], 'two': [3, 4], 'three':[3,3], 'four':[4,4]}
df = pd.DataFrame(data=d)
# create a list from which to `pop` the first value consecutively
ints = list(np.repeat([*range(1, len(df.columns)+1)],2))
# [1, 1, 2, 2, 3, 3, 4, 4]
# use list comprehension inside `MultiIndex.from_tuples`
c = pd.MultiIndex.from_tuples([recodes2[col]
if col in recodes2
else (f'_unknown_{ints.pop(0)}',
f'_unknown_{ints.pop(0)}')
for col in df.columns])
c
MultiIndex([( 'A', 'H'),
( 'AB', 'HB'),
('_unknown_1', '_unknown_1'),
('_unknown_2', '_unknown_2')],
)