The data does not fit on the VictoryBar chart - javascript

I make a graph using the Victory library. I need to build a grouped VictoryBar, so I use VictoryGroup to do that.
But I have a lot of data, and the data doesn't fit on the graph. How can I enlarge the graph so that the y-axis data doesn't get mixed up?
If there is less data, the data is placed on the graph
https://codesandbox.io/s/zen-edison-fb8dy4?file=/src/App.js
import "./styles.css";
import plotData from "./data.json";
import React from "react";
import { VictoryBar, VictoryChart, VictoryGroup } from "victory";
export default function App() {
let arr1 = [];
let arr2 = [];
let arr1Sum = 0;
let arr2Sum = 0;
plotData.map((plotData, index) => {
arr1Sum += plotData.ofac_share;
arr2Sum += plotData.non_ofac_share;
});
plotData.map((plotData, index) => {
arr1.push({
y: (plotData.ofac_share / arr1Sum) * 100,
x: plotData.validator
});
arr2.push({
y: (plotData.non_ofac_share / arr2Sum) * 100,
x: plotData.validator
});
});
return (
<VictoryChart>
<VictoryGroup offsetY={10} colorScale={["tomato", "orange"]}>
<VictoryBar horizontal barWidth={3} data={arr1} />
<VictoryBar horizontal barWidth={3} data={arr2} />
</VictoryGroup>
</VictoryChart>
);
}
enter image description here
I tried changing the 'offset' and 'barWidth' parameter, but it didn't help me

As per documentation, as well as other layout configurations, the VictoryChart component supports a height property:
<VictoryChart height={600}>
<VictoryGroup
offsetY={10}
colorScale={['tomato', 'orange']}
>
<VictoryBar
horizontal
barWidth={3}
data={arr1}
/>
<VictoryBar
horizontal
barWidth={3}
data={arr2}
/>
</VictoryGroup>
</VictoryChart>;
Here is the link to the relevant Victory documentation section, but here is a snippet from that link that summarises:
Victory components have default width, height, and padding props defined in the default grayscale theme.
Hope this helps.

Related

Attempting to render JSX element but getting undefined

So I have this JSX element that I am attempting to render in the Class component. It is essentially a visual provided by D3's React library. However, I am receiving this error upon attempting to render the D3 visual:
Unhandled Rejection (Error): Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Below are some relevant code snippets of where the error is occurring:
The builder function to pass all the necessary props to the D3 library
const buildModelVisual = (dataPoints) => {
console.log("category: " + dataPoints[0].category)
console.log("range: " + dataPoints[0].range)
console.log("frequency: " + dataPoints[0].frequency)
dataPoints[0].frequency.forEach(f =>
console.log("f: " + f)
)
const width = 960,
height = 500,
margins = {top: 50, right: 50, bottom: 50, left: 50},
id = "model-visual",
title = "NaiveBayes Model Visual",
svgClassName = "model-visual-class",
titleClassName = "model-visual-title-class",
legendClassName = "model-legend",
showLegend = true,
showXAxis = true,
showYAxis = true,
showXGrid = false,
showYGrid = false,
ranges = [
...dataPoints[0].range
],
frequencies = [
...dataPoints[0].frequency
],
x = () => {
return ranges.forEach(r => {
return r;
})
},
xOrient = 'bottom',
xTickOrient = 'bottom'
const xDomain = dataPoints[0].range.forEach(r => {
return {
category: dataPoints[0].category, range: r
}
}),
xRangeRoundBands = {interval: [0, width - margins.left - margins.right], padding: 0.1},
xScale = 'ordinal',
xAxisClassName = 'x-axis',
xLabel = dataPoints[0].category,
xLabelPosition = 'bottom',
xTickPadding = 3,
xInnerTickSize = 6,
xOuterTickSize = 6,
y = () => {
return frequencies.forEach(freqInRange => {
return freqInRange.forEach(f => {
return f;
});
})
},
yOrient = 'left',
yTickOrient = 'left',
yRange = [height - margins.top - margins.bottom, 0]
const yDomain = [0, d3.max(
dataPoints[0].frequency,
(f) => {
return f.value
}
)],
yScale = 'linear',
yAxisClassName = 'y-axis',
yLabel = "Population",
yTickFormat = d3.format(".2s"),
yLabelPosition = 'left',
yTickPadding = 4,
yInnerTickSize = 6,
yOuterTickSize = 6
return (
<Chart
title={title}
id={id}
width={width}
height={height}
>
<BarStackChart
title= {title}
data= {ranges}
width= {width}
height= {height}
id= {id}
margins= {margins}
svgClassName= {svgClassName}
titleClassName= {titleClassName}
yAxisClassName= {yAxisClassName}
xAxisClassName= {xAxisClassName}
legendClassName= {legendClassName}
categoricalColors= {d3.scaleOrdinal(d3.schemeCategory10)}
chartSeries = {ranges}
showLegend= {showLegend}
showXAxis= {showXAxis}
showYAxis= {showYAxis}
x= {x}
showXGrid= {showXGrid}
xDomain= {xDomain}
xRangeRoundBands= {xRangeRoundBands}
xScale= {xScale}
xOrient= {xOrient}
xTickOrient= {xTickOrient}
xTickPadding = {xTickPadding}
xInnerTickSize = {xInnerTickSize}
xOuterTickSize = {xOuterTickSize}
xLabel = {xLabel}
xLabelPosition = {xLabelPosition}
y= {y}
showYGrid= {showYGrid}
yOrient= {yOrient}
yRange= {yRange}
yDomain= {yDomain}
yScale= {yScale}
yTickOrient= {yTickOrient}
yTickPadding = {yTickPadding}
yInnerTickSize = {yInnerTickSize}
yOuterTickSize = {yOuterTickSize}
yTickFormat= {yTickFormat}
yLabel = {yLabel}
yLabelPosition = {yLabelPosition}
/>
</Chart>
)
}
The HO class component that is rendering the graph and the interface
constructor(props) {
super(props);
this.ref = React.createRef();
this.state = {
input: "",
output: [],
visual: null
}
}
REST API call (within the default class Component) that sets the data for the BarStackChart
callGetModel = () => {
let getModelRanges = getCall(ApiURL.get_model_ranges);
let getModelFrequencies = getCall(ApiURL.get_model_frequencies);
Promise.all([getModelRanges, getModelFrequencies]).then(data => {
this.setState({output: data})
const dataPoints = [];
for (let value in JSON.parse(data[0].toString())) {
dataPoints.push({
category: value,
range: JSON.parse(data[0].toString())[value],
frequency: JSON.parse(data[1].toString())[value]
})
}
console.log(dataPoints)
const ModelVisual = buildModelVisual(dataPoints)
this.setState({ visual: ModelVisual }) // returns JSX element
console.log(this.state.visual)
});
}
The render method for the class Component
render() {
return <div>
<h3>Welcome to Naive Bayes Java!</h3>
<p>The REST API for this project is hosted at</p>
<a style={{'display':'block', 'paddingBottom':'1.5em', 'color':'rgb(0, 150, 196)'}} href="https://naivebayesjava.herokuapp.com/swagger-ui.html#/">https://naivebayesjava.herokuapp.com/</a>
<button style={{'display':'inline', 'background':'rgb(32, 32, 32)', 'color':'rgb(190, 190, 190)'}} onClick={this.callListOfFiles}>
Get List of Valid Files
</button>
<button style={{'background':'rgb(32, 32, 32)', 'color':'rgb(190, 190, 190)'}} onClick={this.callGetModel}>
Get Model
</button>
<button style={{'background':'rgb(32, 32, 32)', 'color':'rgb(190, 190, 190)'}} onClick={this.callGetModelAccuracy}>
Get Model Accuracy
</button>
<div style={{'margin':'auto', 'display':'block'}}>
<input style={{'background':'rgb(32, 32, 32)', 'color':'rgb(190, 190, 190)'}} type='text' value={this.state.input} onChange={this.handleChange}/>
<button style={{'background':'rgb(32, 32, 32)', 'color':'rgb(190, 190, 190)'}} onClick={this.callSetData}>
Set Training Data File
</button>
</div>
{/* <button onClick={this.callGetTrainingData}>
Get Training Data Arff Files
</button> */}
<div style={{'padding-top':'0.5em'}} ref={this.ref}></div>
<output type='textBox' style={{ 'padding':'1.25em', 'display':'block', 'Height': '30px', 'Width': '300px' }}>
{ Object.keys(this.state.output).map(key => {
return this.state.output[key]
}) }
</output>
{ this.state.visual }
</div>
}
There most definitely is a better way to implement this besides setting a "this.state.visual" JSX Element and calling that in the render method, although as I am really new to both React (started learning about a month ago) and JS (started about 3 months ago) I don't quite know all the common practices with either; just the general theory behind how they work.
This interface and my portfolio are hosted at joshuabaroni.github.io . The interface i am attempting to improve is the NaiveBayes Project Interface under the "Projects" section
Any recommendations would be appreciated! the whole JS file is available upon request.
You likely forgot to export your component from the file it's defined
in, or you might have mixed up default and named imports.
You aren't exporting your classes/functions as it is required.
Exporting without default means it's a "named export". You can have multiple named exports in a single file. So if you do this,
class Template {}
class AnotherTemplate {}
export { Template, AnotherTemplate } // named export
then you have to import these exports using their exact names. So to use these components in another file you'd have to do,
import {Template, AnotherTemplate} from './components/templates'
Alternatively if you export as the default export like this,
export default class Template {}
Then in another file you import the default export without using the {}, like this,
import Template from './components/templates'
There can only be one default export per file. In React it's a convention to export one component from a file, and to export it is as the default export.
You're free to rename the default export as you import it,
import TheTemplate from './components/templates'
And you can import default and named exports at the same time,
import Template,{AnotherTemplate} from './components/templates'

How to set additional options for Chart.js BarCharts using React-Chartkick

I'm trying to display a barchart using React-Chartkick and Chart.js, and I'd like to customise the colours of the bars. Currently, I'm able to set all the bars to the same colour by passing a prop like this: <BarChart colours={["#fff"]} />.
Using LineCharts in React-Chartkick, you can set colours of the lines by passing an array of colours through that prop. BarCharts only seems to accept the first colour, however. Is this a limitation of React-Chartkick, or am I doing something wrong?
I've tried passing options (as described here: https://www.chartjs.org/docs/latest/charts/bar.html#styling ) through the library prop as that is how I've customised the colours of the axes and labels, but this doesn't seem to affect the bars.
Here's my current code:
state = {
chartLibraryOptions: {
borderColor: "#e34402", // does nothing here
backgroundColor: "#e34402", // nor this
scales: {
yAxes: [
{
ticks: { fontColor: "#fff", autoSkip: false }
}
],
xAxes: [
{
ticks: { fontColor: "#fff" }
}
]
}
}
};
render() {
return (
<BarChart
data={this.state.chartData}
library={this.state.chartLibraryOptions}
colors={["#e34402", "#e3b502"]} // All bars are the first colour
/>
);
}
I'm expecting to be able to change the colours of each bar, but after all this I'm not sure if that's possible through Chartkick?
Well, I used the same node package in a project with different approach kinda work for me. Almost all the charts take the same attributes.
Basically, this attribute dataset={{ backgroundColor: ['white', 'yellow' ], }}
is all you need to colour each bar. You can either pass string or array of string to backgroundColor.
The backgroundColor in dataset takes two types of data String and Array(object). Typical examples of passing data are below.
When you set backgroundColor to a string, it applied the same colour to each bar. e.g backgroundColor: 'red'
BarChart - <BarChart dataset={{ backgroundColor: 'red', }} />
When you set backgroundColor to an array, it applied each colour in the array to each bar. e.g backgroundColor: ['red', 'yellow'], then you create a loop of colours base on the data length.
column chart - <ColumnChart dataset={{ backgroundColor: ['red', 'yellow' ], }} />
React Implementation Below:
/* eslint-disable no-plusplus */
import React from 'react';
import { ColumnChart, BarChart } from 'react-chartkick';
import { chartOne } from '../common/chartData';
import 'chart.js';
const MonthlyGrowth = () => {
const handleBgColors = () => {
const firstColor = "#A00B16", secondColor = "#FAA226";
const arrOfBgColors = [];
for (let i = 1; i <= chartOne.length; i++) {
if (i % 2 === 0) {
arrOfBgColors.push(secondColor)
} else {arrOfBgColors.push(firstColor)}
}
return arrOfBgColors;
}
return (
<div className="bukka-card uk-card-default bg-white pt-4 pb-2 mr-1 pl-3 pr-2 pl-
lg-5">
<h2 className="mt-2">4,500,000</h2>
<BarChart
dataset={{ borderWidth: 0, width: 0, backgroundColor: handleBgColors(), }}
data={chartOne}
/>
</div>
)
}
export default MonthlyGrowth;

What is the correct way to set the new coordinates of a view after translation animation in react-native?

I am trying to run a few simple animations using react-native-animatable library. (But I believe the question should be generic to any react animations so adding other tags as well.)
The problem is, in the first time, the image animates just as expected. But when aimed to start second animation animation with the gesture, the image translation starts from its original coordinates.
A search yielt, in Android development (which is obviously not my case) there seems a method, setFillAfter which sets the coordinate after the animation.
My question is, how to set the location (left / top values for example) to the final translated point so that consecutive animation starts from the point the previous translation left.
The expo snack for below code block is here.
import * as React from 'react';
import { Image, StyleSheet, ImageBackground } from 'react-native';
import * as Animatable from 'react-native-animatable';
import { PanGestureHandler, State } from 'react-native-gesture-handler';
import testImg from './test.png';
import backImg from './back.png';
export default class App extends React.Component {
onTestMove(event) {
this.testAnimRef.transitionTo({
translateX: event.nativeEvent.translationX,
translateY: event.nativeEvent.translationY,
}, 0);
}
render() {
return (
<ImageBackground source={backImg} style={{ flex: 1 }} >
<PanGestureHandler
key={`test`}
onGestureEvent={(e) => { this.onTestMove(e) }}
onHandlerStateChange={e => { }}
>
<Animatable.View style={styles._animatable_view}
ref={((ref) => { this.testAnimRef = ref }).bind(this)}
useNativeDriver={true}
>
<Image source={testImg} style={styles._image} />
</Animatable.View>
</PanGestureHandler>
</ImageBackground>
);
}
}
const styles = StyleSheet.create({
_image: {
width: 50,
height: 25,
resizeMode: 'contain',
backgroundColor: 'black',
borderColor: 'gainsboro',
borderWidth: 2,
},
_animatable_view: {
position: "absolute",
top: 200,
left: 100,
},
});
I had the same problem trying to move around some cards in a view, and upon further dragging, they would reset to their origin.
My theory is/was that while the translated view would have its x / y coordinates translated, this would not apply to the parent of that view, and so the animated event passed from that component would initially have the original coordinates (nuke me if I'm wrong here)
So my solution was to keep an initial offset value in state, and maintain this every time the user releases the dragged motion
_onHandleGesture: any
constructor(props: OwnProps) {
super(props)
this.state = {
animationValue: new Animated.ValueXY({ x: 0, y: 0 }),
initialOffset: { x: 0, y: 0 },
}
this._onHandleGesture = (e: PanGestureHandlerGestureEvent) => {
this.state.animationValue.setValue({
x: e.nativeEvent.translationX + this.state.initialOffset.x, <- add initial offset to coordinates passed
y: e.nativeEvent.translationY + this.state.initialOffset.y,
})
}
}
_acceptCard = (cardValue: number) => {
const { targetLocation, onAccept } = this.props
const { x, y } = targetLocation
onAccept(cardValue)
Animated.spring(this.state.animationValue, {
// Some animation here
}).start(() => {
this.setState({ initialOffset: targetLocation }) // <- callback to set state value for next animation start
})
}
and the render method
<PanGestureHandler
onHandlerStateChange={this.onPanHandlerStateChange}
onGestureEvent={this._onHandleGesture}
failOffsetX={[-xThreshold, xThreshold]}
>
<Animated.View
style={{
position: "absolute",
left: 0,
top: 0,
transform: [{ translateX: this.state.animationValue.x }, { translateY: this.state.animationValue.y }],
}}
>
<CardTile size={size} content={content} layout={layout} backgroundImage={backgroundImage} shadow={shadow} />
</Animated.View>
</PanGestureHandler>
This example is based on the react-native-gesture-handler library, but the concept should apply to other solutions.
Dont know if this way is advisable, though it is functional.
Hope this helps!

Scale stage and drag element relative positions not working

Im having small issue with calculating realtime position while scaled stage in KonvaJS React. In my example I have two Rect's small (red) one position is relative to Big Rect (yellow), While dragging big rectangle small one moves relative to big one but when I scale UP or DOWN small one jumps some pixels in x, y positions.
Working example
import React from "react";
import ReactDOM from "react-dom";
import { Rect, Stage, Layer } from "react-konva";
import "./styles.css";
function Box({ attrs, draggable, updateAttr }) {
const onDragStart = e => {
const element = e.target;
if (typeof updateAttr === "function") {
updateAttr(element._lastPos);
}
};
const onDragMove = e => {
const element = e.target;
if (typeof updateAttr === "function") {
updateAttr(element._lastPos);
}
};
const onDragEnd = e => {
const element = e.target;
if (typeof updateAttr === "function") {
updateAttr(element._lastPos);
}
};
return (
<Rect
draggable={draggable}
onDragEnd={onDragEnd}
onDragMove={onDragMove}
onDragStart={onDragStart}
fill={attrs.fill}
x={attrs.x}
y={attrs.y}
width={attrs.width}
height={attrs.height}
/>
);
}
const calculatePos = ({ r1, r2 }) => ({
...r2,
x: parseInt(r1.width + r1.x + 10, 10),
y: parseInt(r1.y + r1.height / 2 - r2.height / 2, 10)
});
const boxAttr = {
x: 50,
y: 50,
width: 200,
height: 150,
fill: "yellow"
};
const secondAttr = calculatePos({
r2: {
fill: "red",
width: 20,
height: 20
},
r1: boxAttr
});
class App extends React.Component {
state = {
scale: 1,
boxAttr,
secondAttr
};
updateScale = scale => {
this.setState({ scale });
};
updateAttr = pos => {
const { secondAttr, boxAttr } = this.state;
this.setState({
secondAttr: calculatePos({
r2: secondAttr,
r1: { ...boxAttr, ...pos }
})
});
};
render() {
const { scale, boxAttr, secondAttr } = this.state;
return (
<div className="App">
<button
onClick={() => {
this.updateScale((parseFloat(scale) + 0.1).toFixed(1));
}}
>
+ Scale ({scale})
</button>
<button
onClick={() => {
this.updateScale((parseFloat(scale) - 0.1).toFixed(1));
}}
>
- Scale ({scale})
</button>
<Stage
scaleX={scale}
scaleY={scale}
width={window.innerWidth}
height={window.innerHeight}
>
<Layer>
<Box
updateAttr={this.updateAttr}
draggable={true}
attrs={boxAttr}
/>
<Box draggable={false} attrs={secondAttr} />
</Layer>
</Stage>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Please help me to update correct x,y for red rectangle.
I know Grouped Draggable solution will fix this issue, but I cannot
implement in my real application due to complex relations that cannot
be group for draggable reason
When the first box is dragged, you have to translate the mouse coordinates on the canvas to the coordinate system inside the scaled canvas to move the second box.
I modified the updateAttr method to do so.
There is a catch however, when the updateAttr is called by konva itself (after the dragEnd event) it does so with translated coordinates. That is why I added a second argument to the updateAttr called doScale. When calling it yourself, set it true and it will translate the coordinates, if konva calls it, there is no second argument and it evaluates to false and does no translation.
The code is available at: https://codesandbox.io/s/6126wz0kkk

React D3: How to use react-d3-tooltip and react-d3-zoom in the same chart?

I am trying to create a Line Chart using React-d3 (www.reactd3.org) with both the Tooltip and Zoom components.
https://github.com/react-d3/react-d3-tooltip
https://github.com/react-d3/react-d3-zoom
However I cannot figure out how to use both components together.
I was able to create a simple LineChart:
import {LineChart} from 'react-d3-basic';
import {LineTooltip, SimpleTooltip} from 'react-d3-tooltip';
import {LineZoom} from 'react-d3-zoom';
render() {
var viewCountData = [
{
"date": new Date(2016, 5, 29),
"Object1":11,
"Object2":13,
"Object3":16
},
{
"date": new Date(2016, 5, 30),
"Object1":23,
"Object2":17,
"Object3":15
}
];
var chartSeries = [
{field: "Object1"},
{field: "Object2"},
{field: "Object3"}
];
var x = function(d) {
return d.date;
};
return (
<LineChart
data= {viewCountData}
chartSeries= {chartSeries}
x= {x}>
</LineChart>
);
}
and add Tooltips by replacing LineChart with LineTooltip:
<LineTooltip
data= {viewCountData}
chartSeries= {chartSeries}
x= {x}>
<SimpleTooltip />
</LineTooltip>
However I cannot figure out how to also use LineZoom. I tried nesting it inside LineTooltip
<LineTooltip ...>
<LineZoom ...>
</LineZoom>
</LineTooltip>
and also having both inside LineChart
<LineChart ...>
<LineTooltip ...>
</LineTooltip>
<LineZoom ...>
</LineZoom>
</LineChart>
but neither worked. Any help would be appreciated, thanks!
I basically modfified the zoom line example to include the voroni utility.
Some quick cursory tests though show there's work to be done compatibility wise, but this should help you
import React, {PropTypes} from 'react';
// import d3 from 'd3';
// import {LineZoom} from 'react-d3-zoom';
import {
Chart,
} from 'react-d3-core';
import {
LineChart,
series
} from 'react-d3-basic';
import ZoomSet from 'react-d3-zoom/lib/inherit';
import ZoomFocus from 'react-d3-zoom/lib/utils/zoom_focus';
import CommonProps from 'react-d3-zoom/lib/commonProps';
// Tooltip
import Voronoi from 'react-d3-tooltip/lib/utils/voronoi';
export default class Line extends ZoomSet {
constructor(props) {
super(props);
const {
contentTooltip,
margins,
width,
height
} = this.props;
this.zoomed = this.zoomed.bind(this);
this.mkXDomain();
this.mkYDomain();
this.state = {
xDomainSet: this.setXDomain,
yDomainSet: this.setYDomain,
onZoom: this.zoomed,
d3EventSet: null,
xRange: this.props.xRange ||
[0, width - margins.left - margins.right],
yRange: this.props.yRange ||
[height - margins.top - margins.bottom, 0],
xRangeRoundBands: this.props.xRangeRoundBands || {
interval: [0, width - margins.left - margins.right],
padding: 1
},
zoomType: 'line'
};
this.mkXScale(this.setXDomain);
this.mkYScale(this.setYDomain);
this.state = Object.assign(this.state, {
xScaleSet: this.setXScale,
yScaleSet: this.setYScale
});
}
static defaultProps = CommonProps
render() {
const {
xDomainSet,
yDomainSet,
contentTooltip
} = this.state;
const voroni = (
<Voronoi
{...this.state}
{...this.props}
// onMouseOver= {(...args)=>console.log(args)}
// onMouseOut= {(...args)=>console.log(args)}
/>
);
const focus = <ZoomFocus {...this.props} />;
// console.log('state', this.state, Chart);
return (
<div>
<Chart {...this.props} {...this.state}>
<LineChart
{...this.props}
{...this.state}
xDomain={xDomainSet} yDomain={yDomainSet}
showZoom/>
{focus}
{voroni}
</Chart>
</div>
);
}
}

Categories