Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

axis Tick for Date values #283

Closed
marcofranssen opened this issue Sep 9, 2018 · 68 comments
Closed

axis Tick for Date values #283

marcofranssen opened this issue Sep 9, 2018 · 68 comments

Comments

@marcofranssen
Copy link

I'm trying to find a good example to get my xAxis ticks to be equally spread based on Date Time values.

I have been trying to accomplish this using http://nivo.rocks/guides/axes documentation. However so far no success. As my data collection is not always on the same interval my graph does not equally draw the visual representation but shows every data point with same distance.

Over here my chart code can be found. https://github.com/marcofranssen/gothermostat/blob/master/web/src/components/TemperaturesChart/TemperaturesChart.js

I would like the data to be shown where x axis represent equally spread during the start and end time.

Is there some documentation available to achieve this, or is this a missing feature?

@plouc
Copy link
Owner

plouc commented Sep 10, 2018

You can use axis tickValues property and set a number which will tell the corresponding d3 axis to use this number of ticks, but it's just a hint, d3 will adapt this count to have sensible ticks, if you really want to keep the number of ticks consistent, then you can compute the values by yourself and pass an array containing those values to tickValues.

@plouc
Copy link
Owner

plouc commented Sep 10, 2018

I just saw that you're using an older version of the package, please upgrade to 0.49.0, there are changes on the way min/max values are defined though, please have a look at http://nivo.rocks/line.
You can now use a time scale (example).

@plouc
Copy link
Owner

plouc commented Sep 10, 2018

The source code for the time x scale demo is available here.

@plouc
Copy link
Owner

plouc commented Sep 14, 2018

@marcofranssen did you succeed in achieving what you wanted?

@marcofranssen
Copy link
Author

@plouc still need to give it a shot in my repo. Recently been very busy in personal life. Will report back results asap. Thanks so far.

@uclanate
Copy link

hi @plouc ! First of all thanks for the awesome plotting library. I think I have a relatively simple question related to this discussion. I'm trying to just set an axis to use time, but not sure how to tell it what format my data is in (preferably I would pass it a JS Date object, or integer representing UNIX timestamp in ms).

I saw this code from your example, but wasn't sure how to adapt it to UNIX timestamp or Date object?
xScale={{
type: 'time',
format: '%Y-%m-%d',
precision: 'day',
}}
axisBottom={{
format: '%b %d',
}}

Thanks so much for the help!!!!

@plouc
Copy link
Owner

plouc commented Sep 18, 2018

@uclanate, you can set format to native to use js date objects.

@plouc
Copy link
Owner

plouc commented Sep 18, 2018

on the xScale I mean.

@uclanate
Copy link

thanks @plouc ! next question, sorry if very basic. For axis labels, the code you used doesn't seem to work:
axisBottom={{format: '%b %d'}}
do I have to do anything special when specifying the format on the axis? I tried playing with both format and labelFormat and neither seemed to work. Thanks again!

@plouc
Copy link
Owner

plouc commented Sep 18, 2018

which version are you using?

@uclanate
Copy link

Ah I'm an idiot. ok it works now!! (I was back on version 0.33). Thanks dude!!!! Great work here and thanks again for getting back so quickly :)

@jedifunk
Copy link

jedifunk commented Oct 8, 2018

@plouc first off, great library, thank you so much. Wondering if its possible to do hours rather than days?
I've got an API returning data in 15 min increments, and I want to set the xScale to say 2 or 4 hour increments.
Is this possible?

@jedifunk
Copy link

I figured it out... for anyone coming to look for something similar, I created an xScale that looks like this:
xScale={{ "type": "time", "format": "%Y-%m-%d %H:%M:%S", "precision": "minute" }}

then in axisBottom I set tickValues: 6 which gets me ticks every 4 hours.

Which is perfectly what I was looking for. Hope this helps anyone else out there.

@jesper-bylund
Copy link

jesper-bylund commented Oct 12, 2018

Desperately trying to figure this out, maybe I'm reading the documentation wrong? Trying to get "ticksValues:5" working for ResponsiveBar but I'm getting every single date.

This is my config:

<ResponsiveBar
            data={this.props.stats}
            keys={['words']}
            indexBy="date"
            margin={{
              top: 50,
              right: 0,
              bottom: 50,
              left: 0
            }}
            padding={0.3}
            colors="nivo"
            borderColor="inherit:darker(1.6)"
            xScale={{
              type: 'time',
              format: '%Y-%m-%d',
              precision: 'day'
            }}
            axisBottom={{
              tickValues: 5,
              orient: 'bottom',
              tickSize: 5,
              tickPadding: 5,
              tickRotation: 90
            }}
            axisLeft={{
              orient: 'left',
              tickSize: 5,
              tickPadding: 5,
              tickRotation: 0
            }}
            labelSkipWidth={12}
            labelSkipHeight={12}
            labelTextColor="inherit:darker(1.6)"
            animate={true}
            motionStiffness={90}
            motionDamping={15}
            enableGridY={false}
            legends={[
              {
                dataFrom: 'keys',
                anchor: 'bottom-right',
                direction: 'column',
                justify: false,
                translateX: 120,
                translateY: 0,
                itemsSpacing: 2,
                itemWidth: 100,
                itemHeight: 20,
                itemDirection: 'left-to-right',
                itemOpacity: 0.85,
                symbolSize: 20,
                effects: [
                  {
                    on: 'hover',
                    style: {
                      itemOpacity: 1
                    }
                  }
                ]
              }
            ]}
          />

my data looks like this:

[
  {
    date: '2018-09-28',
    words: 5
  }
]

And I'm on version "@nivo/bar": "^0.49.1",

@jedifunk
Copy link

@raelmiu can we see more of the dataset? how many dates are there?

In my solution, it still renders each 15 min increment, but setting the tickValues:6, shows a tick every 4 hours, but in between each tick mark there are still all the 15 min increments... thats how it can properly render the chart.

I might suggest jumping over to the discord channel where we can discuss a bit more, instead of here.

@marcofranssen
Copy link
Author

As soon I add the following to my chart it crashes with following exception.

       xScale={{
          type: 'time',
          format: '%Y-%m-%dT%H:%M:%S',
          precision: 'minute'
        }}

I get following exception:

Uncaught TypeError: Cannot read property 'setMilliseconds' of null
    at push../node_modules/@nivo/scales/cjs/nivo-scales.js.precisionCutOffs (nivo-scales.js:86)
    at nivo-scales.js:103
    at Array.forEach (<anonymous>)
    at nivo-scales.js:102
    at nivo-scales.js:120
    at nivo-scales.js:262
    at Array.forEach (<anonymous>)
    at nivo-scales.js:261
    at Array.forEach (<anonymous>)
    at generateSeriesAxis (nivo-scales.js:260)
    at generateSeriesXY (nivo-scales.js:241)
    at Object.computeXYScalesForSeries (nivo-scales.js:204)
    at nivo-line.js:715
    at new WithPropsOnChange (withPropsOnChange.js:92)
    at constructClassInstance (react-dom.development.js:12221)
    at updateClassComponent (react-dom.development.js:14058)
    at beginWork (react-dom.development.js:14687)
    at performUnitOfWork (react-dom.development.js:17242)
    at workLoop (react-dom.development.js:17281)
    at renderRoot (react-dom.development.js:17359)
    at performWorkOnRoot (react-dom.development.js:18252)
    at performWork (react-dom.development.js:18159)
    at performSyncWork (react-dom.development.js:18132)
    at requestWork (react-dom.development.js:18009)
    at scheduleWork (react-dom.development.js:17802)
    at Object.enqueueSetState (react-dom.development.js:12076)
    at ResponsiveWrapper.push../node_modules/react/cjs/react.development.js.Component.setState (react.development.js:404)
    at Object.onResize (nivo-core.js:258)
    at ResizeObserver.WithContentRect._this.measure (with-content-rect.js:146)

I'm also on version 0.49.1. It would really help if there would be some working example.

My current chart can be found over here. Would be great if someone can help me make this work.

https://github.com/marcofranssen/gothermostat/blob/master/web/src/components/TemperaturesChart/TemperaturesChart.js

@jedifunk
Copy link

@marcofranssen perhaps you don't have a need for the time portion... by this I mean the H:M:S. I had the need in my code because my data was coming to me in that format. If you only need YYYY-MM-DD you can just use Y-m-d. And you would want to change the format from minute to day.

Or maybe you need to format using native as suggested to someone else.

@plouc
Copy link
Owner

plouc commented Oct 14, 2018

Maybe you can have a look at the storybook source

@plouc
Copy link
Owner

plouc commented Oct 14, 2018

@marcofranssen, are you sure the dates are all valid ones?

@jesper-bylund
Copy link

can we see more of the dataset? how many dates are there?
I'm afraid not, it's for a client. But it's 30 dates, all formatted as 'YYYY-MM-DD'.

I've checked all the examples but the result is the same:
screenshot 2018-10-15 at 08 43 01

@ingro
Copy link

ingro commented Oct 24, 2018

I've tried to customize the xScale on a Stream component but with no luck, this method is something specific to the Bar component?

@plouc
Copy link
Owner

plouc commented Oct 24, 2018

It's available on several component which use @nivo/scales package, @nivo/stream doesn't use it yet

@jesper-bylund
Copy link

Any hints about my responsiveBar problem above?

@plouc
Copy link
Owner

plouc commented Oct 24, 2018

@raelmiu, it's difficult to help without having the code, if you can reproduce it on https://codesandbox.io for example, I can have a look, I understand it's for a client and you cannot use real data, but maybe you can use fake one.

@jesper-bylund
Copy link

@plouc Here you go, faked Data and all:
https://codesandbox.io/s/6yq5o903v3

Still can't make this work. Tried all sorts of variants.

@plouc
Copy link
Owner

plouc commented Oct 27, 2018

Thank you @raelmiu, sorry, I missed that you're using a bar chart as the issue was initially about a line chart, the bar chart only support point scales (for x axis) and point scales don't support setting tickValues… I should had support for time scales as it's fairly common

@marcofranssen
Copy link
Author

marcofranssen commented Oct 28, 2018

I found the issue. After I stripped the milliseconds from my datetime strings it works. So I think there is still a bug with regards to millis. But for now I'm good as I don't need milli precision.

@jesper-bylund
Copy link

Thank you @raelmiu, sorry, I missed that you're using a bar chart as the issue was initially about a line chart, the bar chart only support point scales (for x axis) and point scales don't support setting tickValues… I should had support for time scales this as it's fairly common

Thanks for the explanation! That makes sense. This is the documentation that confused me: http://nivo.rocks/guides/axes

@plingampally
Copy link

plingampally commented Nov 8, 2018

Does anyone have an example of how to use native as an option for the format in axis?

this works: axisBottom={{format: '%b %d'}} but axisBottom={{format: 'native'}} produces this chart:

image

I'm trying to implement the dynamic date axis that D3 supports eg:
image

@subhashbrillmark
Copy link

subhashbrillmark commented Mar 18, 2020

Is it possible to use date in x axis using stream chart ? If yes can anybody tell me the properties that I have to deal with or any sort of example available for it

@TeijiW
Copy link

TeijiW commented Mar 27, 2020

Thank you very much to all, this issue helped me so much with dates and tickValues, I managed to finish my chart

@aceodus
Copy link

aceodus commented Apr 22, 2020

any updates for scale time on bar chart @plouc ?

@Michaelzvu
Copy link

Michaelzvu commented Jul 2, 2020

You'll have to implement a custom function for this

What do you mean exactly? Where should I use this function? in case Im using {format: native}

Update:

Found the solution inside the storybook code:

stories.add('formatting values', () => (
    <Line
        {...commonProperties}
        curve="monotoneX"
        yScale={{
            type: 'linear',
            stacked: boolean('stacked', true),
        }}
        yFormat={value =>
            `${Number(value).toLocaleString('ru-RU', {
                minimumFractionDigits: 2,
            })} ₽`
        }
    />
))

@kiliancs
Copy link

kiliancs commented Aug 4, 2020

In our case support for scale time in the bar chart is the only thing preventing us from adopting nivo generally.

@brotzky
Copy link

brotzky commented Aug 5, 2020

We ran into the milliseconds bug with ISO format and the solution was to add the Z to our format string

// our data x format for time
x: 2020-07-31T16:50:00Z
xScale={{
  type: 'time',
  format: '%Y-%m-%dT%H:%M:%SZ', // before we forgot the Z which broke the formatter
}}

Without the Z it was not formatting correctly.

@hamhamhammy
Copy link

I agree with others, scale time for the bar chart is a big pain point.

@hacknug
Copy link

hacknug commented Aug 25, 2020

Don't think this approach will work for everyone (maybe not even for myself once we're using real data) but, for now, I've fixed this issue by transforming the <g> that includes the tickValues.

We need to use a unit-less transform so everything is relative to the SVG's viewBox. Adding that to the group with the tickValues after Nivo has updated its responsive styles should be enough to fix the alignment.

handleResize () {
  this.$nextTick(() => {
    if (!process.client) { return this.handleResize() }
    const colsGroup = document.querySelector('svg > g > g:first-of-type')
    if (!colsGroup) { return this.handleResize() }
    const val = colsGroup.querySelector('line').getAttribute('x1')
    colsGroup.setAttribute('transform', `translate(-${val}, 0)`)
  })
}

I'm using this on Vue/Nuxt so you'll probably need to change some things to make it work in React-land but maybe someone will find this workaround useful.

Does anyone know if this transform is the only thing missing in core to fix this issue or there's a bunch of more complex scenarios where this breaks differently?

@spinloop
Copy link
Contributor

We ran into the milliseconds bug with ISO format and the solution was to add the Z to our format string

// our data x format for time
x: 2020-07-31T16:50:00Z
xScale={{
  type: 'time',
  format: '%Y-%m-%dT%H:%M:%SZ', // before we forgot the Z which broke the formatter
}}

Without the Z it was not formatting correctly.

This fixed the issue I was having with milliseconds as well :-)

@omasback
Copy link

I don't want to pile on by creating another issue but I just thought I'd add that the confusion around time axis config is why I'm not going to use nivo. I just want the ticks and tick formatting on my line chart to be dynamic like d3-axis automatically does by default, but I can't figure out how to do it. For example notice how the Victory time axis dynamically changes tick format precision based on the size of the domain. (You can mess around with the domain on this page to see it in action). https://formidable.com/open-source/victory/gallery/brush-and-zoom

@mvdobrinin
Copy link

Bump would be good to have.

@stale
Copy link

stale bot commented Apr 12, 2021

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

@stale stale bot added the stale label Apr 12, 2021
@mvdobrinin
Copy link

Bump.

@stale stale bot removed the stale label Apr 12, 2021
@nhayfield
Copy link

bump. bit by the milliseconds issue

@wyze
Copy link
Contributor

wyze commented Apr 21, 2021

I'm going to close this issue since it has been open quite a while and people are adding in what seems bugs and feature requests here and makes it hard to track. I'll leave with a couple of points:

  • Millisecond issue: this doesn't appear to be anything with the library and a bug in your code. You need to make sure your data matches the provided format to d3-time.
  • Date bar scale: someone should open a new issue/pr with this added functionality. A provided example of what the chart would look like would be great. We do have valueScale and indexScale props exposed now, so those might be able to be extended.

@prachinerik
Copy link

I'm trying to use a timestamp for the x value on my graph but I'm also running into the Cannot read property 'setMilliseconds' of null problem. Not on a bar chart, on a ResponsiveLine chart. :( This thread was what came up when I googled the issue.

Reproduced here: https://codesandbox.io/s/lucid-night-h2pk5

Notably, if I set precision to "week" instead of "day" then I get a different error: Cannot read property 'forEach' of undefined

As it stands, I don't think I'll be able to use the time formatting feature; I'll have to DIY it with moment or something...

I am facing this exact same issue, This is the x-scale I am trying to use xScale={{
type: 'time',
format: '%Y-%m-%d',
useUTC: false,
precision: 'day',
}} How to overcome this?

@plouc
Copy link
Owner

plouc commented Jul 26, 2022

@prachinerik, looking at the codesandbox, it seems like the format specifier is not expressed as expected, also when using time scales, the axis format has to be specified, could you please try the following?

xScale={{
  type: "time",
  format: "%Y-%m-%dT%H:%M:%S%Z",
  precision: "day"
}}
axisBottom={{
  format: "%m/%d"
}}

@prachinerik
Copy link

I tried the suggested changes but am still getting the same error.
My data coming in of this format: "2019-09-23"

@plouc
Copy link
Owner

plouc commented Jul 27, 2022

@prachinerik, then you should adapt for this format:

xScale={{
  type: "time",
  format: "%Y-%m-%d",
  precision: "day"
}}
axisBottom={{
  format: "%Y-%m-%d"
}}

@prachinerik
Copy link

prachinerik commented Jul 27, 2022 via email

@plouc
Copy link
Owner

plouc commented Jul 27, 2022

@prachinerik could you please replicate the issue in codesandbox? I used this for the initial issue, and it works, so maybe something else is wrong, it's really hard to tell without having the full context (data + code).

@saqlain1345
Copy link

saqlain1345 commented Nov 28, 2022

Hi @plouc , Unable to achieve it for line chart using
xScale={{
type: "time",
format: "%Y-%m-%d",
precision: "day"
}}
axisBottom={{
format: "%Y-%m-%d"
}}

need to convert large amout of dates into months in x axis ,
how can i achive that

can you anyone help me with this. I've attached the codesandbox below
https://codesandbox.io/s/nivo-line-chart-forked-r1ul76?file=/src/index.js

@plouc
Copy link
Owner

plouc commented Dec 5, 2022

Hi @saqlain1345, the date format does not match the format used in your data: %Y-%m-%d VS %d-%m-%Y, please see this updated sandbox: https://codesandbox.io/s/nivo-line-chart-forked-kll0nh.

@mdarfaanbaig
Copy link

Hey @plouc Can you please help me resolve the issue.

import React, { useEffect, useState } from 'react';
import { ResponsiveLine } from '@nivo/line';
import { useTheme } from "@mui/material";
import { tokens } from "../theme";

const WaterLineChart = ({ data, timeframe }) => {
const theme = useTheme();
const colors = tokens(theme.palette.mode);

const [formattedData, setFormattedData] = useState([]);

useEffect(() => {
if (!data || data.length === 0) {
setFormattedData([]);
return;
}

let newData = [];

if (timeframe === 'today') {
  newData = data.map(entry => ({
    x: entry.timestamp ? new Date(entry.timestamp).toISOString() : null,
    y: entry.volume,
    type: entry.volume_type

  }));
} else if (timeframe === 'daily') {
  newData = data.map(entry => ({
    x: entry.day,
    y: entry.average_volume,
    type: entry.volume_type
  }));
} else if (timeframe === 'weekly') {
  newData = data.map(entry => ({
    x: entry.week,
    y: entry.average_volume,
    type: entry.volume_type
  }));
} else if (timeframe === 'monthly') {
  newData = data.map(entry => ({
    x: entry.month,
    y: entry.average_volume,
    type: entry.volume_type
  }));
}

setFormattedData(newData);

}, [data, timeframe]);

const storageData = formattedData.filter(entry => entry.type === 'Storage');
const overheadData = formattedData.filter(entry => entry.type === 'Overhead');

const formatXAxis = (value) => {
if (value === null || isNaN(new Date(value).getTime())) {
return value; // Return null or value itself if it's not a valid date
}
if (timeframe === 'daily') {
const date = new Date(value);
return date.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' });
}

if (timeframe === 'weekly' || timeframe === 'monthly') {
  return value;
}
if (timeframe === 'today') {
  const date = new Date(value);
  const hours = date.getHours().toString().padStart(2, '0');
  const minutes = date.getMinutes().toString().padStart(2, '0');
  return `${hours}:${minutes}`;
}
return value;

};

const yAxisLegend = timeframe === 'today' ? "Volume (litres)" : "Average Volume (litres)";

const getTickValues = () => {
if (timeframe === 'today') {
return 'every 1 hour';
}
if (timeframe === 'daily') {
return 'every 1 day';
}
if (timeframe === 'weekly') {
return 'every 1 week';
}
if (timeframe === 'monthly') {
return 'every 1 month';
}
return undefined;
};

return (
<div style={{ width: '100%', height: '75vh' }}>
<ResponsiveLine
data={[
{
id: 'Storage',
data: storageData,
},
{
id: 'Overhead',
data: overheadData,
},
]}
theme={{
axis: {
domain: {
line: {
stroke: colors.grey[100],
},
},
legend: {
text: {
fill: colors.grey[100],
fontSize: 18,
fontWeight: 'bold',
},
},
ticks: {
line: {
stroke: colors.grey[100],
strokeWidth: 1,
},
text: {
fill: colors.grey[100],
},
},
},
legends: {
text: {
fill: colors.grey[100],
fontSize: 16,
},
},
tooltip: {
container: {
color: colors.primary[500],
},
},
}}
colors={{ scheme: "nivo" }}
margin={{ top: 50, right: 110, bottom: 100, left: 60 }}
xScale={{
type: timeframe === 'today' ? 'time' : 'point',
format: timeframe === 'today' ? '%Y-%m-%dT%H:%M:%S.%LZ' : undefined,
precision: timeframe === 'today' ? 'hour' : undefined,
useUTC: false,
}}
yScale={{ type: 'linear', min: 'auto', max: 'auto', stacked: false, reverse: false }}
axisTop={null}
axisRight={null}
axisBottom={{
orient: "bottom",
tickSize: 10,
tickPadding: 5,
tickRotation: 0,
legend: "Time",
legendOffset: 70,
legendPosition: "middle",
format: formatXAxis,
tickValues: getTickValues(), // Dynamically set tickValues based on timeframe
}}
axisLeft={{
orient: "left",
tickSize: 7,
tickPadding: 5,
tickRotation: 0,
legend: yAxisLegend,
legendOffset: -55,
legendPosition: "middle",
}}
enableGridX={false}
enableGridY={false}
pointSize={8}
pointColor={{ theme: "background" }}
pointBorderWidth={2}
pointBorderColor={{ from: "serieColor" }}
pointLabelYOffset={-12}
useMesh={true}
legends={[
{
anchor: "bottom-right",
direction: "column",
justify: false,
translateX: 100,
translateY: 0,
itemsSpacing: 0,
itemDirection: "left-to-right",
itemWidth: 80,
itemHeight: 20,
itemOpacity: 0.75,
symbolSize: 12,
symbolShape: "circle",
symbolBorderColor: "rgba(0, 0, 0, .5)",
effects: [
{
on: "hover",
style: {
itemBackground: "rgba(0, 0, 0, .03)",
itemOpacity: 1,
},
},
],
},
]}
/>

);
};

export default WaterLineChart;

The above is my WaterLineChart.jsx . For timeframe= today I am getting error : Cannot read properties of null (reading 'setMilliseconds')
TypeError: Cannot read properties of null (reading 'setMilliseconds')

Can anyone help me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests