Data Types & astype
Reviewed & published by Brayan K
A dtype is the internal storage type pandas assigns to each column — int64, float64, object, bool, or datetime64 — and it decides how fast and how correctly your operations run.
Part of the free Pandas course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
Learn to read the .dtypes attribute, convert columns with astype(), handle messy values with pd.to_numeric(errors=), and understand why object columns are the slow ones.
What You'll Learn in This Lesson
- Read every column's type with df.dtypes
- Recognise int64, float64, object, bool, datetime64
- Convert with df["col"].astype(...)
- Coerce bad values with pd.to_numeric(errors="coerce")
- Parse dates with pd.to_datetime
- Why object columns waste memory and run slow
1 Reading the Types — df.dtypes
Every column has exactly one dtype, and df.dtypes lists them all at once. The names you will meet most often are int64 (whole numbers), float64 (decimals, and the home of NaN), bool (True/False), datetime64[ns] (timestamps), and object — the catch-all that usually means text, but is also where pandas dumps any column it could not interpret as something cleaner.
import pandas as pd
df = pd.DataFrame({
"id": [1, 2, 3],
"price": [1.5, 3.0, 0.75],
"name": ["Pen", "Notebook", "Eraser"],
"active": [True, False, True],
})
df.dtypes
# id int64
# price float64
# name object
# active boolimport pandas as pd
df = pd.DataFrame({
"id": [1, 2, 3],
"price": [1.5, 3.0, 0.75],
"name": ["Pen", "Notebook", "Eraser"],
"active": [True, False, True],
})
print(df.dtypes)
mem = df.memory_usage(deep=True)
# The fixed-width columns weigh the same on every machine, so print those...
print(mem.drop("name"))
# ...but "name" holds real Python str objects, and CPython 3.12 made every str
# 8 bytes smaller: this frame reports name=188 on Python 3.11 and name=164 on
# 3.12. Print mem["name"] to see yours. What is true on both is the point of
# the lesson — three short words outweigh three int64s, three float64s and the
# index put together:
print("name (object) is the heaviest column:", mem["name"] == mem.max())
# ✅ Expected output:
# id int64
# price float64
# name object
# active bool
# dtype: object
# Index 132
# id 24
# price 24
# active 3
# dtype: int64
# name (object) is the heaviest column: True2 Converting Cleanly — astype()
astype() is the direct way to change a column's type. Call it on a Series and pass the target type — "int64", "float64", "str", "bool". It is strict: if any value cannot be converted, it raises an error rather than guessing. That strictness is a feature, because it stops bad data from silently slipping through.
df["price"] = df["price"].astype("float64") # already float, no-op
df["id"] = df["id"].astype("str") # 1 -> "1"
# Convert several columns at once with a dict:
df = df.astype({"id": "int64", "active": "bool"})import pandas as pd
df = pd.DataFrame({"id": [1, 2, 3], "score": [88.7, 91.2, 75.9]})
# astype is strict and direct
df["id"] = df["id"].astype("str")
print(df["id"].tolist())
# ['1', '2', '3']
# float -> int TRUNCATES (does not round)
df["score_int"] = df["score"].astype("int")
print(df["score_int"].tolist())
# ✅ Expected output:
# ['1', '2', '3']
# [88, 91, 75]3 Taming Messy Data — pd.to_numeric(errors=)
Real spreadsheets are dirty: a price column might hold "1.50", "3.00", and a rogue "N/A". Calling astype("float") on that explodes. pd.to_numeric() is built for this — its errors= argument lets you decide what happens to values that will not parse. errors="coerce" turns each bad value into NaN so the rest of the column converts successfully.
raw = pd.Series(["1.50", "3.00", "N/A", "0.75"])
pd.to_numeric(raw) # raises ValueError on "N/A"
pd.to_numeric(raw, errors="coerce") # "N/A" -> NaN, rest convert
pd.to_datetime(date_strings) # the same idea for datesimport pandas as pd
raw = pd.Series(["1.50", "3.00", "N/A", "0.75"])
# astype would crash; to_numeric with coerce survives
clean = pd.to_numeric(raw, errors="coerce")
print(clean)
print("bad values:", clean.isna().sum())
# bad values: 1
# ✅ Expected output:
# 0 1.50
# 1 3.00
# 2 NaN
# 3 0.75
# dtype: float64
# bad values: 1import pandas as pd
s = pd.Series(["10", "20", "oops", "40"])
# 🎯 YOUR TURN:
# Convert this messy text Series to numbers, turning "oops" into NaN
# instead of crashing. Fill in the errors= value.
nums = pd.to_numeric(s, errors="___")
print(nums)
# Expected output:
# 0 10.0
# 1 20.0
# 2 NaN
# 3 40.0
# dtype: float64! Common Errors (And How to Fix Them)
❌ "could not convert string to float"
A stray non-numeric value blows up a strict astype:
df["price"].astype("float") # ❌ ValueError on "N/A"✅ Fix: coerce the bad values to NaN:
pd.to_numeric(df["price"], errors="coerce") # ✅❌ "Cannot convert non-finite values" to int
astype("int") refuses a column that contains NaN:
df["count"].astype("int") # ❌ fails because of NaN✅ Fix: fill the gaps first, or use the nullable Int64:
df["count"].fillna(0).astype("int") # ✅
df["count"].astype("Int64") # ✅ nullable integer🎯 Mini Challenge: Fix the Types
A small import came in with everything as text. Repair the dtypes.
- Convert qty to integers
- Coerce price to floats, turning "?" into NaN
- Parse sold_on into real datetimes
- Print the cleaned dtypes
import pandas as pd
df = pd.DataFrame({
"qty": ["3", "5", "2"],
"price": ["1.50", "?", "0.75"],
"sold_on": ["2026-01-04", "2026-01-05", "2026-01-06"],
})
df["qty"] = df["qty"].astype("int")
df["price"] = pd.to_numeric(df["price"], errors="coerce")
df["sold_on"] = pd.to_datetime(df["sold_on"])
print(df.dtypes)
# qty int64
# price float64
# sold_on datetime64[ns]
# dtype: object
print(df["price"].tolist())
# [1.5, nan, 0.75]
# ✅ Expected output:
# qty int64
# price float64
# sold_on datetime64[ns]
# dtype: object
# [1.5, nan, 0.75]❓ Frequently Asked Questions
Lesson complete — your columns have the right types!
You can read df.dtypes, convert cleanly with astype, rescue messy data with pd.to_numeric(errors="coerce") and pd.to_datetime, and you know why object columns drag performance down.
Practice quiz
What does df.dtypes show you?
- The data type of every column
- The number of rows
- The column names only
- The missing values
Answer: The data type of every column. df.dtypes lists each column's storage type at once.
Which dtype is the home of NaN and decimal numbers?
- int64
- bool
- float64
- datetime64
Answer: float64. float64 holds decimals and is where NaN lives.
What happens to an int column the moment a single NaN appears?
- It stays int64
- It becomes bool
- It is deleted
- It becomes float64
Answer: It becomes float64. Classic int64 cannot hold NaN, so the column is promoted to float64.
What does pd.Series([88.7, 91.2, 75.9]).astype('int') produce?
- [89, 91, 76]
- [88, 91, 75]
- An error
- [88.0, 91.0, 75.0]
Answer: [88, 91, 75]. astype('int') truncates toward zero rather than rounding.
How is astype() described compared with to_numeric()?
- Strict, raising an error on bad values
- It silently drops bad rows
- It only works on dates
- It always returns strings
Answer: Strict, raising an error on bad values. astype is strict and raises if any value cannot be converted.
What does errors='coerce' do in pd.to_numeric()?
- Raises a ValueError
- Skips the whole column
- Rounds the values
- Turns unparseable values into NaN
Answer: Turns unparseable values into NaN. errors='coerce' replaces values that cannot be parsed with NaN.
After pd.to_numeric(pd.Series(['1.50','3.00','N/A','0.75']), errors='coerce'), what does .isna().sum() return?
- 1
- 0
- 2
- 3
Answer: 1. Only 'N/A' fails to parse, so exactly one NaN appears.
Which function parses a column of date strings into real datetimes?
- pd.to_int
- pd.parse
- pd.to_datetime
- pd.astype_date
Answer: pd.to_datetime. pd.to_datetime parses common formats into a datetime64 column.
Why are object columns slow?
- They are always sorted
- They store pointers to scattered Python objects instead of a contiguous block
- They are stored on disk
- They are encrypted
Answer: They store pointers to scattered Python objects instead of a contiguous block. Object columns hold pointers to separate Python objects, so operations loop in slow Python.
astype('int') fails on a column containing NaN. A safe fix is:
- Delete the column
- Convert to bool first
- Use a for loop
- fillna(0) first, or use the nullable 'Int64' dtype
Answer: fillna(0) first, or use the nullable 'Int64' dtype. Clean the NaNs with fillna, or use the nullable Int64 dtype that allows missing values.
Continue this course
- Previous: The Index: set_index, reset_index & reindex
- Next: Categorical Data — Save memory and add order with the category dtype and the .cat accessor
- Quick reference: Pandas cheat sheet › Cleaning & Missing Data
Frequently asked questions
Why does a column of numbers show up as 'object'?
If even one value cannot be parsed as a number — a stray '$', a 'N/A' string, a blank — pandas falls back to the object dtype and stores the whole column as Python strings. The fix is to clean the bad values, then convert with astype(int)/astype(float) or pd.to_numeric().
What is the difference between astype and to_numeric?
astype() is a strict, general converter: it tries to coerce the whole column and raises an error if anything cannot be converted. pd.to_numeric() is numeric-specific and adds an errors= argument, so you can turn unparseable values into NaN with errors='coerce' instead of crashing. Use to_numeric for messy real-world data.
Why are object columns slow?
An object column is really an array of pointers to separate Python string objects scattered in memory. Operations loop in slow Python rather than fast vectorised C. Numeric and datetime columns store their values in a compact, contiguous block, so math and comparisons run orders of magnitude faster.
How do I convert a column of date strings to real dates?
Use pd.to_datetime(df['col']). It parses common formats automatically and gives you a datetime64 column, which unlocks .dt accessors, date arithmetic, and proper sorting. Like to_numeric, it accepts errors='coerce' to turn unparseable dates into NaT.