Search code examples
pythonmatplotlibseaborn

Multiple facet plots with python


I have data from different locations and the following code to plot rainfall and river discharge for each location separately.

import pandas as pd
import matplotlib.pyplot as plt

def hydrograph_plot(dates, rain, river_flow):
  # figure and subplots
  fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6), sharex=True, gridspec_kw={'height_ratios': [1, 2]})

  # bar graph rainfall (inverse y axis)
  ax1.bar(dates, rain, color='blue', alpha=0.6, label='Chuva (mm)')
  ax1.set_ylabel('Rainfall (mm)', color='blue')
  ax1.set_ylim(max(rain), 0)  # y axis inverted
  ax1.set_title('Hydrograph: Rainfall at ' + rain.name + ' and Flow at ' + river_flow.name) # Concatenate colname to the title
  ax1.legend(loc='upper left')
  ax1.grid(True, linestyle='--', alpha=0.5)

  # line graph for river flow
  ax2.plot(dates, river_flow, color='green', marker='o', label='Nível do Rio (m)')
  ax2.set_xlabel('Data')
  ax2.set_ylabel('StreamFlow (m)', color='green')
  ax2.legend(loc='upper left')
  ax2.grid(True, linestyle='--', alpha=0.5)

  # layout and show
  fig.autofmt_xdate()
  plt.tight_layout()
  plt.show()


    # Example data for multiple locations
    data = {
        'Time': pd.date_range(start='2023-10-01', periods=24, freq='D'),  # Time in hours
        'Rainfall_Loc1': [0, 2, 5, 10, 8, 4, 2, 0, 0, 1, 3, 6, 9, 7, 5, 3, 1, 0, 0, 0, 0, 0, 0, 0],  # Rainfall for Location 1
        'Streamflow_Loc1': [10, 12, 15, 50, 80, 70, 60, 50, 40, 35, 30, 25, 20, 18, 16, 14, 12, 11, 10, 10, 10, 10, 10, 10],  # Streamflow for Location 1
        'Rainfall_Loc2': [0, 1, 3, 7, 12, 10, 6, 3, 1, 0, 0, 0, 2, 4, 6, 8, 7, 5, 3, 1, 0, 0, 0, 0],  # Rainfall for Location 2
        'Streamflow_Loc2': [15, 18, 20, 60, 90, 85, 75, 65, 55, 50, 45, 40, 35, 30, 25, 20, 18, 16, 15, 15, 15, 15, 15, 15],  # Streamflow for Location 2
        'Rainfall_Loc3': [0, 0, 0, 0, 1, 2, 4, 6, 8, 10, 12, 10, 8, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0],  # Rainfall for Location 3
        'Streamflow_Loc3': [20, 22, 25, 70, 100, 95, 85, 75, 65, 60, 55, 50, 45, 40, 35, 30, 25, 22, 20, 20, 20, 20, 20, 20],  # Streamflow for Location 3
        'Rainfall_Loc4': [0, 5, 8, 0, 1, 3, 14, 6, 5, 20, 1, 10, 0, 16, 2, 21, 10, 0, 0, 0, 0, 0, 0, 0],  # Rainfall for Location 3
        'Streamflow_Loc4': [20, 22, 25, 70, 100, 95, 85, 75, 65, 60, 55, 50, 45, 40, 35, 30, 25, 22, 20, 20, 20, 20, 20, 20]  # Streamflow for Location 3
        
    }
    
    df = pd.DataFrame(data)

I would like to plot multiple graphs, as shown in the example of the image.

enter image description here


Solution

  • You can use subplots2grid

    import pandas as pd
    import matplotlib.pyplot as plt
    
    def hydrograph_plot(dates, rain, river_flow, posx, posy):
      # figure and subplots
      ax1 = plt.subplot2grid((6,2), (posy*3, posx), colspan=1, rowspan=1)
      ax2 = plt.subplot2grid((6,2), (posy*3+1, posx), colspan=1, rowspan=2, sharex=ax1)
    
      # bar graph rainfall (inverse y axis)
      ax1.bar(dates, rain, color='blue', alpha=0.6, label='Chuva (mm)')
      ax1.set_ylabel('Rainfall (mm)', color='blue')
      ax1.set_ylim(max(rain), 0)  # y axis inverted
      ax1.set_title('Hydrograph: Rainfall at ' + rain.name + ' and Flow at ' + river_flow.name) # Concatenate colname to the title
      ax1.legend(loc='upper left')
      ax1.grid(True, linestyle='--', alpha=0.5)
    
      # line graph for river flow
      ax2.plot(dates, river_flow, color='green', marker='o', label='Nível do Rio (m)')
      ax2.set_xlabel('Data')
      ax2.set_ylabel('StreamFlow (m)', color='green')
      ax2.legend(loc='upper left')
      ax2.grid(True, linestyle='--', alpha=0.5)
    
    
    data = {
            'Time': pd.date_range(start='2023-10-01', periods=24, freq='D'),  # Time in hours
            'Rainfall_Loc1': [0, 2, 5, 10, 8, 4, 2, 0, 0, 1, 3, 6, 9, 7, 5, 3, 1, 0, 0, 0, 0, 0, 0, 0],  # Rainfall for Location 1
            'Streamflow_Loc1': [10, 12, 15, 50, 80, 70, 60, 50, 40, 35, 30, 25, 20, 18, 16, 14, 12, 11, 10, 10, 10, 10, 10, 10],  # Streamflow for Location 1
            'Rainfall_Loc2': [0, 1, 3, 7, 12, 10, 6, 3, 1, 0, 0, 0, 2, 4, 6, 8, 7, 5, 3, 1, 0, 0, 0, 0],  # Rainfall for Location 2
            'Streamflow_Loc2': [15, 18, 20, 60, 90, 85, 75, 65, 55, 50, 45, 40, 35, 30, 25, 20, 18, 16, 15, 15, 15, 15, 15, 15],  # Streamflow for Location 2
            'Rainfall_Loc3': [0, 0, 0, 0, 1, 2, 4, 6, 8, 10, 12, 10, 8, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0],  # Rainfall for Location 3
            'Streamflow_Loc3': [20, 22, 25, 70, 100, 95, 85, 75, 65, 60, 55, 50, 45, 40, 35, 30, 25, 22, 20, 20, 20, 20, 20, 20],  # Streamflow for Location 3
            'Rainfall_Loc4': [0, 5, 8, 0, 1, 3, 14, 6, 5, 20, 1, 10, 0, 16, 2, 21, 10, 0, 0, 0, 0, 0, 0, 0],  # Rainfall for Location 3
            'Streamflow_Loc4': [20, 22, 25, 70, 100, 95, 85, 75, 65, 60, 55, 50, 45, 40, 35, 30, 25, 22, 20, 20, 20, 20, 20, 20]  # Streamflow for Location 3
    
    }
    
    df = pd.DataFrame(data)
    
    fig=plt.figure()
    hydrograph_plot(df.Time, df.Rainfall_Loc1, df.Streamflow_Loc1, 0,0)
    hydrograph_plot(df.Time, df.Rainfall_Loc2, df.Streamflow_Loc2, 1,0)
    hydrograph_plot(df.Time, df.Rainfall_Loc3, df.Streamflow_Loc3, 0,1)
    hydrograph_plot(df.Time, df.Rainfall_Loc4, df.Streamflow_Loc4, 1,1)
    # layout and show
    fig.autofmt_xdate()
    plt.tight_layout()
    plt.show()
    

    (I do not argue that it is the best way to do it, with hydrograph_plot needing to be aware of how many total plots there are. Maybe total shape could be an argument. But that is the way that left most of your code unchanged)

    Note the sharex argument, which ensure that ax1 and ax2, even if they are, each time, just 2 among 8 plots, do share x-axis. enter image description here