如何基于另一DataFrame的条件为DataFrame应用style.applymap方法
解决DataFrame.style.applymap指定多位置上色的问题
我有两个尺寸相同的3×3 DataFrame:一个是布尔型(存储True/False),另一个是数值型。需求是允许用户输入布尔矩阵的坐标,将对应位置从False改为True(已为True则忽略操作),之后使用style.applymap函数为数值型DataFrame中与布尔矩阵True值对应的元素上色。但在使用subset参数时遇到困难,无法指定多个行索引,运行时出现KeyError: "None of [Index([(1, 2)], dtype='object')] are in the [index]"错误。
原始错误代码示例:
import pandas as pd import numpy as np Occupato = pd.DataFrame(np.zeros(shape = (3, 3))) for i in range(3): Occupato = Occupato.astype({i: bool}) normale = pd.DataFrame(np.random.randint(0,10,size=(3, 3)), columns=list('ABC')) def colore(v, colore): return f"background-color: {colore};" idx = ('Insert coordinates as [x, y]: ') Occupato.iloc[idx[0], idx[1]] normale.style.applymap(colore, colore='blue', subset = (row), [columns])
方案一:在样式函数中直接结合布尔矩阵判断(推荐)
这种方式不需要复杂的subset构造,直接在样式函数内检查当前元素的位置是否在布尔矩阵中为True,逻辑更直观:
import pandas as pd import numpy as np # 初始化布尔型DataFrame(直接指定dtype=bool更简洁) Occupato = pd.DataFrame(np.zeros((3, 3), dtype=bool), columns=list('ABC')) # 初始化数值型DataFrame normale = pd.DataFrame(np.random.randint(0, 10, size=(3, 3)), columns=list('ABC')) def colore(val, bool_df): # 获取当前元素的行、列索引 row_idx, col_idx = val.name[0], val.name[1] # 检查对应布尔矩阵位置是否为True if bool_df.iloc[row_idx, col_idx]: return "background-color: blue;" # 不需要上色则返回空字符串 return "" # 获取用户输入并更新布尔矩阵 coord_input = input("Insert coordinates as x,y: ") x, y = map(int, coord_input.split(',')) # 仅当当前为False时更新(避免重复操作) if not Occupato.iloc[x, y]: Occupato.iloc[x, y] = True # 应用样式,将布尔矩阵传递给样式函数 styled_result = normale.style.applymap(colore, bool_df=Occupato) # 展示结果 styled_result
方案二:正确构造subset参数指定目标位置
如果一定要通过subset参数限定上色范围,需要将布尔矩阵中所有True的位置转换成行索引数组和列索引数组的组合,注意这里的行和列是一一对应的,而非笛卡尔积:
import pandas as pd import numpy as np Occupato = pd.DataFrame(np.zeros((3, 3), dtype=bool), columns=list('ABC')) normale = pd.DataFrame(np.random.randint(0, 10, size=(3, 3)), columns=list('ABC')) def colore(v): return "background-color: blue;" # 获取用户输入并更新布尔矩阵 coord_input = input("Insert coordinates as x,y: ") x, y = map(int, coord_input.split(',')) if not Occupato.iloc[x, y]: Occupato.iloc[x, y] = True # 收集布尔矩阵中所有True的位置 true_rows, true_cols = np.where(Occupato) # 将列索引转换为对应的列名(因为normale的列是'A'/'B'/'C') target_cols = normale.columns[true_cols] # 正确构造subset:(行索引列表, 列名列表) styled_result = normale.style.applymap(colore, subset=(true_rows, target_cols)) styled_result
错误原因解析
之前的代码存在两个核心问题:
- 坐标输入处理错误:仅定义了字符串提示,未通过
input()获取用户输入并转换为整数索引; - subset参数使用错误:
- 语法错误:
subset = (row), [columns]不符合Python语法,正确格式应为subset=(行选择器, 列选择器); - 索引理解错误:若传递
(1,2)作为行选择器,会被当成单个索引值而非行1和行2,需用数组/列表形式传递多个行索引。
- 语法错误:
内容的提问来源于stack exchange,提问作者Carlos Alfonzo
相关产品推荐
相关产品推荐

