I'm trying to create a grouped bar chart with 70 samples and 2 series. Similar to this example:
http://bl.ocks.org/882152
However one series is [0 ... 1] and the other series is [0 ... 1.000.000]. I can't recreate the example with my numbers.
I also don't really get the example. Shouldn't be the variables switched, i.e. x -> y, y0 -> x0 and y1 -> x0? Or don't they stand for the x and y axis?
Thank's!
Edit:
Here is an example that demonstrates my problem (look in the console).
http://jsfiddle.net/kQSGF/3/
The problem seems to come from the scale definition:
var x = d3.scale.linear().domain([0, 1]).range([h, 0]);
The domain is set to [0,1] but only your first data series actually falls in that range.
you could consider setting the domain to the extent of your data, and reversing the output range so that it shows the values in your data instead of the 'non' value amount as a bar:
var x = d3.scale.linear().domain(d3.extent(d3.merge(data))).range([0,h]);
Note that you will still be unlikely to see your smaller data series, as the ranges of your data are so significantly different
Related
I have a D3 plot where I plot time (hh:mm) on x against some values on y axis.
For the x scale I use this logic
const currentData : string[] = ['06:00', '12:00', '18:00'];
this.x = d3Scale.scalePoint(
[0, this.width - this.margin.right - this.margin.left])
.domain(
currentData.map(
(d: PlotData) => d.time))
.round(true);
That plots all my data nicely. Now I would like to use this.x to return the value of a time point that is currently not in my original data. But when I run it with
this.x('14:00')
it returns NAN , which seems is because the input is not in the currentData array and to work only on values from the array.
Do I need to interpolate this value myself or is there a D3 function to take this.x and figure the this.x('14:00') internally?
Thanks,
EL
The other answer suggests the right approach, to use d3-scaleTime. I'd like to dig into why you see your problem, and then provide details to the solution with d3-scaleTime.
Ordinal Scales
d3.scalePoint is an ordinal scale. The domain represents a discrete and discontinuous set of values. The mapping of the domain to the range is based on the order in which domain values are specified. The first value in the domain is mapped to the first value in the range (with d3.scalePoint) regardless of its value as compared to other values in the domain.
As a result, only values that exist in the domain can be mapped to the range: there is no semantic/quantitative relationship between values in the domain that allows interpolation of in between values. Because you are using quantitative rather than qualitative or ordinal data, you want to use a scale that treats the domain as continuous.
There may be cases where you'd want to use an d3.scalePoint/Band scale with time based data, but in those cases you'd need to be able to construct a complete domain of all values that need to be plotted.
That your scale appears to plot your data as continuous at first is coincidental, if your middle domain value was '07:00', 'June' or 'yesterday', it would appear in the middle of your range. That you chose '12:00' hides the fact that the scale isn't positioning this based on its value but only on its index.
Continuous Scales
d3.scaleTime is one of d3's continuous scales
Continuous scales map a continuous, quantitative input domain to a continuous output range. (docs)
Domains and Ranges
For d3's continuous scales, domain and range must contain the same number of elements - if there are more than two elements in each, the domain and range are split into segments:
.domain([a,b,c]) // where a,b,c are quantitative
.range([1,2,3])
Values between a and b will be interpolated between 1 and 2, likewise, values between b and c will be interpolated between 2 and 3. If domain and range have a different number of elements, D3 will skip excess values of the one that has more.
It appears in your case that just two domain values will be sufficient, the minimum and maximum values you want to pass to the scale.
Using Dates
d3.scaleTime's domain needs to be set with date objects not a string such as '12:00', also, when passing a value to the scale (scale(x)) we need to pass a date object. We can create a basic d3 date parser with:
d3.parseTime("%H:%M")
Which gives us:
const currentData = ['06:00', '12:00', '18:00'];
const parse = d3.timeParse("%H:%M")
const x = d3.scaleTime()
.range([0,100])
.domain(d3.extent(currentData, (time) => parse(time)))
console.log("15:00 scales to: ", x(parse('15:00')))
console.log("06:00 scales to: ", x(parse('06:00')))
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
This should map your domain as before, but also allows you to also plot values that aren't in your initial dataset and have them mapped properly to the range based on their value.
It might be easier to instead use scaleTime
const currentData : string[] = ['06:00', '12:00', '18:00'];
this.x = d3.scaleTime()
.domain((d: PlotData) => d.time))
.range([0, this.width - this.margin.right - this.margin.left]);
this.x(new DateTime('xyz:abc'))
I see in the docs, you can specify x,y coordinates when using the time scale.
I'm wondering if I can do this using a different scale. I have 10 categories that I would like to place my x points in and then sum up the y values for each group of x 's.
My x categories are 0-10, 10-20, 20-30, .... 90-100 with a total of 10 categories.
For instance - point(15, 1923) will go in Bin 2, and the value of the bar will be all of the y values in bin 2.
Suppose I have a linechart with multiple lines (the number is dynamic) and I would need to always scale the Y so that all lines are shown - so the scale should always be based on the range with highest values. Is there a way how to it automatically? I found some example with automatic yScaling using d3.max but that is done for a known dataset. In my case, I do not know what range will be the one to use.
Following the c3js documentation there is no option for Bubble chart. One workaround for that is to setup scatter plot and specify point radius, but all of the bubbles will be the same height.
point = {
r: function(d) {
var num = d.value;
return num
},
Adding the value of axis inside the r solve the problem, but now the problem is how to setup very high or very low values ? For e.g if there is 1 000 000 value the whole chart will be colored. Is there any simple workarounds for that ?
First of all, set r to return the square root of your chosen variable e.g. return sqrt(num), that way a circle representing a data point 100 times the size of another has 100, not 10,000, times the area (area=pi r2 and all that)
If the numbers are still too big use a linear scale to restrict them to a usable size:
rscale = d3.scale.linear().domain([1,1000]).range([0,10])
and then return rscale(sqrt(num))
If your problem is to represent large and small values on the same chart so small values don't disappear and large values don't exceed the chart size look at using a d3 log scale:
rscale = d3.scale.log().base(10).domain([1,1000]).range([0,10])
Of course on a log scale the areas aren't linearly proportionate any more so whether the sqrt step is necessary is debatable. If you don't just remember to adjust the domain to account for this - change it to domain([1,1000000])
if you don't know the size of your numbers beforehand it will be worthwhile looping through your dataset to pick out the min and max to plug into the domain value: domain([your_min, your_max]). my examples above all assume a max of one million.
Here's an example I forked on jsfiddle, numbers from a few hundred to over a hundred thousand are displayed using a log scale and all are visible but the differences are still obvious:
http://jsfiddle.net/m9gcno5n/
For preparing graphs from data that exists in MySQL tables, I am using DimpleJS framework. I am able to plot graphs, everything works. One question though -
In a simple bar chart, with category values on X axis and measure values on Y axis, is there a way to limit the number of X axis categories being displayed? Example, let's say this is my data set: [(A,1), (B,2), (C,1), (D,5), (E,4)]
So in my dataset, there are 5 categories (X axis - A, B,C,D,E) and corresponding measures that I will be displaying in Y axis. Question is, I just want to display only 3 of the measures - let's say only first three, in this case (A,1), (B,2) and (C,1), although my dataset has two more (D,5) and (E,4).
Is there any way to restrict it in JS/ DimpleJS? I have limited control on the dataset that is coming in.
Thanks.
Use dimple.filterData after you receive the dataset but before you provide the data to dimple. This does not mutate the data so won't affect any other operations. I'm not sure what your actual category field is but it should look similar to this :
var chartData = dimple.filterData(originalDataset, 'category', ["A", "B", "C"]);
var chart = new dimple.chart(svg, chartData);
Otherwise there is not a provided way to restrict a category axis from showing values present in the data.