aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts2
-rw-r--r--src/client/views/nodes/DataVizBox/components/Histogram.tsx134
-rw-r--r--src/client/views/nodes/DataVizBox/components/LineChart.tsx2
-rw-r--r--src/client/views/nodes/DataVizBox/components/PieChart.tsx13
4 files changed, 86 insertions, 65 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 5ef033e35..426eaa14d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1143,7 +1143,7 @@ export namespace Docs {
}
export function DataVizDocument(url: string, options?: DocumentOptions, overwriteDoc?: Doc) {
- return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', ...options }, undefined, undefined, undefined, overwriteDoc);
+ return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', type: 'dataviz', ...options }, undefined, undefined, undefined, overwriteDoc);
}
export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
index c07c85f7e..5fbe92563 100644
--- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx
+++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
@@ -38,6 +38,9 @@ export class Histogram extends React.Component<HistogramProps> {
private _histogramRef: React.RefObject<HTMLDivElement> = React.createRef();
private _histogramSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
private numericalData: boolean = false;
+ private numericalXData: boolean = false; // whether the data is organized by numbers rather than categoreis
+ private numericalYData: boolean = false; // whether the y axis is controlled by provided data rather than frequency
+ private maxBins = 15; // maximum number of bins that is readable on a normal sized doc
@observable _currSelected: any | undefined = undefined;
// TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates
@@ -45,7 +48,7 @@ export class Histogram extends React.Component<HistogramProps> {
if (this.props.axes.length < 1) return [];
if (this.props.axes.length < 2) {
var ax0 = this.props.axes[0];
- if (/\d/.test(this.props.pairs[0][ax0])){ this.numericalData = true }
+ if (/\d/.test(this.props.pairs[0][ax0])){ this.numericalXData = true }
return this.props.pairs
?.filter(pair => (!this.incomingLinks.length ? true : Array.from(Object.keys(pair)).some(key => pair[key] && key.startsWith('select'))))
.map(pair => ({ [ax0]: (pair[this.props.axes[0]])}))
@@ -53,7 +56,8 @@ export class Histogram extends React.Component<HistogramProps> {
var ax0 = this.props.axes[0];
var ax1 = this.props.axes[1];
- if (/\d/.test(this.props.pairs[0][ax0])) { this.numericalData = true;}
+ if (/\d/.test(this.props.pairs[0][ax0])) { this.numericalXData = true;}
+ if (/\d/.test(this.props.pairs[0][ax1]) && this.props.pairs.length < this.maxBins) { this.numericalYData = true;}
return this.props.pairs
?.filter(pair => (!this.incomingLinks.length ? true : Array.from(Object.keys(pair)).some(key => pair[key] && key.startsWith('select'))))
.map(pair => ({ [ax0]: (pair[this.props.axes[0]]), [ax1]: (pair[this.props.axes[1]]) }))
@@ -79,7 +83,7 @@ export class Histogram extends React.Component<HistogramProps> {
.lastElement();
}
@computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } {
- if (this.numericalData){
+ if (this.numericalXData){
const data = this.data(this._histogramData);
return {xMin: Math.min.apply(null, data), xMax: Math.max.apply(null, data), yMin:0, yMax:0}
}
@@ -182,7 +186,7 @@ export class Histogram extends React.Component<HistogramProps> {
getAnchor = (pinProps?: PinProps) => {
const anchor = Docs.Create.ConfigDocument({
//
- title: 'line doc selection' + this._currSelected?.x,
+ title: 'histogram doc selection' + this._currSelected?.x,
});
PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.dataDoc);
anchor.presDataVizSelection = this._currSelected ? new List<number>([this._currSelected.x, this._currSelected.y]) : undefined;
@@ -227,9 +231,9 @@ export class Histogram extends React.Component<HistogramProps> {
})
return valid;
})
- var field = dataSet[0]? Object.keys(dataSet[0])[0]: undefined;
+ var field = dataSet[0]? Object.keys(dataSet[0])[0] : undefined;
const data = validData.map((d: { [x: string]: any; }) => {
- if (this.numericalData) { return +d[field!].replace(/\$/g, '').replace(/\%/g, '') }
+ if (this.numericalXData) { return +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') }
return d[field!]
})
return data;
@@ -239,26 +243,43 @@ export class Histogram extends React.Component<HistogramProps> {
d3.select(this._histogramRef.current).select('svg').remove();
d3.select(this._histogramRef.current).select('.tooltip').remove();
- var field = Object.keys(dataSet[0])[0]
- const data = this.data(dataSet);
+ var data = this.data(dataSet);
let uniqueArr: unknown[] = [...new Set(data)]
- var numBins = uniqueArr.length
- var startingPoint = 0;
- var endingPoint = numBins;
- var translateXAxis = 0;
- if (this.numericalData) {
- if (Number.isInteger(this.rangeVals.xMin!)){
- numBins = this.rangeVals.xMax! - this.rangeVals.xMin! + 1;
+ var numBins = this.numericalXData? (this.rangeVals.xMax! - this.rangeVals.xMin! + 1) : uniqueArr.length
+ var translateXAxis = !this.numericalXData || numBins<this.maxBins ? width/(numBins+1)/2 : 0;
+ if (numBins>this.maxBins) numBins = this.maxBins;
+ var startingPoint = this.numericalXData? this.rangeVals.xMin! : 0;
+ var endingPoint = this.numericalXData? this.rangeVals.xMax! : numBins;
+ var xAxisTitle = Object.keys(dataSet[0])[0]
+ var yAxisTitle = this.numericalYData ? Object.keys(dataSet[0])[1] : 'frequency';
+ var histDataSet = dataSet.filter((d: { [x: string]: unknown; }) => {
+ var valid = true;
+ Object.keys(dataSet[0]).map(key => {
+ if (!d[key] || Number.isNaN(d[key])) valid = false;
+ })
+ return valid;
+ });
+ if (!this.numericalXData) {
+ var histStringDataSet: { frequency: any, label: any }[] = [];
+ if (this.numericalYData){
+ for (let i=0; i<dataSet.length; i++){
+ histStringDataSet.push({frequency: dataSet[i][yAxisTitle], label: dataSet[i][xAxisTitle]})
+ }
+ }
+ else{
+ for (let i=0; i<uniqueArr.length; i++){
+ histStringDataSet.push({frequency: 0, label: uniqueArr[i]})
+ }
+ for (let i=0; i<data.length; i++){
+ let sliceData = histStringDataSet.filter(each => each.label==data[i])
+ sliceData[0].frequency = sliceData[0].frequency + 1;
+ }
}
- startingPoint = this.rangeVals.xMin!;
- endingPoint = this.rangeVals.xMax!;
- if (numBins>15) numBins = 15;
- else translateXAxis = width/(numBins+1) / 2;
+ histDataSet = histStringDataSet
}
- else translateXAxis = width/(numBins+1) / 2;
- const svg = (this._histogramSvg = d3
+ var svg = (this._histogramSvg = d3
.select(this._histogramRef.current)
.append("svg")
.attr("class", "graph")
@@ -268,32 +289,24 @@ export class Histogram extends React.Component<HistogramProps> {
.attr("transform",
"translate(" + this.props.margin.left + "," + this.props.margin.top + ")"));
- var x: any;
- if (this.numericalData){
- x = d3.scaleLinear()
- .domain([startingPoint!, endingPoint!])
+ var x = d3.scaleLinear()
+ .domain(this.numericalXData? [startingPoint!, endingPoint!] : [0, numBins])
.range([0, width ]);
- }
- else {
- x = d3.scaleLinear()
- .domain([0, numBins])
- .range([0, width]);
- }
-
var histogram = d3.histogram()
.value(function(d) {return d})
.domain([startingPoint!, endingPoint!])
.thresholds(x.ticks(numBins-1))
var bins = histogram(data)
var eachRectWidth = width/(bins.length)
- var graphStartingPoint = bins[0].x1! - (bins[1].x1! - bins[1].x0!)
+ var graphStartingPoint = bins[0].x1? bins[0].x1! - (bins[1].x1! - bins[1].x0!) : 0;
bins[0].x0 = graphStartingPoint;
x = x.domain([graphStartingPoint, endingPoint])
.range([0, Number.isInteger(this.rangeVals.xMin!)? (width-eachRectWidth) : width ])
var xAxis;
- if (!this.numericalData) { // if the data is strings rather than numbers
- uniqueArr.sort()
+ if (!this.numericalXData) { // reorganize if the data is strings rather than numbers
+ // uniqueArr.sort()
+ histDataSet.sort()
for (let i=0; i<data.length; i++){
var index = 0
for (let j=0; j<uniqueArr.length; j++){
@@ -303,23 +316,27 @@ export class Histogram extends React.Component<HistogramProps> {
}
bins[index].push(data[i])
}
+ bins.pop();
+ eachRectWidth = width/(bins.length)
bins.forEach(d => d.x0 = d.x0!)
xAxis = d3.axisBottom(x)
- .ticks(numBins-1)
+ .ticks(bins.length-1)
.tickFormat( i => uniqueArr[i])
.tickPadding(10)
+ x.range([0, width-eachRectWidth])
+ x.domain([0, bins.length-1])
translateXAxis = eachRectWidth / 2;
}
else {
xAxis = d3.axisBottom(x)
.ticks(numBins-1)
}
-
- const maxFrequency = d3.max(bins, function(d) { return d.length; })
+ const maxFrequency = this.numericalYData? d3.max(histDataSet, function(d) {return d.frequency.replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')})
+ : d3.max(bins, function(d) { return d.length; })
var y = d3.scaleLinear()
.range([height, 0]);
- y.domain([0, maxFrequency!]);
+ y.domain([0, +maxFrequency!]);
var yAxis = d3.axisLeft(y)
.ticks(maxFrequency!)
svg.append("g")
@@ -337,16 +354,14 @@ export class Histogram extends React.Component<HistogramProps> {
var left = this.numericalData? d.x0-1: d.x0;
var right = (this.numericalData && d.x0!=d.x1)? d.x1-1: d.x1;
if ((left*eachRectWidth ) <= pointerX && pointerX <= (right*eachRectWidth )){
- // var showSelected = !this.numericalData? dataSet[index] : this.props.pairs[index];
- var showSelected = dataSet[index]
- showSelected['frequency'] = d.length;
- console.log('showSelected', showSelected)
- console.log('current', this._currSelected)
+ var showSelected = histDataSet[index]
+ var selectedDisplay = {[xAxisTitle]: showSelected.label, [yAxisTitle]: showSelected.frequency}
+ // showSelected['frequency'] = d.length;
sameAsCurrent = this._currSelected?
- (showSelected[Object.keys(showSelected)[0]]==this._currSelected![Object.keys(showSelected)[0]]
- && showSelected[Object.keys(showSelected)[1]]==this._currSelected![Object.keys(showSelected)[1]])
+ (selectedDisplay[xAxisTitle]==this._currSelected![xAxisTitle]
+ && selectedDisplay[yAxisTitle]==this._currSelected![yAxisTitle])
: false;
- this._currSelected = sameAsCurrent? undefined: showSelected;
+ this._currSelected = sameAsCurrent? undefined: selectedDisplay;
return true
}
return false;
@@ -358,30 +373,37 @@ export class Histogram extends React.Component<HistogramProps> {
}
if (!sameAsCurrent!) selected.attr('class', 'histogram-bar hover');
});
-
svg.on('click', onPointClick);
- // axis titles
svg.append("text")
.attr("transform", "translate(" + (width/2) + " ," + (height+40) + ")")
.style("text-anchor", "middle")
- .text(field);
+ .text(xAxisTitle);
svg.append("text")
- .attr("transform", "rotate(-90)")
+ .attr("transform", "rotate(-90)" + " " + "translate( 0, " + -10 + ")")
.attr("x", -(height/2))
.attr("y", -20)
.style("text-anchor", "middle")
- .text('frequency');
-
+ .text(yAxisTitle);
d3.format('.0f')
-
svg.selectAll("rect")
.data(bins)
.enter()
.append("rect")
- .attr("transform", function(d) { return "translate(" + x(d.x0!) + "," + y(d.length) + ")"; })
+ .attr("transform", this.numericalYData?
+ function (d) {
+ var eachData = histDataSet.filter((data: { label: number; }) => {return data.label==d[0]})
+ var length = eachData[0].frequency.replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
+ return "translate(" + x(d.x0!) + "," + y(length) + ")";
+ }
+ : function(d) { return "translate(" + x(d.x0!) + "," + y(d.length) + ")"; })
+ .attr("height", this.numericalYData?
+ function(d) {
+ var eachData = histDataSet.filter((data: { label: number; }) => {return data.label==d[0]})
+ var length = eachData[0].frequency.replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
+ return height-y(length)}
+ : function(d) { return height - y(d.length)})
.attr("width", eachRectWidth)
- .attr("height", function(d) { return height - y(d.length); })
.attr("class", 'histogram-bar')
};
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index 637ed0ead..91baf095d 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -310,7 +310,7 @@ export class LineChart extends React.Component<LineChartProps> {
.style("text-anchor", "middle")
.text(this.props.axes[0]);
svg.append("text")
- .attr("transform", "rotate(-90)")
+ .attr("transform", "rotate(-90)" + " " + "translate( 0, " + -10 + ")")
.attr("x", -(height/2))
.attr("y", -20)
.attr("height", 20)
diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
index 37c435411..fc24e5821 100644
--- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
@@ -182,7 +182,7 @@ export class PieChart extends React.Component<PieChartProps> {
getAnchor = (pinProps?: PinProps) => {
const anchor = Docs.Create.ConfigDocument({
//
- title: 'line doc selection' + this._currSelected?.x,
+ title: 'piechart doc selection' + this._currSelected?.x,
});
PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.dataDoc);
anchor.presDataVizSelection = this._currSelected ? new List<number>([this._currSelected.x, this._currSelected.y]) : undefined;
@@ -260,12 +260,6 @@ export class PieChart extends React.Component<PieChartProps> {
})
return valid;
});
-
- var pie = d3.pie();
- var arc = d3.arc()
- .innerRadius(0)
- .outerRadius(radius);
-
if (this.byCategory){
let uniqueCategories = [...new Set(data)]
var pieStringDataSet: { frequency: any, label: any }[] = [];
@@ -282,6 +276,11 @@ export class PieChart extends React.Component<PieChartProps> {
var trackDuplicates : {[key: string]: any} = {};
data.forEach((eachData: any) => !trackDuplicates[eachData]? trackDuplicates[eachData] = 0: null)
+ var pie = d3.pie();
+ var arc = d3.arc()
+ .innerRadius(0)
+ .outerRadius(radius);
+
const onPointClick = action((e: any) => {
// check the 4 'corners' of each slice and see if the pointer is within those bounds to get the slice the user clicked on
const pointer = d3.pointer(e);