def tornadoPlot(self, simId, scenario, resultName, tornadoType, sliderValue, selectedData): corrDF = self.getCorrDF(simId, scenario, resultName) if tornadoType == 'normalized': squared = corrDF.spearman**2 corrDF['normalized'] = squared / squared.sum() corrDF['sign'] = 1 corrDF.ix[(corrDF.spearman < 0), 'sign'] = -1 corrDF[ 'value'] = corrDF.normalized * corrDF.sign # normalized squares with signs restored plotColumn = 'value' title = 'Normalized rank correlation' else: plotColumn = 'spearman' title = 'Rank correlation' title += ' of {} for scenario {}'.format(resultName, scenario) varCount = min( 20, len(corrDF) ) # if sliderValue is None else len(corrDF[corrDF['abs'] >= sliderValue])) varNames = list(reversed(corrDF.index[:varCount])) values = list(reversed(corrDF[plotColumn][:varCount])) plotData = go.Bar( orientation='h', x=values, y=varNames, width=0.6, marker=dict( color=getColor('TornadoPlotBar'), line=dict(color=getColor('TornadoPlotLine'), width=0), ), textposition='none', name='Rank correlation', hoverinfo='skip', # prevents hover events ) layout = updateStyle('Plot', title=title, margin=updateStyle('PlotMargin', l=300), xaxis=dict(range=[-1, 1]), dragmode='select') figure = dict(data=[plotData], layout=layout) return figure
def showDistribution(project, simId, scenario, outputName, sliderInfo, distOptions, selectedData): _logger.debug('showDistribution(%s, %s, %s, %s, %s)' % (project, simId, scenario, outputName, selectedData)) # Clear selectedData if context changes context = (project, simId, scenario, outputName) if not data.histogramContext or context != data.histogramContext: _logger.debug('showDistribution: clearing context') selectedData = sliderInfo = None data.histogramContext = context # if called before values are known, return an empty plot if not (project and scenario and outputName) or simId is None: plotData = {'x': []} title = 'Please select a model output to plot' annotations = None else: plotData, title, annotations = data.distPlot( simId, scenario, outputName, sliderInfo, distOptions, selectedData) # TBD: generalize this if Oct16: if outputName == 'percent-change': xtitle = 'Change from baseline fuel' xticksuffix = '%' else: # latex formatting is broken, but HTML works fine # xtitle = '$g CO_2 MJ^{-1}$' if Oct16 else '' xtitle = 'g CO<sub>2</sub> MJ<sup>-1</sup>' xticksuffix = None else: xtitle = data.db.getOutputUnits(outputName) xticksuffix = '' layout = updateStyle('Plot', title=title, yaxis={ 'title': 'Probability density', 'tickvals': [] }, xaxis={ 'title': xtitle, 'ticksuffix': xticksuffix, }, margin=getStyle('PlotMarginWithXTitle'), dragmode='select', showLegend=True, legend=dict(x=0.0, y=1.0, bgcolor=getColor('PlotBg'))) if annotations: layout['annotations'] = annotations figure = dict(data=plotData, layout=layout) return figure
def layout(self): layout = html.Div( [ dataStore(id='paraCoords-vars'), dataStore(id='dist-selected'), html.H1('Monte Carlo Simulation Explorer', className='title'), html.Table( [ html.Tr([ html.Th('Project'), html.Th('Simulation'), html.Th('Scenario'), html.Th('Model Output'), ]), html.Tr([ html.Td(self.projectChooser()), html.Td(self.simChooser()), html.Td(self.scenarioChooser()), html.Td(self.outputChooser()) ]) ], style={ 'width': '100%', 'border': '1px solid black', 'table-layout': 'fixed', 'background-color': getColor('ChooserBgColor') }), html.Div( [ # distribution cluster and tornado chart html.Div([ html.Div(self.distributionSectionLayout(), className='cell twocol'), html.Div(self.tornadoSectionLayout(), className='cell twocol') ], className='row'), # parallel coordinates section html.Div([ html.Div([ html.P([ 'Brush vertically over ranges to filter; click outside the selected range to reset. ', 'Drag variable names below to reorder. Slider sets the number of vars to plot' ], style=getStyle('HelpText')), html.Div( [ dcc.Slider( id='paraCoords-slider', min=1, max=10, step=1, marks={ value: sliderLabel(value) for value in range(1, 11, 1) }, updatemode='drag', value=6), ], style={ 'margin-bottom': 30, 'margin-left': 'auto', 'margin-right': 'auto', 'width': 400 }), html.Div(dcc.Graph(id='paraCoords')), ], className='cell onecol') ], className='row'), # Correlation convergence section html.Div([ html.Div([ html.Span('Correlation convergence', style=getStyle('Label')), html.Div('', style={'height': 15}), html.Div(dcc.Graph(id='corr-convergence')), ], className='cell onecol') ], className='row'), # Scatterplot section html.Div([ html.Div([ html.Div([ html.Div(style={'height': 15}), html.Span( 'Model Inputs (random variables)', style=getStyle('Label')), html.Div(style={'height': 15}), self.inputChooser(multi=True) ], style=updateStyle('Chooser', width=900)), html.Div(style={'height': 15}), html.Div([ html.Span('Model Outputs', style=getStyle('Label')), self.multiOutputChooser() ], style=updateStyle('Chooser', width=900)), html.Div([ html.Button('Create scatterplot', id='scatterplot-button', style=getStyle('Button')) ], style={'margin': 5}), html.Div([dcc.Graph(id='scatter-matrix')], style=updateStyle('AutoMargins')) ], className='cell onecol') ], className='row'), ], className='table', style={ 'width': 1200, 'margin-left': 'auto', 'margin-right': 'auto' }), ], style=getStyle('Page')) return layout
def scatterPlots(self, simId, scenario, inputs, outputs): """ Plot a set of small scatterplots showing correlation between chosen model inputs and outputs. :param simId: (int) simulation id :param scenario: (str) scenario name :param inputs: (iterable of str) names of inputs to plot :param outputs: (iterable of str) names of outputs to plot :return: dash 'figure' data. """ allInputs = self.getParameterValues(simId) inputsDF = allInputs[inputs] outputsDF = pd.DataFrame(columns=outputs) for output in outputs: outputsDF[output] = self.getOutValues(simId, scenario, output) inputsDF = inputsDF.iloc[ outputsDF.index] # select only trials for which we have results numIns = len(inputs) numOuts = len(outputs) fig = tools.make_subplots(rows=numOuts, cols=numIns, vertical_spacing=0.01, shared_xaxes=True, shared_yaxes=True) layout = fig['layout'] for row, output in enumerate(outputs): yAxisNum = row + 1 xAxisNum = 0 for col, input in enumerate(inputs): xAxisNum += 1 trace = go.Scatter( x=inputsDF[input], y=outputsDF[output], hoverinfo='skip', # don't show or fire events mode='markers', marker=dict(size=1, opacity=0.9, color=getColor('ScatterPoints'))) fig.append_trace(trace, row + 1, col + 1) # Compute the names of the corresponding axes xaxis = "xaxis{}".format(xAxisNum) yaxis = "yaxis{}".format(yAxisNum) layout[xaxis].update(title=input, showgrid=False, zeroline=False) layout[yaxis].update(title=output, showgrid=False, zeroline=False) layout.update( margin=dict(l=50, r=30, t=30, b=30), height=140 * numOuts + 20, #width= 130 * numIns, showlegend=False, font=dict(size=9, family=getFont('PlotText'))) return fig
def distPlot(self, simId, scenario, outputName, sliderInfo, distOptions, selectedData): """ Generate the plot data for the distribution plot. Return three items: the plot data, title, and annotations (might be empty list). """ annotations = [] colors = [] values = self.getOutValues(simId, scenario, outputName) if values is None: title = 'Please select a model output to plot' plotData = [ dict( type='bar', x=[], y=[], name=outputName, showlegend=False, ) ] return plotData, title, annotations title = 'Distribution of %s for scenario %s' % (outputName, scenario) bins = min( 100, len(values)) or 1 # 1 for corner case of no data; bins must be > 0 # Generate histogram data to be able to color bars directly counts, edges = np.histogram(values, bins, density=True) barCount = len(counts) # Plot the counts on x-axis at the mean of each pair of edges barValues = [np.mean(edges[i:i + 1]) for i in range(barCount)] if sliderInfo: minQ, maxQ = sliderInfo minX = values.quantile(q=minQ / 100.0, interpolation='linear') maxX = values.quantile(q=maxQ / 100.0, interpolation='linear') else: minX, maxX = selectedData['range']['x'] if selectedData else ( edges[0], edges[-1]) # Sub-select trials based on selection range to show in parallel coords plot self.selectedResults = values[values.between(minX, maxX, inclusive=True)] # highlight selected bars by desaturating non-selected items active = getColor('DistBarsActive') inactive = getColor('DistBarsInactive') for i in range(barCount): colors.append(active if minX <= barValues[i] <= maxX else inactive) distPlot = ff.create_distplot([values], [outputName], bin_size=1, show_hist=False, show_rug=False, curve_type='kde') # TBD: generalize this if outputName == 'percent-change': tickvalues = ['%d%%' % int(value) for value in barValues] else: tickvalues = ['%.2f' % value for value in barValues] plotData = [ dict(type='bar', x=barValues, y=list(counts), name=outputName, histnorm='probability density', marker=dict(color=colors), showlegend=False, text=tickvalues, hoverinfo='text') ] if 'kde' in distOptions: distData = distPlot.data[0] plotData.append( dict( type='scatter', x=distData['x'], y=distData['y'], marker=dict(color=getColor('KDE')), hoverinfo='skip', # prevents hover events showlegend=False, )) showMean = 'mean' in distOptions showMedian = 'median' in distOptions if showMean or showMedian: info = values.describe() maxY = max(counts) labelSize = 12 bgcolor = getColor('PlotBg') mean = info['mean'] median = info['50%'] def lineData(name, x, maxY, color): d = dict(type='scatter', mode='lines', name=name, x=[x, x], y=[0.0000001, maxY], marker=dict(color=color)) return d def notation(x, y, xanchor, color): # TBD: generalize this if outputName == 'percent-change': text = '%d%%' % x else: text = '%.2f' % x d = dict(x=x, y=y, text=text, xanchor=xanchor, yanchor='top', showarrow=False, bgcolor=bgcolor, opacity=0.7, font=dict(family='Lato', size=labelSize, color=color)) return d if showMean: color = getColor('MeanLineColor') xanchor = 'right' if mean <= median else 'left' plotData.append(lineData('mean', mean, maxY, color)) annotations.append(notation(mean, 0.95 * maxY, xanchor, color)) if showMedian: color = getColor('MedianLineColor') xanchor = 'right' if median < mean else 'left' plotData.append(lineData('median', median, maxY, color)) annotations.append(notation(median, maxY, xanchor, color)) return plotData, title, annotations