Pandas含重名列时np.where报错是否为预期行为?
问题分析与解答
可正常运行的代码示例
df = pd.DataFrame(np.random.randn(10,3),columns=['A','B','C']) df['D'] = np.nan for i in range(df.shape[1]-1): df['D'] = np.where(df.iloc[:,i] == df.min(axis=1), df.iloc[:,i].shift(-1), df['D']) df
运行失败的代码示例(将列B改为A)
df = pd.DataFrame(np.random.randn(10,3),columns=['A','A','C']) df['D'] = np.nan for i in range(df.shape[1]-1): df['D'] = np.where(df.iloc[:,i] == df.min(axis=1), df.iloc[:,i].shift(-1), df['D']) df
我的问题
即使未通过标签调用列A,出现该报错是否为np.where的预期行为?还是我编写的代码存在错误?
Jupyter中完整错误信息
ValueError Traceback (most recent call last) <ipython-input-184-2ac8fa635230> in <module> 5 df['D'] = np.where(df.iloc[:,i] == df.min(axis=1), 6 df.iloc[:,i].shift(-1), ----> 7 df['D']) 8 df C:\ProgramData\Anaconda3\lib\site-packages\pandas\core\frame.py in __setitem__(self, key, value) 2936 else: 2937 # set column -> 2938 self._set_item(key, value) 2939 2940 def _setitem_slice(self, key, value): C:\ProgramData\Anaconda3\lib\site-packages\pandas\core\frame.py in _set_item(self, key, value) 2999 self._ensure_valid_index(value) 3000 value = self._sanitize_column(key, value) -> 3001 NDFrame._set_item(self, key, value) 3002 3003 # check if we are modifying a copy C:\ProgramData\Anaconda3\lib\site-packages\pandas\core\generic.py in _set_item(self, key, value) 3622 3623 def _set_item(self, key, value) -> None: -> 3624 self._data.set(key, value) 3625 self._clear_item_cache() 3626 C:\ProgramData\Anaconda3\lib\site-packages\pandas\core\internals\managers.py in set(self, item, value) 1084 unfit_val_locs = [] 1085 removed_blknos = [] -> 1086 for blkno, val_locs in libinternals.get_blkno_placements(blknos, group=True): 1087 blk = self.blocks[blkno] 1088 blk_locs = blklocs[val_locs.indexer] pandas\_libs\internals.pyx in get_blkno_placements() pandas\_libs\internals.pyx in pandas._libs.internals.get_blkno_indexers() ValueError: Buffer has wrong number of dimensions (expected 1, got 0)
咱们来拆解这个问题:
首先,这个报错不是np.where的预期行为,问题根源出在pandas对重复列名的内部处理机制上,你的代码逻辑本身没有错误,但忽略了pandas对列名的隐性依赖。
当DataFrame存在重复列名(比如两个'A')时,哪怕你用iloc按位置索引列,返回的Series依然会带有重复的列名'A'。在执行df['D'] = ...赋值操作时,pandas的内部数据块管理器(block manager)在处理数据维度时,会因为重复列名的存在出现混淆——它无法正确识别对应的数据块维度,最终抛出了维度不匹配的错误。
简单来说,pandas本身并不推荐使用重复列名,很多底层操作都会因为同名列出现异常,哪怕你用位置索引避开了直接调用列名,也绕不开内部对列名的校验和处理。
解决方法
- 最根本的方式:避免使用重复列名,给每个列分配唯一的标识,比如把第二个'A'改成'A2';
- 如果必须处理带重复列名的DataFrame,先给列重命名再执行逻辑:
# 重命名重复列 df.columns = ['A1', 'A2', 'C'] # 再执行你的原有代码逻辑 df['D'] = np.nan for i in range(df.shape[1]-1): df['D'] = np.where(df.iloc[:,i] == df.min(axis=1), df.iloc[:,i].shift(-1), df['D'])
内容的提问来源于stack exchange,提问作者Thiago
相关产品推荐
相关产品推荐

