Fun with Learning Technology
LearnCoursesQuestionsTracksToolsNewsExplorePractice
Fun with Learning Technology

A new problem, explained clearly, every day.

Subscribe
Learn
  • Lessons
  • Topics
  • News
  • Tools
  • Courses
  • Career tracks
  • Everything
Site
  • About
  • Contact
  • Support
  • Privacy
  • Terms
Get the daily one

One email per new problem. No spam.

Request a tutorial

Requests shape what gets made next.

© 2026 Fun with Learning TechnologyRSS
Home›Courses›Pandas›DataFrames — Creation and Basics

Getting Started

DataFrames — Creation and Basics

A DataFrame is a two-dimensional, size-mutable, and potentially heterogeneous tabular data structure that acts as the primary container for data analysis. It matters because it organizes structured information into rows and columns, enabling vectorized operations that are significantly faster than manual iterations. You reach for it whenever you need to clean, transform, or perform statistical analysis on datasets ranging from small experimental files to massive production logs.

Understanding the DataFrame Architecture

At its core, a DataFrame is an abstraction that maps column names to Series objects, where every column must share the same index. This design is foundational because it ensures data alignment; when you perform an operation, the library automatically aligns data based on index labels rather than positional order. This is why you can perform mathematical operations between two DataFrames even if their rows are in different orders. The DataFrame also manages the memory layout for you, treating columns as contiguous blocks of memory which allows for high-performance vectorized computations. By decoupling the label from the position, it provides a robust interface for data manipulation that remains predictable as your data grows. Understanding that the index is a core component is critical, as it dictates how data is queried and how different datasets merge or join together seamlessly during complex workflows.

import pandas as pd

# Creating a DataFrame from a dictionary
# Keys become column headers; values become the data
data = {'Revenue': [100, 200, 300], 'Costs': [50, 75, 120]}
df = pd.DataFrame(data)

# The index allows for labeled lookups
df.index = ['Q1', 'Q2', 'Q3']
print(df)

Loading Data from External Sources

While creating DataFrames from dictionaries is useful for testing, professional workflows almost exclusively rely on loading external data. The library provides standardized functions that handle the heavy lifting of parsing text-based formats like CSV or structured formats like Excel. The critical reason these functions are preferred over manual file parsing is their ability to infer types automatically. When reading a file, the parser analyzes the initial rows to guess whether a column should be an integer, float, or object, while also handling edge cases like missing values or malformed delimiters. By specifying parameters like 'parse_dates', you can guide this inference process to ensure your time-series data is treated correctly from the moment it is loaded. This early stage of data ingestion is vital because the accuracy of your entire subsequent analytical pipeline depends on the data being represented by the correct internal types.

import pandas as pd

# Loading a CSV file; specifying the index_col ensures data alignment
# 'parse_dates' converts text strings into proper datetime objects
df = pd.read_csv('sales_data.csv', index_col='Date', parse_dates=['Date'])

# Preview the first 5 rows to verify structure
print(df.head())

Basic Inspection and Meta-Analysis

Once your data is loaded, the first step is always to verify its shape and contents. Using the 'info()' method provides a comprehensive snapshot, showing the count of non-null entries, memory usage, and the inferred data types. This is essential because mismatched types often lead to silent errors where numeric calculations fail or strings are treated as distinct categories when they should be numeric. By identifying null values early, you can make informed decisions about whether to drop rows, fill values with a default, or investigate the source of the missing data. The 'describe()' method takes this further by providing statistical summaries such as mean, standard deviation, and quartiles for numeric columns. This systematic exploration prevents you from blindly performing operations on data that might be corrupted or improperly formatted, effectively acting as your first line of defense in quality assurance.

import pandas as pd

# Summary stats for numeric columns
# Provides mean, count, min, max, and percentiles
stats = df.describe()

# Inspect memory usage and non-null counts
# This helps catch missing data or unexpected object types
df.info()

Selecting and Filtering Subsets

Selecting specific data is the most frequent operation you will perform. The primary accessors, 'loc' and 'iloc', represent the fundamental difference between label-based and position-based indexing. 'loc' is preferred when your data has meaningful indices, allowing you to access rows by name, which makes your code more readable and resilient to data reordering. 'iloc' is restricted to integer-based positions, making it ideal for scenarios like 'take the first 10 rows' regardless of what the index labels are named. Boolean indexing is the bridge between these, where you pass a conditional expression that filters the rows based on their values. This works because the comparison operator is applied to the entire series at once, generating a mask that the DataFrame uses to exclude false rows. Mastering this allows you to create highly dynamic and reactive data filtering pipelines that respond to changing conditions in your dataset.

import pandas as pd

# Boolean indexing to filter rows based on conditions
# Select rows where Revenue is greater than 150
high_revenue = df[df['Revenue'] > 150]

# Label-based selection with loc
# 'Q1' refers to the index label, 'Costs' to the column name
print(df.loc['Q1', 'Costs'])

Modifying and Adding Columns

A DataFrame is not static; you will frequently create new insights by combining or transforming existing columns. When you assign a new column using the 'df['new'] = ...' syntax, the library ensures that the calculation is applied to every row simultaneously, which is known as broadcasting. This approach is superior to loops because it utilizes highly optimized C-level code, significantly reducing the overhead. When performing these operations, ensure that the lengths of your new data match the existing index. If the data has a different length or missing index matches, the library will introduce 'NaN' values to maintain structural integrity. This behavior is intentional, designed to highlight where data alignment has failed so you can address it explicitly. By leveraging this behavior, you can transform messy raw data into structured features ready for modeling or reporting in a clean, declarative fashion.

import pandas as pd

# Vectorized calculation: creating a profit margin column
# This operation is applied to the entire column at once
df['Profit'] = df['Revenue'] - df['Costs']

# Adding a constant value to a new column
df['Tax_Rate'] = 0.2
print(df.head())

Key points

  • A DataFrame acts as a collection of aligned Series sharing a common index.
  • The index is a crucial component that dictates how data is accessed and merged.
  • Using vectorized operations is significantly more efficient than manual Python loops.
  • Loading data requires careful attention to inferred data types and date parsing.
  • The 'info()' method is the most reliable way to assess data health and memory usage.
  • Use 'loc' for label-based lookups and 'iloc' for position-based indexing.
  • Boolean masking allows for flexible filtering by applying logical conditions to data.
  • New columns are best added via vectorized expressions to maintain performance.

Common mistakes

  • Mistake: Passing a list of lists to the DataFrame constructor without specifying columns. Why it's wrong: Pandas will assign default integer labels (0, 1, 2) which leads to unreadable code. Fix: Always explicitly define the 'columns' parameter when creating DataFrames from lists.
  • Mistake: Assuming a dictionary of lists will always result in rows corresponding to the list order. Why it's wrong: While standard in modern Pandas, dictionary order was not guaranteed in older Python versions, leading to potential data misalignment. Fix: Use an explicit list of column names or an ordered dictionary if column order is critical.
  • Mistake: Mutating a DataFrame without using .copy() when creating a subset. Why it's wrong: Modifying a slice (view) will often trigger a SettingWithCopyWarning and unintended side effects on the original object. Fix: Use .copy() to ensure you are working on an independent object.
  • Mistake: Confusing the index with a data column. Why it's wrong: The index is metadata used for alignment and lookup, whereas columns are actual data. Performing operations that rely on position often fails if the index is not handled properly. Fix: Use reset_index() if you need to treat the index as a standard column.
  • Mistake: Trying to create a DataFrame from a scalar value without an index. Why it's wrong: A scalar like '5' cannot be broadcast into a DataFrame structure without specifying an index, causing a ValueError. Fix: Provide a list of index labels or the length of the desired rows.

Interview questions

What is a DataFrame in Pandas and how does it differ from a Series?

A DataFrame is the fundamental two-dimensional data structure in Pandas, representing tabular data much like a spreadsheet, with labeled axes for both rows and columns. In contrast, a Series is a one-dimensional array-like object that holds a single column of data. The primary difference is dimensionality: a Series represents a single attribute or variable, whereas a DataFrame acts as a container for multiple Series that share a common index, allowing for complex multi-dimensional data manipulation and analysis.

What are the most common ways to create a DataFrame from scratch?

You can create a DataFrame in several ways, but the most common are using dictionaries or lists of lists. Using a dictionary of lists is highly efficient because keys become column names and values represent row data. For example, 'pd.DataFrame({'A': [1, 2], 'B': [3, 4]})' is standard practice. Alternatively, you can use a list of dictionaries, which is useful when dealing with JSON-like data, or pass NumPy arrays directly when performance for numerical data is critical.

Compare the two approaches: initializing a DataFrame with a list of dictionaries versus a dictionary of lists.

Choosing between these two approaches depends on your data source structure. A dictionary of lists is usually faster to initialize because the column structure is defined upfront, making it ideal for column-oriented data processing. Conversely, a list of dictionaries is more flexible when rows contain missing values for certain columns, as Pandas automatically inserts NaNs for missing keys. While dictionary-of-lists is more performant for creating large static datasets, list-of-dictionaries is safer for inconsistent data imports.

How do you inspect the structure of a newly created DataFrame?

To inspect a DataFrame, I primarily use the '.head()' and '.info()' methods. '.head()' provides a quick glance at the first five rows, which helps verify that the data loaded correctly. '.info()' is even more powerful because it outputs the column names, data types, the number of non-null entries, and the total memory usage. Together, these methods allow me to identify potential data type mismatches or missing values before proceeding with complex analytical tasks.

Explain how to handle custom indexing when creating a DataFrame.

When creating a DataFrame, you can pass a list of labels to the 'index' parameter to override the default integer-based range index. This is essential when your dataset has a logical unique identifier, such as timestamps or user IDs, which makes row selection much more intuitive. By setting an index at creation, you enable powerful features like 'df.loc[]' label-based indexing, which makes code more readable and significantly improves the speed of lookups compared to filtering by integer position.

What happens when you create a DataFrame from a dictionary that has nested data or missing keys?

If a dictionary has missing keys for certain rows, Pandas automatically creates a column with 'NaN' values for the rows where the key was absent, ensuring the DataFrame maintains a rectangular shape. If the data is nested, Pandas does not automatically expand the nested structure; it stores the dictionaries as objects within that cell. To handle this, I would use 'pd.json_normalize()' prior to DataFrame creation to flatten the structure, or use the '.apply(pd.Series)' method afterward to convert nested objects into separate columns.

All Pandas interview questions →

Check yourself

1. When creating a DataFrame from a dictionary of lists, what happens if the lists are of unequal length?

  • A.The DataFrame automatically pads the shorter lists with NaN values
  • B.The DataFrame is created using only the shortest list length
  • C.It raises a ValueError as all arrays must be of the same length
  • D.The DataFrame repeats values in the shorter list to match the longest one
Show answer

C. It raises a ValueError as all arrays must be of the same length
Pandas requires that all lists in a dictionary be of equal length because a DataFrame represents a rectangular structure. Options 1, 2, and 4 are incorrect because Pandas does not implicitly pad, truncate, or repeat data during construction.

2. Which of the following is the most efficient way to access a specific column by name in a DataFrame named 'df'?

  • A.df.loc[:, 'column_name']
  • B.df['column_name']
  • C.df.column_name
  • D.df.iloc[:, 1]
Show answer

B. df['column_name']
Bracket notation df['column_name'] is the standard and safest way to access columns. Using dot notation (option 2) can fail if the column name conflicts with DataFrame methods or contains spaces. Option 0 and 3 are for position/label indexing, which are less direct than key-based access.

3. If you initialize a DataFrame with an empty list, what is the default behavior regarding columns?

  • A.It creates a DataFrame with one empty column labeled '0'
  • B.It creates a DataFrame with no columns and no index
  • C.It creates a DataFrame with one empty row
  • D.It raises an error because the structure is ambiguous
Show answer

B. It creates a DataFrame with no columns and no index
An empty list passed to the constructor creates an empty DataFrame. Option 0 and 2 are wrong because there is no data to infer dimensions. Option 3 is wrong because the constructor successfully initializes an empty container.

4. What is the primary function of the 'index' parameter when creating a DataFrame from a list of dictionaries?

  • A.It specifies the row labels for the data rows
  • B.It renames the columns of the resulting DataFrame
  • C.It sorts the resulting DataFrame by the chosen index
  • D.It forces the DataFrame to only keep matching keys
Show answer

A. It specifies the row labels for the data rows
The 'index' parameter assigns custom labels to rows, whereas columns are derived from dictionary keys. Options 1, 2, and 3 are incorrect because they refer to column naming, sorting, or filtering, which are different processes.

5. Why is it often preferred to use a list of dictionaries over a dictionary of lists when building a DataFrame row-by-row?

  • A.A list of dictionaries is significantly faster for memory allocation
  • B.It allows rows to have different keys, which Pandas will resolve with NaN
  • C.It is the only way to support non-numeric data types
  • D.It automatically transposes the data for better performance
Show answer

B. It allows rows to have different keys, which Pandas will resolve with NaN
Using a list of dictionaries is flexible because it allows missing keys (which Pandas handles by inserting NaN), making it safer for heterogeneous data. Option 0 is wrong as dictionary of lists is usually faster. Options 2 and 3 are irrelevant to the structure.

Take the full Pandas quiz →

← PreviousSeries — Creation and OperationsNext →Reading Data: CSV, Excel, JSON, SQL

Pandas

34 lessons, free to read.

All lessons →

Track your progress

Sign in to mark lessons done, score quizzes and keep notes.

Open in the app