Formatting text value

I’m probably overlooking something simple but how can I format the text value using Plot? I’m trying to do something like “10” => “10 year”. Somehow I need to format the value of “count” in the example below.

    Plot.text(data, 
                Plot.groupX(
                  { y: "count", text: "count" },
                  { x: "job_level"},
              )),

The way to go about this is to write a reducer function for the text property. In this case, the reducer is passed an array of all of the items in the group, so in your case you can use the length of the items in that array:

  Plot.groupX(
    { y: "count", text: v => `${v.length} year` },
    { x: "job_level"},
  )

If you were to change the reducer from “count” to something else, then you’d have to make the corresponding change to the text reducer.

1 Like

Thanks a lot @duaneatat! I’m just really curious where did you get the magic variable v from? Think I missed that in the docs.

(How) Could I access v later in the code to for example to something like dy: v.length > 10 ? -10 : -20

Hi Marc,

The v in that example is the parameter that is passed to the reducer. We have some “built-in” reducers, such as “sum”, “count”, or “mean”, but in all cases you can write your own reducer. For example, we could use a similar reducer for the y channel:

  Plot.groupX(
    { y: v => v.length, text: v => `${v.length} year` },
    { x: "job_level"},
  )

And to be clear, v could be any variable name.

Thanks again for clearing that up. I wasn’t aware of how I could access the underlying value of “count” as a variable.

@Cobus @duaneatat Is it possible to do something like dy: v.length > 10 ? -10 : -20? This obviously isn’t the way but what I would like is to change my dy value based on the value of the y axis.

Hi Marc! Can you share a notebook that illustrates the limitation you’re trying to work through? Thanks so much in advance! :pray:

Duane

Unfortunately the dataset I’m using doesn’t allow me to share the notebook publicly. But the code below is what I’m wrangling with. It plots a text label with the count. What I want is that when the count is low (eg <5) dy is set to 0 and otherwise it should be 10. Hope this provides enough info.

    Plot.text(data, 
                Plot.groupX(
                  { y: "count", text: v => v.length},
                  { x: "about_you_job_level", 
                   filter: countries !== null ? (d) => d.about_you_country === countries : null, 
                   fill: "#5F6363",
                   // dy: v.count > 10 ? -10 : -20 // TODO can we do an if here based on count?
                  },
              )),

Ah, I see what you’re saying now. Unfortunately it’s not possible to set the dy style property as a channel, only constant values are allowed for that property. So if you want to have different dy values, you will need to us different marks, and then filter as appropriate. For example:

Plot.text(
  data,
  Plot.groupX(
    {
      y: "count",
      text: (v) => v.length,
      filter: (v) => v.length > 10
    },
    {
      x: "about_you_job_level",
      filter:
        countries !== null ? (d) => d.about_you_country === countries : null,
      fill: "#5F6363",
      dy: -10
    }
  )
)

and then have another mark that filters for v.length <= 10 with the appropriate dy value.

The reason for this is that Plot uses channels to encode data, and only supports channels for things that make sense for encoding data, like position and color. Plot doesn’t support general dynamic or “varying” attributes. That said, I can see your use case here is totally reasonable and valid, and it took some research on my end to get to the bottom of this, so it confused me as well.

Take a look at this issue for more discussion: Should *any* constant property be expressible, somehow, as a data-driven channel if desired? · Issue #175 · observablehq/plot · GitHub

Got it @duaneatat and having separate marks is doable.

Now when I try the filter I get a TypeError: Cannot read properties of null (reading '7').

Any pointers on what might be going on here?

This has something to do with my data and not the filter because it does work in other places.