Pandas astype(): Convert Column Types Safely

Use astype() to cast a Pandas Series or DataFrame to a chosen data type. Inspect the source values first, choose how to handle invalid or missing input, and assign the converted result. A successful cast does not by itself validate business rules.

Create the student DataFrame 🔝

Start with numeric marks and inspect dtypes before conversion. Keep the original table available to compare values and types.

import pandas as pd
import numpy as np

def student_data():
    return pd.DataFrame({
        "NAME": ["Ravi", "Raju", "Alex", "Ron", "King", "Jack"],
        "ID": [1, 2, 3, 4, 5, 6],
        "MATH": [80, 40, 70, 70, 70, 30],
        "ENGLISH": [80, 70, 40, 50, 60, 30]
    })

df = student_data()
print(df.dtypes)
print("MATH:", df["MATH"].dtype)
print("NAME:", df["NAME"].dtype)

Expected output

NAME         str
ID         int64
MATH       int64
ENGLISH    int64
dtype: object
MATH: int64
NAME: str

Compare text concatenation with numeric addition 🔝

Converting marks to text changes how + behaves. The original str conversion appends "2" to each score; numeric addition increases each score by 2. Prefer nullable string when missing values should remain missing.

df = student_data()
print("Text concatenation:")
print(df["MATH"].astype(str) + "2")
print("Numeric addition:")
print(df["MATH"] + 2)
assert (df["MATH"] + 2).tolist() == [82, 42, 72, 72, 72, 32]

Expected output

Text concatenation:
0    802
1    402
2    702
3    702
4    702
5    302
Name: MATH, dtype: str
Numeric addition:
0    82
1    42
2    72
3    72
4    72
5    32
Name: MATH, dtype: int64

Convert one column to int32 🔝

Call astype() on the selected Series and assign the result back to update the table. The method returns a converted object; a call alone does not update the column.

df = student_data()
converted = df["MATH"].astype("int32")
print(converted)
print("Source type:", df["MATH"].dtype)
df["MATH"] = converted
print("Updated type:", df["MATH"].dtype)
assert str(df["MATH"].dtype) == "int32"

Expected output

0    80
1    40
2    70
3    70
4    70
5    30
Name: MATH, dtype: int32
Source type: int64
Updated type: int32

Convert several columns with a dtype mapping 🔝

A dictionary maps column names to their intended dtypes. Columns not included in the mapping retain their types. Assign the returned DataFrame rather than assuming the original changed.

df = student_data()
converted = df.astype({"NAME": "string", "ID": "int32", "MATH": "float64"})
print(converted.dtypes)
print("Source MATH:", df["MATH"].dtype)
assert str(converted["ID"].dtype) == "int32"
assert str(converted["MATH"].dtype) == "float64"

Expected output

NAME        string
ID           int32
MATH       float64
ENGLISH      int64
dtype: object
Source MATH: int64

Cast a compatible numeric table to one dtype 🔝

Use a single dtype for an entire table only when every selected column is compatible. Casting a table containing names to float will fail. Select relevant columns explicitly.

df = student_data()
scores = df[["MATH", "ENGLISH"]].astype("float64")
print(scores.head(2))
print(scores.dtypes)
assert scores.dtypes.eq(np.dtype("float64")).all()

Expected output

   MATH  ENGLISH
0  80.0     80.0
1  40.0     70.0
MATH       float64
ENGLISH    float64
dtype: object

Handle invalid input: raise versus ignore 🔝

The default errors="raise" reports a failed conversion. errors="ignore" suppresses conversion errors but may leave data unconverted; check the resulting dtype. astype() does not accept errors="coerce". Use to_numeric() when invalid numeric text should become missing.

values = pd.Series(["10", "bad", "30"], dtype="string")
try:
    values.astype("int64")
except ValueError:
    print("Conversion rejected invalid numeric text.")
ignored = values.astype("int64", errors="ignore")
print("After ignore:", ignored.tolist())
print("Dtype:", ignored.dtype)
assert ignored.equals(values)

Expected output

Conversion rejected invalid numeric text.
After ignore: ['10', 'bad', '30']
Dtype: string

Parse numeric text before choosing its storage type 🔝

Preserve the source values so you can identify parsing failures. Coercion marks invalid input as missing, but it does not explain why input was invalid. Here the values are whole numbers, so nullable Int64 is appropriate.

raw = pd.Series(["10", "bad", None, "30"], dtype="string")
numeric = pd.to_numeric(raw, errors="coerce")
failed = raw.notna() & numeric.isna()
result = numeric.astype("Int64")
print(result)
print("Invalid inputs:", raw.loc[failed].tolist())
assert result.isna().sum() == 2

Expected output

0      10
1    <NA>
2    <NA>
3      30
dtype: Int64
Invalid inputs: ['bad']

Keep missing integers with Int64 🔝

Plain int64 cannot represent missing values. Nullable Int64 can hold integer values and pd.NA. Filling an unknown quantity with zero changes its meaning, so use fillna() only when that replacement is justified.

values = pd.Series([1.0, float("nan"), 3.0])
try:
    values.astype("int64")
except (ValueError, TypeError):
    print("Plain int64 cannot store a missing value.")
nullable = values.astype("Int64")
print(nullable)
assert nullable.isna().sum() == 1

Expected output

Plain int64 cannot store a missing value.
0       1
1    <NA>
2       3
dtype: Int64

Check fractional values before integer conversion 🔝

Casting finite floating-point values to a NumPy integer dtype truncates toward zero; it does not round. Nullable integer conversion rejects non-integral values. If the domain requires whole units, validate first rather than silently losing fractions.

values = pd.Series([2.9, -2.9, 4.0])
print("Truncated:", values.astype("int64").tolist())
fractional = values.mod(1).ne(0)
print("Values needing a policy:", values.loc[fractional].tolist())
assert fractional.tolist() == [True, True, False]
# Choose and document rounding or rejection before casting.

Expected output

Truncated: [2, -2, 4]
Values needing a policy: [2.9, -2.9]

Preserve identifiers and missing text 🔝

Use string storage for identifiers that contain leading zeros. Casting an already parsed integer to string cannot recover lost zeros. Declare the type during import when the original formatting matters; see read_csv().

codes = pd.Series(["0012", None, "0045"], dtype="object")
text = codes.astype("string")
print(text)
print("Missing:", text.isna().sum())
assert text.iloc[0] == "0012"
assert pd.isna(text.iloc[1])

Expected output

0    0012
1    <NA>
2    0045
dtype: string
Missing: 1

Map Boolean text before casting 🔝

A non-empty string such as "False" is truthy when cast to bool. Define the allowed text labels, normalize them, map to logical values and then use nullable boolean. Review unknown labels.

raw = pd.Series(["Yes", "No", None, "unknown"], dtype="string")
flags = raw.str.strip().str.lower().map({"yes": True, "no": False}).astype("boolean")
print(flags)
print("Unrecognized:", raw.loc[raw.notna() & flags.isna()].tolist())
assert flags.iloc[0] == True and flags.iloc[1] == False

Expected output

0     True
1    False
2     <NA>
3     <NA>
dtype: boolean
Unrecognized: ['unknown']

Convert a label column to category 🔝

Use category for a defined vocabulary, and explicitly define order when comparisons should follow a scale. Validate labels before casting to a restricted category set: unknown labels become missing. Explore categorical data.

labels = pd.Series(["low", "high", "medium", "other"], dtype="string")
category_type = pd.CategoricalDtype(["low", "medium", "high"], ordered=True)
unknown = labels.notna() & ~labels.isin(category_type.categories)
print("Unknown labels:", labels.loc[unknown].tolist())
known = labels.loc[~unknown].astype(category_type)
print(known)
print("Above low:", known.loc[known > "low"].tolist())

Expected output

Unknown labels: ['other']
0       low
1      high
2    medium
dtype: category
Categories (3, str): ['low' < 'medium' < 'high']
Above low: ['high', 'medium']

Use date parsing for date text 🔝

For text dates, to_datetime() provides formats and a parsing-error policy. astype() is a casting tool, not a replacement for an explicit date-parsing workflow. Timezone localization and conversion should use the relevant datetime methods.

raw = pd.Series(["31/01/2026", "bad-date", None], dtype="string")
dates = pd.to_datetime(raw, format="%d/%m/%Y", errors="coerce")
print(dates)
print("Invalid date inputs:", raw.loc[raw.notna() & dates.isna()].tolist())
assert dates.isna().sum() == 2

Expected output

0   2026-01-31
1          NaT
2          NaT
dtype: datetime64[us]
Invalid date inputs: ['bad-date']

Check the range before narrowing integer storage 🔝

A smaller integer dtype has a limited range. Do not rely on casting to detect overflow; validate against the target dtype limits before converting. A smaller dtype is useful only when it can represent every required value.

values = pd.Series([0, 120, 300], dtype="int64")
limits = np.iinfo(np.int8)
fits = values.between(limits.min, limits.max)
print("int8 range:", limits.min, limits.max)
print("Out-of-range values:", values.loc[~fits].tolist())
safe = values.loc[fits].astype("int8")
print("Validated conversion:", safe.tolist())
assert not fits.all()

Expected output

int8 range: -128 127
Out-of-range values: [300]
Validated conversion: [0, 120]

Practical workflow: convert a sales extract 🔝

Keep raw data for audit, normalize text and parse numeric inputs before applying a schema. Quantities must be non-negative whole numbers here. Unknown quantities stay missing. This synthetic sample uses floats for demonstration; define suitable precision rules for production financial calculations.

raw_sales = pd.DataFrame({
    "code": ["0012", "0045", "0070", "0081"],
    "quantity": ["2", "bad", None, "0"],
    "price": ["12.50", "3", "5", "8"],
    "paid": ["yes", "no", "unknown", "yes"]
})
sales = raw_sales.copy()
quantity = pd.to_numeric(sales["quantity"], errors="coerce")
known = quantity.dropna()
assert known.ge(0).all() and known.mod(1).eq(0).all()
sales["quantity"] = quantity
sales["price"] = pd.to_numeric(sales["price"], errors="coerce")
sales["paid"] = sales["paid"].astype("string").str.lower().map({"yes": True, "no": False})
sales = sales.astype({"code": "string", "quantity": "Int64", "price": "Float64", "paid": "boolean"})
sales["revenue"] = sales["quantity"] * sales["price"]
print(sales)
print(sales.dtypes)
assert sales.loc[0, "code"] == "0012"

Expected output

   code  quantity  price   paid  revenue
0  0012         2   12.5   True     25.0
1  0045      <NA>    3.0  False     <NA>
2  0070      <NA>    5.0   <NA>     <NA>
3  0081         0    8.0   True      0.0
code         string
quantity      Int64
price       Float64
paid        boolean
revenue     Float64
dtype: object

Report failed conversions and unknown totals 🔝

Invalid non-missing input and originally missing input are different problems. Report them separately. Sum with min_count=1 to avoid treating an entirely unknown subtotal as zero. See sum() and info().

failed_quantity = raw_sales["quantity"].notna() & sales["quantity"].isna()
print("Invalid quantity codes:", raw_sales.loc[failed_quantity, "code"].tolist())
print("Originally missing quantities:", raw_sales["quantity"].isna().sum())
print("Unknown revenue rows:", sales["revenue"].isna().sum())
print("Known revenue subtotal:", sales["revenue"].sum(min_count=1))
assert sales["revenue"].sum(min_count=1) == 25

Expected output

Invalid quantity codes: ['0045']
Originally missing quantities: 1
Unknown revenue rows: 2
Known revenue subtotal: 25.0

Assign the result and understand copy behavior 🔝

astype() returns a result rather than modifying the source in place. In Pandas 3.0, the copy argument is ignored and deprecated; avoid using it to control conversion semantics. Work with the returned object and assign it explicitly. In older versions, copy behavior can differ.

source = pd.DataFrame({"quantity": [1, 2]})
converted = source.astype({"quantity": "float64"})
converted.loc[0, "quantity"] = 9.5
print("Source values:", source["quantity"].tolist())
print("Converted values:", converted["quantity"].tolist())
assert source["quantity"].tolist() == [1, 2]

Expected output

Source values: [1, 2]
Converted values: [9.5, 2.0]

Common questions and next steps 🔝

Why did the source dtype stay unchanged? Assign the converted result. Can astype coerce invalid text? No; parse with to_numeric() or to_datetime() first. Why does int64 reject missing values? Choose nullable Int64 when missing integers are valid. Why did a decimal disappear? NumPy integer casting truncates; validate fractions first. Why did errors="ignore" leave text? It suppresses failures without guaranteeing conversion. Does convert_dtypes() parse numeric strings? It chooses suitable nullable types from existing values; explicit parsing is still needed. Continue with data cleaning.

Exercise: convert quantities without losing codes 🔝

Create codes "0001", "0002" and "0003" with quantity text "4", "bad" and None. Keep codes as string, parse quantities and store them as nullable Int64. Identify invalid non-missing input and verify that two quantities remain missing.

Exercise solution 🔝

Parsing determines which values are valid numbers; casting establishes the intended storage type.

raw = pd.DataFrame({"code": ["0001", "0002", "0003"], "quantity": ["4", "bad", None]})
clean = raw.copy()
clean["quantity"] = pd.to_numeric(clean["quantity"], errors="coerce")
failed = raw["quantity"].notna() & clean["quantity"].isna()
clean = clean.astype({"code": "string", "quantity": "Int64"})
print(clean)
print("Invalid codes:", raw.loc[failed, "code"].tolist())
assert clean.loc[0, "code"] == "0001"
assert clean["quantity"].isna().sum() == 2

Expected output

   code  quantity
0  0001         4
1  0002      <NA>
2  0003      <NA>
Invalid codes: ['0002']

Practice in Google Colab 🔝

Open in Google Colab View on GitHub
Run the examples in order, change the source values and inspect conversion results and validation checks. Save your own copy to keep edits. All sample tables are included in the notebook.

Continue with dtypes, select_dtypes(), to_timedelta() and date differences.

Reference: Pandas DataFrame.astype documentation.




Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer