GithubHelp home page GithubHelp logo

Comments (8)

mbostock avatar mbostock commented on April 28, 2024 2

Sure, the same thing in D3 would look like this:

untitled (59)

{
  const width = 640;
  const height = 400;
  const marginTop = 20;
  const marginRight = 20;
  const marginBottom = 30;
  const marginLeft = 40;

  const x = d3.scaleBand()
      .domain(d3.utcMonths(
        d3.min(aapl, (d) => d3.utcMonth(d.Date)),
        d3.max(aapl, (d) => d3.utcMonth.offset(d3.utcMonth(d.Date), 1))))
      .range([marginLeft, width - marginRight]);

  const y = d3.scaleLinear()
      .domain([0, d3.max(aapl, (d) => d.Close)])
      .range([height - marginBottom, marginTop]);

  const svg = d3.create("svg")
      .attr("width", width)
      .attr("height", height)
      .attr("viewBox", [0, 0, width, height])
      .attr("style", "max-width: 100%; height: auto;");

  svg.append("g")
      .attr("transform", `translate(0,${height - marginBottom})`)
      .call(d3.axisBottom(x)
           .tickFormat(d3.utcFormat("%Y"))
           .tickValues(d3.utcYears(...d3.extent(x.domain()))))
      .call((g) => g.select(".domain").remove());

  svg.append("g")
      .attr("transform", `translate(${marginLeft},0)`)
      .call(d3.axisLeft(y))
      .call((g) => g.select(".domain").remove());

  svg.append("g")
    .selectAll()
    .data(d3.rollup(aapl, (D) => d3.median(D, (d) => d.Close), (d) => d3.utcMonth(d.Date)))
    .join("rect")
      .attr("x", ([key]) => x(key) + 0.5)
      .attr("width", x.bandwidth() - 1)
      .attr("y", ([, value]) => y(value))
      .attr("height", ([, value]) => y(0) - y(value));

  return svg.node();
}

https://observablehq.com/d/681d761602f1014d

It’d be a bit harder to get Plot’s multi-line time axis, since d3-axis doesn’t support multi-line text (which is implemented by Plot’s text mark).

Also I think there’s an off-by-one above in some cases: it’s a little tricky to get d3.utcMonths/d3.utcYears etc. to return an inclusive upper bound, since they like to treat the upper bound as exclusive.

from d3-axis.

curran avatar curran commented on April 28, 2024

Here's an example using scaleTime only, which computes the length of one bar based on time intervals.

https://vizhub.com/curran/14055e4902844acca03ba97d02d1bc72?edit=files&file=index.html#L177

      const now = new Date();
      const oneMonthWidth =
        xScale(d3.timeMonth.ceil(now)) -
        xScale(d3.timeMonth.floor(now));

      const marks = g
        .selectAll('.mark')
        .data(data);
      marks
        .enter()
        .append('rect')
        .attr('class', 'mark')
        .merge(marks)
        .attr('x', (d) => xScale(xValue(d)))
        .attr('y', (d) => yScale(yValue(d)))
        .attr('width', oneMonthWidth)
        .attr(
          'height',
          (d) => innerHeight - yScale(yValue(d))
        )

Here's another example that leverages bin, which has a nice API for getting the min and max values for each bin.

https://vizhub.com/curran/a95f227912474d4a9bbe88a3c6c33ab9?edit=files

Padding is the only "unsolved problem" in this space, but that could be added by adding a constant to X and subtracting double that constant to width.

What I'm saying here is, you can accomplish the thing you describe using the existing D3 API. So I'm not sure the argument to add more API surface area to D3 for this is very strong (but it's a cool idea for sure!).

from d3-axis.

IPWright83 avatar IPWright83 commented on April 28, 2024

I'm just revisiting this, I built an example here https://ipwright83.github.io/chart-io/?path=/story/xycharts-mixedplots--mixed-stacked-column-plots and haven't yet managed to crack padding.

If you go for the adding a constant to X, would that not pick the ticks out of whack with the data points? I tried an option tweaking the range/domain to add padding which allows you to render data points fully (but was Jacky to get working) and then left my axis line happy at the start/end :(

from d3-axis.

Fil avatar Fil commented on April 28, 2024

I tend to use the same solution as @curran’s, often with a bit of padding to leave a pixel between the bars.

With Plot you would write (https://observablehq.com/@recifs/shifted-time-bars):

Plot.rectY(
  aapl,
  Plot.mapX(
    (d) => d.map((d) => d3.utcDay.offset(d, -14)), // shift bars left by half a month
    Plot.binX(
      { y: "median", title: "min" },
      { title: "Date", x: "Date", y: "Close", interval: "month" }
    )
  )
).plot()

untitled (1)

It would be cool if interval.offset supported fractional offsets, but dealing with time is so difficult that I don't see this happening.

from d3-axis.

mbostock avatar mbostock commented on April 28, 2024

I don’t recommend using a time axis if you want to represent time as ordinal (i.e., with bars centered over the ticks); offsetting the position by 14 days won’t work well because months are different lengths (28–31 days, and days are 23–25 hours depending on your time zone).

In Plot, I recommend using a band (ordinal) scale, combining barX and groupX:

untitled (57)

Plot.barY(
  aapl,
  Plot.groupX(
    { y: "median", title: "min" },
    { title: "Date", x: "Date", y: "Close" }
  )
).plot({
  marginBottom: 80,
  x: {
    interval: "month",
    tickRotate: 90
  }
})

Plot is smart enough to know that the ordinal scale represents time thanks to the interval option, but unfortunately it’s still not smart enough to reduce the number of ticks shown automatically, or to apply the new multi-line time axis by default. You can get a better result by setting the ticks explicitly:

untitled (58)

Plot.barY(
  aapl,
  Plot.groupX(
    { y: "median", title: "min" },
    { title: "Date", x: "Date", y: "Close" }
  )
).plot({
  x: {
    interval: "month",
    tickFormat: "%Y",
    ticks: d3.utcYears(...d3.extent(aapl, (d) => d.Date))
  }
})

There should be a way to get the equivalent of the time axis for ordinal time scales… I’ll file an issue over in the Plot repo.

from d3-axis.

IPWright83 avatar IPWright83 commented on April 28, 2024

Is there any equivalent approach for applying an interval like property in D3? I'm not using Plot, but I might look at the source for some inspiration. A band scale may work, but I'd like to get an axis rendering with the knowledge of time so we don't need every tick

from d3-axis.

Fil avatar Fil commented on April 28, 2024

https://github.com/d3/d3-time#_interval

from d3-axis.

IPWright83 avatar IPWright83 commented on April 28, 2024

Thanks @Fil, I'll see if I can apply that to my band scale somehow (and also try some padding on a linear scale) and share my results.

from d3-axis.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.