Creating a plot with two y-axes can be a useful way to visualize data that share the same x-axis but have different scales or units. Matplotlib, a powerful plotting library in Python, provides straightforward methods to achieve this. Here’s a step-by-step guide.
Start by importing Matplotlib and other necessary libraries.
12
importmatplotlib.pyplotaspltimportnumpyasnp
Generate Sample Data
For demonstration, create two datasets that you want to visualize on the same plot.
123
x=np.linspace(0,10,100)# Common x-axisy1=np.sin(x)# First datasety2=np.exp(x/3)# Second dataset
Create the Primary Plot
Use the subplots method to create a figure and axis for the primary y-axis.
1234567
fig,ax1=plt.subplots()# Plot the first datasetax1.plot(x,y1,'b-',label='Sine Wave')ax1.set_xlabel('X-axis')ax1.set_ylabel('Sine',color='b')ax1.tick_params(axis='y',labelcolor='b')
Add a Secondary Y-Axis
Use the twinx method to add a second y-axis that shares the same x-axis.
123456
ax2=ax1.twinx()# Create a second axes that shares the same x-axis# Plot the second datasetax2.plot(x,y2,'r-',label='Exponential Growth')ax2.set_ylabel('Exponential',color='r')ax2.tick_params(axis='y',labelcolor='r')
Add a Title and Legend
Customize the plot with titles and legends.
12345
fig.suptitle('Plot with Two Y-Axes')# Optional: Add legendsax1.legend(loc='upper left')ax2.legend(loc='upper right')
importmatplotlib.pyplotaspltimportnumpyasnp# Generate datax=np.linspace(0,10,100)y1=np.sin(x)y2=np.exp(x/3)# Create primary y-axis plotfig,ax1=plt.subplots()ax1.plot(x,y1,'b-',label='Sine Wave')ax1.set_xlabel('X-axis')ax1.set_ylabel('Sine',color='b')ax1.tick_params(axis='y',labelcolor='b')# Create secondary y-axisax2=ax1.twinx()ax2.plot(x,y2,'r-',label='Exponential Growth')ax2.set_ylabel('Exponential',color='r')ax2.tick_params(axis='y',labelcolor='r')# Add title and legendsfig.suptitle('Plot with Two Y-Axes')ax1.legend(loc='upper left')ax2.legend(loc='upper right')# Display the plotplt.show()
Output
The resulting plot will have:
- A shared x-axis.
- The sine wave (y1) on the left y-axis with blue labels and a blue line.
- The exponential growth (y2) on the right y-axis with red labels and a red line.
Key Notes
Use different colors for each axis to improve readability.
Ensure that the datasets have a logical relationship or justification for being visualized together.
If additional customization is needed, Matplotlib’s extensive documentation can be a helpful resource.
Another example
Code to create a dual y-axis time series plot visualizing atmospheric CO₂ concentrations and global temperature anomalies. The plot combines a line plot for CO₂ and a bar plot for temperature anomalies.
pandas: Used for loading and processing time series data.
matplotlib.pyplot: Used for plotting.
numpy: Provides numerical utilities.
Load and Prepare CO₂ Data
1 2 3 4 5 6 7 8 910
# Load CO₂ dataco2_data=pd.read_csv("./inputs/co2_mm_mlo.csv",# Replace with your file pathskiprows=40,# Skip header rows specific to the datasetna_values=["-99.99"]# Handle missing values)co2_data=co2_data.dropna()# Remove rows with missing data# Create a datetime column from year and monthco2_data['date']=pd.to_datetime(co2_data[['year','month']].assign(day=1))
Loads monthly CO₂ data from Mauna Loa Observatory.
Cleans the dataset and combines year and month into a datetime object for plotting.
Load and Prepare Temperature Anomaly Data
1 2 3 4 5 6 7 8 9101112
# Load temperature anomaly datatemp_data=pd.read_csv("./inputs/GLB.Ts+dSST.csv",# Replace with your file pathskiprows=1# Skip header row)# Rename and select relevant columnstemp_data=temp_data.rename(columns={"Year":"year","J-D":"anomaly"})temp_data=temp_data[['year','anomaly']]# Add a datetime column (mid-year for annual data)temp_data['date']=pd.to_datetime(temp_data['year'].astype(str)+'-07-01')
Loads global temperature anomaly data and cleans it by renaming and selecting necessary columns.
Converts the year column into a datetime object for alignment with CO₂ data.
Align and Filter Data
123456789
# Filter data to shared date rangestart_date=max(co2_data['date'].min(),temp_data['date'].min())end_date=min(co2_data['date'].max(),temp_data['date'].max())co2_filtered=co2_data[(co2_data['date']>=start_date)&(co2_data['date']<=end_date)]temp_filtered=temp_data[(temp_data['date']>=start_date)&(temp_data['date']<=end_date)]# Ensure 'anomaly' column is numeric and drop invalid rowstemp_filtered['anomaly']=pd.to_numeric(temp_filtered['anomaly'],errors='coerce')temp_filtered=temp_filtered.dropna(subset=['anomaly'])
Aligns CO₂ and temperature data by filtering to the overlapping date range.
Converts the anomaly column to numeric and removes invalid rows.
Separate Positive and Negative Anomalies
123
# Separate data into positive and negative anomaliespositive_anomalies=temp_filtered[temp_filtered['anomaly']>0]negative_anomalies=temp_filtered[temp_filtered['anomaly']<=0]
Splits the temperature anomaly data into two subsets for positive (red) and negative (blue) bars.
Creates the primary y-axis for CO₂ data with a green line plot.
Add Secondary Y-Axis for Temperature Anomalies
1 2 3 4 5 6 7 8 91011
# Add a second axis for temperature anomaliesax2=ax1.twinx()# Plot bars with different colors for anomaliesax2.bar(positive_anomalies['year'],positive_anomalies['anomaly'],color='r',alpha=0.7,label='Positive Anomaly')ax2.bar(negative_anomalies['year'],negative_anomalies['anomaly'],color='b',alpha=0.7,label='Negative Anomaly')# Set secondary y-axis label and formattingax2.set_ylabel('Temperature Anomaly (°C)',color='k')ax2.tick_params(axis='y',colors='k')ax2.legend(loc='upper right')
Adds a secondary y-axis for temperature anomalies.
Uses a bar plot with red bars for positive anomalies and blue bars for negative anomalies.
Add Title and Display the Plot
12345
# Add title and gridplt.title('Atmospheric CO₂ and Global Temperature Anomalies')plt.grid()plt.tight_layout()plt.show()
Adds a title, grid, and adjusts layout to prevent overlap.
importpandasaspdimportmatplotlib.pyplotaspltimportnumpyasnp# Load CO₂ dataco2_data=pd.read_csv("./inputs/co2_mm_mlo.csv",# Replace with your file pathskiprows=40,na_values=["-99.99"])co2_data=co2_data.dropna()co2_data['date']=pd.to_datetime(co2_data[['year','month']].assign(day=1))# Load temperature anomaly datatemp_data=pd.read_csv("./inputs/GLB.Ts+dSST.csv",# Replace with your file pathskiprows=1)temp_data=temp_data.rename(columns={"Year":"year","J-D":"anomaly"})temp_data=temp_data[['year','anomaly']]temp_data['date']=pd.to_datetime(temp_data['year'].astype(str)+'-07-01')# Mid-year for annual datastart_date=max(co2_data['date'].min(),temp_data['date'].min())end_date=min(co2_data['date'].max(),temp_data['date'].max())co2_filtered=co2_data[(co2_data['date']>=start_date)&(co2_data['date']<=end_date)]temp_filtered=temp_data[(temp_data['date']>=start_date)&(temp_data['date']<=end_date)]# Ensure 'anomaly' column is numerictemp_filtered['anomaly']=pd.to_numeric(temp_filtered['anomaly'],errors='coerce')# Drop rows with NaN values in 'anomaly' after conversiontemp_filtered=temp_filtered.dropna(subset=['anomaly'])# Separate data into positive and negative anomaliespositive_anomalies=temp_filtered[temp_filtered['anomaly']>0]negative_anomalies=temp_filtered[temp_filtered['anomaly']<=0]# Plottingfig,ax1=plt.subplots(figsize=(10,6))# Plot CO₂ dataax1.plot(co2_filtered['year'],co2_filtered['average'],'g-',label='CO₂ (ppm)')ax1.set_xlabel('Year')ax1.set_ylabel('CO₂ Concentration (ppm)',color='g')ax1.tick_params(axis='y',colors='g')ax1.legend(loc='upper left')# Add a second axis for temperature anomaliesax2=ax1.twinx()# Plot bars with different colorsax2.bar(positive_anomalies['year'],positive_anomalies['anomaly'],color='r',alpha=0.7,label='Positive Anomaly')ax2.bar(negative_anomalies['year'],negative_anomalies['anomaly'],color='b',alpha=0.7,label='Negative Anomaly')# Set secondary y-axis label and formattingax2.set_ylabel('Temperature Anomaly (°C)',color='k')ax2.tick_params(axis='y',colors='k')ax2.legend(loc='upper right')# Add title and gridplt.title('Atmospheric CO₂ and Global Temperature Anomalies')plt.grid()plt.tight_layout()plt.show()
Output
Left Y-Axis: CO₂ concentration (ppm) as a green line plot.
Right Y-Axis: Temperature anomalies (°C) as red and blue bars.
The plot provides a clear visualization of the relationship between CO₂ levels and global temperature anomalies over time.
Key Notes
Replace file paths ("./inputs/co2_mm_mlo.csv" and "./inputs/GLB.Ts+dSST.csv") with actual file locations on your system.
Ensure data formats match the code expectations, or adjust column names and parsing logic as needed.
Hi, I'm Ben, the Founder of moonbooks.org. I work as a research scientist specializing in Earth satellite remote sensing, particularly focusing on Fire detection using VIIRS and ABI sensors. My areas of expertise include Python, Machine Learning, and Open Science. Feel free to reach out if you notice any errors on the site. I am constantly striving to enhance it. Thank you!