Introduction
Common issues with TensorFlow CSV readers stem from incorrect column types, missing default values, header row handling, and mismatched feature columns. TensorFlow's tf.data.experimental.make_csv_dataset and tf.data.experimental.CsvDataset expect precise column specifications. The most frequent mistake is not providing column_defaults that match the CSV's actual data types, causing silent parsing failures or InvalidArgumentError. The modern approach uses tf.data.experimental.make_csv_dataset which handles most configuration automatically.
Basic CSV Reading with tf.data
1import tensorflow as tf
2
3# Method 1: make_csv_dataset (recommended — handles most details)
4dataset = tf.data.experimental.make_csv_dataset(
5 "data.csv",
6 batch_size=32,
7 label_name="target", # Column to use as label
8 num_epochs=1, # How many times to read the file
9 shuffle=True,
10 ignore_errors=True # Skip malformed rows
11)
12
13# Each element is (OrderedDict of features, label tensor)
14for features, label in dataset.take(1):
15 print(features)
16 print(label)
Common Issue 1: Missing column_defaults
1# CSV file: id,name,score,passed
2# 1,Alice,95.5,true
3# 2,Bob,87.0,false
4
5# WRONG — no defaults, TF cannot infer types
6dataset = tf.data.experimental.CsvDataset(
7 "data.csv",
8 record_defaults=[], # Empty — causes error
9 header=True
10)
11
12# RIGHT — provide a default for each column with the correct type
13dataset = tf.data.experimental.CsvDataset(
14 "data.csv",
15 record_defaults=[
16 tf.constant(0, dtype=tf.int32), # id (int)
17 tf.constant("", dtype=tf.string), # name (string)
18 tf.constant(0.0, dtype=tf.float32), # score (float)
19 tf.constant("", dtype=tf.string) # passed (string — bool not supported directly)
20 ],
21 header=True
22)
23
24for row in dataset.take(2):
25 print(row)
record_defaults tells TensorFlow the type of each column AND what value to use if a field is empty. The number of defaults must match the number of columns.
1# WRONG — header=False (default) treats the first row as data
2dataset = tf.data.experimental.CsvDataset(
3 "data.csv",
4 record_defaults=[tf.constant(""), tf.constant("")],
5 # header=False is the default — first row "name,score" becomes data
6)
7
8# RIGHT — skip the header row
9dataset = tf.data.experimental.CsvDataset(
10 "data.csv",
11 record_defaults=[tf.constant(""), tf.constant(0.0)],
12 header=True # Skip the first line
13)
Common Issue 3: Type Mismatch
1# CSV has: "95.5" but you declared int default
2# record_defaults=[tf.constant(0)] # int32 — fails on "95.5"
3
4# Fix: use float default for decimal values
5record_defaults = [tf.constant(0.0)] # float32
6
7# Or let make_csv_dataset infer types
8dataset = tf.data.experimental.make_csv_dataset(
9 "data.csv",
10 batch_size=32,
11 column_defaults=[0, "", 0.0, ""], # Python types work too
12 label_name="target"
13)
If a column contains 95.5 but the default is tf.int32, TensorFlow raises InvalidArgumentError: Field 2 in record is not a valid int32.
Common Issue 4: Selecting Specific Columns
1# Only read certain columns (skip others)
2dataset = tf.data.experimental.CsvDataset(
3 "data.csv",
4 record_defaults=[tf.constant(0.0), tf.constant(0.0)],
5 header=True,
6 select_cols=[2, 3] # Only read columns at index 2 and 3
7)
8
9# With make_csv_dataset — use select_columns by name
10dataset = tf.data.experimental.make_csv_dataset(
11 "data.csv",
12 batch_size=32,
13 select_columns=["score", "target"], # Column names from header
14 label_name="target"
15)
Common Issue 5: Missing Values
1# CSV with missing values:
2# 1,Alice,95.5
3# 2,Bob,
4# 3,,87.0
5
6# Provide meaningful defaults for missing values
7dataset = tf.data.experimental.CsvDataset(
8 "data.csv",
9 record_defaults=[
10 tf.constant(0, dtype=tf.int32),
11 tf.constant("Unknown", dtype=tf.string), # Default for missing names
12 tf.constant(0.0, dtype=tf.float32) # Default for missing scores
13 ],
14 header=True
15)
Empty fields are replaced with the corresponding default value. If no default is provided and a field is empty, TensorFlow raises an error.
1import tensorflow as tf
2
3def build_csv_pipeline(file_pattern, batch_size=32, label_col="target"):
4 # Read all CSV files matching the pattern
5 dataset = tf.data.experimental.make_csv_dataset(
6 file_pattern,
7 batch_size=batch_size,
8 label_name=label_col,
9 num_epochs=1,
10 shuffle=True,
11 shuffle_buffer_size=10000,
12 prefetch_buffer_size=tf.data.AUTOTUNE,
13 num_parallel_reads=tf.data.AUTOTUNE,
14 ignore_errors=True
15 )
16
17 # Preprocessing
18 def preprocess(features, label):
19 # Normalize numeric features
20 features["score"] = (features["score"] - 50.0) / 50.0
21 return features, label
22
23 dataset = dataset.map(preprocess, num_parallel_calls=tf.data.AUTOTUNE)
24 dataset = dataset.prefetch(tf.data.AUTOTUNE)
25
26 return dataset
27
28# Usage
29train_ds = build_csv_pipeline("data/train_*.csv")
30model.fit(train_ds, epochs=10)
Using pandas as an Alternative
1import pandas as pd
2import tensorflow as tf
3
4# For smaller datasets, pandas is simpler
5df = pd.read_csv("data.csv")
6
7# Convert to tf.data.Dataset
8features = df.drop("target", axis=1)
9labels = df["target"]
10
11dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))
12dataset = dataset.shuffle(len(df)).batch(32).prefetch(tf.data.AUTOTUNE)
For datasets that fit in memory, loading with pandas first avoids the complexity of TensorFlow's CSV API.
Debugging CSV Reading Issues
1# Print the first few records to verify parsing
2dataset = tf.data.experimental.make_csv_dataset("data.csv", batch_size=1, num_epochs=1)
3
4for batch in dataset.take(3):
5 features, label = batch if isinstance(batch, tuple) else (batch, None)
6 for key, value in features.items():
7 print(f"{key}: {value.numpy()}")
8 if label is not None:
9 print(f"label: {label.numpy()}")
10 print("---")
Common Pitfalls
Wrong number of record_defaults: The number of defaults must exactly match the number of columns in the CSV. If the CSV has 5 columns but you provide 4 defaults, you get a cryptic error about record format.
Forgetting header=True: The default is header=False. If your CSV has a header row and you do not set this, the header becomes the first data record, causing type errors (e.g., parsing "score" as a float).
Field delimiter mismatch: The default delimiter is comma. For tab-separated files, set field_delim="\t". For semicolons (common in European CSVs), use field_delim=";".
Quoted fields with commas: CSV values containing commas must be quoted ("New York, NY"). TensorFlow's CSV reader handles standard quoting, but non-standard escaping (backslash instead of double-quote) causes parse failures.
Using CsvDataset when make_csv_dataset is simpler: CsvDataset requires manual batching, shuffling, and type specification. make_csv_dataset handles all of this automatically and should be the default choice for most use cases.
Summary
Use tf.data.experimental.make_csv_dataset for most CSV reading — it handles headers, types, batching, and shuffling automatically
Always provide record_defaults with the correct types when using CsvDataset
Set header=True if your CSV has a header row
Use select_columns (by name) or select_cols (by index) to read only needed columns
Use ignore_errors=True to skip malformed rows instead of crashing
For small datasets, loading with pandas and converting to tf.data.Dataset is simpler