🏠 back to Observable

formatting title on hover in PLOT

Hello,

I am sure this is a silly problem, but I need to understand how to work two things out.
I have a large table with song/Film/Year/Composer/Lyricist/Singers. The link is Giitaayan Archive

Problem 1: The first graph shows each song as a line, and the title on hover works fine. I can modify it as showing the film, composer, singers etc.
Now I want to be able to show one or more of those PLUS the COUNT when I change the bins into films, composers, year, etc. For example, when I modify the graph to show each ‘rect’ as a composer, I want to be able to show <composer: count> in title on hover. I can accomplish either count or composer, (or year/lyricist etc.), but not both. Would please someone modify the code attached and show me?

Problem 2: Is it possible to create a graph based on x = values in two rows? Like in the code below, x: “Year”, if I want to do x: “Composer” PLUS “Singers”, to show the same person who appears both as composer and singer? Is it possible?

Thanks a ton and sorry for posting problems with obvious solutions,
Rini

Plot.rectY(
      giitaayanW,
      Plot.binX(
        { y: "count", title: "count" },
        {
          rx: 15,
          x: "Year",
          fill: "Composer",
          title: `${(d) => d.Composer}, ${(v) => v.count}`
        }
      )
    ),

For the first question, try maybe

    Plot.rectY(
      giitaayanW,
      Plot.binX(
        { y: "count", title: v => `${v[0].Composer}, ${v.length}` },
        {
          rx: 15,
          x: "Year",
          fill: "Composer",
        }
      )
    ),

if the title reducer is given only as an output, it gets passed the whole list of data for each group (i.e. all the films by each composer), and can return the name (taken from the first element), and the length of the group (number of films).

I’m less sure about the second question. Grouping is exclusive (each film will belong to only one group). You should be able to create a new list where each song appears once for its composer and once for each of its singers, then group from that, but it means a bit of “data wrangling” before you send it to plot. A good tool to do this is Array.flatMap(), for example:

data = giitaayanW.flatMap(song => [
    {...song, person: song.Composer, role: "composer"},
    ... (song.Singers || "").split(", ")
        .filter(singer => singer) // remove empty strings
        .map(singer => ({...song, person: singer, role: "singer"}))
])

and then you would group this data on “person”, and maybe reduce the title to show the number of songs and roles…

2 Likes

Thanks so much, @Fil !
The first solution works perfectly (I will try the second problem soon). A follow-up on the first solution. Why can’t I stack the Composers in descending order, by having the biggest rect at the bottom, then thinning out as I go up?

1 Like

Ordering for the stackY transform can be done by adding an order: "value" option. But it will stack from smaller to larger, and what you want is the opposite.

To reverse the order, you can use reverse: true, however if you call this from within the Plot.binX transform, reverse will be consumed by Plot.binX, and not passed up to Plot.stackY (which, in your case, doesn’t appear in the code because it is implicitly called by Plot.rectY). So, to make reverse reach the stack transform, we need to write a destructured options object.

Plot.rectY(giitaayanW, {
      ...Plot.binX(
        {
          y: "count",
          //      order: (a, b) => d3.descending(a.length, b.length),
          title: (v) => `${v[0].Composer}: ${v.length}`
        },
        {
          rx: 15,
          x: "Year",
          fill: "Composer"
        }
      ),
      order: "value",
      reverse: true
    })

I would also encourage you to test order: "sum" instead of order: "value"; it will make the ordering identical in each pile, making it easier to follow a composer across the years.

3 Likes

Thanks again, @Fil ! I am going to have tons of use for both order and sum, in various of my projects.

1 Like

Hi @Fil,

I have tried out the solution to my second issue in the original posting, and am now able to just map one singer, Lata, from the singers column. This is the last chart on the page.
I have a follow-up question/issue here. I am happy with the chart, but people without coding interests/skills who will be just looking at the graph will try to find the logic of binning and want information on a yearly basis. For instance, the years 1940-1945 share 3 columns. How, if I can at all, plot rects by year or (5 year or 10 years) and include the years in title on hover? Like, the most prolific singer in the chart, Lata, has columns with 99, 173 etc. songs… What are the year(s) for 99 songs? For 173 songs etc.?

Apologize for all these questions. I am a latecomer to the world of computing; my PhD was in Comparative Literature.

One more newbie question: where can I read more about the significance of the three dots (…) in so many of the code snippets that I see?

Sincerely,
Rini

https://observablehq.com/@rbhttchr/hindifilmsongs_basic

You can also use an explicit stack transform, rather than relying on the implicit one, so that you can pass the order and reverse options to the stack transform explicitly.

Plot.rectY(
  giitaayanW,
  Plot.stackY(
    {order: "sum", reverse: true},
    Plot.binX(
      {y: "count", title: v => `${v[0].Composer}: ${v.length}`},
      {rx: 15, x: "Year", fill: "Composer"}
    )
  )
)

Thanks so much, @mbostock ! I now know two ways of doing the stack transform.
Do you have any suggestions for making it clear what year does each rect represent? This is form the last query that I asked:
“people without coding interests/skills who will be just looking at the graph will try to find the logic of binning and want information on a yearly basis. For instance, the years 1940-1945 share 3 columns. How, if I can at all, plot rects by year or (5 year or 10 years) and include the years in title on hover? Like, the most prolific singer in the chart, Lata, has columns with 99, 173 etc. songs… What are the year(s) for 99 songs? For 173 songs etc.?”

Thanks a ton,
Rini

You could presumably do something like d3.extent(v, d => d.Year) to compute the years represented by each bin, but that’s not ideal because it could be a subset of the bin’s extent. You’d probably have to call d3.bin yourself if you want this, rather than relying on Plot’s bin transform. Here’s the related issue in the Plot repo: