# observable plot cell: how to order y axis by a specific field

**URL:** <https://talk.observablehq.com/t/observable-plot-cell-how-to-order-y-axis-by-a-specific-field/8120>\
**Category:** Help\
**Created:** [July 23, 2023, 10:18am UTC](https://talk.observablehq.com/t/observable-plot-cell-how-to-order-y-axis-by-a-specific-field/8120 "2023-07-23T10:18:38Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![aborruso](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aborruso/32/4334_2.png) [@aborruso](https://talk.observablehq.com/u/aborruso)\
**Post date:** [July 23, 2023, 10:18am UTC](https://talk.observablehq.com/t/observable-plot-cell-how-to-order-y-axis-by-a-specific-field/8120/1 "2023-07-23T10:18:38Z")

</div>

Hi,  
I have this [plot cell chart](https://observablehq.com/d/ed74fd6690651923#calendario), and I would like to sort the y axis items list using the `latitude` field.

I have added these setting but these do not work.

How to set it properly?

Thank you

```auto
        {
          x: "data",
          y: "citta",
          fill: "livello_n",
          inset: 1,
          tip: false,
          sort: "latitude",
          reverse: true
        }

```

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [July 23, 2023, 3:17pm UTC](https://talk.observablehq.com/t/observable-plot-cell-how-to-order-y-axis-by-a-specific-field/8120/2 "2023-07-23T15:17:16Z")

</div>

When you set the **sort** mark option to a field name such as _latitude_, you’re applying a [sort transform](https://observablehq.com/plot/transforms/sort) to sort the order in which the marks are drawn, _i.e._ the _z_ order. (And here because there’s also a group transform, you’re sorting the data prior to grouping, which has no effect on the resulting display.) Likewise the **reverse** mark option simply reverses the _z_ order.

In Plot speak, what you’re asking is how to set the **domain** of the _y_ scale.

The most literal way of setting the domain would be to supply the list of values in the desired order:

```js
Plot.plot({
  y: {
    label: null,
    domain: [
      "CATANIA",
      "REGGIO CALABRIA",
      "PALERMO",
      "MESSINA",
      "CAGLIARI",
      "NAPOLI",
      "BARI",
      "LATINA",
      "CAMPOBASSO",
      …
    ]
  },
  marks: …
})

```

But there are various ways to do this more implicitly from the data.

One way is to use [d3.groupSort](https://d3js.org/d3-array/group#groupSort): you group by the data by _citta_ and then return the corresponding _latitude_ for that _citta_. Assuming that your data is consistent and all rows with a given _citta_ have the same _latitude_, you can pull out the first row for each group and return its latitude:

```js
Plot.plot({
  y: {
    label: null,
    domain: d3.groupSort(
      diecigiorni,
      ([d]) => d.latitude,
      (d) => d.citta
    )
  },
  marks: …
})

```

In other cases, you might do something like compute the median value for each group:

```js
Plot.plot({
  y: {
    label: null,
    domain: d3.groupSort(
      diecigiorni,
      (D) => d3.median(D, (d) => d.latitude),
      (d) => d.citta
    )
  },
  marks: …
})

```

In Plot, you can implicitly set an ordinal scale’s domain using the [mark **sort** option](https://observablehq.com/plot/features/scales#sort-mark-option). This imputes the given ordinal scale’s domain from the values in a given mark channel. This feature is often used to produce a sorted bar chart, where the bars are ordered from shortest to longest or _vice versa_ ([second example here](https://observablehq.com/@observablehq/plot-vertical-bars)).

It’s a little trickier to use this feature here because the _latitude_ field is not used as a visual encoding; it’s merely associated with the _citta_ field. And also, you’re using a group transform — although the group transform doesn’t seem to be necessary here since there is only one row in the dataset per _citta_ and _data_. So just to demonstrate, if you set the _y_ channel to the _latitude_ field instead of the _citta_ field, and removed the group transform, you can use the **sort** option like so:

 ![untitled (38)](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/2/2879962d0200171ee7f0b77c861e2dce19d5fd10.png)

```js
Plot.plot({
  …,
  marks: [
    Plot.cell(diecigiorni, {
      x: "data",
      y: "latitude",
      fill: "livello_n",
      inset: 1,
      sort: { y: "y" }
    })
  ]
})

```

If you want the group transform still, it’s like so (the group transform groups by _x_ and _y_, so you can still reference it for imputing the _y_ scale domain after grouping):

```js
Plot.plot({
  …,
  marks: [
    Plot.cell(
      diecigiorni,
      Plot.group(
        { fill: "max" },
        {
          x: "data",
          y: "latitude",
          fill: "livello_n",
          inset: 1,
          sort: { y: "y" }
        }
      )
    )
  ]
})

```

But now your _y_ scale ticks are hard to read, clearly, and the chart wouldn’t correctly handle two cities having the same latitude. 😁

So now that we’ve seen how the **sort** option works in the most direct case, let’s go back to using the _citta_ field for _y_ and change the **sort** option to use the **reduce** option to find the latitude of each city:

 ![untitled (39)](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/7/7b5b4af110ca5110e176a465aad19f783f3c83c2.png)

```js
Plot.plot({
  …,
  marks: [
    Plot.cell(diecigiorni, {
      x: "data",
      y: "citta",
      fill: "livello_n",
      inset: 1,
      sort: {
        y: "y",
        reduce: ([y]) => diecigiorni.find((d) => d.citta === y).latitude
      }
    })
  ]
})

```

Note that the reduce function is passed an array of values from the mark’s _y_ channel: that’s because there are multiple rows in the data associated with each _citta_, but to order the _y_ domain we must return a single orderable value for each distinct _citta_. Again, as long as the data is consistent with the _citta_ and _latitude_ columns, it’s fine to just pull out the first value using square brackets. (I also omitted the group transform, but you could put it back in as shown in the other examples; it doesn’t affect how the reduce function is defined here.)

Lastly, you could do this by passing _data_ to the **sort** option for the _y_ scale; that way you don’t have to reference `diecigiorni` in the reduce function. Then the reduce function is passed an array of data instead of an array of _y_ channel values. That looks like this:

```js
Plot.plot({
  …,
  marks: [
    Plot.cell(diecigiorni, {
      x: "data",
      y: "citta",
      fill: "livello_n",
      inset: 1,
      sort: {
        y: "data",
        reduce: ([d]) => d.latitude
      }
    })
  ]
})

```

If you still want the group transform, you’ll need an extra set of brackets in your reduce function because now the reducer will be passed the grouped data for each _y_ value:

```js
Plot.plot({
  …,
  marks: [
    Plot.cell(
      diecigiorni,
      Plot.group(
        { fill: "max" },
        {
          x: "data",
          y: "citta",
          fill: "livello_n",
          inset: 1,
          sort: {
            y: "data",
            reduce: ([[d]]) => d.latitude
          }
        }
      )
    )
  ]
})

```

Hope this helps.

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [July 23, 2023, 3:41pm UTC](https://talk.observablehq.com/t/observable-plot-cell-how-to-order-y-axis-by-a-specific-field/8120/3 "2023-07-23T15:41:15Z")

</div>

Oh, and to combine with your other question on [date formats](https://talk.observablehq.com/t/observable-plot-cell-and-date-label/8116), here is some complete code:

 ![calendario (1)](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/b/b6ab716294455a6fde8069155d7d6572f8632ef0.png)

```js
calendario = Plot.plot({
  marginLeft: 120,
  marginBottom: 40,
  padding: 0,
  color: {
    domain: [0, 1, 2, 3],
    range: ["#5bd601", "#e8d203", "#ff7f02", "#c40001"]
  },
  label: null,
  x: {
    type: "band",
    tickFormat(x, i, X) {
      const format1 = (x) => x.toLocaleString("it", { day: "numeric" });
      const format2 = (x) => x.toLocaleString("it", { month: "short" });
      const f1 = format1(x);
      const f2 = format2(x);
      return i > 0 && f2 === format2(X[i - 1]) ? f1 : [f1, f2].join("\n");
    }
  },
  marks: [
    Plot.cell(diecigiorni, {
      x: "data",
      y: "citta",
      fill: "livello_n",
      inset: 0.5,
      sort: { y: "data", reduce: ([d]) => -d.latitude },
      tip: true,
      title: (d) =>
        `${d.citta}, livello ${d.livello_n}\n${d.data.toLocaleString("it", {
          day: "numeric",
          month: "long",
          year: "numeric"
        })}`
    })
  ]
})

```

I also used the **tip** option instead of a separate tip mark for simplicity. Let me know if you have another question there. 🙂

---

<div class="post-metadata">

**Author:** ![aborruso](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aborruso/32/4334_2.png) [@aborruso](https://talk.observablehq.com/u/aborruso)\
**Post date:** [July 23, 2023, 5:09pm UTC](https://talk.observablehq.com/t/observable-plot-cell-how-to-order-y-axis-by-a-specific-field/8120/4 "2023-07-23T17:09:50Z")

</div>

“ **The great and the powerful Bostok** ”!!

It is a great honor and an immense pleasure to receive your reply, thank you very much.

I have applied your code and now I have sorted the y axis. I have edited inserting one character, because I want reverse sorting, to have North-South ordering (these are Italian cities, and the colors are related to heat waves).  
It works great.

However, I take the opportunity to talk to you about two related things, which seem important to me.

**The first one** is closely related. This function, to sort the order of ordinal axes, using one of the dataframe fields, seems to me an option that should be ready already. Something that can immediately interest all users who need to use this type of graph.  
I know, observable plot doesn’t think like that, but it would be nice to change it so that the sort option is available. Also in cell chart.  
To be able to simply add something like

```auto
sorty: "fieldname",
reverse: true

```

Before writing here, I tried to sort the dataframe by latitude, but then the sorting by citta was forced in any case. Another option might be to make it depend on the ordering of the dataframe.

**The second one** is related to **tooltips**. Related to this calendar view, I have the URLs of the heatwave bulletins in PDF.  
I would like to be able to build a sticky tooltip, in which I have an hyperlink to open then PDFS in a \_blank window.

In the same way I can do in [this map](https://www.datawrapper.de/_/elo50/) in datawrapper.  
I have tried to build this map [using Plot](https://observablehq.com/d/19a3553a052966b0#cell-12), but [it’s not a native way to do it this in Plot](https://talk.observablehq.com/t/observable-plot-tip-how-to-style-it-and-add-some-hyperlink/8097).

So I take advantage of having you here, to tell you that I think it’s very useful to be able to customize the tooltips.

🫶 Thank you very much

[![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/9/9748b6c6c1f49bdf8c0b81e6b5ab3c297ac8ad56.png)](https://www.datawrapper.de/_/elo50/)

---

<div class="post-metadata">

**Author:** ![aborruso](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aborruso/32/4334_2.png) [@aborruso](https://talk.observablehq.com/u/aborruso)\
**Post date:** [July 23, 2023, 5:12pm UTC](https://talk.observablehq.com/t/observable-plot-cell-how-to-order-y-axis-by-a-specific-field/8120/5 "2023-07-23T17:12:37Z")

</div>

Dear @mbostock I have replied to you in a wrong thread:  
[https://talk.observablehq.com/t/observable-plot-cell-and-date-label/8116/7](https://talk.observablehq.com/t/observable-plot-cell-and-date-label/8116/7)

Thank you very much

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [July 23, 2023, 6:09pm UTC](https://talk.observablehq.com/t/observable-plot-cell-how-to-order-y-axis-by-a-specific-field/8120/6 "2023-07-23T18:09:13Z")

</div>

> [@aborruso](#):
>
> This function, to sort the order of ordinal axes, using one of the dataframe fields, seems to me an option that should be ready already. … To be able to simply add something like
> 
> ```auto
> sorty: "fieldname",
> reverse: true
> 
> ```

We discussed the following syntax in [#867](https://github.com/observablehq/plot/issues/867#issuecomment-1554968406):

```js
sort: {y: "fieldname", reverse: true}

```

The issue is that you can already use this to sort by channel values:

```js
sort: {y: "x", reverse: true}

```

Or more accurately:

```js
sort: {y: "x", order: "descending"}

```

And more succinctly:

```js
sort: {y: "-x"}

```

If we allow you to specify a field name as well as a channel name, then there is potential confusion as to whether the name refers to a field or a channel. For example, you could have a CSV file with a column named “x”. And we likely can’t support the convenient shorthand minus syntax for descending order, since a field name could start with a hyphen.

Possibly we could disambiguate using the following syntax for channels:

```js
sort: {y: {channel: "x", order: "descending"}}

```

And this syntax for fields:

```js
sort: {y: {value: "latitude", order: "descending"}}

```

We already allow the latter for channel names… so I’d probably want to think a bit how to introduce this without breaking backwards compatibility. But in any case it’s not really that different than using the **reduce** option as I showed above.

The current solution is a bit circuitous: you define an extra channel using the **channels** option, and then you can reference that for sorting. For example:

```js
Plot.plot({
  marks: [
    Plot.barX(clean_monthly_april, {
      y: "title",
      x: "activations",
      channels: {difference: "difference"},
      sort: {y: "difference"}
    })
  ]
})

```

Anyway, I’ve reopened the issue… we can probably find an acceptable solution to the ambiguity issue, and I agree this is a common enough concern.

> [@aborruso](#):
>
> I would like to be able to build a sticky tooltip, in which I have an hyperlink to open then PDFS in a \_blank window.

I’m not sure yet how we’ll solve this. As you found we have a [#1612](https://github.com/observablehq/plot/issues/1612) for customizing the format of values in the tip mark, but you’re asking for something different which is supporting rich text and links, such as through Markdown or HTML. That would be difficult to implement because the tip mark is rendered in SVG, and we intentionally limited its expressiveness so we could make the implementation simpler and focus on displaying readable values. Perhaps we need a separate mark type that uses e.g. foreignObject to embed rich content.

---

<div class="post-metadata">

**Author:** ![mcmcclur](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mcmcclur/32/2364_2.png) [@mcmcclur](https://talk.observablehq.com/u/mcmcclur)\
**Post date:** [July 24, 2023, 8:43am UTC](https://talk.observablehq.com/t/observable-plot-cell-how-to-order-y-axis-by-a-specific-field/8120/7 "2023-07-24T08:43:38Z")

</div>

> [@aborruso](#):
>
> I would like to be able to build a sticky tooltip, in which I have an hyperlink to open then PDFS in a \_blank window.

You can generate a map with `Plot.geo` and then manipulate it a bit further with a little Javascript to add tooltips that are more flexible than what you can do with Plot alone. I generated the following map, which looks a bit like your Datawrapper map, using this technique:

https://observablehq.com/embed/baeb8cbead1ed3d2?cells=map%2Ctippy_style

Note that the tooltips are stick, have a clickable link, and can contain arbitrary HTML.

---

<div class="post-metadata">

**Author:** ![aborruso](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aborruso/32/4334_2.png) [@aborruso](https://talk.observablehq.com/u/aborruso)\
**Post date:** [July 24, 2023, 8:59am UTC](https://talk.observablehq.com/t/observable-plot-cell-how-to-order-y-axis-by-a-specific-field/8120/8 "2023-07-24T08:59:31Z")

</div>

> [@mcmcclur](#):
>
> You can generate a map with `Plot.geo` and then manipulate it a bit further with a little Javascript to add tooltips that are more flexible than what you can do with Plot alone. I generated the following map, which looks a bit like your Datawrapper map, using this technique:

You are very kind, thank you very much.  
I will use it and once it will be ready I will share the URL here.

I think it’s important to have a native way to do it in Plot.

Thank you

---

<div class="post-metadata">

**Author:** ![bz-jm](https://avatars.discourse-cdn.com/v4/letter/b/bb73d2/32.png) [@bz-jm](https://talk.observablehq.com/u/bz-jm)\
**Post date:** [July 25, 2023, 5:49pm UTC](https://talk.observablehq.com/t/observable-plot-cell-how-to-order-y-axis-by-a-specific-field/8120/9 "2023-07-25T17:49:41Z")

</div>

@mcmcclur I was actually attempting to achieve something similar and posted a question here: [Plot Pointer - x,y coordinates](https://talk.observablehq.com/t/plot-pointer-x-y-coordinates/8124) I ran into an issue getting the coordinates for the “dots” (as I didn’t think to try and replace the marks with something new). My plot has more dots then just the dots which need tooltips… wondering if you have any thoughts/insights into how to select specific dots from a set of marks?
