You are here: Home / Services / Data Analysis and Visualization / Visualization / Software / NCL / PyNGL / DKRZ PyNGL example scatterplot with two additional histograms attached

DKRZ PyNGL example scatterplot with two additional histograms attached

This example creates a scatter plot of data a(t), b(t), colored by c(t). Additionally the counts of items referring to the axis are attached as histograms to the scatter plot.

Example script:

#  File:
#    scatter_plot_add_2_histograms.py
#
#  Synopsis:
#    Create a scatter plot of the data a(t), b(t) and color the
#    polymarker by the color of variable c(t). Add additional
#    histograms of the counts per selected axis range to the plot.
#
#  Category:
#    xy plot
#    polylines, polygons
#    functions
#    text
#
#  Based on DKRZ's NCL example:
#    scatter_plot_add_2_histograms.ncl
#
#  Author:
#    Karin Meier-Fleischer
#  
#  Date of initial publication:
#    December, 2018
#
#  Description:
#    Create a scatter plot of the data a(t), b(t) and color the
#    polymarker by the color of variable c(t). Add additional
#    histograms of the counts per selected axis range to the plot.#
#
#  Effects illustrated:
#    o  Create an xy-plot
#    o  Define functions
#    o  Add polymarker, polylines, and polygons
#    o  Add text
#
#  Output:
#     One visualization is produced.     
#
'''
  PyNGL Example:     scatter_plot_add_2_histograms.py

  -  Create an xy-plot
  -  Define functions
  -  Add polymarker, polylines, and polygons
  -  Add text
'''
import numpy as np
import Ngl

#----------------------------------------------------------------------
# Procedure add_histogram: attach a to the plot
#----------------------------------------------------------------------
def add_histogram(wks, plot, data, levels, opt):

#-- retrieve viewport width and height
   vpx = Ngl.get_float(plot,"vpXF")                 #-- retrieve plot x-position
   vpy = Ngl.get_float(plot,"vpYF")                 #-- retrieve plot y-position
   vpw = Ngl.get_float(plot,"vpWidthF")             #-- retrieve plot width
   vph = Ngl.get_float(plot,"vpHeightF")            #-- retrieve plot height
 
   if(opt):
      if(hasattr(opt,'orientation')):
         if(opt.orientation == 'horizontal'):
            w    =  vpw
            h    =  0.2*vph
            ampa = -0.50
            amor = -0.66
         else:
            opt.orientation == 'vertical'
            h    = vph
            w    = 0.2*vpw
            ampa = 0.58
            amor = 0.0
      
      if(hasattr(opt,'numLevels')):
         nt = opt.numLevels
      else:
         nt = 10

      if(hasattr(opt,'xRange')):
         min1d = opt.xRange[0]
         max1d = opt.xRange[1]
      else:
         min1d = min(data)
         max1d = max(data)

   hist_levels = np.linspace(min1d, max1d,nt+1)
   
#-- get the values (counts) for the bars
   count = []
   for i in range(0,len(hist_levels)-1):
      xind  = Ngl.ind(np.logical_and(data >= hist_levels[i], data < hist_levels[i+1]))
      count.append(len(xind))

#-- set resources
   hres                             = Ngl.Resources()
   hres.nglDraw                     = False
   hres.nglFrame                    = False
 
   if(opt.orientation == "horizontal"):
      hres.vpWidthF                    =  w
      hres.vpHeightF                   =  h
      hres.tmYMajorGrid                =  True
      hres.tmYMajorGridLineColor       = "gray50"
      hres.tmYMajorGridLineDashPattern =  2
      hres.trXMinF                     =  min1d
      hres.trXMaxF                     =  max1d
      hres.trYMinF                     =  0
      hres.trYMaxF                     =  max(count)+1
      
   elif(opt.orientation == "vertical"):
      hres.vpWidthF                    =  w
      hres.vpHeightF                   =  h
      hres.tmXMajorGrid                =  True
      hres.tmXMajorGridLineColor       = "gray50"
      hres.tmXMajorGridLineDashPattern =  2
      hres.trXMinF                     =  0
      hres.trXMaxF                     =  max(count)+1
      hres.trYMinF                     =  min1d
      hres.trYMaxF                     =  max1d
      
   hres.tmXBLabelFontHeightF        =  0.014
   hres.tmYLLabelFontHeightF        =  0.014
   hres.tmXBLabelStride             =  2
   hres.tmXBLabelDeltaF             = -0.7
   hres.tmYLLabelDeltaF             = -0.7
   hres.tmLabelAutoStride           =  True         #-- use nice tick mark labels
   
   histo = Ngl.blank_plot(wks, hres)                #-- create blank plot
   
#-- set resources for the polylines
   polyres                   =  Ngl.Resources()     #-- set up defaults
   polyres.gsLineColor       = 'black'              #-- color of lines
   polyres.gsFillColor       = 'gray70'             #-- color of lines
   polyres.gsLineThicknessF  =  1.5                 #-- thickness of lines
   polyres.gsLineDashPattern =  0
   
#-- compute the x- and y-values for the bars and create polygons and polylines
   plid = []
   pgid = []
   for i in range(0,len(hist_levels)-1):
      if(opt.orientation == 'horizontal'):
         x = [hist_levels[i], hist_levels[i+1], hist_levels[i+1], hist_levels[i], hist_levels[i]]
         y = [0,0,count[i],count[i],0]
      elif(opt.orientation == 'vertical'):
         y = [hist_levels[i], hist_levels[i+1], hist_levels[i+1], hist_levels[i], hist_levels[i]]
         x = [0,0,count[i],count[i],0]
      pgid.append(Ngl.add_polygon(wks,histo,x,y,polyres))   #-- add polygon to histo
      plid.append(Ngl.add_polyline(wks,histo,x,y,polyres))  #-- add polyline to histo

#-- attach histogram to plot
   amres                  =  Ngl.Resources()
   amres.amJust           = "CenterLeft"
   amres.amParallelPosF   =  ampa
   amres.amOrthogonalPosF =  amor
   annoidpg = Ngl.add_annotation(plot,histo,amres)
   
   return(plot)


#----------------------------------------------------------------------
# Procedure add_labelbar: attach a vertical labelbar to the right
#                         side of the plot
#----------------------------------------------------------------------
def add_labelbar(wks,plot,colors,levels, orient):
#-- retrieve viewport width and height
   vpx = Ngl.get_float(plot,"vpXF")             #-- retrieve plot x-position
   vpy = Ngl.get_float(plot,"vpYF")             #-- retrieve plot y-position
   vpw = Ngl.get_float(plot,"vpWidthF")         #-- retrieve plot width
   vph = Ngl.get_float(plot,"vpHeightF")        #-- retrieve plot height

   if(orient == "horizontal"):
      vw   = vpw
      vh   = 0.1*vph
      ampa = -0.5
      amor = 0.63
   elif(orient == "vertical"):
      vw   = 0.15*vpw
      vh   = 0.99*vph
      ampa = 0.55
      amor = 0.0

#-- define labels and retrieve the number of color boxes
   labels = levels.astype('str')                #-- convert to type string
   nboxes = len(colors[:,0])                    #-- number of color boxes (nlevels+1)
   
#-- create labelbar
   lbres                    =  Ngl.Resources()  #-- labelbar only resources
   lbres.vpWidthF           =  vw               #-- labelbar width
   lbres.vpHeightF          =  vh               #-- labelbar height
   lbres.nglDraw            =  False
   
   lbres.lbAutoManage       =  False            #-- Necessary to control sizes
   lbres.lbFillColors       =  colors           #-- labelbar colors
   lbres.lbMonoFillPattern  =  True             #-- Solid fill pattern
   lbres.lbLabelFontHeightF =  0.016            #-- font height. default is small
   lbres.lbLabelAlignment   = "InteriorEdges"   #-- line of box
   lbres.lbOrientation      =  orient
   lbres.lbPerimOn          =  False

   lbid = Ngl.labelbar_ndc(wks,nboxes,list(labels),0.1,0.5,lbres)

#-- attach labelbar to the plot
   amres                  =  Ngl.Resources()
   amres.amJust           = "CenterLeft"
   amres.amParallelPosF   =  ampa
   amres.amOrthogonalPosF =  amor

   annoid = Ngl.add_annotation(plot,lbid,amres)

#-------------------------------------------------------
#--              MAIN
#-------------------------------------------------------
#-- set minimum, maximum and interval value to group the data to color and create the labelbar
cmin =  -0.8
cmax =   0.8
cint =   0.01
nt   = 100                                      #-- number of data per array

#-- x-,y-data
min1d = -4.0
max1d =  6.0
hist_levels = np.linspace(min1d, max1d,22)
 
#-- generate random data 100 points (e.g. time steps) data for x-axis
a = np.random.uniform(min1d,max1d,nt)

#-- data for y-axis
b = np.random.uniform(min1d,max1d,nt)

#-- data for coloring
c = np.random.uniform(cmin,cmax,nt)

#-- title string
title = "Scatter plot:  x=a(t), y=b(t), colored by c(t)"

#-- set colormap name
colmap = "MPL_viridis"                          #-- colormap blue-green,yellow

#-- define levels for labelbar and colors
levels1 =  np.arange(np.ceil(cmin*10),np.floor(cmax*10),1)/10.0  #-- define levels
levels2 =  np.arange(np.ceil(cmin*10),np.floor(cmax*10),1)/10.0  #-- define levels
nlevels =  levels1.size                         #-- number of levels

#-- open workstation
wks_type = "png"
wksres          = Ngl.Resources()
wksres.wkWidth  = 1200
wksres.wkHeight = 1200
wks  = Ngl.open_wks(wks_type,"plot_scatter_attach_two_histograms",wksres)
Ngl.define_colormap(wks,colmap)

#-- retrieve the colors# don't use the first dark blues
colors  =  Ngl.retrieve_colormap(wks)           #-- retrieve color map
colind  =  np.linspace(20,255,nlevels+1).astype('int') #-- don't use first 20 colors
colors  =  colors[colind,:]
ncolors =  len(colors)

#-- resources
res                   =  Ngl.Resources()        #-- plot mods desired
res.nglDraw           =  False                  #-- don't draw plot, yet
res.nglFrame          =  False                  #-- don't advance frame, yet

res.trYMinF           =  min1d                  #-- y-axis minimum to have enough space for legend
res.trYMaxF           =  max1d                  #-- y-axis maximum
res.trXMinF           =  min1d                  #-- y-axis minimum to have enough space for legend
res.trXMaxF           =  max1d                  #-- y-axis maximum

res.tmLabelAutoStride =  True                   #-- use nice tick mark labels

res.vpWidthF          =  0.63                   #-- viewport width
res.vpHeightF         =  0.63                   #-- viewport height
res.vpXF              =  0.12                   #-- viewport x-position
res.vpYF              =  0.75                   #-- viewport y-position

#-- create blank plot
plot = Ngl.blank_plot(wks, res)

#-- group data and add it as filled circle markers, slightly transparent
mres                    =  Ngl.Resources()
mres.gsMarkerThicknessF =  2.5                  #-- marker thickness
mres.gsMarkerOpacityF   =  0.8                  #-- marker opacity

pltmarker = []
for i in range(0,nlevels-2):
   ii = Ngl.ind(np.logical_and(levels1[i] <= c, levels1[i+1] > c))
   mres.gsMarkerIndex  =  16                    #-- filled circle marker
   mres.gsMarkerSizeF  =  0.01                  #-- marker size
   mres.gsMarkerColor  =  colors[i,:]           #-- marker color
   pltmarker.append(Ngl.add_polymarker(wks,plot,a[ii],b[ii],mres)) #-- add marker

#-- add labelbar
add_labelbar(wks,plot,colors,levels1,'horizontal')

#-- special histogram resources for function add_histogram
opt             =  Ngl.Resources()
opt.numLevels   =  20                           #-- number of levels
opt.barColor    = 'gray60'                      #-- fill color
opt.xRange      = [min1d,max1d]                 #-- x-axis range

#-- add histogram at the top
opt.orientation = 'horizontal'
pltid1 = add_histogram(wks, plot, a, levels1, opt)

#-- add histogram at the right side
opt.orientation = 'vertical'
pltid2 = add_histogram(wks, plot, b, levels2, opt)

#-- write title to plot
title = "Scatter plot:  x=a(t), y=b(t), colored by c(t)"

txres               =  Ngl.Resources()
txres.txJust        = 'CenterCenter'
txres.txFontHeightF =  0.018
xx = Ngl.get_float(plot,'vpXF')+Ngl.get_float(plot,'vpWidthF')/2
Ngl.text_ndc(wks,title,xx,0.96,txres)
 
#-- draw the plot and advance the frame
Ngl.draw(plot)
Ngl.frame(wks)

Result:

Document Actions