pandas Series

Data Science · Lesson 8 of 32 6 min read Updated

NumPy arrays are fast, but their values are only identified by position: temps[3] tells you nothing about which day or city that is. pandas adds labels. Its two main objects are the Series, a single labelled column of data, and the DataFrame, a table made of several Series.

This lesson covers the Series. Almost everything you learn here, such as selecting by label, vectorised maths and summary methods, works the same way on DataFrame columns.

Creating a Series

A Series has two parts: the values (a NumPy-style array) and the index (the labels). If you do not give an index, pandas numbers the rows from 0.

PythonRun it yourself
import pandas as pd

rain = pd.Series([3.2, 0.0, 12.5, 41.0, 7.8])
print(rain)

monthly_rain = pd.Series(
    [15, 25, 45, 120, 260, 650],
    index=["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
    name="Kochi rainfall (mm)",
)
print(monthly_rain)
Output
0     3.2
1     0.0
2    12.5
3    41.0
4     7.8
dtype: float64
Jan     15
Feb     25
Mar     45
Apr    120
May    260
Jun    650
Name: Kochi rainfall (mm), dtype: int64

The left column is the index and the right column holds the values. Under the values pandas shows the name (if you set one) and the dtype. You can also build a Series from a dictionary, where the keys become the index:

PythonRun it yourself
import pandas as pd

population_lakh = pd.Series({"Rohtak": 3.7, "Jaipur": 30.5, "Pune": 31.2, "Kochi": 6.0})
print(population_lakh)
print(population_lakh.index)
print(population_lakh.values)

cities = pd.Series(["Rohtak", "Jaipur", None])
print(cities)
Output
Rohtak     3.7
Jaipur    30.5
Pune      31.2
Kochi      6.0
dtype: float64
Index(['Rohtak', 'Jaipur', 'Pune', 'Kochi'], dtype='str')
[ 3.7 30.5 31.2  6. ]
0    Rohtak
1    Jaipur
2       NaN
dtype: str

The last Series holds text, and its dtype is str. In pandas 3.0 text columns get this dedicated string type; older versions showed object. The missing value None is stored as NaN, pandas' usual marker for "no value". You will learn more about both in Handling Missing Values and What's New in pandas 3.0.

Selecting values: labels and positions

Because a Series has labels and positions, pandas gives you two explicit selectors:

  • .loc[...] selects by label.
  • .iloc[...] selects by integer position, starting at 0.
PythonRun it yourself
import pandas as pd

marks = pd.Series([78, 91, 55, 84, 69], index=["Aarav", "Priya", "Kabir", "Meera", "Rohan"])

print(marks.loc["Priya"])
print(marks.iloc[0])
print(marks.loc["Kabir":"Rohan"])      # label slices INCLUDE the end
print(marks.iloc[1:3])                 # position slices exclude the end
print(marks.loc[["Aarav", "Meera"]])
print(marks[marks >= 80])              # boolean filter, like NumPy
Output
91
78
Kabir    55
Meera    84
Rohan    69
dtype: int64
Priya    91
Kabir    55
dtype: int64
Aarav    78
Meera    84
dtype: int64
Priya    91
Meera    84
dtype: int64

One surprise: a label slice such as "Kabir":"Rohan" includes Rohan, while a position slice 1:3 stops before position 3. Labels have no natural "next one", so pandas includes both ends.

Maths aligns on labels

Arithmetic between two Series works element by element, like NumPy, with one big difference: pandas matches values by label, not by position. This is called alignment.

PythonRun it yourself
import pandas as pd

jan = pd.Series({"Delhi": 120, "Pune": 85, "Kochi": 40})
feb = pd.Series({"Pune": 95, "Kochi": 55, "Jaipur": 70})

print(jan + feb)
print(jan.add(feb, fill_value=0))
print((jan * 1.18).round(1))      # with a single number, like NumPy
Output
Delhi       NaN
Jaipur      NaN
Kochi      95.0
Pune      180.0
dtype: float64
Delhi     120.0
Jaipur     70.0
Kochi      95.0
Pune      180.0
dtype: float64
Delhi    141.6
Pune     100.3
Kochi     47.2
dtype: float64

Delhi only appears in January and Jaipur only in February, so for those labels one side is missing and the result is NaN. Alignment protects you from silently adding Pune's sales to Kochi's just because they happened to be in the same position. When missing should mean zero, use the method form add() with fill_value=0. Notice also that the result is sorted by label and its dtype became float64, because NaN is a decimal value.

Summary methods

A Series has dozens of methods. These are the ones you will use every day:

MethodReturns
sum(), mean(), median(), min(), max(), std()One summary number (missing values are skipped)
idxmax(), idxmin()The label of the largest or smallest value
count()Number of non-missing values
sort_values(), sort_index()A sorted copy
value_counts()How often each distinct value appears
unique(), nunique()Distinct values, and how many there are
describe()Count, mean, std, min, quartiles and max in one go
PythonRun it yourself
import pandas as pd

temps = pd.Series([31.5, 33.0, 29.8, 35.2, None, 34.1, 32.7],
                  index=["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"])

print("Mean:", round(temps.mean(), 2), " Count:", temps.count())
print("Hottest day:", temps.idxmax())
print(temps.sort_values(ascending=False).head(3))
print(temps.describe().round(2))

modes = pd.Series(["UPI", "Card", "UPI", "Cash", "UPI", "Card"])
print(modes.value_counts())
Output
Mean: 32.72  Count: 6
Hottest day: Thu
Thu    35.2
Sat    34.1
Tue    33.0
dtype: float64
count     6.00
mean     32.72
std       1.91
min      29.80
25%      31.80
50%      32.85
75%      33.83
max      35.20
dtype: float64
UPI     3
Card    2
Cash    1
Name: count, dtype: int64

Unlike NumPy's plain mean(), pandas skips missing values by default, so the Friday gap does not ruin the average. count() confirms that only 6 of 7 days have a reading. value_counts() returns a new Series whose index holds the distinct values, sorted from most to least common.

Common mistakes

  • Expecting position-based maths. Two Series are added by matching labels. If labels differ, you get NaN.
  • Forgetting that label slices include the end. s.loc["a":"c"] includes "c"; s.iloc[0:2] excludes position 2.
  • Using s[0] on a labelled Series. Write s.iloc[0] for the first value.
  • Checking dtype == object for text. In pandas 3.0 text columns have dtype str.

Key points

  • A Series is a 1-D array of values with an index of labels and an optional name.
  • Create it from a list (default 0, 1, 2 index), a list plus index=, or a dictionary.
  • Use .loc for labels (slices include the end) and .iloc for positions.
  • Arithmetic aligns on labels; unmatched labels give NaN unless you use fill_value.
  • Summary methods such as mean(), idxmax() and value_counts() skip missing values.

Exercise

Create a Series of five students' science marks with their names as the index (choose your own). Print the name of the top scorer, the average rounded to one decimal, and only the students who scored below the average. Then add 5 grace marks to everyone and print the new Series.

Show solution
Python
import pandas as pd

science = pd.Series({"Ananya": 74, "Farhan": 58, "Isha": 89, "Rohan": 63, "Meera": 81})
print("Top:", science.idxmax())
avg = science.mean()
print("Average:", round(avg, 1))
print(science[science < avg])
print(science + 5)
Output
Top: Isha
Average: 73.0
Farhan    58
Rohan     63
dtype: int64
Ananya    79
Farhan    63
Isha      94
Rohan     68
Meera     86
dtype: int64

science < avg creates a boolean Series that filters the rows. Adding a single number is applied to every value, just like with NumPy.

Quick Quiz

1. What does pd.Series({"a": 1, "b": 2}) + pd.Series({"b": 10, "c": 20}) give for label "a"?

2. Which selector picks values by integer position?

3. What does s.idxmax() return?

4. What dtype does pandas 3.0 give pd.Series(["Pune", "Kochi"])?

Learn Data Science with a mentor

Free lessons get you started. Our Data Science & Machine Learning training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call