3.1 Data Structures — Pandas Series & DataFrames
3.1 Pandas Series & DataFrames
pandas is one of the most widely used Python libraries for data manipulation and analysis.
Before cleaning, transforming, visualizing, or analyzing a dataset, it is essential to understand the two fundamental pandas data structures:
- Series
- DataFrame
A good understanding of these structures makes later pandas operations such as filtering, grouping, merging, and data cleaning much easier.
- What pandas is
- Why pandas is used in Data Science
- Series and its structure
- Creating Series
- Series values and index
- Series data types
- Series indexing and slicing
- Series modification
- DataFrames and their structure
- Creating DataFrames
- Rows and columns
- DataFrame index and columns
- DataFrame shape and dimensions
- Selecting columns and rows
- Adding and modifying columns
- Series vs DataFrame
- Practical Data Science examples
1. What Is pandas?
pandas is an open-source Python library designed for working with structured and tabular data.
It provides convenient tools for:
- Reading datasets
- Cleaning data
- Filtering records
- Sorting data
- Grouping data
- Handling missing values
- Combining datasets
- Performing statistical analysis
- Preparing data for visualization and Machine Learning
pandas is built to work naturally with structured data such as spreadsheets and database tables.
2. Importing pandas
pandas is conventionally imported using the alias
pd.
import pandas as pd
Once imported, pandas functionality can be accessed through
pd.
import pandas as pd
data = pd.Series([10, 20, 30])
print(data)
3. The Two Core pandas Data Structures
| Structure | Dimensions | Description |
|---|---|---|
| Series | 1D | A one-dimensional labeled array. |
| DataFrame | 2D | A two-dimensional labeled tabular data structure with rows and columns. |
Series → one-dimensional
DataFrame → two-dimensional
4. What Is a Series?
A Series is a one-dimensional labeled array capable of holding data values along with an associated index.
A Series can be thought of as a single labeled column of data.
import pandas as pd
marks = pd.Series([78, 85, 92, 74])
print(marks)
Output:
0 78
1 85
2 92
3 74
dtype: int64
Notice that pandas automatically creates an index beginning at
0.
5. Understanding the Structure of a Series
0 78
1 85
2 92
3 74
The structure contains two important components:
- Index: Labels identifying each element.
- Values: Actual data stored in the Series.
Conceptually:
Index Value
0 78
1 85
2 92
3 74
6. Accessing Series Values
The .values attribute provides the underlying values.
marks = pd.Series([78, 85, 92, 74])
print(marks.values)
Output:
[78 85 92 74]
The returned object is a NumPy-based array representation of the Series values.
7. Accessing the Series Index
marks = pd.Series([78, 85, 92, 74])
print(marks.index)
Output will describe the index range, for example:
RangeIndex(start=0, stop=4, step=1)
The index identifies the positions or labels associated with the values.
8. Series Data Type
The .dtype attribute identifies the data type of the
values stored in the Series.
marks = pd.Series([78, 85, 92, 74])
print(marks.dtype)
Output:
int64
The exact dtype can depend on the values and platform.
9. Creating a Series with Custom Labels
A Series does not have to use the default numeric index.
Custom labels can be supplied using the index
parameter.
marks = pd.Series(
[78, 85, 92],
index=["Alice", "Brian", "Clara"]
)
print(marks)
Output:
Alice 78
Brian 85
Clara 92
dtype: int64
Now the names are used as labels.
10. Accessing Series Data Using Labels
marks = pd.Series(
[78, 85, 92],
index=["Alice", "Brian", "Clara"]
)
print(marks["Brian"])
Output:
85
The label "Brian" identifies the required value.
11. Creating a Series from a Dictionary
A dictionary is a natural way to create a labeled Series because dictionary keys can become the Series index.
marks = {
"Alice": 78,
"Brian": 85,
"Clara": 92
}
series = pd.Series(marks)
print(series)
Output:
Alice 78
Brian 85
Clara 92
dtype: int64
12. Creating a Series from a Scalar
A scalar value can also be used to create a Series when an index is provided.
series = pd.Series(
100,
index=["A", "B", "C"]
)
print(series)
Output:
A 100
B 100
C 100
dtype: int64
The scalar value is assigned to every supplied index label.
13. Series Indexing
Series values can be accessed using labels or positional indexing.
marks = pd.Series(
[78, 85, 92, 74],
index=["A", "B", "C", "D"]
)
print(marks["C"])
Output:
92
Positional access can be performed using .iloc.
print(marks.iloc[2])
Output:
92
14. Label-Based Selection with .loc
.loc is used primarily for label-based selection.
marks = pd.Series(
[78, 85, 92, 74],
index=["A", "B", "C", "D"]
)
print(marks.loc["C"])
Output:
92
15. Position-Based Selection with .iloc
.iloc uses integer positions.
marks = pd.Series(
[78, 85, 92, 74]
)
print(marks.iloc[1])
Output:
85
.loc → label-based
.iloc → integer-position-based
16. Slicing a Series
A Series can be sliced using .iloc for positional
slicing.
marks = pd.Series(
[78, 85, 92, 74, 88]
)
print(marks.iloc[1:4])
Output:
1 85
2 92
3 74
dtype: int64
As with standard Python slicing, the ending position is excluded.
17. Filtering a Series
Boolean conditions can be used to select values satisfying a condition.
marks = pd.Series([45, 67, 82, 91, 38])
passed = marks[marks >= 50]
print(passed)
Output:
1 67
2 82
3 91
dtype: int64
18. Modifying a Series
Existing values can be modified using their labels or positions.
marks = pd.Series(
[78, 85, 92],
index=["A", "B", "C"]
)
marks["B"] = 90
print(marks)
Output:
A 78
B 90
C 92
dtype: int64
19. Adding a New Series Element
A new label can be assigned a value.
marks = pd.Series(
[78, 85, 92],
index=["A", "B", "C"]
)
marks["D"] = 88
print(marks)
Output:
A 78
B 85
C 92
D 88
dtype: int64
20. Arithmetic Operations on Series
pandas Series support vectorized arithmetic operations.
marks = pd.Series([70, 80, 90])
print(marks + 5)
print(marks * 2)
Output:
0 75
1 85
2 95
dtype: int64
0 140
1 160
2 180
dtype: int64
21. What Is a DataFrame?
A DataFrame is a two-dimensional labeled data structure containing rows and columns.
It is similar to:
- A spreadsheet table
- A database table
- A collection of aligned Series
import pandas as pd
data = {
"Name": ["Alice", "Brian", "Clara"],
"Age": [20, 21, 19],
"Score": [85, 91, 78]
}
df = pd.DataFrame(data)
print(df)
Output:
Name Age Score
0 Alice 20 85
1 Brian 21 91
2 Clara 19 78
22. Understanding a DataFrame
Name Age Score
0 Alice 20 85
1 Brian 21 91
2 Clara 19 78
The DataFrame contains:
- Rows: Individual records.
- Columns: Variables or attributes.
- Index: Labels identifying rows.
- Column labels: Names identifying fields.
23. Creating a DataFrame from a Dictionary
One of the most common ways to create a DataFrame is from a dictionary of lists.
data = {
"Name": ["Alice", "Brian", "Clara"],
"Age": [20, 21, 19],
"Score": [85, 91, 78]
}
df = pd.DataFrame(data)
print(df)
Each dictionary key becomes a column name.
Each list provides the values for that column.
24. Creating a DataFrame from a List of Lists
A DataFrame can also be created from nested lists.
data = [
["Alice", 20, 85],
["Brian", 21, 91],
["Clara", 19, 78]
]
df = pd.DataFrame(
data,
columns=["Name", "Age", "Score"]
)
print(df)
Output:
Name Age Score
0 Alice 20 85
1 Brian 21 91
2 Clara 19 78
25. Creating a DataFrame from Series
Multiple Series can be combined into a DataFrame.
names = pd.Series(
["Alice", "Brian", "Clara"]
)
scores = pd.Series(
[85, 91, 78]
)
df = pd.DataFrame({
"Name": names,
"Score": scores
})
print(df)
26. DataFrame Index
By default, pandas creates a numeric index beginning at zero.
df = pd.DataFrame({
"Name": ["Alice", "Brian", "Clara"],
"Score": [85, 91, 78]
})
print(df.index)
The result describes a default RangeIndex.
27. Creating a DataFrame with a Custom Index
df = pd.DataFrame(
{
"Score": [85, 91, 78],
"Grade": ["A", "A+", "B"]
},
index=["Alice", "Brian", "Clara"]
)
print(df)
Output:
Score Grade
Alice 85 A
Brian 91 A+
Clara 78 B
28. Accessing DataFrame Columns
The .columns attribute returns the column labels.
df = pd.DataFrame({
"Name": ["Alice", "Brian", "Clara"],
"Age": [20, 21, 19],
"Score": [85, 91, 78]
})
print(df.columns)
29. DataFrame Shape
The .shape attribute returns:
(number_of_rows, number_of_columns)
print(df.shape)
For the previous DataFrame:
(3, 3)
This means:
- 3 rows
- 3 columns
30. DataFrame Dimensions
The .ndim attribute returns the number of dimensions.
print(df.ndim)
Output:
2
A DataFrame is therefore a two-dimensional structure.
31. DataFrame Data Types
The .dtypes attribute displays the data type of each
column.
print(df.dtypes)
Example output:
Name object
Age int64
Score int64
dtype: object
The exact dtype can vary depending on the data and pandas/Python environment.
32. Selecting a Single Column
A column can be selected using square brackets.
print(df["Name"])
The result is a Series.
0 Alice
1 Brian
2 Clara
Name: Name, dtype: object
Selecting one column with
df["Name"] normally returns a Series.
33. Selecting Multiple Columns
A list of column names can be supplied to select multiple columns.
result = df[
["Name", "Score"]
]
print(result)
Output:
Name Score
0 Alice 85
1 Brian 91
2 Clara 78
Selecting multiple columns returns a DataFrame.
34. Dot Notation for Columns
In some situations, a column can also be accessed using attribute notation.
print(df.Name)
This can be convenient, but bracket notation is generally more explicit and works for column names containing spaces or names that conflict with DataFrame attributes.
print(df["Name"])
Prefer df["column_name"] when writing reusable
and robust pandas code.
35. Selecting Rows with .iloc
.iloc selects rows using integer positions.
print(df.iloc[0])
This returns the first row as a Series.
Name Alice
Age 20
Score 85
Name: 0, dtype: object
36. Selecting Multiple Rows with .iloc
print(df.iloc[0:2])
Output:
Name Age Score
0 Alice 20 85
1 Brian 21 91
The result is a DataFrame.
37. Selecting Rows with .loc
.loc is used for label-based selection.
df = pd.DataFrame(
{
"Name": ["Alice", "Brian", "Clara"],
"Score": [85, 91, 78]
},
index=["S01", "S02", "S03"]
)
print(df.loc["S02"])
Output:
Name Brian
Score 91
Name: S02, dtype: object
38. Selecting Rows and Columns with .loc
.loc can select both rows and columns simultaneously.
print(
df.loc[
["S01", "S03"],
["Name", "Score"]
]
)
Output:
Name Score
S01 Alice 85
S03 Clara 78
39. Selecting Rows and Columns with .iloc
.iloc can also select both dimensions using integer
positions.
print(
df.iloc[
[0, 2],
[0, 1]
]
)
This selects the first and third rows and the first and second columns.
40. Adding a New Column
A new column can be created by assigning values to a new column name.
df["Passed"] = [
True,
True,
True
]
print(df)
Output:
Name Score Passed
0 Alice 85 True
1 Brian 91 True
2 Clara 78 True
41. Creating a Calculated Column
DataFrame columns support vectorized calculations.
df["Bonus"] = df["Score"] + 5
print(df)
This calculates a bonus-adjusted score for every record.
42. Modifying an Existing Column
df["Score"] = df["Score"] + 2
print(df["Score"])
Every score is increased by 2 using vectorized pandas operations.
43. Removing a Column
A column can be removed using drop().
df = df.drop(
columns=["Bonus"]
)
print(df)
The returned DataFrame does not contain the specified column.
Modern pandas code commonly uses
df.drop(columns=["ColumnName"])
because the intention is explicit.
44. Number of Rows
The built-in len() function returns the number of rows
in a DataFrame.
print(len(df))
Alternatively, the first value of shape represents
the number of rows.
print(df.shape[0])
45. Number of Columns
print(df.shape[1])
Here:
df.shape[0] → number of rows
df.shape[1] → number of columns
46. Copying a DataFrame
When you need an independent copy of a DataFrame, use
.copy().
df_copy = df.copy()
This is useful when transformations should be performed on a separate DataFrame without unintentionally modifying the original object.
47. Series vs DataFrame
| Feature | Series | DataFrame |
|---|---|---|
| Dimensions | 1D | 2D |
| Structure | Labeled array | Rows and columns |
| Typical representation | Single column | Complete table |
| Index | Yes | Yes |
| Column labels | No separate columns | Yes |
| Common constructor | pd.Series() |
pd.DataFrame() |
48. A DataFrame Is Made of Series
Each column of a DataFrame can be viewed as a Series.
df = pd.DataFrame({
"Name": ["Alice", "Brian", "Clara"],
"Score": [85, 91, 78]
})
name_series = df["Name"]
print(type(name_series))
The result is a pandas Series.
This relationship is important:
A DataFrame can be understood as multiple aligned Series sharing the same row index.
49. Practical Example — Student Dataset
Consider a simple student dataset.
import pandas as pd
students = pd.DataFrame({
"Name": [
"Alice",
"Brian",
"Clara",
"David"
],
"Age": [
20,
21,
19,
22
],
"Math": [
82,
91,
76,
88
],
"Science": [
85,
89,
81,
92
]
})
print(students)
This DataFrame represents a typical tabular dataset.
50. Exploring the Student DataFrame
Select a Column
print(students["Math"])
Calculate Mean
print(students["Math"].mean())
Find Highest Score
print(students["Math"].max())
Filter Students
high_scorers = students[
students["Math"] >= 85
]
print(high_scorers)
This demonstrates how a DataFrame becomes the foundation for practical data analysis.
51. Common Ways to Create a Series
| Source | Syntax |
|---|---|
| List |
pd.Series([10, 20, 30])
|
| Dictionary |
pd.Series({"A": 10, "B": 20})
|
| Scalar |
pd.Series(10, index=["A", "B"])
|
| NumPy array |
pd.Series(np.array([10, 20]))
|
52. Common Ways to Create a DataFrame
| Source | Common Approach |
|---|---|
| Dictionary of lists |
pd.DataFrame(data)
|
| List of lists |
pd.DataFrame(data, columns=...)
|
| Dictionary of Series |
pd.DataFrame({"A": series_a})
|
| NumPy array |
pd.DataFrame(array)
|
53. Important Series & DataFrame Attributes
| Attribute | Purpose |
|---|---|
.index |
Returns row/index labels. |
.values |
Provides underlying values. |
.dtype |
Data type of a Series. |
.dtypes |
Data types of DataFrame columns. |
.columns |
DataFrame column labels. |
.shape |
Number of rows and columns. |
.ndim |
Number of dimensions. |
54. Examination Questions
Multiple Choice Questions
Q1. Which pandas data structure is one-dimensional?
- DataFrame
- Series
- Table
- Matrix
Answer: B — Series
Q2. Which function is used to create a pandas DataFrame?
pd.Table()pd.DataFrame()pd.Frame()pd.Dataset()
Answer: B — pd.DataFrame()
Q3. Which attribute returns the dimensions of a DataFrame?
.size.dimension.shape.form
Answer: C — .shape
Q4. What is returned by df["Name"]?
- DataFrame
- Series
- List
- Dictionary
Answer: B — Series
Q5. Which accessor is primarily used for label-based selection?
.iloc.loc.index.label
Answer: B — .loc
Q6. Which accessor is used for integer-position-based selection?
.loc.iloc.position.integer
Answer: B — .iloc
Q7. What does df.shape return?
- Only the number of rows
- Only the number of columns
- Rows and columns
- Data types
Answer: C — Rows and columns
Short Answer Questions
- Define a pandas Series.
- Define a pandas DataFrame.
- Differentiate between Series and DataFrame.
- What is the purpose of the index in pandas?
-
Differentiate between
.locand.iloc. - What is returned when a single DataFrame column is selected?
-
What does the
.shapeattribute represent? - How can a new column be added to a DataFrame?
55. Pandas Interview Questions
Q1. What is the difference between a Series and a DataFrame?
View Answer
A Series is a one-dimensional labeled array, whereas a DataFrame is a two-dimensional labeled tabular structure consisting of rows and columns.
Q2. Can a DataFrame contain columns with different data types?
View Answer
Yes. Different DataFrame columns can have different data types, such as integers, floating-point values, strings, booleans, dates, and other supported types.
Q3. What is the difference between loc and iloc?
View Answer
loc is primarily label-based, while
iloc is integer-position-based.
Q4. Why is an index important in pandas?
View Answer
The index provides labels for Series elements or DataFrame rows and is used for selection, alignment, and many data manipulation operations.
Q5. How do you select multiple columns from a DataFrame?
View Answer
df[["Name", "Score"]]
A list of column names is supplied inside the brackets.
Q6. What does df.shape return?
View Answer
It returns a tuple containing the number of rows and
columns in the form
(rows, columns).
56. Practical Lab Exercise
Build a Student DataFrame
Create a pandas DataFrame containing at least five students with the following fields:
- Name
- Age
- Math
- Science
- English
Then perform the following tasks:
- Display the complete DataFrame.
- Display its shape.
- Display its column names.
- Display its data types.
- Select only the Math column.
- Select Math and Science columns.
- Select the first three rows using
.iloc. - Select a row using
.loc. - Calculate the average Math score.
- Add a Total column.
- Add an Average column.
- Filter students whose average is at least 80.
57. Mini Project — Student Performance Data Structure
Build a small student performance dataset using pandas.
import pandas as pd
students = pd.DataFrame({
"Name": [
"Alice",
"Brian",
"Clara",
"David",
"Emma"
],
"Math": [
82,
91,
76,
88,
95
],
"Science": [
85,
89,
81,
92,
90
],
"English": [
78,
94,
84,
86,
91
]
})
students["Total"] = (
students["Math"]
+ students["Science"]
+ students["English"]
)
students["Average"] = (
students["Total"] / 3
)
print(students)
This single example introduces an important Data Science workflow:
| Step | Operation |
|---|---|
| 1 | Create structured data. |
| 2 | Store it in a DataFrame. |
| 3 | Select columns. |
| 4 | Perform vectorized calculations. |
| 5 | Create derived columns. |
| 6 | Prepare the dataset for further analysis. |
58. Common Mistakes to Avoid
- Confusing Series and DataFrame: A Series is 1D; a DataFrame is 2D.
-
Confusing loc and iloc:
locuses labels;ilocuses integer positions. -
Using incorrect brackets:
df["Name"]selects one column, whiledf[["Name", "Score"]]selects multiple columns. -
Assuming shape means total number of elements:
shapereturns rows and columns. - Ignoring column names: Column names are essential when selecting and transforming DataFrame data.
- Unexpected index alignment: pandas aligns Series and DataFrame data by labels in many operations, not merely by visual position.
59. Pandas Series & DataFrame Cheatsheet
| Task | Syntax |
|---|---|
| Create Series | pd.Series([1, 2, 3]) |
| Create DataFrame | pd.DataFrame(data) |
| Series values | s.values |
| Index | df.index |
| Columns | df.columns |
| Data types | df.dtypes |
| Shape | df.shape |
| Dimensions | df.ndim |
| Select one column | df["Name"] |
| Select multiple columns | df[["Name", "Score"]] |
| Label selection | df.loc[...] |
| Position selection | df.iloc[...] |
| Add column | df["New"] = values |
| Remove column | df.drop(columns=["New"]) |
| Copy DataFrame | df.copy() |
60. Key Takeaways
- pandas is a major Python library for data manipulation and analysis.
- A Series is a one-dimensional labeled array.
- A DataFrame is a two-dimensional labeled table.
- A DataFrame consists of rows and columns and can be viewed as a collection of aligned Series.
- Every Series and DataFrame has an index.
-
.shapereturns(rows, columns). -
.locis primarily used for label-based selection. -
.ilocis used for integer-position-based selection. -
df["Column"]normally returns a Series. -
df[["Column1", "Column2"]]returns a DataFrame. - Columns can be created or transformed using vectorized operations.
Series represents one labeled dimension of data; DataFrame represents a complete labeled table. Master these two structures before moving into pandas data ingestion, inspection, cleaning, and transformation.