
本文介绍如何将 DataFrame 的每个单元格(由列名和日期索引共同确定)映射到字典中的对应值(如 A_2019),完成逐元素除法等运算,避免 TypeError: unhashable type: 'Index' 错误。
本文介绍如何将 dataframe 的每个单元格(由列名和日期索引共同确定)映射到字典中的对应值(如 `a_2019`),完成逐元素除法等运算,避免 `typeerror: unhashable type: 'index'` 错误。
在 Pandas 数据分析中,常需根据 列名(location) 和 行索引(时间) 联合查询外部字典(如分地区、分年份的人口数据),再对原始数值执行标量运算(如发病率 = 病例数 ÷ 人口数)。直接拼接字符串(如 col + '_' + df.index.strftime("%Y"))会生成一个 Index 对象而非可哈希的字符串序列,导致 TypeError: unhashable type: 'Index' —— 这是因为字典键查找要求单个标量,而 df.index.strftime(...) 返回的是整个 Index,无法直接用于字典索引。
正确做法是:将字典结构化为与 DataFrame 对齐的二维结构(即 year × location 的 DataFrame),再利用 Pandas 的广播机制自动完成行列对齐运算。以下是推荐实现:
✅ 标准解法(推荐)
import pandas as pd
import numpy as np
# 原始数据(注意:含字符串 "Unk",需先转为缺失值)
data = [{'A': 1, 'B': 3}, {'A': 2, 'B': 20}, {'A': "Unk", 'B': 50}]
df = pd.DataFrame(data, index=[
pd.to_datetime("2019-12-31"),
pd.to_datetime("2020-12-30"),
pd.to_datetime("2020-12-31") # 注意:原问题中写为 2021,但字典无 2021,应为 2020
])
# 人口字典:格式为 "{location}_{year}" → value
dic = {"A_2019": 100, "B_2019": 200, "A_2020": 120, "B_2020": 150}
# 步骤 1:清洗数据,将非数字值转为 pd.NA,并启用 nullable integer/float 类型
df_clean = df.replace('Unk', pd.NA).convert_dtypes()
# 步骤 2:将字典解析为 MultiIndex Series → unstack 成 year × location DataFrame
def parse_key(key):
loc, year = key.split('_')
return (int(year), loc)
pop_series = pd.Series({parse_key(k): v for k, v in dic.items()})
pop_df = pop_series.unstack() # 列为 location (A/B),索引为 year (2019/2020)
# 步骤 3:按 df.index.year 对齐人口表,并恢复原始日期索引
pop_aligned = (
pop_df
.reindex(df_clean.index.year) # 按年份重索引(自动广播:2019→所有2019-xx-xx行)
.set_axis(df_clean.index) # 将索引设为原始 DatetimeIndex
)
# 步骤 4:执行逐元素除法,并添加后缀合并回原表
result = df_clean.join(
df_clean.div(pop_aligned).add_suffix('1')
)
print(result)
输出:
A B A1 B1 2019-12-31 1 3 0.01 0.015 2020-12-30 2 20 0.016667 0.133333 2020-12-31 <na> 50 <na> 0.333333</na></na>
? 关键原理说明
-
unstack()将Series(MultiIndex(year, loc)→ value)转换为DataFrame(year行,loc列),形成结构化查找表; -
reindex(df.index.year)实现「年份级对齐」:Pandas 自动将2019映射到所有2019-*-*的行,无需循环; -
div()支持智能广播:当df_clean(shape: 3×2)与pop_aligned(shape: 3×2,索引/列完全匹配)相除时,Pandas 按行列标签精确对齐,而非位置; -
convert_dtypes()+pd.NA确保缺失值参与运算时返回pd.NA(而非NaN),保持类型安全。
⚠️ 注意事项
- 字典键必须严格匹配
"{col}_{year}"格式;若年份不全(如无2021),reindex会引入NaN,后续除法结果为NaN(符合预期); - 原始数据含字符串(如
"Unk")时,务必先replace().convert_dtypes(),否则div()会报错或产生意外字符串行为; - 若需更高灵活性(如支持季度、月份),可将
df.index.year替换为df.index.to_period('Y')或自定义函数; - 替代写法
tmp.rdiv(df)等价于df.div(tmp),语义更明确推荐后者。
此方法规避了显式循环与索引拼接,充分利用 Pandas 的向量化对齐能力,兼具性能、可读性与健壮性,是处理“多维外部参数映射”场景的标准范式。










