Pandas - Convert Column to Float

The `astype()` method provides the most straightforward approach for converting a pandas column to float when your data is already numeric or cleanly formatted.

Key Insights

  • Converting columns to float in pandas requires handling non-numeric values, missing data, and errors to avoid runtime failures
  • Use astype() for clean data, pd.to_numeric() with error handling for messy data, and apply() for custom conversion logic
  • Performance matters at scale—vectorized operations outperform iterative approaches by orders of magnitude, especially on datasets with millions of rows

Basic Conversion with astype()

The astype() method provides the most straightforward approach for converting a pandas column to float when your data is already numeric or cleanly formatted.

import pandas as pd
import numpy as np

# Create sample DataFrame
df = pd.DataFrame({
    'price': ['10.5', '20.3', '15.7', '8.9'],
    'quantity': ['100', '200', '150', '75']
})

# Convert single column to float
df['price'] = df['price'].astype(float)

# Convert multiple columns
df[['price', 'quantity']] = df[['price', 'quantity']].astype(float)

print(df.dtypes)
# price       float64
# quantity    float64

This method works efficiently but raises a ValueError if it encounters non-numeric values. For production code with untrusted data sources, you need more robust error handling.

Handling Non-Numeric Values with pd.to_numeric()

Real-world data rarely arrives clean. The pd.to_numeric() function provides three error-handling strategies: ‘raise’ (default), ‘coerce’, and ‘ignore’.

# Messy data with non-numeric values
df = pd.DataFrame({
    'revenue': ['1000.50', '2500.75', 'N/A', '3200.00', 'NULL', '1500.25'],
    'cost': ['500', '1200', '1500', 'error', '800', '600']
})

# Coerce errors to NaN
df['revenue'] = pd.to_numeric(df['revenue'], errors='coerce')
df['cost'] = pd.to_numeric(df['cost'], errors='coerce')

print(df)
#    revenue    cost
# 0  1000.50   500.0
# 1  2500.75  1200.0
# 2      NaN  1500.0
# 3  3200.00     NaN
# 4      NaN   800.0
# 5  1500.25   600.0

# Check for conversion issues
print(f"Revenue NaN count: {df['revenue'].isna().sum()}")
print(f"Cost NaN count: {df['cost'].isna().sum()}")

The errors='coerce' parameter converts unparseable values to NaN, allowing you to identify and handle problematic data downstream. Use errors='ignore' to leave the column unchanged if conversion fails entirely.

Cleaning Data Before Conversion

Often you need to strip formatting characters before conversion. Common culprits include currency symbols, commas, percentage signs, and whitespace.

df = pd.DataFrame({
    'price': ['$1,234.56', '$2,345.67', '$3,456.78'],
    'discount': ['15%', '20%', '10%'],
    'tax_rate': [' 8.5 ', ' 9.0 ', ' 7.25']
})

# Remove currency symbols and commas
df['price'] = df['price'].str.replace('$', '', regex=False).str.replace(',', '', regex=False).astype(float)

# Remove percentage signs and divide by 100
df['discount'] = df['discount'].str.rstrip('%').astype(float) / 100

# Strip whitespace
df['tax_rate'] = df['tax_rate'].str.strip().astype(float)

print(df)
#       price  discount  tax_rate
# 0  1234.56      0.15      8.50
# 1  2345.67      0.20      9.00
# 2  3456.78      0.10      7.25

print(df.dtypes)
# price       float64
# discount    float64
# tax_rate    float64

Chain string methods to handle multiple formatting issues. The regex=False parameter in str.replace() treats the pattern as a literal string, improving performance for simple replacements.

Converting Entire DataFrames

When working with CSV imports or database extracts, you often need to convert multiple columns simultaneously.

# Sample data with mixed types
df = pd.DataFrame({
    'id': ['1', '2', '3'],
    'price': ['10.5', '20.3', '15.7'],
    'quantity': ['100', '200', '150'],
    'name': ['Product A', 'Product B', 'Product C']
})

# Specify dtypes during read (best practice)
# df = pd.read_csv('data.csv', dtype={'price': float, 'quantity': float})

# Convert multiple columns after creation
numeric_columns = ['price', 'quantity']
df[numeric_columns] = df[numeric_columns].apply(pd.to_numeric, errors='coerce')

# Alternative: convert all possible columns
df = df.apply(pd.to_numeric, errors='ignore')

print(df.dtypes)
# id           int64    # automatically converted
# price      float64
# quantity   float64
# name        object    # remained as string

The apply(pd.to_numeric, errors='ignore') pattern attempts to convert every column, leaving non-numeric columns unchanged. This works well for exploratory analysis but use explicit column specifications in production.

Handling Missing Values

Decide how to handle NaN values before or after conversion based on your business logic.

df = pd.DataFrame({
    'sales': ['1000', None, '2000', np.nan, '3000', ''],
    'returns': ['100', '200', 'invalid', '150', '50', '75']
})

# Convert with coercion
df['sales'] = pd.to_numeric(df['sales'], errors='coerce')
df['returns'] = pd.to_numeric(df['returns'], errors='coerce')

# Strategy 1: Fill with zero
df['sales_filled'] = df['sales'].fillna(0)

# Strategy 2: Fill with mean
df['returns_filled'] = df['returns'].fillna(df['returns'].mean())

# Strategy 3: Forward fill
df['sales_ffill'] = df['sales'].fillna(method='ffill')

# Strategy 4: Drop rows with any NaN
df_clean = df.dropna(subset=['sales', 'returns'])

print(df)
#    sales  returns  sales_filled  returns_filled  sales_ffill
# 0  1000.0    100.0        1000.0          100.00       1000.0
# 1     NaN    200.0           0.0          200.00       1000.0
# 2  2000.0      NaN        2000.0          131.25       2000.0
# 3     NaN    150.0           0.0          150.00       2000.0
# 4  3000.0     50.0        3000.0           50.00       3000.0
# 5     NaN     75.0           0.0           75.00       3000.0

Choose your strategy based on domain requirements. Financial data often uses zero-filling, while scientific data might prefer interpolation or row removal.

Custom Conversion Logic

Complex scenarios require custom parsing logic using apply() with lambda functions or custom functions.

def parse_measurement(value):
    """Parse measurements like '10.5kg', '20.3m', '15L'"""
    if pd.isna(value):
        return np.nan
    
    # Extract numeric part
    numeric_part = ''.join(c for c in str(value) if c.isdigit() or c == '.')
    
    try:
        return float(numeric_part)
    except ValueError:
        return np.nan

df = pd.DataFrame({
    'weight': ['10.5kg', '20.3kg', '15.7kg', 'unknown'],
    'volume': ['100L', '200L', '150L', '75L']
})

# Apply custom function
df['weight_numeric'] = df['weight'].apply(parse_measurement)
df['volume_numeric'] = df['volume'].apply(parse_measurement)

print(df)
#      weight volume  weight_numeric  volume_numeric
# 0    10.5kg   100L            10.5           100.0
# 1    20.3kg   200L            20.3           200.0
# 2    15.7kg   150L            15.7           150.0
# 3   unknown    75L             NaN            75.0

Performance Considerations

For large datasets, conversion performance matters. Vectorized operations significantly outperform iterative approaches.

import time

# Create large dataset
df = pd.DataFrame({
    'value': [str(i + 0.5) for i in range(1000000)]
})

# Method 1: astype (fastest)
start = time.time()
result1 = df['value'].astype(float)
print(f"astype: {time.time() - start:.4f} seconds")

# Method 2: pd.to_numeric (slightly slower, more robust)
start = time.time()
result2 = pd.to_numeric(df['value'])
print(f"pd.to_numeric: {time.time() - start:.4f} seconds")

# Method 3: apply with lambda (slowest, avoid for simple conversions)
start = time.time()
result3 = df['value'].apply(lambda x: float(x))
print(f"apply(lambda): {time.time() - start:.4f} seconds")

# Typical output:
# astype: 0.0234 seconds
# pd.to_numeric: 0.0456 seconds  
# apply(lambda): 2.3421 seconds

Use astype() for clean data where you control the source. Use pd.to_numeric() for external data sources. Reserve apply() for complex custom logic that can’t be vectorized.

Validation After Conversion

Always validate conversions in production pipelines to catch data quality issues early.

df = pd.DataFrame({
    'price': ['10.5', '20.3', 'invalid', '15.7'],
    'quantity': ['100', '200', '150', '75']
})

# Convert with coercion
df['price'] = pd.to_numeric(df['price'], errors='coerce')
df['quantity'] = pd.to_numeric(df['quantity'], errors='coerce')

# Validation checks
def validate_conversion(df, column_name, allow_nan=False):
    if not allow_nan and df[column_name].isna().any():
        nan_count = df[column_name].isna().sum()
        raise ValueError(f"{column_name} has {nan_count} NaN values after conversion")
    
    if df[column_name].dtype not in ['float64', 'float32']:
        raise TypeError(f"{column_name} is not float type: {df[column_name].dtype}")
    
    return True

try:
    validate_conversion(df, 'price', allow_nan=False)
except ValueError as e:
    print(f"Validation failed: {e}")
    # Handle the error: log, alert, or clean the data
    
# Check for infinite values
if np.isinf(df['price']).any():
    print("Warning: infinite values detected")
    df['price'] = df['price'].replace([np.inf, -np.inf], np.nan)

Build validation into your data pipelines to fail fast when source data quality degrades. This prevents downstream calculation errors and makes debugging easier.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.