如何用sklearn.preprocessing.OrdinalEncoder处理多列?解决报错
使用OrdinalEncoder处理多列分类数据的错误解决
问题场景
以下代码尝试用OrdinalEncoder处理多列分类数据时触发错误:
import pandas as pd from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.preprocessing import OrdinalEncoder # Define categorical columns and mapping dictionary categorical_cols = ['color', 'shape', 'size'] mapping = {'red': 0, 'green': 1, 'blue': 2, 'circle': 0, 'square': 1, 'triangle': 2, 'small': 0, 'medium': 1, 'large': 2} cols = ['color','size'] # Define ColumnTransformer to preprocess categorical columns preprocessor = ColumnTransformer( transformers=[ ('orlEncdr_with_map', Pipeline(steps=[('orlEnc_with_map', OrdinalEncoder(categories=[list(mapping.keys())], dtype=int))]), cols), ]) # Load sample data data = pd.DataFrame({'color': ['red', 'green', 'blue', 'red'], 'shape': ['circle', 'square', 'triangle', 'triangle'], 'size': ['small', 'medium', 'large', 'medium']}) # Apply preprocessor to data preprocessed_data = preprocessor.fit_transform(data) # View preprocessed data print(preprocessed_data)
错误信息
ValueError Traceback (most recent call last) ~\AppData\Local\Temp\ipykernel_38148\1089712396.py in <module> 18 19 # Apply preprocessor to data ---> 20 preprocessed_data = preprocessor.fit_transform(data) 21 22 # View preprocessed data ~\Anaconda3\lib\site-packages\sklearn\compose\_column_transformer.py in fit_transform(self, X, y) 673 self._validate_remainder(X) 674 ---> 675 result = self._fit_transform(X, y, _fit_transform_one) 676 677 if not result: ~\Anaconda3\lib\site-packages\sklearn\compose\_column_transformer.py in _fit_transform(self, X, y, func, fitted, column_as_strings) 604 ) 605 try: ---> 606 return Parallel(n_jobs=self.n_jobs)( 607 delayed(func)( 608 transformer=clone(trans) if not fitted else trans, ~\Anaconda3\lib\site-packages\joblib\parallel.py in __call__(self, iterable) 1046 # remaining jobs. 1047 self._iterating = False -> 1048 if self.dispatch_one_batch(iterator): 1049 self._iterating = self._original_iterator is not None 1050 ~\Anaconda3\lib\site-packages\joblib\parallel.py in dispatch_one_batch(self, iterator) 862 return False 863 else: -> 864 self._dispatch(tasks) 865 return True 866 ~\Anaconda3\lib\site-packages\joblib\parallel.py in _dispatch(self, batch) 780 with self._lock: 781 job_idx = len(self._jobs) -> 782 job = self._backend.apply_async(batch, callback=cb) 783 # A job can complete so quickly than its callback is 784 # called before we get here, causing self._jobs to ~\Anaconda3\lib\site-packages\joblib\_parallel_backends.py in apply_async(self, func, callback) 206 def apply_async(self, func, callback=None): 207 """Schedule a func to be run""" -> 208 result = ImmediateResult(func) 209 if callback: 210 callback(result) ~\Anaconda3\lib\site-packages\joblib\_parallel_backends.py in __init__(self, batch) 570 # Don't delay the application, to avoid keeping the input 571 # arguments in memory -> 572 self.results = batch() 573 574 def get(self): ~\Anaconda3\lib\site-packages\joblib\parallel.py in __call__(self) 261 # change the default number of processes to -1 262 with parallel_backend(self._backend, n_jobs=self._n_jobs): -> 263 return [func(*args, **kwargs) 264 for func, args, kwargs in self.items] 265 ~\Anaconda3\lib\site-packages\joblib\parallel.py in <listcomp>(.0) 261 # change the default number of processes to -1 262 with parallel_backend(self._backend, n_jobs=self._n_jobs): -> 263 return [func(*args, **kwargs) 264 for func, args, kwargs in self.items] 265 ~\Anaconda3\lib\site-packages\sklearn\utils\fixes.py in __call__(self, *args, **kwargs) 214 def __call__(self, *args, **kwargs): 215 with config_context(**self.config): -> 216 return self.function(*args, **kwargs) 217 218 ~\Anaconda3\lib\site-packages\sklearn\pipeline.py in _fit_transform_one(transformer, X, y, weight, message_clsname, message, **fit_params) 891 with _print_elapsed_time(message_clsname, message): 892 if hasattr(transformer, "fit_transform"): -> 893 res = transformer.fit_transform(X, y, **fit_params) 894 else: 895 res = transformer.fit(X, y, **fit_params).transform(X) ~\Anaconda3\lib\site-packages\sklearn\pipeline.py in fit_transform(self, X, y, **fit_params) 432 fit_params_last_step = fit_params_steps[self.steps[-1][0]] 433 if hasattr(last_step, "fit_transform"): -> 434 return last_step.fit_transform(Xt, y, **fit_params_last_step) 435 else: 436 return last_step.fit(Xt, y, **fit_params_last_step).transform(Xt) ~\Anaconda3\lib\site-packages\sklearn\base.py in fit_transform(self, X, y, **fit_params) 850 if y is None: 851 # fit method of arity 1 (unsupervised transformation) -> 852 return self.fit(X, **fit_params).transform(X) 853 else: 854 # fit method of arity 2 (supervised transformation) ~\Anaconda3\lib\site-packages\sklearn\preprocessing\_encoders.py in fit(self, X, y) 884 885 # `_fit` will only raise an error when `self.handle_unknown="error"` -> 886 self._fit(X, handle_unknown=self.handle_unknown, force_all_finite="allow-nan") 887 888 if self.handle_unknown == "use_encoded_value": ~\Anaconda3\lib\site-packages\sklearn\preprocessing\_encoders.py in _fit(self, X, handle_unknown, force_all_finite) 82 if self.categories != "auto": 83 if len(self.categories) != n_features: -> 84 raise ValueError( 85 "Shape mismatch: if categories is an array," 86 " it has to be of shape (n_features,)." ValueError: Shape mismatch: if categories is an array, it has to be of shape (n_features,).
当把cols = ['size']改为单列时代码可正常运行,请问如何修改代码才能用OrdinalEncoder处理多列数据?
解决方案
错误原因
报错核心是OrdinalEncoder的categories参数格式不匹配:处理N列数据时,categories必须是包含N个列表的数组,每个列表对应一列的分类取值顺序。原代码中categories=[list(mapping.keys())]只传入一个列表,而要处理['color','size']两列,因此触发形状不匹配错误。同时原代码的mapping把不同列的分类混在一起,逻辑上也不合理——color列取值为红/绿/蓝,size列是小/中/大,两者分类集合完全不同,应分开定义。
修改后的代码
import pandas as pd from sklearn.compose import ColumnTransformer from sklearn.preprocessing import OrdinalEncoder # 为每一列单独定义分类顺序 category_mappings = { 'color': ['red', 'green', 'blue'], 'size': ['small', 'medium', 'large'] } cols = ['color', 'size'] # 提取对应列的分类列表,顺序与cols一致 categories_list = [category_mappings[col] for col in cols] # 定义预处理流程 preprocessor = ColumnTransformer( transformers=[ ('ordinal_encoder', OrdinalEncoder(categories=categories_list, dtype=int), cols), ]) # 加载数据 data = pd.DataFrame({ 'color': ['red', 'green', 'blue', 'red'], 'shape': ['circle', 'square', 'triangle', 'triangle'], 'size': ['small', 'medium', 'large', 'medium'] }) # 执行预处理 preprocessed_data = preprocessor.fit_transform(data) print(preprocessed_data)
代码说明
- 拆分分类映射:为每一列单独定义分类取值顺序,避免不同列分类混淆。
- 匹配categories格式:根据待处理列的顺序,生成对应长度的分类列表
categories_list,确保其长度与待处理列数一致。 - 简化流程:仅需
OrdinalEncoder一步处理时,无需嵌套Pipeline,直接在ColumnTransformer中使用即可。
运行结果
输出结果为:
[[0. 0.] [1. 1.] [2. 2.] [0. 1.]]
对应color列红(0)/绿(1)/蓝(2)和size列小(0)/中(1)/大(2)的编码结果。
内容的提问来源于stack exchange,提问作者parvij
相关产品推荐
相关产品推荐

