0

I am creating line graphs with either the year or month along the x axis.

Here is the simplified code for the monthly line graph:

import matplotlib.pyplot as plt
import iris
import iris.coord_categorisation as iriscc
import iris.plot as iplt
import iris.quickplot as qplt
import iris.analysis.cartography
import cf_units

#this file is split into parts as follows:
    #PART 1: load and format CORDEX models
    #PART 2: load and format observed data
    #PART 3: format data
    #PART 4: plot data

def main():
    #PART 1: CORDEX MODELS
    #bring in all the models we need and give them a name
    CCCma = '/exports/csce/datastore/geos/users/s0XXXX/Climate_Modelling/AFR_44_tas/ERAINT/1979-2012/tas_AFR-44_ECMWF-ERAINT_evaluation_r1i1p1_CCCma-CanRCM4_r2_mon_198901-200912.nc'

    #Load exactly one cube from given file
    CCCma = iris.load_cube(CCCma)


    #remove flat latitude and longitude and only use grid latitude and grid longitude to make consistent with the observed data, also make sure all of the longitudes are monotonic 
    lats = iris.coords.DimCoord(CCCma.coord('latitude').points[:,0], \
                                standard_name='latitude', units='degrees')
    lons = CCCma.coord('longitude').points[0]
    for i in range(len(lons)):
        if lons[i]>100.:
            lons[i] = lons[i]-360.
    lons = iris.coords.DimCoord(lons, \
                                standard_name='longitude', units='degrees')

    CCCma.remove_coord('latitude')
    CCCma.remove_coord('longitude')
    CCCma.remove_coord('grid_latitude')
    CCCma.remove_coord('grid_longitude')
    CCCma.add_dim_coord(lats, 1)
    CCCma.add_dim_coord(lons, 2)

    #we are only interested in the latitude and longitude relevant to Malawi

    Malawi = iris.Constraint(longitude=lambda v: 32.5 <= v <= 36., \
                         latitude=lambda v: -17. <= v <= -9.) 

    CCCma = CCCma.extract(Malawi)

    #time constraignt to make all series the same
    iris.FUTURE.cell_datetime_objects = True
    t_constraint = iris.Constraint(time=lambda cell: 1989 <= cell.point.year <= 2008)
    CCCma = CCCma.extract(t_constraint)



    #PART 2: OBSERVED DATA
    #bring in all the files we need and give them a name
    CRU= '/exports/csce/datastore/geos/users/s0XXXX/Climate_Modelling/Actual_Data/cru_ts4.00.1901.2015.tmp.dat.nc'

    #Load exactly one cube from given file
    CRU = iris.load_cube(CRU, 'near-surface temperature')

    #define the latitude and longitude
    lats = iris.coords.DimCoord(CRU.coord('latitude').points, \
                                standard_name='latitude', units='degrees')
    lons = CRU.coord('longitude').points

    #we are only interested in the latitude and longitude relevant to Malawi 
    Malawi = iris.Constraint(longitude=lambda v: 32.5 <= v <= 36., \
                         latitude=lambda v: -17. <= v <= -9.) 
    CRU = CRU.extract(Malawi)

    #time constraignt to make all series the same
    iris.FUTURE.cell_datetime_objects = True
    t_constraint = iris.Constraint(time=lambda cell: 1989 <= cell.point.year <= 2008)
    CRU = CRU.extract(t_constraint)



    #PART 3: FORMAT DATA  
    #data is in Kelvin, but we would like to show it in Celcius
    CCCma.convert_units('Celsius')                                                    

    #bring time data into allignment
    new_unit = cf_units.Unit('days since 1900-01-01', calendar = '365_day')
    CCCma.coord('time').convert_units(new_unit)                                                  

    #add years and months to data
    iriscc.add_year(CCCma, 'time')
    iriscc.add_year(CRU, 'time')
    iriscc.add_month(CCCma, 'time')
    iriscc.add_month(CRU, 'time')

    #We are interested in plotting the data by month, so we need to take a mean of all the data by month
    CCCmaYR = CCCma.aggregated_by('month', iris.analysis.MEAN)
    CRUYR = CRU.aggregated_by('month', iris.analysis.MEAN) 

    #regridding scheme requires spatial areas, therefore the longitude and latitude coordinates must be bounded. If the longitude and latitude bounds are not defined in the cube we can guess the bounds based on the coordinates
    CCCmaYR.coord('latitude').guess_bounds()
    CCCmaYR.coord('longitude').guess_bounds()
    CRUYR.coord('latitude').guess_bounds()
    CRUYR.coord('longitude').guess_bounds()

    #Returns an array of area weights, with the same dimensions as the cube
    CCCmaYR_grid_areas = iris.analysis.cartography.area_weights(CCCmaYR)
    CRUYR_grid_areas = iris.analysis.cartography.area_weights(CRUYR)

    #We want to plot the mean for the whole region so we need a mean of all the lats and lons
    CCCmaYR_mean = CCCmaYR.collapsed(['latitude', 'longitude'], iris.analysis.MEAN, weights=CCCmaYR_grid_areas) 
    CRUYR_mean = CRUYR.collapsed(['latitude', 'longitude'], iris.analysis.MEAN, weights=CRUYR_grid_areas)


    #PART 4: PLOT LINE GRAPH                                                                                               
    #assign the line colours and set x axis to months
    qplt.plot(CCCmaYR_mean.coord('month'),CCCmaYR_mean, label='CanRCM4_ERAINT', lw=1.5, color='blue')
    qplt.plot(CRUYR_mean.coord('month'), CRUYR_mean, label='Observed', lw=2, color='black')

    #create a legend and set its location to under the graph
    plt.legend(loc="upper center", bbox_to_anchor=(0.5,-0.05), fancybox=True, shadow=True, ncol=2)

    #create a title
    plt.title('Mean Near Surface Temperature for Malawi by month 1989-2008', fontsize=11)   

    #add grid lines
    plt.grid()

    #save the image of the graph and include full legend
    #plt.savefig('ERAINT_Temperature_LineGraph_Annual', bbox_inches='tight')

    #show the graph in the console
    iplt.show()                                               

if __name__ == '__main__':
    main()

This produces a plot which looks like this: enter image description here

How can I change the tick marks to show me all month names? I would also like the graph to finish at December (no white space after).

Similarly, for the yearly line graph, here is the simplified code:

import matplotlib.pyplot as plt
import iris
import iris.coord_categorisation as iriscc
import iris.plot as iplt
import iris.quickplot as qplt
import iris.analysis.cartography

#this file is split into parts as follows:
    #PART 1: load and format CORDEX models
    #PART 2: load and format observed data
    #PART 3: format data
    #PART 4: plot data

def main():
    #PART 1: CORDEX MODELS
    #bring in all the models we need and give them a name
    CCCma = '/exports/csce/datastore/geos/users/s0XXXX/Climate_Modelling/AFR_44_tas/ERAINT/1979-2012/tas_AFR-44_ECMWF-ERAINT_evaluation_r1i1p1_CCCma-CanRCM4_r2_mon_198901-200912.nc'  

    #Load exactly one cube from given file
    CCCma = iris.load_cube(CCCma) 

    #remove flat latitude and longitude and only use grid latitude and grid longitude to make consistent with the observed data, also make sure all of the longitudes are monotonic 
    lats = iris.coords.DimCoord(CCCma.coord('latitude').points[:,0], \
                                standard_name='latitude', units='degrees')
    lons = CCCma.coord('longitude').points[0]
    for i in range(len(lons)):
        if lons[i]>100.:
            lons[i] = lons[i]-360.
    lons = iris.coords.DimCoord(lons, \
                                standard_name='longitude', units='degrees')

    CCCma.remove_coord('latitude')
    CCCma.remove_coord('longitude')
    CCCma.remove_coord('grid_latitude')
    CCCma.remove_coord('grid_longitude')
    CCCma.add_dim_coord(lats, 1)
    CCCma.add_dim_coord(lons, 2)

    #we are only interested in the latitude and longitude relevant to Malawi      
    Malawi = iris.Constraint(longitude=lambda v: 32.5 <= v <= 36., \
                         latitude=lambda v: -17. <= v <= -9.) 

    CCCma = CCCma.extract(Malawi)

    #time constraignt to make all series the same
    iris.FUTURE.cell_datetime_objects = True
    t_constraint = iris.Constraint(time=lambda cell: 1989 <= cell.point.year <= 2008)

    CCCma = CCCma.extract(t_constraint)                                               


    #PART 2: OBSERVED DATA

    #bring in all the files we need and give them a name
    CRU= '/exports/csce/datastore/geos/users/s0XXXX/Climate_Modelling/Actual_Data/cru_ts4.00.1901.2015.tmp.dat.nc'

    #Load exactly one cube from given file
    CRU = iris.load_cube(CRU, 'near-surface temperature')

    #define the latitude and longitude
    lats = iris.coords.DimCoord(CRU.coord('latitude').points, \
                                standard_name='latitude', units='degrees')
    lons = CRU.coord('longitude').points

    #we are only interested in the latitude and longitude relevant to Malawi 
    Malawi = iris.Constraint(longitude=lambda v: 32.5 <= v <= 36., \
                         latitude=lambda v: -17. <= v <= -9.) 
    CRU = CRU.extract(Malawi)

    #time constraignt to make all series the same
    iris.FUTURE.cell_datetime_objects = True
    t_constraint = iris.Constraint(time=lambda cell: 1989 <= cell.point.year <= 2008)
    CRU = CRU.extract(t_constraint)

    #PART 3: FORMAT DATA       
    #data is in Kelvin, but we would like to show it in Celcius
    CCCma.convert_units('Celsius') 

    #add years to data
    iriscc.add_year(CCCma, 'time')
    iriscc.add_year(CRU, 'time')

    #We are interested in plotting the data by month, so we need to take a mean of all the data by month
    CCCma = CCCma.aggregated_by('year', iris.analysis.MEAN)
    CRU = CRU.aggregated_by('year', iris.analysis.MEAN)    

    #regridding scheme requires spatial areas, therefore the longitude and latitude coordinates must be bounded. If the longitude and latitude bounds are not defined in the cube we can guess the bounds based on the coordinates
    CCCma.coord('latitude').guess_bounds()
    CCCma.coord('longitude').guess_bounds()
    CRU.coord('latitude').guess_bounds()
    CRU.coord('longitude').guess_bounds()

    #Returns an array of area weights, with the same dimensions as the cube
    CCCma_grid_areas = iris.analysis.cartography.area_weights(CCCma)
    CRU_grid_areas = iris.analysis.cartography.area_weights(CRU)

    #We want to plot the mean for the whole region so we need a mean of all the lats and lons
    CCCma_mean = CCCma.collapsed(['latitude', 'longitude'], iris.analysis.MEAN, weights=CCCma_grid_areas)
    CRU_mean = CRU.collapsed(['latitude', 'longitude'], iris.analysis.MEAN, weights=CRU_grid_areas)                                              



    #PART 4: PLOT LINE GRAPH  
    #assign the line colours
    qplt.plot(CCCma_mean.coord('year'), CCCma_mean, label='CanRCM4_ERAINT', lw=1.5, color='blue')
    qplt.plot(CRU_mean.coord('year'), CRU_mean, label='Observed', lw=2, color='black')

    #create a legend and set its location to under the graph
    plt.legend(loc="upper center", bbox_to_anchor=(0.5,-0.05), fancybox=True, shadow=True, ncol=2)

    #create a title
    plt.title('Mean Near Surface Temperature for Malawi 1989-2008', fontsize=11)   

    #add grid lines
    plt.grid()

    #save the image of the graph and include full legend
    #plt.savefig('ERAINT_Temperature_LineGraph_Annual', bbox_inches='tight')

    #show the graph in the console
    iplt.show()




if __name__ == '__main__':
    main()

and this produces this graph: enter image description here

As you can see I have limited my data from 1989 to 2008, but the axis goes from 1985 to 2010, how can I make this tighter?

Thank you!

ErikaAWT
  • 67
  • 2
  • 14
  • Your "simplified code" is really not very simple at all - can you recreate your issue using a minimal example with data we can actually recreate? – Ken Syme Sep 18 '17 at 11:58
  • sorry I'm running it with multiple models and observed data sets. So when I said simplified, I only meant I had only left one of each in. Is there not a simple way to limit what the x axis displays? – ErikaAWT Sep 18 '17 at 12:31
  • Can you at least bin all the data loading and processing and just give a simple array/dataframe with the dates and values you are trying to plot? No one will be able to help currently as we can't get the data you are using - and frankly it's not important to the problem you are having! – Ken Syme Sep 18 '17 at 12:33
  • 1
    The usual way for setting the limits is to use `plt.xlim( (xmin, xmax) )`. I'm unsure on exactly the dataformat you are using though, and matplotlib can do some magic when using dates on the xaxis which is probably what you want, especially for the months version. – Ken Syme Sep 18 '17 at 12:46
  • Thank you that helped fix the yearly plots! Very helpful. Is there a similar code when the x axis isn't numerical (e.g. month names)? Also, how do you set the tick marks when they aren't numerical? – ErikaAWT Sep 18 '17 at 12:56
  • That's where it depends how the data has been plotted - usually it has to be numeric, so maybe setting the ticks to be something like `plt.xticks(range(12), calendar.month_abbr[1:13])`. – Ken Syme Sep 18 '17 at 13:19
  • Thanks for this. So I had to add in the month_number to the data first with this code: `iriscc.add_month_number(CCCma,'time') iriscc.add_month_number(CRU,'time')` and then add in your code `plt.xticks(range(12), calendar.month_abbr[0:12])` This now shows all the months at the bottom (except December oddly), but all data is there, just not the December label. Thanks! – ErikaAWT Sep 18 '17 at 14:27
  • oh I also had to `import calendar` and change my x axis in the qplot `qplt.plot(CCCmaYR_mean.coord('month_number'),CCCmaYR_mean, label='CanRCM4_ERAINT', lw=1.5, color='blue')` `qplt.plot(CRUYR_mean.coord('month_number'), CRUYR_mean, label='Observed', lw=2, color='black')` – ErikaAWT Sep 18 '17 at 14:35

1 Answers1

1

For your monthly graph you may be able to change it by setting the xticks - this has to be numeric but you can also set labels to use instead of the numbers. Something like

plt.xticks(range(12), calendar.month_abbr[1:13])

may work (depends on the format of your data, you may need to plot month number rather than month name). You will need to import calendar to get the above working.

For your yearly graph you should just be able to set the x-axis limits using

plt.xlim((xmin, xmax))

where xmin is probably 1989 and xmax is 2008.

Ken Syme
  • 3,532
  • 2
  • 17
  • 19