diff --git a/README.md b/README.md index 434d9285..69d54c9d 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ Clone the repository. $> git clone https://github.com/SOCR/SOCRAT.git Now, lets install all the dependencies. Go into the root folder, + $> cd SOCRAT $> npm install $> bower install @@ -72,7 +73,7 @@ Copyright and License **The LGPL v3.0 License** -Copyright (c) 2013-2015 Statistics Online Computational Resource [(SOCR)](http://www.StatisticsResource.org) +Copyright (c) 2013-2016 Statistics Online Computational Resource [(SOCR)](http://www.StatisticsResource.org) All SOCR programs, materials, tools and resources are developed by and freely disseminated to the entire community. Users may revise, extend, redistribute, modify under the terms of the Lesser GNU General Public License diff --git a/app/app.coffee b/app/app.coffee index 3fca2c7e..cefba07b 100644 --- a/app/app.coffee +++ b/app/app.coffee @@ -240,10 +240,9 @@ App.run([ core.register 'charts', charts core.start 'charts' + #core.register 'importer', importer #core.start 'importer' - core.register 'charts', charts - core.start 'charts' # add module to the list of Tools to appear in Tools tab dropdown tools = [ diff --git a/app/partials/analysis-nav.jade b/app/partials/analysis-nav.jade index 4fd690bb..1d4b89bf 100644 --- a/app/partials/analysis-nav.jade +++ b/app/partials/analysis-nav.jade @@ -5,7 +5,7 @@ ul.nav.navbar-nav li(ng-class="getClass('/wrangleData')") a(ng-href='#/wrangleData') Wrangle Data li.dropdown - a(href="#", data-toggle="dropdown", role="button", aria-haspopup="true", aria-expanded="false").dropdown-toggle + a(href="", data-toggle="dropdown", role="button", aria-haspopup="true", aria-expanded="false").dropdown-toggle | Tools   span.caret ul.dropdown-menu diff --git a/app/partials/analysis/charts/sidebar.jade b/app/partials/analysis/charts/sidebar.jade index 99368294..8b336ad8 100644 --- a/app/partials/analysis/charts/sidebar.jade +++ b/app/partials/analysis/charts/sidebar.jade @@ -4,6 +4,9 @@ div(ng-controller="sideChartsCtrl") h4 Variables div p {{graphSelect.message}} + //div(ng-show=categorical).form-inline + input(ng-model="selectorCat" type="checkbox" id="categorical") + p X is a categorical variable div.form-inline div.form-horizontal(ng-show="graphSelect.x") label(for = "x") Add X @@ -11,6 +14,10 @@ div(ng-controller="sideChartsCtrl") div.form-horizontal(ng-show="graphSelect.y") label(for = "y") Add Y select#y.form-control(ng-change="changeVar(selector2,headers,'y');" ng-model="selector2" ng-options="v as v.value for v in headers") + div(ng-show = "labelVar") + input(ng-model="labelCheck" type="checkbox" id = "label") + p Check if Y variable is the labels column + div.form-horizontal(ng-show="graphSelect.z") label(for = "z") Add Radius select#z.form-control(ng-change="changeVar(selector3,headers,'z');" ng-model="selector3" ng-options="v as v.value for v in headers") diff --git a/app/partials/analysis/getData/main.jade b/app/partials/analysis/getData/main.jade index b9920fe1..0606413b 100644 --- a/app/partials/analysis/getData/main.jade +++ b/app/partials/analysis/getData/main.jade @@ -4,7 +4,7 @@ div(ng-controller='getDataMainCtrl') p.lead To perform any analysis, you need data to begin with. Choose one of the available methods |to get the data into the web-app. - form.socrData(collapse="showState.socrData") + form.socrData(uib-collapse="showState.socrData") legend SOCR Datasets div.form-group select.form-control( @@ -12,7 +12,7 @@ div(ng-controller='getDataMainCtrl') ng-options="item as item.name for item in socrDatasets track by item.id") button.btn.btn-primary(ng-click="getSocrData()") Load - div.worldBank(collapse="showState.worldBank") + div.worldBank(uib-collapse="showState.worldBank") h3 WorldBank Data span Size: input(type="text",class="input-mini", ng-model="size") @@ -21,8 +21,8 @@ div(ng-controller='getDataMainCtrl') option(value='2.4_OOSC.RATE') Out of School Children rate button.btn.btn-primary(ng-click="getWB()") Load - div.generate(collapse="showState.generate") - ul.nav.nav-pills.nav-stacked + div.generate(uib-collapse="showState.generate") + ul.nav.nav-pills.nav-stacked.disabled li a Binomial Coin Toss li @@ -34,10 +34,14 @@ div(ng-controller='getDataMainCtrl') li a The Beta Distribution Experiment - br - handsontable(purpose="json") - - - - + form.jsonUrl(uib-collapse="showState.jsonParse") + legend Enter URL to parse JSON file + div.form-group + label(for="getDataJsonUrlInput").sr-only Enter URL + div.btn-group.btn-group-justified(role="group") + input#getDataJsonUrlInput(type='text', placeholder='URL to JSON ...', ng-model="jsonUrl").form-control + span#getDataJsonUrlClear(ng-click="jsonUrl=''").glyphicon.glyphicon-remove-circle + button.btn.btn-primary(ng-click="getJsonByUrl()") Parse + br + handsontable(purpose="json") \ No newline at end of file diff --git a/app/partials/analysis/getData/sidebar.jade b/app/partials/analysis/getData/sidebar.jade index 5cdc84d5..de05de60 100644 --- a/app/partials/analysis/getData/sidebar.jade +++ b/app/partials/analysis/getData/sidebar.jade @@ -2,29 +2,35 @@ div(ng-controller="getDataSidebarCtrl") p. Data input div.list-group a#getDataGrid.list-group-item( - href="#" + href="" ng-click="show('grid')" ng-class="{active:selected == 'getDataGrid'}") | Copy-paste to spreadsheet a#getDataSocrData.list-group-item( - href="#" + href="" ng-click="show('socrData')" ng-class="{active:selected == 'getDataSocrData'}") | Load SOCR dataset a#getDataWorldBank.list-group-item( - href="#" + href="" ng-click="show('worldBank')" ng-class="{active:selected == 'getDataWorldBank'}") | Load data from WorldBank - a#getDataGenerate.list-group-item.disabled( - href="#" + a#getDataGenerate.list-group-item( + href="" ng-click="show('generate')" ng-class="{active:selected == 'getDataGenerate'}") | Use data generator - a.list-group-item.disabled(href="#", ng-click="getJson()", ng-class="{active:selected == 'getDataJson'}") - | Parse JSON fileParse SOCR Data page - a.list-group-item.disabled(href="#", ng-click="save()", ng-class="{active:selected == 'getDataSocrPage'}") - | Parse SOCR Data page + a#getDataJson.list-group-item( + href="" + ng-click="show('jsonParse')" + ng-class="{active:selected == 'getDataJson'}") + | Parse JSON file by URL + a#getDataSocrPage.list-group-item.disabled( + href="" + ng-click="save()" + ng-class="{active:selected == 'getDataSocrPage'}") + | Parse SOCR Data page by URL // accordion(close-others="true") diff --git a/app/partials/analysis/tools/machineLearning/kMeans/sidebar.jade b/app/partials/analysis/tools/machineLearning/kMeans/sidebar.jade index d85813b4..3ef32d46 100644 --- a/app/partials/analysis/tools/machineLearning/kMeans/sidebar.jade +++ b/app/partials/analysis/tools/machineLearning/kMeans/sidebar.jade @@ -1,53 +1,55 @@ div(ng-controller="kMeansSidebarCtrl") form legend Parameters - div.checkbox - label - input(type="checkbox", ng-model="wholedataseton") - | Cluster whole dataset (visualize 2D projection) - div.form-group - label X column   - //- show data header column names - select(ng-model="xCol", ng-options="col for col in cols").form-control - div.form-group - label Y column   - //- show data header column names - select(ng-model="yCol", ng-options="col for col in cols").form-control - div.form-group - fieldset(ng-disabled="!labelson") - label Labels column   + fieldset(ng-disabled="kmeanson") + div.checkbox + label + input(type="checkbox", ng-model="wholedataseton") + | Cluster whole dataset (visualize 2D projection) + div.form-group + label X column   //- show data header column names - select(ng-model="labelCol", ng-options="col for col in cols").form-control - div.checkbox - label - input(type="checkbox", ng-model="labelson") - | Labels present - div.form-group - label k   - select(ng-model="k", ng-options="k for k in ks").form-control - div.checkbox(ng-hide="!labelson") - label - input( - type="checkbox" - ng-model="accuracyon" - ng-disabled="k !== numUniqueLabels.num || labelCol !== numUniqueLabels.labelCol") - | Compute accuracy - div.form-group - label Distance   - select(ng-model="dist", ng-options="dist for dist in distances").form-control - div.form-group - label Initialization   - select(ng-model="initMethod", ng-options="init for init in inits").form-control - button.btn.btn-info( - type="submit" - aria-label="Get k value" - ng-click="detectKValue()" - ng-disabled="!labelson") - | Detect k - button.btn.btn-primary( - type="submit" - aria-label="Run k-Means" - ng-click="run()" - ng-disabled="kmeanson") - | Run  - span.glyphicon.glyphicon-refresh(aria-hidden="true", ng-class="running") + select(ng-model="xCol", ng-options="col for col in cols", ng-change="updateDataPoints()").form-control + div.form-group + label Y column   + //- show data header column names + select(ng-model="yCol", ng-options="col for col in cols", ng-change="updateDataPoints()").form-control + div.form-group + fieldset(ng-disabled="!labelson") + label Labels column   + //- show data header column names + select(ng-model="labelCol", ng-options="col for col in cols").form-control + div.checkbox + label + input(type="checkbox", ng-model="labelson") + | Labels present + div.form-group + fieldset.form-inline + label k   + select(ng-model="k", ng-options="k for k in ks").form-control + button.btn.btn-info( + type="submit" + aria-label="Get k value" + ng-click="detectKValue()" + ng-disabled="!labelson") + | Detect k + div.checkbox(ng-hide="!labelson") + label + input( + type="checkbox" + ng-model="accuracyon" + ng-disabled="k !== numUniqueLabels.num || labelCol !== numUniqueLabels.labelCol") + | Compute accuracy + div.form-group + label Distance   + select(ng-model="dist", ng-options="dist for dist in distances").form-control + div.form-group + label Initialization   + select(ng-model="initMethod", ng-options="init for init in inits").form-control + button.btn.btn-primary( + type="submit" + aria-label="Run k-Means" + ng-click="run()" + ng-disabled="kmeanson") + | Run  + span.glyphicon.glyphicon-refresh(aria-hidden="true", ng-class="running") diff --git a/app/scripts/analysis/charts/charts.coffee b/app/scripts/analysis/charts/charts.coffee index 4641ecf3..9784c7a0 100644 --- a/app/scripts/analysis/charts/charts.coffee +++ b/app/scripts/analysis/charts/charts.coffee @@ -83,9 +83,9 @@ charts = angular.module('app_analysis_charts', []) name: 'Bar Graph' value: 0 x: true - y: false + y: true z: false - message: "Choose a numerical variable for x and a categorical variable for y." + message: "Use option x to choose a numerical or categorical variable, or choose one categorical variable and one numerical variable." , name: 'Scatter Plot' value: 1 @@ -114,6 +114,13 @@ charts = angular.module('app_analysis_charts', []) y: false z: false message: "Choose one variable to put into a pie chart." + , + name: 'Stream Graph' + value: 5 + x: true + y: true + z: false + message: "Choose two numerical variables" ] $scope.graphSelect = {} @@ -130,7 +137,7 @@ charts = angular.module('app_analysis_charts', []) for i in [1...len] by 1 tmp = - x: parseFloat _chartData[$scope.graphInfo.x][i].value + x: _chartData[$scope.graphInfo.x][i].value obj.push tmp else if $scope.graphInfo.y isnt "" and $scope.graphInfo.z is "" @@ -138,8 +145,8 @@ charts = angular.module('app_analysis_charts', []) for i in [1...len] by 1 tmp = - x: parseFloat _chartData[$scope.graphInfo.x][i].value - y: parseFloat _chartData[$scope.graphInfo.y][i].value + x: _chartData[$scope.graphInfo.x][i].value + y: _chartData[$scope.graphInfo.y][i].value obj.push tmp else @@ -147,9 +154,9 @@ charts = angular.module('app_analysis_charts', []) for i in [1...len] by 1 tmp = - x: parseFloat _chartData[$scope.graphInfo.x][i].value - y: parseFloat _chartData[$scope.graphInfo.y][i].value - z: parseFloat _chartData[$scope.graphInfo.z][i].value + x: _chartData[$scope.graphInfo.x][i].value + y: _chartData[$scope.graphInfo.y][i].value + z: _chartData[$scope.graphInfo.z][i].value obj.push tmp return obj @@ -163,8 +170,12 @@ charts = angular.module('app_analysis_charts', []) $rootScope.$broadcast 'charts:graphDiv', results + $scope.labelVar = false + $scope.labelCheck = null $scope.changeName = () -> $scope.graphInfo.graph = $scope.graphSelect.name + + $scope.createGraph() $scope.changeVar = (selector,headers, ind) -> @@ -172,6 +183,7 @@ charts = angular.module('app_analysis_charts', []) if selector.value is h.value then $scope.graphInfo[ind] = parseFloat h.key $scope.createGraph() + sb = ctrlMngr.getSb() token = sb.subscribe @@ -211,34 +223,10 @@ charts = angular.module('app_analysis_charts', []) format: _format ]) -#.factory 'stackedBar', [ -# () -> -# _drawStack = (width, height, ) -> -# x = d3.scale.ordinal() -# .rangeRoundBands([0, width], .1) -# -# y = d3.scale.linear() -# .rangeRound([height, 0]) -# -# color = d3.scale.ordinal() -# .range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]) -# -# xAxis = d3.svg.axis() -# .scale(x) -# .orient("bottom") -# -# yAxis = d3.svg.axis() -# .scale(y) -# .orient("left") -# .tickFormat(d3.format(".2s")) -# -# -# drawStack:_drawStack -#] - -.factory 'scatterplot', [ + +.factory 'scatterPlot', [ () -> - _drawScatterplot = (data,ranges,width,height,_graph,container,gdata) -> + _drawScatterPlot = (data,ranges,width,height,_graph,container,gdata) -> x = d3.scale.linear().domain([ranges.xMin,ranges.xMax]).range([ 0, width ]) y = d3.scale.linear().domain([ranges.yMin,ranges.yMax]).range([ height, 0 ]) @@ -254,8 +242,8 @@ charts = angular.module('app_analysis_charts', []) yMap = (d)-> y yValue(d) # set up fill color - cValue = (d)-> d.y - color = d3.scale.category10() + #cValue = (d)-> d.y + #color = d3.scale.category10() # x axis _graph.append("g") @@ -265,7 +253,6 @@ charts = angular.module('app_analysis_charts', []) .append('text') .attr('class', 'label') .attr('transform', 'translate(' + (width / 2) + ',' + 40 + ')') -# .style('text-anchor', 'end') .text gdata.xLab.value # y axis @@ -278,14 +265,12 @@ charts = angular.module('app_analysis_charts', []) .attr('y', -50 ) .attr('x', -(height / 2)) .attr("dy", ".71em") -# .style("text-anchor", "end") .text gdata.yLab.value # add the tooltip area to the webpage tooltip = container .append('div') .attr('class', 'tooltip') - # .style('opacity', 0) # draw dots _graph.selectAll('.dot') @@ -295,7 +280,8 @@ charts = angular.module('app_analysis_charts', []) .attr('r', 5) .attr('cx', xMap) .attr('cy', yMap) - .style('fill', (d)->color cValue(d)) + .style('fill', 'DodgerBlue') + .attr('opacity', '0.5') .on('mouseover', (d)-> tooltip.transition().duration(200).style('opacity', .9) tooltip.html('
(' + xValue(d)+ ',' + yValue(d) + ')
') @@ -303,30 +289,26 @@ charts = angular.module('app_analysis_charts', []) .on('mouseout', (d)-> tooltip. transition().duration(500).style('opacity', 0)) - - - drawScatterplot: _drawScatterplot - + drawScatterPlot: _drawScatterPlot ] .factory 'histogram',[ () -> - _drawHist = (_graph,data,container,gdata,width,height) -> + _drawHist = (_graph,data,container,gdata,width,height,ranges) -> container.append('input').attr('id', 'slider').attr('type','range').attr('min', '1').attr('max','10').attr('step', '1').attr('value','5') bins = null dataHist = null arr = data.map (d) -> parseFloat d.x - x = d3.scale.linear().domain([0,d3.max arr]).range([0,width]) - + x = d3.scale.linear().domain([ranges.xMin, ranges.xMax]).range([0,width]) plotHist = (bins) -> $('#slidertext').remove() container.append('text').attr('id', 'slidertext').text('Bin Slider: '+bins).attr('position','relative').attr('left', '50px') dataHist = d3.layout.histogram().bins(bins)(arr) - y = d3.scale.linear().domain([0, d3.max dataHist.map (i) -> i.length]).range([0, height]) + y = d3.scale.linear().domain([0,d3.max dataHist.map (i) -> i.length]).range([height,0]) yAxis = d3.svg.axis().scale(y).orient("left") xAxis = d3.svg.axis().scale(x).orient("bottom") @@ -343,7 +325,6 @@ charts = angular.module('app_analysis_charts', []) .append('text') .attr('class', 'label') .attr('transform', 'translate(' + (width / 2) + ',' + 40 + ')') -# .style('text-anchor', 'end') .text gdata.xLab.value # y axis @@ -356,7 +337,6 @@ charts = angular.module('app_analysis_charts', []) .attr('y', -50 ) .attr('x', -(height / 2)) .attr("dy", ".71em") -# .style("text-anchor", "end") .text "Count" bar = _graph.selectAll('.bar') @@ -365,19 +345,21 @@ charts = angular.module('app_analysis_charts', []) bar.enter() .append("g") + rect_width = width/bins bar.append('rect') -# .style('fill', 'steelblue') - .attr('x', (d,i) -> x d.x) + .attr('x', (d) -> x d.x) .attr('y', (d) -> height - y d.y) - .attr('width', (d) -> x d.dx) + .attr('width', rect_width) .attr('height', (d) -> y d.y) + .attr("stroke","white") + .attr("stroke-width",1) .on('mouseover', () -> d3.select(this).transition().style('fill', 'orange')) .on('mouseout', () -> d3.select(this).transition().style('fill', 'steelblue')) bar.append('text') - .attr('x', (d,i) -> x d.x) + .attr('x', (d) -> x d.x) .attr('y', (d) -> height - y d.y) - .attr('dx', (d) -> .5*x d.dx) + .attr('dx', (d) -> .5*rect_width) .attr('dy', '20px') .attr('fill', '#fff') .attr('text-anchor', 'middle') @@ -396,73 +378,70 @@ charts = angular.module('app_analysis_charts', []) .factory 'pie', [ () -> - pieData = null + valueSum = 0 makePieData = (data) -> - pieMax = d3.max(data, (d)->parseFloat d.x) - pieMin = d3.min(data, (d)->parseFloat d.x) - maxPiePieces = 7 # set magic constant to variable - rangeInt = Math.ceil((pieMax - pieMin)/maxPiePieces) - console.log rangeInt - piePieces = new Array(maxPiePieces - 1) # create array with numbers of pie pieces - for i in [0..maxPiePieces-1] by 1 - piePieces[i] = [] - - for el in data - index = Math.floor((el.x - pieMin)/rangeInt) - piePieces[index].push(el.x) # assign each el.x to a piePiece - console.log "piePieces[" + index + "]=" + piePieces[index] - - obj = {} - for i in [0..maxPiePieces-1] by 1 - obj[i] = piePieces[i].length - - pieData = d3.entries obj - for d in pieData - d.key = (d.key*rangeInt)+pieMin+"-"+((d.key-1)*rangeInt)+pieMin - return pieData + valueSum = 0 + counts = {} + if(!isNaN(data[0].x)) # data is number + pieMax = d3.max(data, (d)-> parseFloat d.x) + pieMin = d3.min(data, (d)-> parseFloat d.x) + maxPiePieces = 7 # set magic constant to variable + rangeInt = Math.ceil((pieMax - pieMin) / maxPiePieces) + counts = {} + for val in data + index = Math.floor((val.x - pieMin) / rangeInt) + groupName = index + "-" + (index + rangeInt) + #console.log groupName + counts[groupName] = counts[groupName] || 0 + counts[groupName]++ + valueSum++ + else # data is string + for i in [0..data.length-1] by 1 + currentVar = data[i].x + counts[currentVar] = counts[currentVar] || 0 + counts[currentVar]++ + valueSum++ + + obj = d3.entries counts + return obj _drawPie = (data,width,height,_graph) -> - makePieData(data) radius = Math.min(width, height) / 2 - arc = d3.svg.arc() .outerRadius(radius) .innerRadius(0) - labelArc = d3.svg.arc() - .outerRadius(radius-10) - .innerRadius(radius-10) - - color = d3.scale.ordinal().range(["#ffffcc","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#0c2c84"]) - + #color = d3.scale.ordinal().range(["#ffffcc","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#0c2c84"]) + color = d3.scale.category20c() arcOver = d3.svg.arc() .outerRadius(radius + 10) - # .innerRadius(0+10) pie = d3.layout.pie() -#.value (d) -> d.count - .value (d) -> parseFloat d.value - type = (d) -> - d.y = +d.y - return d + .value((d)-> d.value) + .sort(null) + + formatted_data = makePieData(data) + #console.log formatted_data + #console.log pie(formatted_data) arcs = _graph.selectAll(".arc") - .data(pie(pieData)) + .data(pie(formatted_data)) .enter() .append('g') .attr("class", "arc") arcs.append('path') .attr('d', arc) - .attr('fill', (d) -> color(d.data.key)) - .on('mouseenter', (d) -> d3.select(this).attr("stroke","white").transition().attr("d", arcOver).attr("stroke-width",5)) + .attr('fill', (d) -> color(d.data.value)) + .on('mouseenter', (d) -> d3.select(this).attr("stroke","white") .transition().attr("d", arcOver).attr("stroke-width",3)) .on('mouseleave', (d) -> d3.select(this).transition().attr('d', arc).attr("stroke", "none")) arcs.append('text') .attr('id','tooltip') .attr('transform', (d) -> 'translate('+arc.centroid(d)+')') .attr('text-anchor', 'middle') - .text (d) -> d.data.key + .text (d) -> d.data.key + ': ' + parseFloat(100*d.data.value/valueSum).toFixed(2) + '%' + drawPie: _drawPie ] @@ -474,16 +453,26 @@ charts = angular.module('app_analysis_charts', []) xAxis = d3.svg.axis().scale(x).orient('bottom') yAxis = d3.svg.axis().scale(y).orient('left') - r = d3.scale.linear() - .domain([d3.min(data, (d)-> parseFloat d.z), d3.max(data, (d)-> parseFloat d.z)]) - .range([3,15]) - - rValue = (d)->parseFloat d.z + zIsNumber = !isNaN(data[0].z) + + r = 0 + rValue = 0 + if(zIsNumber) + r = d3.scale.linear() + .domain([d3.min(data, (d)-> parseFloat d.z), d3.max(data, (d)-> parseFloat d.z)]) + .range([3,15]) + rValue = (d) -> parseFloat d.z + else + r = d3.scale.linear() + .domain([5, 5]) + .range([3,15]) + rValue = (d) -> d.z tooltip = container .append('div') .attr('class', 'tooltip') - # .style('opacity', 0) + + color = d3.scale.category10() # x axis _graph.append("g") @@ -493,7 +482,6 @@ charts = angular.module('app_analysis_charts', []) .append('text') .attr('class', 'label') .attr('transform', 'translate(' + (width / 2) + ',' + 40 + ')') -# .style('text-anchor', 'end') .text gdata.xLab.value # y axis @@ -506,21 +494,31 @@ charts = angular.module('app_analysis_charts', []) .attr('y', -50 ) .attr('x', -(height / 2)) .attr("dy", ".71em") -# .style("text-anchor", "end") .text gdata.yLab.value - # create circle _graph.selectAll('.circle') .data(data) .enter().append('circle') - .attr('fill', 'yellow') + .attr('fill', + if(zIsNumber) + 'yellow' + else + (d) -> color(d.z)) .attr('opacity', '0.7') - .attr('stroke', 'orange') + .attr('stroke', + if(zIsNumber) + 'orange' + else + (d) -> color(d.z)) .attr('stroke-width', '2px') .attr('cx', (d) -> x d.x) .attr('cy', (d) -> y d.y) - .attr('r', (d) -> r d.z) + .attr('r', (d) -> + if(zIsNumber) # if d.z is number, use d.z as radius + r d.z + else # else, set radius to be 8 + 8) .on('mouseover', (d) -> tooltip.transition().duration(200).style('opacity', .9) tooltip.html('
'+gdata.zLab.value+': '+ rValue(d)+'
') @@ -532,57 +530,296 @@ charts = angular.module('app_analysis_charts', []) .factory 'bar', [ () -> + _setAxisPar = (x,y,xAxis,yAxis,type, width, height) -> + ord = d3.scale.ordinal() + lin = d3.scale.linear() + + switch type + when "xCat" or "xCatAndyNum" + x = ord.rangeRoundBands([0, width], .1) + y = lin.range([ height, 0 ]) + when "xNum" or "xNumAndyCat" + x = lin.range([ 0, width ]) + y = ord.rangeRoundBands([height, 0], .1) + when "xNumAndyNum" + x = lin.range([ 0, width ]) + y = lin.range([ height, 0 ]) + else + alert "Two categorical variables" + + + xAxis = d3.svg.axis().scale(x).orient('bottom') + yAxis = d3.svg.axis().scale(y).orient('left') + _drawBar = (width,height,data,_graph,gdata) -> x = d3.scale.linear().range([ 0, width ]) y = d3.scale.linear().range([ height, 0 ]) + + xAxis = d3.svg.axis().scale(x).orient('bottom') yAxis = d3.svg.axis().scale(y).orient('left') x.domain([d3.min(data, (d)->parseFloat d.x), d3.max(data, (d)->parseFloat d.x)]) y.domain([d3.min(data, (d)->parseFloat d.y), d3.max(data, (d)->parseFloat d.y)]) + #without y + if !data[0].y + #Works + if isNaN data[0].x + counts = {} + for i in [0..data.length-1] by 1 + currentVar = data[i].x + counts[currentVar] = counts[currentVar] || 0 + counts[currentVar]++ + counts = d3.entries counts +# console.log counts + x = d3.scale.ordinal().rangeRoundBands([0, width], .1) + xAxis = d3.svg.axis().scale(x).orient('bottom') + x.domain(counts.map (d) -> d.key) + y.domain([d3.min(counts, (d)-> parseFloat d.value), d3.max(counts, (d)-> parseFloat d.value)]) + + _graph.append('g') + .attr('class', 'x axis') + .attr('transform', 'translate(0,' + height + ')') + .call xAxis + .append('text') + .attr('class', 'label') + .attr('transform', 'translate(' + (width / 2) + ',' + 40 + ')') + .text gdata.xLab.value + + _graph.append('g') + .attr('class', 'y axis') + .call yAxis + .append('text') + .attr('transform', 'rotate(-90)') + .attr('y', -50 ) + .attr('x', -(height / 2)) + .attr('dy', '1em') + .text "Count" + + # create bar elements + _graph.selectAll('rect') + .data(counts) + .enter().append('rect') + .attr('class', 'bar') + .attr('x',(d)-> x d.key ) + .attr('width', x.rangeBand()) + .attr('y', (d)-> y d.value ) + .attr('height', (d)-> Math.abs(height - y d.value)) + .attr('fill', 'steelblue') + + + else #data is numerical and only x. height is rect width, width is x of d.x, + #y becomes the categorical + y = d3.scale.ordinal().rangeRoundBands([height, 0], .1) + yAxis = d3.svg.axis().scale(y).orient('left') + + y.domain((d) -> d.x) + + _graph.append('g') + .attr('class', 'x axis') + .attr('transform', 'translate(0,' + height + ')') + .call xAxis + .append('text') + .attr('class', 'label') + .attr('transform', 'translate(' + (width / 2) + ',' + 40 + ')') + .text gdata.xLab.value + + _graph.append('g') + .attr('class', 'y axis') + .call yAxis + .append('text') + .attr('transform', 'rotate(-90)') + .attr('y', -50 ) + .attr('x', -(height / 2)) + .attr('dy', '1em') + .text "Null" + + rectWidth = height/data.length + # create bar elements + _graph.selectAll('rect') + .data(data) + .enter().append('rect') + .attr('class', 'bar') + #.attr('x',(d)-> x d.x ) + .attr('width', (d)-> x d.x) + .attr('y', (d,i)-> i*rectWidth) + .attr('height', rectWidth) + .attr('fill', 'steelblue') + + + + #with y + else + #y is categorical + if isNaN data[0].y + + y = d3.scale.ordinal().rangeRoundBands([0, height], .1) + y.domain(data.map (d) -> d.y) + yAxis = d3.svg.axis().scale(y).orient('left') + + _graph.append('g') + .attr('class', 'x axis') + .attr('transform', 'translate(0,' + height + ')') + .call xAxis + .append('text') + .attr('class', 'label') + .attr('x', width-80) + .attr('y', 30) + .text gdata.xLab.value + + _graph.append('g') + .attr('class', 'y axis') + .call yAxis + .append('text') + .attr('transform', 'rotate(-90)') + .attr("x", -80) + .attr("y", -40) + .attr('dy', '1em') + .text gdata.yLab.value + + _graph.selectAll('rect') + .data(data) + .enter().append('rect') + .attr('class', 'bar') + #.attr('x',(d)-> x d.x ) + .attr('width', (d) -> Math.abs(x d.x)) + .attr('y', (d)-> y d.y ) + .attr('height', y.rangeBand()) + .attr('fill', 'steelblue') + + + else if !isNaN data[0].y + if isNaN data[0].x + console.log "xCat and yNum" + x = d3.scale.ordinal().rangeRoundBands([0, width], .1) + x.domain(data.map (d) -> d.x) + xAxis = d3.svg.axis().scale(x).orient('bottom') + #y.domain([d3.min(data, (d)-> parseFloat d.y), d3.max(data, (d)-> parseFloat d.y)]) + + _graph.append('g') + .attr('class', 'x axis') + .attr('transform', 'translate(0,' + height + ')') + .call xAxis + .append('text') + .attr('class', 'label') + .attr('transform', 'translate(' + (width / 2) + ',' + 40 + ')') + .text gdata.xLab.value + + _graph.append('g') + .attr('class', 'y axis') + .call yAxis + .append('text') + .attr('transform', 'rotate(-90)') + .attr('y', -50 ) + .attr('x', -(height / 2)) + .attr('dy', '1em') + .text "Count" + + # create bar elements + _graph.selectAll('rect') + .data(data) + .enter().append('rect') + .attr('class', 'bar') + .attr('x',(d)-> x d.x ) + .attr('width', x.rangeBand()) + .attr('y', (d)-> y d.y ) + .attr('height', (d)-> Math.abs(height - y d.y)) + .attr('fill', 'steelblue') + else + + #else if !isNaN data[0].y and !isNaN data[0].x + rectWidth = width / data.length + + _graph.append('g') + .attr('class', 'x axis') + .attr('transform', 'translate(0,' + height + ')') + .call xAxis + .append('text') + .attr('class', 'label') + .attr('x', width-80) + .attr('y', 30) + .text gdata.xLab.value + + _graph.append('g') + .attr('class', 'y axis') + .call yAxis + .append('text') + .attr('transform', 'rotate(-90)') + .attr("x", -80) + .attr("y", -40) + .attr('dy', '1em') + .text gdata.yLab.value + + + # create bar elements + _graph.selectAll('rect') + .data(data) + .enter().append('rect') + .attr('class', 'bar') + .attr('x',(d)-> x d.x ) + .attr('width', rectWidth) + .attr('y', (d)-> y d.y ) + .attr('height', (d)-> Math.abs(height - y d.y) ) + .attr('fill', 'steelblue') - _graph.append('g') - .attr('class', 'x axis') - .attr('transform', 'translate(0,' + height + ')') - .call xAxis - .append('text') - .attr('class', 'label') - .attr('x', width-80) - .attr('y', 30) - .text gdata.xLab.value - - _graph.append('g') - .attr('class', 'y axis') - .call yAxis - .append('text') - .attr('transform', 'rotate(-90)') - .attr("x", -80) - .attr("y", -40) - .attr('dy', '1em') - .text gdata.yLab.value - rectWidth = width / data.length - # create bar elements - _graph.selectAll('rect') - .data(data) - .enter().append('rect') - .attr('class', 'bar') - .attr('x',(d)-> x d.x ) - .attr('width', rectWidth) - .attr('y', (d)-> y d.y ) - .attr('height', (d)-> Math.abs(height - y d.y) ) - .attr('fill', 'steelblue') drawBar: _drawBar ] +.factory 'streamGraph', [ + () -> + + _randomSample = (data, n) -> + #take an array of objects, and the desired number of random ones. return array of objects + for d in data + d.x = +d.x + d.y = +d.y + + random = [] + for i in [0...n-1] by 1 + random.push(data[Math.floor(Math.random() * data.length)]) + return random + + _streamGraph = (data,ranges,width,height,_graph) -> + n = 20 + m = 100 + stack = d3.layout.stack().offset('wiggle') + layers = stack(d3.range(n).map () -> _randomSample(data,m)) + console.log layers + x = d3.scale.linear() + .domain([0, m - 1]) + .range([0, width]); + + y = d3.scale.linear() + .domain([0, d3.max(layers, (layer)-> d3.max(layer, (d) -> return d.y0 + d.y))]) + .range([height, 0]) + + color = d3.scale.linear() + .range(["#aad", "#556"]) + + area = d3.svg.area() + .x((d) -> x d.x) + .y0((d) -> y d.y0) + .y1((d) -> y(d.y0 + d.y)) + + _graph.selectAll("path") + .data(layers) + .enter().append("path") + .attr("d", area) + .style("fill", () -> color(Math.random())) + + streamGraph: _streamGraph +] + .directive 'd3Charts', [ - 'scatterplot', + 'scatterPlot', 'histogram', 'pie', 'bubble', - 'bar' - (scatterplot,histogram,pie,bubble,bar) -> + 'bar', + 'streamGraph' + (scatterPlot,histogram,pie,bubble,bar,streamGraph) -> restrict: 'E' template: "
" link: (scope, elem, attr) -> @@ -600,10 +837,14 @@ charts = angular.module('app_analysis_charts', []) if newChartData gdata = newChartData data = newChartData.data +# _label = null + + console.log data #id = '#'+ newInfo.name container = d3.select(elem.find('div')[0]) container.selectAll('*').remove() + console.log "test" svg = container.append('svg').attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom) #svg.select("#remove").remove() _graph = svg.append('g').attr("transform", "translate(" + margin.left + "," + margin.top + ")") @@ -615,17 +856,21 @@ charts = angular.module('app_analysis_charts', []) xMax: d3.max data, (d) -> parseFloat d.x yMax: d3.max data, (d) -> parseFloat d.y +# $scope.on 'Charts: labels y', (events, data) -> +# _label = data + switch gdata.name when 'Bar Graph' bar.drawBar(width,height,data,_graph,gdata) when 'Bubble Chart' bubble.drawBubble(ranges,width,height,_graph,data,gdata,container) when 'Histogram' - histogram.drawHist(_graph,data,container,gdata,width,height) + histogram.drawHist(_graph,data,container,gdata,width,height,ranges) when 'Pie Chart' _graph = svg.append('g').attr("transform", "translate(300,250)").attr("id", "remove") pie.drawPie(data,width,height,_graph) when 'Scatter Plot' - scatterplot.drawScatterplot(data,ranges,width,height,_graph,container,gdata) + scatterPlot.drawScatterPlot(data,ranges,width,height,_graph,container,gdata) + when 'Stream Graph' + streamGraph.streamGraph(data,ranges,width,height,_graph) ] - diff --git a/app/scripts/analysis/getData/getData.coffee b/app/scripts/analysis/getData/getData.coffee index 41173ae7..5de01be0 100644 --- a/app/scripts/analysis/getData/getData.coffee +++ b/app/scripts/analysis/getData/getData.coffee @@ -106,10 +106,11 @@ getData = angular.module('app_analysis_getData', [ console.log 'handsontable data updated to db' _setData = (data) -> - console.log '%c inputCache set called for the project'+$stateParams.projectId+':'+$stateParams.forkId, 'color:steelblue' + console.log '%c inputCache set called for the project' + $stateParams.projectId + ':' + $stateParams.forkId, + 'color:steelblue' # TODO: fix checking existance of parameters to default table name #SOCR-140 - if data? or $stateParams.projectId? or $stateParams.forkId? + if data? or $stateParams.projectId? or $stateParams.forkId? _data = data unless data is 'edit' # clear any previous db update broadcast messages @@ -239,7 +240,7 @@ getData = angular.module('app_analysis_getData', [ # get the sandbox made for this module # sb = getDataSb.getSb() # console.log 'sandbox created' - $scope.jsonUrl = 'url..' + $scope.jsonUrl = '' flag = true $scope.selected = null @@ -265,12 +266,17 @@ getData = angular.module('app_analysis_getData', [ $scope.selected = 'getDataWorldBank' $scope.$emit 'change in showStates', 'worldBank' - when 'generate' + when 'generate' $scope.selected = 'getDataGenerate' $scope.$emit 'change in showStates', 'generate' + when 'jsonParse' + $scope.selected = 'getDataJson' + $scope.$emit 'change in showStates', 'jsonParse' + # getJson $scope.getJson = -> + console.log 123 console.log $scope.jsonUrl if $scope.jsonUrl is '' @@ -359,6 +365,7 @@ getData = angular.module('app_analysis_getData', [ d3.text url, (dataResults) -> if dataResults?.length > 0 + # parse to unnamed array dataResults = d3.csv.parseRows dataResults _data = columnHeader: dataResults.shift() @@ -376,12 +383,32 @@ getData = angular.module('app_analysis_getData', [ else console.log 'rejected:' + msg + $scope.getJsonByUrl = -> + d3.json $scope.jsonUrl, + (dataResults) -> + if dataResults?.length > 0 + _data = + columnHeader: dataResults.shift() + data: [null, dataResults] + # purpose is helps in pin pointing which + # handsontable directive to update. + purpose: 'json' + console.log 'resolved' + # pass a message to update the handsontable div + # data is the formatted data which plugs into the + # handontable. + $scope.$emit 'update handsontable', _data + # switch the accordion from getJson to grid + # $scope.$emit("change in showStates","grid") + else + console.log 'rejected:' + msg + try - _showState = new showState(['grid', 'socrData', 'worldBank', 'generate'], $scope) + _showState = new showState(['grid', 'socrData', 'worldBank', 'generate', 'jsonParse'], $scope) catch e console.log e.message - # Adding Listeners + # adding listeners $scope.$on 'update showStates', (obj, data) -> _showState.set data @@ -389,7 +416,7 @@ getData = angular.module('app_analysis_getData', [ console.log 'get data main div loaded' ]) -# Helps sidebar accordion to keep in sync with the main div. +# Helps sidebar accordion to keep in sync with the main div .factory('showState', -> (obj, scope) -> if arguments.length is 0 @@ -410,8 +437,6 @@ getData = angular.module('app_analysis_getData', [ scope.showState[index] = false else scope.showState[i] = true - #console.log "final state" - #console.log scope.showState ) # ### diff --git a/app/scripts/analysis/tools/machineLearning/kMeans/kmeans.coffee b/app/scripts/analysis/tools/machineLearning/kMeans/kmeans.coffee index 3cc2682c..c840ab5b 100644 --- a/app/scripts/analysis/tools/machineLearning/kMeans/kmeans.coffee +++ b/app/scripts/analysis/tools/machineLearning/kMeans/kmeans.coffee @@ -87,6 +87,9 @@ kMeans = angular.module('app_analysis_kMeans', []) # safe enforce $scope.$digest to activate directive watchers $timeout updateChartData + $scope.$on 'kmeans:updateDataPoints', (event, dataPoints) -> + _update dataPoints + _finish = (results=null) -> msgManager.broadcast 'kmeans:done', results showResults results @@ -117,9 +120,14 @@ kMeans = angular.module('app_analysis_kMeans', []) wholedataseton: true accuracyon: false + updateDataPoints = (data) -> + xCol = data.header.indexOf $scope.xCol + yCol = data.header.indexOf $scope.yCol + data = ([row[xCol], row[yCol]] for row in data.data) + msgManager.broadcast 'kmeans:updateDataPoints', data + # set initial values for sidebar controls initSidebarControls = (initControlValues) -> - params = kmeans.getParameters() $scope.ks = [params.minK..params.maxK] $scope.distances = params.distances @@ -149,6 +157,10 @@ kMeans = angular.module('app_analysis_kMeans', []) $scope.kmeanson = off if $scope.labelson $scope.numUniqueLabels = detectKValue data + $scope.updateDataPoints = () -> + updateDataPoints data + $timeout -> + updateDataPoints data setDetectedKValue = (detectedK) -> if detectedK.num <= 10 @@ -191,7 +203,7 @@ kMeans = angular.module('app_analysis_kMeans', []) data = ([row[xCol], row[yCol]] for row in data.data) # re-check if possible to compute accuracy - if $scope.labelson and $scope.k is $scope.numUniqueLabels and $scope.accuracyon + if $scope.labelson and $scope.k is $scope.numUniqueLabels.num and $scope.accuracyon acc = on obj = @@ -219,6 +231,7 @@ kMeans = angular.module('app_analysis_kMeans', []) msgScope: ['kMeans'] listener: (msg, data) -> updateSidebarControls(data) + updateDataPoints(data) $scope.detectKValue = -> detectedK = detectKValue data setDetectedKValue detectedK @@ -558,7 +571,7 @@ kMeans = angular.module('app_analysis_kMeans', []) distanceType = distanceType.toLowerCase() initMethod = initMethod.toLowerCase() - _updateGraph data +# _updateGraph data if initMethod is 'forgy' centroids = _initCentroids data, k diff --git a/app/styles/custom.less b/app/styles/custom.less index c18e50d2..ad9284b4 100644 --- a/app/styles/custom.less +++ b/app/styles/custom.less @@ -72,7 +72,7 @@ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); to { transform: scale( 1 ) rotate( 360deg ); } } -#nav-clear-tool-search { +.clear-search-input { position: absolute; right: 15px; top: 0; @@ -84,6 +84,23 @@ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); color: #ccc; } -#nav-tool-search { +.margin-search-input { margin: 2px 10px; } + +#nav-clear-tool-search { + .clear-search-input +} + +#nav-tool-search { + .margin-search-input +} + +#getDataJsonUrlClear { + .clear-search-input; + right: 5px; +} + +#getDataJsonUrlInput { + padding-right: 20px; +} diff --git a/bower.json b/bower.json index 4bdf59ec..73270917 100644 --- a/bower.json +++ b/bower.json @@ -1,34 +1,34 @@ { "author": "Alexandr Kalinin, Selvam Palanimalai", "name": "SOCRAT", - "version": "v0.1.5-alpha.4", + "version": "v0.1.5-alpha.5", "description": "Flexible HTML5/JS toolkit for interactive data analysis and visuzalization", "homepage": "https://github.com/SOCR/SOCRAT", "dependencies": { - "angular": "^1.3.0", + "angular": "^1.5.0", "jquery-layout": "~1.4.3", "jquery-highlight": "~3.3.0", "jquery-hoverintent": "https://github.com/briancherne/jquery-hoverIntent.git#~1.8.1", - "datatables": "DataTables#~1.10.9", - "d3": "~3.5.6", - "angular-animate": "^1.3.0", - "angular-cookies": "^1.3.0", - "angular-messages": "^1.3.0", - "angular-resource": "^1.3.0", - "angular-route": "^1.3.0", - "angular-sanitize": "^1.3.0", - "angular-touch": "^1.3.0", - "angular-ui": "~0.4.0", + "datatables": "DataTables#~1.10.11", + "d3": "~3.5.15", + "angular-animate": "^1.5.0", + "angular-cookies": "^1.5.0", + "angular-messages": "^1.5.0", + "angular-resource": "^1.5.0", + "angular-route": "^1.5.0", + "angular-sanitize": "^1.5.0", + "angular-touch": "^1.5.0", + "angular-ui": "^0.4.0", "angular-ui-router": "~0.2.15", - "ngHandsontable": "angular-ui-handsontable#~0.7.0", + "ngHandsontable": "angular-ui-handsontable#~0.8.0", "wrangler": "https://github.com/alxndrkalinin/wrangler.git", - "bootstrap": "^3.2.0", - "angular-bootstrap": "~0.14.2", - "jstat": "~1.4.6", + "bootstrap": "^3.3.6", + "angular-bootstrap": "~1.1.2", + "jstat": "~1.5.2", "jsfeat": "~0.0.8" }, "devDependencies": { - "angular-mocks": "^1.3.0" + "angular-mocks": "^1.5.0" }, "appPath": "app", "moduleName": "expApp", diff --git a/package.json b/package.json index c213bc39..a4f4e60f 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,13 @@ { "author": "Alexandr Kalinin, Selvam Palanimalai", "name": "SOCRAT", - "version": "v0.1.5-alpha.4", + "version": "v0.1.5-alpha.5", "description": "Flexible HTML5/JS toolkit for interactive data analysis and visuzalization", "homepage": "https://github.com/SOCR/SOCRAT", + "repository": { + "type": "git", + "url": "https://github.com/SOCR/SOCRAT.git" + }, "devDependencies": { "coffee-script": "*", "grunt": "^0.4.5", @@ -50,5 +54,5 @@ "scripts": { "test": "grunt test" }, - "license": "MIT" + "license": "LGPL v3.0" }