ValueError: could not convert string to float in Scikit-Learn
Transform categorical string features into numerical arrays using OneHotEncoder or OrdinalEncoder within a ColumnTransformer.
Root Cause Analysis
This error occurs when Python tries to train a Scikit-Learn estimator on a feature matrix containing raw text strings that cannot be implicitly converted into floating-point numbers.
1. Scikit-Learn Numeric Input Contract
Scikit-Learn algorithms expect numerical feature matrices X of numeric dtypes (such as float64 or float32). When estimator.fit(X, y) is called, Scikit-Learn converts inputs using check_array(X, dtype=np.float64). If any column contains text strings like 'high', 'France', or 'Male', the conversion raises ValueError: could not convert string to float: 'high'.
2. Common Causes in Tabular Datasets
This error arises when: (1) unencoded categorical columns are passed directly to model.fit(); (2) numeric columns contain currency symbols or commas (e.g. '$1,200'); or (3) header row names are accidentally included as data rows.
3. Resolving Categorical Columns
Apply OneHotEncoder (for nominal data) or OrdinalEncoder (for ordinal rankings) to transform categorical strings into valid numeric feature matrices.
4. Structuring Pipelines with ColumnTransformer
Use ColumnTransformer to apply StandardScaler to numerical columns and OneHotEncoder to categorical columns in a single, leak-free pipeline.
Reproduction Code (MCVE)
import numpy as np
from sklearn.ensemble import RandomForestClassifier
# Feature matrix with unencoded categorical string 'high'
X = np.array([[10.0, 'high'], [20.0, 'low'], [30.0, 'medium']], dtype=object)
y = np.array([0, 1, 0])
# ValueError: could not convert string to float: 'high'
clf = RandomForestClassifier(random_state=42)
clf.fit(X, y)
Solution 1: Transform Categorical Columns with OrdinalEncoder
Encode string columns into numeric integers using OrdinalEncoder before passing features to the estimator.
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import OrdinalEncoder
X_raw = np.array([[10.0, 'high'], [20.0, 'low'], [30.0, 'medium']], dtype=object)
y = np.array([0, 1, 0])
# Encode categorical column (index 1)
encoder = OrdinalEncoder()
X_encoded = X_raw.copy()
X_encoded[:, 1] = encoder.fit_transform(X_raw[:, [1]]).ravel()
X_numeric = X_encoded.astype(float)
clf = RandomForestClassifier(random_state=42)
clf.fit(X_numeric, y)
print('Model fitted successfully. Feature importances:', clf.feature_importances_)
Solution 2: Use ColumnTransformer with OneHotEncoder
Build a modular ColumnTransformer that handles numeric scaling and one-hot encoding simultaneously.
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
df = pd.DataFrame({
'age': [25, 45, 35],
'tier': ['silver', 'gold', 'bronze'],
'target': [0, 1, 0]
})
X = df[['age', 'tier']]
y = df['target']
preprocessor = ColumnTransformer(transformers=[
('num', StandardScaler(), ['age']),
('cat', OneHotEncoder(sparse_output=False), ['tier'])
])
pipeline = Pipeline([
('pre', preprocessor),
('clf', LogisticRegression())
])
pipeline.fit(X, y)
print('Pipeline trained successfully.')
A common bug is calling pd.get_dummies() separately on train and test datasets, which leads to differing column sets if a category is missing in the test split. Always use sklearn.preprocessing.OneHotEncoder(handle_unknown='ignore') inside a Pipeline to ensure robust column alignment. Contrast this ValueError with ValueError: Unknown label type: 'unknown', which occurs on target vector y rather than feature matrix X. Sanitize currency and formatted string columns with .str.replace(',', '').astype(float) before preprocessing.