Pandas .loc()赋值触发TypeError: Must provide strings错误的解决方法
问题
我有一个尺寸为(36484909, 113)的DataFrame model_data_utsset,执行以下代码时,第三、四行的.loc()操作正常,但第五行赋值时返回TypeError: Must provide strings错误。已确认列表feats仅包含字符串列名,且.loc()返回的DataFrame与数组dummy形状一致,使用的Pandas版本为2.2.0。
代码片段:
from sklearn.preprocessing import RobustScaler scaler = RobustScaler() trans = scaler.fit(model_data_utsset.loc[model_data_utsset['cmdb_ci']==ci, feats]) dummy = trans.transform(model_data_utsset.loc[ model_data_utsset['cmdb_ci']==ci, feats]) model_data_utsset.loc[model_data_utsset['cmdb_ci']==ci, feats] = dummy
完整报错回溯:
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) /tmp/ipykernel_3506448/3789644235.py in <module> 5 dummy = trans.transform(model_data_utsset.loc[ model_data_utsset['cmdb_ci']==ci, feats]) ----> 6 model_data_utsset.loc[model_data_utsset['cmdb_ci']==ci, feats] = dummy 7 print(len(model_data_utsset.loc[model_data_utsset['cmdb_ci']==ci, feats])) ~/.local/lib/python3.9/site-packages/pandas/core/indexing.py in __setitem__(self, key, value) 910 911 iloc = self if self.name == "iloc" else self.obj.iloc ---> 912 iloc._setitem_with_indexer(indexer, value, self.name) 913 914 def _validate_key(self, key, axis: AxisInt): ~/.local/lib/python3.9/site-packages/pandas/core/indexing.py in _setitem_with_indexer(self, indexer, value, name) 1944 if take_split_path: 1945 # We have to operate column-wise -> 1946 self._setitem_with_indexer_split_path(indexer, value, name) 1947 else: 1948 self._setitem_single_block(indexer, value, name) ~/.local/lib/python3.9/site-packages/pandas/core/indexing.py in _setitem_with_indexer_split_path(self, indexer, value, name) 1984 # TODO: avoid np.ndim call in case it isn't an ndarray, since 1985 # that will construct an ndarray, which will be wasteful -> 1986 self._setitem_with_indexer_2d_value(indexer, value) 1987 1988 elif len(ilocs) == 1 and lplane_indexer == len(value) and not is_scalar(pi): ~/.local/lib/python3.9/site-packages/pandas/core/indexing.py in _setitem_with_indexer_2d_value(self, indexer, value) 2059 # casting to list so that we do type inference in setitem_single_column 2060 value_col = value_col.tolist() -> 2061 self._setitem_single_column(loc, value_col, pi) 2062 2063 def _setitem_with_indexer_frame_value(self, indexer, value: DataFrame, name: str): ~/.local/lib/python3.9/site-packages/pandas/core/indexing.py in _setitem_single_column(self, loc, value, plane_indexer) 2166 # set value into the column (first attempting to operate inplace, then 2167 # falling back to casting if necessary) -> 2168 self.obj._mgr.column_setitem(loc, plane_indexer, value) 2169 2170 self.obj._clear_item_cache() ~/.local/lib/python3.9/site-packages/pandas/core/internals/managers.py in column_setitem(self, loc, idx, value, inplace_only) 1336 col_mgr.setitem_inplace(idx, value) 1337 else: -> 1338 new_mgr = col_mgr.setitem((idx,), value) 1339 self.iset(loc, new_mgr._block.values, inplace=True) 1340 ~/.local/lib/python3.9/site-packages/pandas/core/internals/managers.py in setitem(self, indexer, value, warn) 414 self = self.copy() 415 -> 416 return self.apply("setitem", indexer=indexer, value=value) 417 418 def diff(self, n: int) -> Self: ~/.local/lib/python3.9/site-packages/pandas/core/internals/managers.py in apply(self, f, align_keys, **kwargs) 362 applied = b.apply(f, **kwargs) 363 else: -> 364 applied = getattr(b, f)(**kwargs) 365 result_blocks = extend_blocks(applied, result_blocks) 366 ~/.local/lib/python3.9/site-packages/pandas/core/internals/blocks.py in setitem(self, indexer, value, using_cow) 2054 2055 try: -> 2056 values[indexer] = value 2057 except (ValueError, TypeError): 2058 if isinstance(self.dtype, IntervalDtype): ~/.local/lib/python3.9/site-packages/pandas/core/arrays/string_.py in __setitem__(self, key, value) 467 value = np.asarray(value, dtype=object) 468 if len(value) and not lib.is_string_array(value, skipna=True): -> 469 raise TypeError("Must provide strings.") 470 471 mask = isna(value) TypeError: Must provide strings.
解决方案
问题根源
报错来自Pandas的StringArray类型校验:你要赋值的feats列是String dtype,但RobustScaler.transform()输出的是数值型numpy数组,直接赋值会触发字符串类型的强制校验,导致报错。
具体解决方法
方法1:提前将目标列转为数值类型
在预处理前,把feats列转为适合数值操作的类型(比如float64),从根源解决类型不匹配问题:
# 先转换列类型 model_data_utsset[feats] = model_data_utsset[feats].astype('float64') # 再执行缩放和赋值 scaler = RobustScaler() trans = scaler.fit(model_data_utsset.loc[model_data_utsset['cmdb_ci']==ci, feats]) dummy = trans.transform(model_data_utsset.loc[model_data_utsset['cmdb_ci']==ci, feats]) model_data_utsset.loc[model_data_utsset['cmdb_ci']==ci, feats] = dummy
方法2:将dummy转为DataFrame后赋值
把numpy数组转为和目标子集索引、列名完全匹配的DataFrame,让Pandas自动处理类型兼容逻辑:
from sklearn.preprocessing import RobustScaler import pandas as pd scaler = RobustScaler() # 先提取目标子集,避免重复写条件 subset = model_data_utsset.loc[model_data_utsset['cmdb_ci']==ci, feats] trans = scaler.fit(subset) dummy = trans.transform(subset) # 转为DataFrame,复用原子集的索引和列名 dummy_df = pd.DataFrame(dummy, index=subset.index, columns=subset.columns) model_data_utsset.loc[model_data_utsset['cmdb_ci']==ci, feats] = dummy_df
方法3:直接赋值底层values(快速绕过类型检查)
如果确认数值类型安全,可以直接赋值numpy数组的底层值,跳过部分Pandas的类型校验:
model_data_utsset.loc[model_data_utsset['cmdb_ci']==ci, feats] = dummy.values
验证建议
操作前可以先检查feats列的 dtype,确认是否为字符串类型:
print(model_data_utsset[feats].dtypes)
如果显示string,优先用方法1转换类型;如果是数值类型仍报错,可检查列中是否混入了字符串格式的无效值。
内容的提问来源于stack exchange,提问作者Mark Pundurs
相关产品推荐
相关产品推荐

