;+ ; NAME: ; plotcolumns ; PURPOSE: ; Makes a stack plot. ; ; Input is provided for n 'groups'. The groups are plotted ; as n separate collections of columns (of one or more columns ; per collection) along the x-axis. ; ; Example: 10 groups describing the years 2001 through 2010. ; The x-axis will have labels 2001, 2002, ..., 2010 with ; collections of columns near each label. ; ; Each group (optionally) can contain m 'items'. ; The collection of columns for each group consists of m columns ; (one for each item). ;. ; Example: each group has 4 items, one for each quarter of ; the year. ; ; Each item in the group can have l categories. The l categories ; are stacked on top of each other for each item column. ; ; Example: categories 'coffee','tea','water','beer','soda'. The input ; data could be ounces of liquid consumed for each category ; per quarter for the years 2001 through 2010. ; ; For the example: ; ; xvalues = [2001,2002,...,2010] = array(10) ; yvalues = array(10,4,5) ounces for 10 years, 4 quarter, 5 categories ; item_names = ['Q1','Q2','Q3','Q4'] ; stack_names = ['coffee','tea','water','beer','soda'] ; ; CALLING SEQUENCE: PRO plotcolumns, xvalues, yvalues, $ item_names = item_names , $ stack_names = stack_names , $ stack_empty = stack_empty , $ group_spacing = group_spacing , $ item_spacing = item_spacing , $ sort_stack = sort_stack , $ label_stack = label_stack , $ stack_format = stack_format , $ top_stacks = top_stacks , $ stack_colors = stack_colors , $ legend = legend , $ silent = silent , $ xalign = xalign , $ _extra = _extra ; INCLUDE: @compile_opt.pro ; INPUTS: ; xvalues array[n]; type: string or numeric ; yvalues array[n,m,l], array[n,l]; type: numeric ; values for m 'items' in l 'stacks' ; for n 'ngroups' ; OPTIONAL INPUTS: ; item_names=item_names ; array[m]; type: string; default: none ; NOT USED YET ; string labels for the 'm' items ; stack_names=stack_names ; array[l]; type: string; default: none ; string labels for the 'l' stacks ; used in the legend (see below) ; group_spacing=group_spacing ; scalar; type: integer >= 0; default: 1 ; sets the space between the 'n' groups ; in units of the column width. ; item_spacing=item_spacing ; scalar; type: integer >=0; default: group_spacing ; sets the space between the 'm' items ; of a group in units of the column width. ; sort_stacks=sort_stacks ; scalar; type: integer; default: 0 ; allowed values: 0,1,2 ; 1: sort the stacks in order of decreasing ; value of the sums yvalues[*,0,l], ; i.e. the sum of the values in group 0 ; 2: sort the stacks in order of decreasing ; value of the sums yvalues[*,*,l], ; i.e. the sum of the values in all groups ; The first stack in this order will be ; plotted at the bottom of each column. ; top_stacks=top_stacks ; scalar; type: integer; default: none ; if the input array yvalues contains more than ; 'top_stacks' stacks (l > top_stacks), then ; only 'top_stacks-1' stacks from yvalues are ; used, and one stack is added containing the ; sum of the stacks 'top_stacks-1' to 'l-1'. ; If top_stacks is set then also 'sort_stacks' ; is assumed set to at least 1. ; stack_colors=stack_colors ; array[l]; type: integer ; the color indices to be used to color the ; 'l' stacks in each column ; legend=legend ; array[2], array[4] or array[6] ; determines the layout of the legend of 'l' ; colored blocks for each stack member, with ; next to it the corresponding 'stack_name' string. ; All numbers are in normal coordinates ; legend[0:1]: upper-left corner of legend. ; legend[2] : horizontal distance between ; color block and label ; legend[3] : vertical distance between color blocks ; legend[4] : length of color block ; legend[5] : height of color block ; xalign=xalign ; scalar; type: numeric between 0 and 1; default: 0 ; By default the label for each group is plotted at the ; left side of the collection of columns for each group. ; The location can be controlled with xalign; e.g. ; xalign=0.5 will center the label on the collection ; of columns for each group. ; silent=silent ; PROCEDURE: ; The bargraph produced consists of m x n columns: ; a group of m consecutive columns, repeated n times. ; Each column shows the numbers for the l stacks ; stacked on top of each other. ; ; MODIFICATION HISTORY: ; DEC-2012, Paul Hick (UCSD/CASS; pphick@ucsd.edu) ;- sz = size(yvalues,/dim) IF n_elements(sz) EQ 1 THEN BEGIN sz = [sz[0],1] yvalues = reform(yvalues,sz,/overwrite) ENDIF IF n_elements(sz) EQ 2 THEN BEGIN sz = [sz[0],1,sz[1]] yvalues = reform(yvalues,sz,/overwrite) ENDIF InitVar, sort_stack, 0 IF IsType(top_stacks,/defined) THEN sort_stack >= 1 sort_stack = (sort_stack > 0) < 2 ngroup = sz[0] nitem = sz[1] nstack = sz[2] IF IsType(stack_empty,/defined) THEN BEGIN index_empty = (where(stack_names EQ stack_empty))[0] IF index_empty EQ -1 THEN message, 'label "'+stack_empty+'" for empty stack entry not found' yvalues_empty = yvalues[*,*,index_empty] yvalues = index_empty EQ 0 ? yvalues[*,*,1:* ] : $ index_empty EQ nstack-1 ? yvalues[*,*,0:nstack-2] : $ [yvalues[*,*,0:index_empty-1],yvalues[*,*,index_empty+1:*]] nstack -= 1 ENDIF IF IsType(stack_colors,/undefined) THEN BEGIN ctable = 38 stack_colors = 8*(2*[1,12,8,15,3,6,7,11,10]-1) loadct, ctable IF n_elements(stack_colors) GT nstack THEN stack_colors = stack_colors[0:nstack-1] ENDIF IF nstack GT n_elements(stack_colors) THEN BEGIN message, /info, 'colors: '+strjoin(stack_colors,',') message, '# colors is '+strcompress(n_elements(stack_colors),/rem)+'; not enough for '+strcompress(nstack,/rem)+' stack names' ENDIF legend_order = indgen(nstack) IF sort_stack NE 0 THEN BEGIN CASE sort_stack OF 1: stack_order = reverse(sort( total( yvalues[*,0,*],1) )) 2: stack_order = reverse(sort( total( reform(yvalues,ngroup*nitem,nstack),1) )) ENDCASE IF (where(stack_order NE indgen(nstack)))[0] NE -1 THEN BEGIN yvalues = yvalues[*,*,stack_order] stack_names = stack_names [stack_order] stack_colors = stack_colors[stack_order] legend_order = legend_order[stack_order] ENDIF ENDIF IF IsType(top_stacks,/defined) THEN BEGIN IF nstack GT top_stacks THEN BEGIN yvalues[*,*,top_stacks-1] = total(yvalues[*,*,top_stacks-1:nstack-1],3) yvalues = yvalues[*,*,0:top_stacks-1] stack_names = [stack_names[0:top_stacks-2],'All '+strcompress(nstack-top_stacks+1,/rem)+' other'] nstack = top_stacks legend_order = legend_order[0:nstack-1] stack_colors = stack_colors[0:nstack-1] CASE sort_stack OF 1: stack_order = [reverse(sort( total(yvalues[*,0,0:nstack-2],1) )),nstack-1] 2: stack_order = [reverse(sort( total( reform(yvalues[*,*,0:nstack-2],ngroup*nitem,nstack-1),1 ) )),nstack-1] ENDCASE IF (where(stack_order NE indgen(nstack)))[0] NE -1 THEN BEGIN yvalues = yvalues[*,*,stack_order] stack_names = stack_names [stack_order] stack_colors = stack_colors[stack_order] legend_order = legend_order[stack_order] ENDIF ENDIF ENDIF IF ngroup NE n_elements(xvalues) THEN BEGIN whatis, xvalues, 'xvalues' whatis, yvalues, 'yvalues' message, 'nr of elements in xvalues must match 1st dim of yvalues' ENDIF CASE 1 OF IsType(xvalues,/string ): BEGIN cvalues = xvalues END IsTYPE(xvalues,/generic_int ): BEGIN cvalues = IsType(format,/defined) ? string(xvalues,format=format) : strcompress(xvalues,/rem) END IsType(xvalues,/generic_float): BEGIN InitVar, format, '(F10.2)' cvalues = string(xvalues,format=format) END ENDCASE cvalues = strtrim(cvalues,2) InitVar, group_spacing, 1 InitVar, item_spacing, group_spacing ; Each 'group' is covered by ncol columns: one for each item, plus ; item_spacing columns for each space in between items. ; nitem : 1 for each group item ; (nitem-1)*item_spacing : empty columns between group items ; group_spacing : empty columns between successive groups ncol = nitem+(nitem-1)*item_spacing+group_spacing ; rvalues sets the position of the centers of the columns. ; The ncol columns for each 'group' cover [0,1] in dataunits, where ; 0 is the position of the label (cvalues). ; (this can be shifted by setting xalign) ; The empty group_spacing columns are split evenly at the beginning ; and end of [0,1]. rvalues = replicate(1,ncol)#indgen(ngroup)+((0.5+0.5*group_spacing+findgen(ncol))/ncol)#replicate(1,ngroup) rvalues = reform(rvalues,ncol*ngroup,/overwrite) d = make_array( dim=[ncol*ngroup,nstack], type=IsType(yvalues), value=0 ) ; Identify the indices in 'd' where the yvalues are stored. ; All group_spacing and item_spacing columns will stay at zero. i = replicate(1,ngroup)#((1+item_spacing)*indgen(nitem))+ncol*indgen(ngroup)#replicate(1,nitem) ; d[*,0] contains counts for layer 0 ; d[*,1] contains sum of counts for layer 0 and 1. ; .. ; d[*,n] contains sum of counts for layers 0 through n ; .. ; d[*,nstack-1] contains sum of counts for all layers FOR n=nstack-1,0,-1 DO d[i,n:nstack-1] += reform(yvalues[*,*,n],ngroup*nitem)#replicate(1,nstack-n) IF IsType(label_stack,/defined) THEN BEGIN InitVar, stack_format, '(F10.2)' ylabels = IsType(yvalues,/generic_float) ? string(yvalues,format=stack_format) : strcompress(yvalues,/rem) ylabels = strtrim(ylabels,2) ylabels = reform( ylabels, size(yvalues,/dim), /overwrite) n = where(yvalues LE label_stack) IF n[0] NE -1 THEN ylabels[n] = '' e = strarr( ncol*ngroup,nstack ) p = fltarr( ncol*ngroup,nstack ) FOR n=0,nstack-1 DO BEGIN e[i,n] = ylabels[*,*,n] p[i,n] = (n EQ 0) ? 0.5*d[i,n] : 0.5*(d[i,n]+d[i,n-1]) ENDFOR ENDIF ; All numbers are in normal coordinates nlegend = n_elements(legend) IF nlegend GE 1 THEN x0 = legend[0] ELSE x0 = 0.10 IF nlegend GE 2 THEN y0 = legend[1] ELSE y0 = 0.90 IF nlegend GE 3 THEN dx = legend[2] ELSE dx = 0.02 ; dist from end of color bar to begin of label IF nlegend GE 4 THEN dx = legend[3] ELSE dy = 0.03 ; height diff between color bars IF nlegend GE 5 THEN ddx = legend[4] ELSE ddx = 0.05 ; length of color bar IF nlegend GE 6 THEN ddy = legend[5] ELSE ddy = 0.02 ; height of color bar ; Plot the stacks in the count array in reverse order: ; the last layer in d contains the total counts for all ; stacks. These will create the highest bar. ; Subsequent stacks will overplot lower bars. InitVar, xalign, 0 FOR n=nstack-1,0,-1 DO BEGIN color = stack_colors[n] ; The first time the axes are created IF IsType(label_stack,/defined) THEN BEGIN barname = e[*,n] barpos = p[*,n] ENDIF plotbars, rvalues, d[*,n] , $ oplot = n NE nstack-1 , $ shade = color , $ /noline , $ silent = silent , $ barname = barname , $ barpos = barpos , $ _extra = _extra , $ ; Only used if n=nstack-1 (for plotting the axes) xstyle = 1 , $ ;xrange = [0,ngroup-1]+[-1,+1], $ xrange = [0,ngroup-1]+[0,+1], $ xtickn = cvalues , $ xtickv = indgen(ngroup)+xalign, $ xticks = ngroup-1 ; Legend IF nlegend GT 0 THEN BEGIN ;y = y0-(nstack-n)*dy y = y0-legend_order[n]*dy polyfill, x0+[0,1,1,0]*ddx, y+[0,0,1,1]*ddy, /normal, color=color xyouts, x0+ddx+dx, y, stack_names[n], /normal, _extra=_extra ENDIF ENDFOR RETURN & END