Python Pandas - Series
Pandas - Series
Section titled “Pandas - Series”A Pandas Series is a fundamental one-dimensional labeled array structure. It’s capable of holding data of various types (integer, string, float, Python objects, etc.). Think of it as a single column in a spreadsheet or a more powerful version of a Python list or a NumPy array, with an associated index.
The axis labels collectively form the index, providing meaningful names or identifiers for each data point.
pandas.Series Constructor
Section titled “pandas.Series Constructor”You create a Series using the pd.Series() constructor:
pandas.Series(data=None, index=None, dtype=None, name=None, copy=False)Key parameters:
| Parameter | Description |
|---|---|
data | The data to store in the Series. Can be a Python list, NumPy array, dictionary, scalar value, or another Series. |
index | The index labels for the data. Must be hashable and have the same length as data. If None (default) and data is array-like, a RangeIndex (0, 1, 2, …) is created. If data is a dict, keys are used as index unless index is specified. |
dtype | Desired data type for the Series (e.g., 'int64', 'float64', 'object', 'category', 'datetime64[ns]). If None, the type is inferred from the data. |
name | An optional name attribute for the Series. |
copy | If True, always copy the input data. Default is False (may copy or use a reference depending on input). |
You can create a Series from various inputs:
- Python list or NumPy array
- Python dictionary
- Scalar value (requires an index)
Creating Series
Section titled “Creating Series”Create an Empty Series
Section titled “Create an Empty Series”You can create an empty Series, but you usually need to specify the dtype to avoid ambiguity.
# Import pandasimport pandas as pd
# Create an empty Series (specify dtype)s_empty = pd.Series(dtype='object')print(s_empty)Output:
Series([], dtype: object)(Note: The default dtype for a completely empty Series without specified dtype might vary across Pandas versions or contexts, often defaulting to float64 historically, but object is safer when explicitly creating an empty one intended for non-numeric data later.)
Create from a List or NumPy Array
Section titled “Create from a List or NumPy Array”If data is a list or NumPy array and no index is provided, a default integer index [0, 1, ..., n-1] is created.
import pandas as pdimport numpy as np
data_list = ['a', 'b', 'c', 'd']data_array = np.array(['a', 'b', 'c', 'd'])
# Create Series from list (default index)s_from_list = pd.Series(data_list)print("Series from list (default index):")print(s_from_list)Output:
Series from list (default index):0 a1 b2 c3 ddtype: objectYou can provide a custom index:
# Create Series from array with custom indexcustom_index = [100, 101, 102, 103]s_custom_index = pd.Series(data_array, index=custom_index)print("\nSeries from array (custom index):")print(s_custom_index)Output:
Series from array (custom index):100 a101 b102 c103 ddtype: objectCreate from a Dictionary
Section titled “Create from a Dictionary”If data is a dictionary, its keys are used as the index labels by default (in insertion order for Python 3.7+, or sorted order in older Python/Pandas versions). The dictionary values become the Series data.
import pandas as pd
data_dict = {'x': 10.0, 'y': 20.0, 'z': 30.0}
# Create Series from dict (keys become index)s_from_dict = pd.Series(data_dict)print("Series from dict (default index):")print(s_from_dict)Output:
Series from dict (default index):x 10.0y 20.0z 30.0dtype: float64If you provide an index argument when creating from a dictionary, Pandas will align the dictionary values based on the provided index labels. Missing labels will result in NaN values.
# Create Series from dict with explicit indexexplicit_index = ['y', 'z', 'w', 'x'] # 'w' is not in dict keyss_dict_explicit_index = pd.Series(data_dict, index=explicit_index)print("\nSeries from dict (explicit index):")print(s_dict_explicit_index)Output:
Series from dict (explicit index):y 20.0z 30.0w NaNx 10.0dtype: float64Observe: The order follows explicit_index. The value for ‘w’ is NaN because ‘w’ was not a key in data_dict.
Create from a Scalar Value
Section titled “Create from a Scalar Value”If data is a single scalar value (like a number or string), you must provide an index. The scalar value will be repeated for each label in the index.
import pandas as pd
# Create Series from a scalars_from_scalar = pd.Series(5, index=[0, 1, 2, 3])print("Series from scalar:")print(s_from_scalar)Output:
Series from scalar:0 51 52 53 5dtype: int64Accessing Data in a Series
Section titled “Accessing Data in a Series”You can access data in a Series using methods similar to NumPy arrays and Python dictionaries, primarily using index labels or integer positions.
Best Practice: For clarity and consistency, especially when dealing with integer indexes that could be confused with positions, it’s recommended to use .loc[] for label-based access and .iloc[] for integer position-based access, even with Series.
import pandas as pd
s = pd.Series([10, 20, 30, 40, 50], index=['a', 'b', 'c', 'd', 'e'])print("Sample Series:")print(s)Accessing by Integer Position (.iloc[])
Section titled “Accessing by Integer Position (.iloc[])”Use .iloc[] with integer indices (0-based).
# Retrieve the first element (position 0)print("\nFirst element (position 0):")print(s.iloc[0])
# Retrieve the first three elements (positions 0, 1, 2)print("\nFirst three elements (slice 0:3):")print(s.iloc[0:3]) # or s.iloc[:3]
# Retrieve the last three elements (slice from position -3 onwards)print("\nLast three elements (slice -3:):")print(s.iloc[-3:])Output:
Sample Series:a 10b 20c 30d 40e 50dtype: int64
First element (position 0):10
First three elements (slice 0:3):a 10b 20c 30dtype: int64
Last three elements (slice -3:):c 30d 40e 50dtype: int64Accessing by Label (.loc[])
Section titled “Accessing by Label (.loc[])”Use .loc[] with index labels.
# Retrieve a single element by label 'a'print("\nElement with label 'a':")print(s.loc['a'])
# Retrieve multiple elements using a list of labelsprint("\nElements with labels 'a', 'c', 'd':")print(s.loc[['a', 'c', 'd']])
# Retrieve a range of elements using label slicing (inclusive)print("\nElements from label 'b' to 'd' (inclusive):")print(s.loc['b':'d'])Output:
Element with label 'a':10
Elements with labels 'a', 'c', 'd':a 10c 30d 40dtype: int64
Elements from label 'b' to 'd' (inclusive):b 20c 30d 40dtype: int64Direct Indexing ([])
Section titled “Direct Indexing ([])”Direct [] indexing on a Series primarily attempts label lookup first. If the index contains integers, this can sometimes be ambiguous. Slicing with integers usually refers to position.
# Direct indexing (attempts label lookup first)print("\nDirect access label 'a':", s['a'])
# Direct slicing (usually positional)print("\nDirect slice [:3]:")print(s[:3])
# If a label is not found, it raises a KeyErrortry: print(s['f'])except KeyError as e: print(f"\nError accessing label 'f': {e}")Output:
Direct access label 'a': 10
Direct slice [:3]:a 10b 20c 30dtype: int64
Error accessing label 'f': 'f'While direct [] access is common for Series, using .loc and .iloc explicitly can make your code more readable and less prone to errors, especially when dealing with integer labels or transitioning between Series and DataFrames.