Pandas: Getting Started with Series
Last updated: 2026-08-26
A Series is the most fundamental data structure in Pandas — think of it as a "labeled one-dimensional array". A Series is made of two parts: data (a NumPy ndarray) and labels (an Index). It is the Index that lifts a Series beyond an ordinary array and gives each value a "name". This section starts from what a Series really is and walks you through creating, operating on, and using its most common methods.
1. What You'll Learn
- ❶ What a Series really is (ndarray + Index)
- ❷ The Index label system and how to locate values
- ❸ Multiple ways to create a Series
- ❹ Vectorized operations on a Series
- ❺ Common methods (head / tail / describe / value_counts)
2. Alice's Coffee Shop Sales Diary
(1) The Pain: Numbers Without Names
Alice runs a coffee shop. She used NumPy to record her daily sales for a week:
import numpy as np
sales = np.array([320, 280, 350, 410, 390, 520, 480])
# What day is 520? Position 5... but which day?
print(sales[5]) # 520 — but what does this number mean?
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
Is 520 Friday or Saturday? sales[5] doesn't tell you. You have to maintain a separate "position → weekday" lookup table.
(2) The Solution: Series Makes Data Self-Describing
▶ Example: Labeled Sales with Series (Difficulty ⭐)
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
sales = pd.Series(
[320, 280, 350, 410, 390, 520, 480],
index=['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
name='daily_sales'
)
print(sales)
# Mon 320
# Tue 280
# Wed 350
# Thu 410
# Fri 390
# Sat 520
# Sun 480
# Name: daily_sales, dtype: int64
print(sales['Sat']) # 520 — label-based access
print(sales.iloc[5]) # 520 — position-based access
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
A Series binds data and labels together — sales['Sat'] is far more self-explanatory than sales[5].
(3) The Payoff: The Index Is the Data's Name Tag
With Index labels, data becomes self-describing:
- Label-based access:
s['Mon']is clearer thans[0] - Aligned operations: two Series automatically align by label when combined
- Rich semantics: an Index can be dates, strings, or even multi-level labels
3. The Internal Structure of a Series
(1) Two Components: values + index
graph LR
A[Series] --> B[values: NumPy ndarray]
A --> C[index: Index object]
B --> D[320, 280, 350, ...]
C --> E["Mon, Tue, Wed, ..."]
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
| Property | Type | Description |
|---|---|---|
s.values |
np.ndarray | The underlying data array |
s.index |
pd.Index | The row labels |
s.name |
str | The Series name |
s.dtype |
np.dtype | The element data type |
s.shape |
tuple | The shape (one-dimensional, e.g. (7,)) |
s.size |
int | The number of elements |
▶ Example: Inspecting Series Properties (Difficulty ⭐)
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
import numpy as np
s = pd.Series([320, 280, 350], index=['Mon', 'Tue', 'Wed'])
print(f"Type of values: {type(s.values)}") # <class 'numpy.ndarray'>
print(f"Type of index: {type(s.index)}") # <class 'pandas.core.indexes.base.Index'>
print(f"dtype: {s.dtype}") # int64
print(f"shape: {s.shape}") # (3,)
print(f"size: {s.size}") # 3
print(f"name: {s.name}") # None
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
(2) Series vs ndarray Comparison
| Feature | NumPy ndarray | Pandas Series |
|---|---|---|
| Labels | None (position only) | Yes (Index) |
| Mixed types | Not supported | Not recommended but possible (object) |
| Missing values | np.nan (float only) | NaN / pd.NA (full coverage) |
| Operation alignment | By position | By label |
| Name | None | name property |
| Common statistics | np.mean etc. | Method chain like .mean() |
| Under the hood | ndarray | ndarray + Index |
.values, it degrades into a plain ndarray.
4. Multiple Ways to Create a Series
(1) From a List
▶ Example: Creating a Series from a List (Difficulty ⭐)
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
# Default index: 0, 1, 2, ...
s1 = pd.Series([10, 20, 30, 40])
print(s1)
# 0 10
# 1 20
# 2 30
# 3 40
# Custom index
s2 = pd.Series([10, 20, 30, 40], index=['a', 'b', 'c', 'd'])
print(s2['b']) # 20
# With name
s3 = pd.Series([10, 20, 30], name='scores')
print(s3.name) # scores
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
(2) From a Dictionary
▶ Example: Creating a Series from a Dictionary (Difficulty ⭐)
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
# Keys become index, values become data
population = pd.Series({
'Tokyo': 13960000,
'New York': 8336000,
'London': 8982000,
'Paris': 2161000
})
print(population)
# Tokyo 13960000
# New York 8336000
# London 8982000
# Paris 2161000
print(population['London']) # 8982000
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
The advantage of creating from a dictionary: keys automatically become the Index, with no need to specify it manually.
(3) From a NumPy ndarray
▶ Example: Creating a Series from an ndarray (Difficulty ⭐)
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import numpy as np
import pandas as pd
arr = np.array([3.14, 2.72, 1.41, 1.73])
s = pd.Series(arr, index=['pi', 'e', 'sqrt2', 'sqrt3'])
print(s)
# pi 3.14
# e 2.72
# sqrt2 1.41
# sqrt3 1.73
print(f"Underlying type: {type(s.values)}") # numpy.ndarray
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
(4) From a Scalar (Broadcasting)
▶ Example: Creating a Series by Broadcasting a Scalar (Difficulty ⭐)
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
# Broadcast scalar to fill all index positions
s = pd.Series(0, index=['A', 'B', 'C', 'D'])
print(s)
# A 0
# B 0
# C 0
# D 0
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
(5) Comparison of Creation Methods
| Method | Syntax | Index Source | Use Case |
|---|---|---|---|
| List | pd.Series([1,2,3]) |
Default RangeIndex | Quick testing |
| List + index | pd.Series([1,2,3], index=...) |
Manually specified | When you need custom labels |
| Dictionary | pd.Series({'a':1, 'b':2}) |
Dictionary keys | Label-value pairs |
| ndarray | pd.Series(np.array(...)) |
Default or specified | Converting NumPy data |
| Scalar | pd.Series(0, index=...) |
Manually specified | Initialization / filling |
5. Accessing a Series
(1) Label Access vs Position Access
▶ Example: Two Ways to Access (Difficulty ⭐⭐)
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
s = pd.Series([320, 280, 350, 410],
index=['Mon', 'Tue', 'Wed', 'Thu'])
# Label-based access (preferred)
print(s['Tue']) # 280
print(s.loc['Tue']) # 280 — explicit label access
# Position-based access
print(s.iloc[1]) # 280 — explicit position access
# Slice by label (INCLUDES endpoint)
print(s['Mon':'Wed'])
# Mon 320
# Tue 280
# Wed 350
# Slice by position (EXCLUDES endpoint, like Python)
print(s.iloc[0:3])
# Mon 320
# Tue 280
# Wed 350
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
'Mon':'Wed' includes Wed), while position slicing excludes the endpoint (iloc[0:3] does not include position 3). This is the most common point of confusion for Pandas beginners.
(2) Comparison of Access Methods
| Method | Syntax | Endpoint | Recommended For |
|---|---|---|---|
s[label] |
Shorthand | — | A single label |
s.loc[label] |
Explicit label | Includes endpoint | Making intent clear |
s.iloc[pos] |
Explicit position | Excludes endpoint | Indexing by position |
s[[l1,l2]] |
List selection | — | Multiple labels |
6. Operations on a Series
(1) Vectorized Operations
▶ Example: Vectorized Operations on a Series (Difficulty ⭐)
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
sales = pd.Series([320, 280, 350, 410],
index=['Mon', 'Tue', 'Wed', 'Thu'])
# Arithmetic operations
print(sales * 1.1) # 10% price increase
# Mon 352.0
# Tue 308.0
# Wed 385.0
# Thu 451.0
# Comparison operations
print(sales > 300)
# Mon True
# Tue False
# Wed True
# Thu True
# Filter with boolean
print(sales[sales > 300])
# Mon 320
# Wed 350
# Thu 410
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
(2) Index-Aligned Operations
This is one of the most important features of a Series: operations align by Index, not by position.
▶ Example: Index-Aligned Operations (Difficulty ⭐⭐)
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
week1 = pd.Series({'Mon': 320, 'Tue': 280, 'Wed': 350})
week2 = pd.Series({'Tue': 310, 'Wed': 370, 'Thu': 400})
# Auto-align by index, non-matching → NaN
total = week1 + week2
print(total)
# Mon NaN # only in week1
# Tue 590.0 # 280 + 310
# Wed 720.0 # 350 + 370
# Thu NaN # only in week2
# Fill missing with 0 instead of NaN
total_filled = week1.add(week2, fill_value=0)
print(total_filled)
# Mon 320.0
# Tue 590.0
# Wed 720.0
# Thu 400.0
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
.add(fill_value=0) can replace the + operator to specify a fill strategy for missing values.
7. Common Series Methods
(1) Statistics and Description
| Method | Description | NumPy Equivalent |
|---|---|---|
s.mean() |
Mean | np.mean(arr) |
s.median() |
Median | np.median(arr) |
s.std() |
Standard deviation | np.std(arr) |
s.min() / s.max() |
Min / max | np.min() / np.max() |
s.sum() |
Sum | np.sum(arr) |
s.describe() |
Statistical summary | None |
s.value_counts() |
Frequency counts | None |
s.unique() |
Unique values | np.unique(arr) |
▶ Example: Statistical Methods (Difficulty ⭐)
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
scores = pd.Series([85, 92, 78, 95, 88, 92, 78, 85, 90, 92],
name='exam_score')
print(scores.describe())
# count 10.000000
# mean 87.500000
# std 5.976143
# min 78.000000
# 25% 85.000000
# 50% 89.000000
# 75% 92.000000
# max 95.000000
print(scores.value_counts())
# 92 3
# 85 2
# 78 2
# 95 1
# 90 1
# 88 1
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
(2) Inspection and Filtering
▶ Example: head / tail / isin (Difficulty ⭐)
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
s = pd.Series(range(1, 101)) # 1 to 100
print(s.head()) # first 5
print(s.tail(3)) # last 3
# isin: membership test
fruits = pd.Series(['apple', 'banana', 'cherry', 'apple', 'date'])
print(fruits.isin(['apple', 'cherry']))
# 0 True
# 1 False
# 2 True
# 3 True
# 4 False
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
(3) Handling NaN
▶ Example: NaN Handling Methods (Difficulty ⭐)
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
import numpy as np
s = pd.Series([10, np.nan, 30, np.nan, 50])
print(s.isna()) # detect NaN
# 0 False
# 1 True
# 2 False
# 3 True
# 4 False
print(s.notna()) # detect non-NaN
print(s.dropna()) # remove NaN: [10.0, 30.0, 50.0]
print(s.fillna(0)) # fill NaN with 0
print(s.fillna(s.mean())) # fill with mean value
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
8. Complete Example: Coffee Shop Weekly Sales Analysis
▶ Example: End-to-End Coffee Shop Sales Analysis (Difficulty ⭐⭐⭐)
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
import numpy as np
# ============================================
# Comprehensive example: Coffee shop weekly
# sales analysis using Series
# ============================================
# 1. Create sales data with day labels
sales = pd.Series({
'Mon': 320, 'Tue': 280, 'Wed': 350,
'Thu': 410, 'Fri': 390, 'Sat': 520, 'Sun': 480
}, name='daily_sales_USD')
# 2. Basic statistics
print("=== Weekly Sales Report ===")
print(f"Total: ${sales.sum():,}")
print(f"Daily avg: ${sales.mean():.0f}")
print(f"Best day: {sales.idxmax()} (${sales.max():,})")
print(f"Worst day: {sales.idxmin()} (${sales.min():,})")
print(f"Std dev: ${sales.std():.0f}")
# 3. Weekend vs weekday analysis
weekday_avg = sales[['Mon','Tue','Wed','Thu','Fri']].mean()
weekend_avg = sales[['Sat','Sun']].mean()
print(f"\nWeekday avg: ${weekday_avg:.0f}")
print(f"Weekend avg: ${weekend_avg:.0f}")
print(f"Weekend boost: {(weekend_avg/weekday_avg - 1)*100:.1f}%")
# 4. Daily change
change = sales.pct_change() * 100
print(f"\nBiggest daily drop: {change.min():.1f}% on {change.idxmin()}")
print(f"Biggest daily gain: {change.max():.1f}% on {change.idxmax()}")
# 5. Cumulative sales
cum_sales = sales.cumsum()
print(f"\nSales target $2,000 reached on: {cum_sales[cum_sales >= 2000].index[0]}")
# 6. Ranking
print(f"\nSales ranking:\n{sales.rank(ascending=False).astype(int)}")
Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
Expected output (excerpt):
=== Weekly Sales Report ===
Total: $2,750
Daily avg: $393
Best day: Sat ($520)
Worst day: Tue ($280)
Std dev: $82
Weekday avg: $350
Weekend avg: $500
Weekend boost: 42.9%
Biggest daily drop: -12.5% on Tue
Biggest daily gain: 33.3% on Sat
❓ FAQ
.values to extract the ndarray.s.index.is_unique.s.mean(skipna=True) automatically skip NaN (the default behavior). s.dropna() removes missing values, and s.fillna() fills them.unique() returns the de-duplicated array (no counts), while value_counts() returns how often each value appears (sorted in descending order). Use value_counts for frequencies, and unique when you just need the distinct list.s[...] (which could be either a label or a position).s[...] is ambiguous — it could be a label or a position (when the Index is integer-based). s.loc[label] explicitly means by label, and s.iloc[pos] explicitly means by position, so the explicit form is recommended.📖 Summary
- A Series is essentially ndarray + Index + name; the underlying values are a NumPy array
- The Index makes data self-describing and supports label-based access (loc) and position-based access (iloc)
- Creation methods: list / dictionary / ndarray / scalar broadcasting — creating from a dictionary is the most natural
- Series operations align automatically by Index; non-matching labels produce NaN
- Label slicing includes the endpoint (
'Mon':'Wed'includes Wed); position slicing excludes the endpoint (iloc[0:3]excludes 3) - Common methods: describe() / value_counts() / head() / tail() / isna() / fillna() / dropna()
- A Series suits one-dimensional data; use a DataFrame for two-dimensional tabular data
📝 Exercises
- Basic (Difficulty ⭐): Create a Series in 3 ways (list + index / dictionary / ndarray), and print the index and values properties for each.
- Intermediate (Difficulty ⭐⭐): Create two Series whose Index values don't fully match (e.g. monthly sales), add them with add(fill_value=0), and compare the result against using the + operator directly.
- Challenge (Difficulty ⭐⭐⭐): Use a Series to simulate a product's monthly sales data (12 months), then: calculate cumulative sales → find the month the cumulative total breaks 50,000 → compute the month-over-month growth rate → identify the months with the highest and lowest growth rates.
← Previous: Getting Started with Pandas · Next: DataFrame Core →