Skip to content

Distribution Modeler

Jake Close edited this page Dec 12, 2017 · 25 revisions

Project Overview:

Distribution Modeler Module plots the density distribution histogram graph with a distribution model curve plotted on top. The module provides users with the ability to select different distributions and visualize how they fit to the data. Also, the module allows users to modify/interact with the parameters that make up the distribution. For instance, a normal distribution is calculated based off of the mean, variance, and standard deviation. By changing these values in real time, the model curve is recalculated and visualized. This provides users with a better understanding of how parameters influence the distribution.

Project Structure:

The project has many moving pieces and might seem a little overwhelming at first. Thankfully, the artists of Jake and Tem have created this lovely diagram to help visualize the project structure and understand how the Angular project works.

At a high level, the project consists of 5 parts.

(The front/back end is not really a client side / server side relationship. With angular, the jade file and controller use two way binding to interact. At the top of the jade file, you import the controller, which provides with access to all of its functions and variables. While your not really making a traditional "back end" call, the analogy of a front-end backend service (we found) is an easier way to imagine how it works.) It uses the Model View Controller design methodology (MV).

  1. Front End Component: Main.Jade/ Sidebar.jade

  2. Back End Component: Main.controller.coffee , Main.controller.coffee

  3. Graphical Component: ModelerViz.directive.coffee

  • File handles all of the graphing for the the charts + model data. It is imported into the main jade file. Uses d3.js to add graph.
  1. Router Component: ModelerRouter.service.coffee

*Works as a single point of access for the main controller to interact with the distributions. Main controller only has to call each function with the distribution name and router figures out which distribution to use.

  1. Distributions: ModelerDistributionNormal.service.coffee (manyMore) *Implementation of the algorithms required to generate the model data. Each one has the API (functions) that are requested by the router. *ModelerGetParams.service.coffee: many helper functions used in calculation, also extract parameters from dataset.

How to implement a new distribution:

For this example, we will use the Normal Distribution.

  1. Create new file for service following the name style "ModelerDistribution" + "distribution Name"
ModelerDistributionNormal.service.coffe
  1. Implement Normal Distribution Class
  • Create class outline from base class
'use strict'

BaseService = require 'scripts/BaseClasses/BaseService.coffee'

###
  @name:
  @type: service
  @desc: Normal Distribution Class
###

module.exports = class NormalDist extends BaseService
  @inject 'socrat_analysis_modeler_getParams'
  initialize: () ->
    @calc = @socrat_analysis_modeler_getParams

  • Add required member variables and member functions

Member Variables: Name and parameters involved in creating model distribution

    @NormalMean = 5
    @NormalStandardDev = 1
    @NormalVariance = 1
    @name = 'Normal'

  • Member functions: Include:
  • getName: returns name of the distribution
  • getParams: returns models current distribution parameters
  • getChartData: returns the model data by calling get"PDF"points
  • setParams: updates the models current distribution parameters from the Main Controller (UI).
  • PDF: Distributions Probability Density Function
  • CDF: Cumulative Distribution Function (these can be found on wikipedia)
  • get"PDF"Points: creates and array of [{x:, y:} , {x:, y:}] data on interval based off of the PDF function of the dataset
  getName: () ->
    return @name
  getGaussianFunctionPoints: (leftBound, rightBound) ->
    data = []
    for i in [leftBound...rightBound] by .1
      data.push
        x: i
        y: @PDF(i)
    console.log(data)
    data

  getChartData: (params) ->
    
    curveData = getGaussianFunctionPoints( params.xMin , params.xMax)
    return curveData


  stdNormalCDF: (x) ->
    return 0.5 * 0.5 * @calc.erf( x/ Math.sqrt(2))
  

  PDF: (x) ->
    return (1 / (@NormalStandardDev * Math.sqrt(Math.PI * 2))) * Math.exp(-(Math.pow(i - @NormalMean, 2) / (2 * @NormalVariance)))

  CDF: (x)->
    return @stdNormalCDF((x-@NormalMean)/ @NormalStandardDev)

  getParams: () ->
    params =
      mean: @NormalMean
      standardDev: @NormalStandardDev
      variance: @NormalVariance

  setParams: (newParams) ->
    @NormalMean = parseFloat(newParams.stats.mean.toPrecision(4))
    @NormalStandardDev =parseFloat(newParams.stats.standardDev.toPrecision(4))
    @NormalVariance = parseFloat(newParams.stats.variance.toPrecision(4))
  1. Import the service into the my_module.module file, under the component/service section with the rest of the distributions.
 'socrat_modeler_distribution_Normal': require 'scripts/analysis/tools/Modeler/ModelerDistributionNormal.service.coffee'
  1. Import the new distribution into the router page ModelerRouter.service.coffe, add it to the available models list.
  • Inject the file name
 'socrat_modeler_distribution_normal',
  • Initialize the distribution
@Normal = @socrat_modeler_distribution_normal

*Add to available models

@models = [@Normal, @Laplace, @ChiSquared, @MaxwellBoltzman, @LogNormal, @Cauchy, @Exponential]
  1. Lastly, add the distribution to the DistList.service.coffee so the model appears in the SideBar.
   name: 'Normal'
      value: 0
      x: ['integer', 'number', 'string']
      y: false
      z: false
      message: "This class models a normal distribution fitting curve to the dataset"
      xLabel: "Add x"

Router

How to Implement Dynamic Sliders

This part shows how to add Dynamic Sliders. To add dynamic slider, one need to add the slider element in the DOM, particularly in the app/partials/analysis/tools/modeler/main.jade document. For example: Normal distribution Mean slider is added as following:


.row.form-inline
            label
              span.glyphicon.glyphicon-question-sign(
              aria-hidden="true"
              uib-tooltip="Mean of the dataset"
              tooltip-placement="left"
              )  
              | μ
              sub 1
              |  
              input.form-control.input-sm.flat(
              string-to-number
              ng-model="mainArea.NormalMean"
              ng-click="mainArea.NormalSync()"
              ng-keypress ="mainArea.NormalPress($event)"
              ng-disabled="mainArea.deployed"
              type="number"
              step="0.001"
              name='NormalMean'
              )
            #NormalMean

This creates slider element and makes the two way binding. Functions need to implement: NormalSync(), NormalPress($event) in the ModelerMainController.

Current Issues / Future Work

  • Dynamic Y Range using quantile function
  • Slider range/ step values