# Facet custom sort

**URL:** <https://talk.observablehq.com/t/facet-custom-sort/8002>\
**Category:** Help\
**Created:** [June 5, 2023, 11:32am UTC](https://talk.observablehq.com/t/facet-custom-sort/8002 "2023-06-05T11:32:02Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Beatriz](https://avatars.discourse-cdn.com/v4/letter/b/f9ae1b/32.png) [@Beatriz](https://talk.observablehq.com/u/Beatriz)\
**Post date:** [June 5, 2023, 11:32am UTC](https://talk.observablehq.com/t/facet-custom-sort/8002/1 "2023-06-05T11:32:02Z")

</div>

I’m trying to have some sorted facets based on a interval variable containing the next values:

```auto
["0 - 1.3", "1.3 - 5.04", "5.04 - 7.7", "7.7 - 10.2", "10.2 - 14.5"]

```

I want to override alphabetical sort that considers that ‘10.2 - 14.5’ \< ‘5.04 - 7.7’

Here is the complete dataset [Sun hours study / Beatriz Martínez | Observable](https://observablehq.com/d/5ba784b3de5c008c#seasonsrdata)

Thanks!

---

<div class="post-metadata">

**Author:** ![Fil](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/fil/32/207_2.png) [@Fil](https://talk.observablehq.com/u/Fil)\
**Post date:** [June 5, 2023, 11:55am UTC](https://talk.observablehq.com/t/facet-custom-sort/8002/2 "2023-06-05T11:55:19Z")

</div>

For example, you could sort the domain of the y facets like so:

```auto
Plot.plot({
  marginLeft: 100,
  fy: { domain: d3.sort(new Set(Plot.valueof(seasonsrdata, "sun_interval")), parseFloat) },
  marks: [Plot.dotX(seasonsrdata, { fy: "sun_interval", x: "sun" })]
})

```

---

<div class="post-metadata">

**Author:** ![Beatriz](https://avatars.discourse-cdn.com/v4/letter/b/f9ae1b/32.png) [@Beatriz](https://talk.observablehq.com/u/Beatriz)\
**Post date:** [June 5, 2023, 1:10pm UTC](https://talk.observablehq.com/t/facet-custom-sort/8002/3 "2023-06-05T13:10:15Z")

</div>

I understand now, facet settings act as axis settings.

I also tried to use another numerical variable to sort the facets (i.e. sun\_min) and plot custom tick labels based on the intervals. It didn´t work.

How can I get sth like this working?

```auto
Plot.plot({
  marginLeft: 100,
   y: { ticks: ["a", "b", "c", "d", "e"] },
  marks: [Plot.dotX(seasonsrdata, { fy: "sun_min", x: "sun" })]
})

```

Thanks!

---

<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:** [June 5, 2023, 2:52pm UTC](https://talk.observablehq.com/t/facet-custom-sort/8002/4 "2023-06-05T14:52:45Z")

</div>

There’s no _y_ scale in the code you posted, so setting _y_ options will not have any effect.

Are you trying to order the _fy_ scale? Faceting should only be used for ordinal or nominal data; Plot doesn’t currently support automatic binning of quantitative for facets. Though see [#14](https://github.com/observablehq/plot/issues/14) to upvote and for workarounds. That said, you can use faceting here for quantitative data if the domain has low cardinality.

Can you explain in more words what you are trying to accomplish? Is “a” supposed to be the label for minimum _sun\_min_ value? If so, the _fy_ **tickFormat** option is probably what you want. But make sure the labels match the expected values.

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

```js
Plot.plot({
  marginLeft: 80,
  fy: { tickFormat: (d, i) => `${["a", "b", "c", "d", "e"][i]} (${d.toFixed(2)})` },
  marks: [Plot.dotX(seasonsrdata, { fy: "sun_min", x: "sun" })]
})

```

---

<div class="post-metadata">

**Author:** ![Beatriz](https://avatars.discourse-cdn.com/v4/letter/b/f9ae1b/32.png) [@Beatriz](https://talk.observablehq.com/u/Beatriz)\
**Post date:** [June 5, 2023, 9:27pm UTC](https://talk.observablehq.com/t/facet-custom-sort/8002/5 "2023-06-05T21:27:49Z")

</div>

Sorry about the copy-pasted code. I was trying to sort the `fy` scale in this case but I guess it would work the same way when sorting the `y` scale.  
The variable `sun_min` can be considered ordinal as far as represents the lower bound of the `sun_interval`. A couple of data objects as a sample (I have 365, one record for each day of the year)

```auto
[{
  date: 2021-09-27
  sun: 0
  sun_interval: "0 - 1.3"
  sun_min: 0
  sun_max: 1.3
},
{
  date: 2021-09-28
  sun: 2.2
  sun_interval: "1.3 - 5.04"
  sun_min: 1.3
  sun_max: 5.04
}]

```

What I am really trying is to do group by sun interval, count the days in each interval, and show them sorted ‘numerically’ instead of ‘alphabetically’. The range “10.2 - 14.5” is in the middle of the scale 😕

 ![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/1/15dcf9dea372b512ea9422a9f307ff0b8dca87ed.png)

```auto
Plot.plot({
  marginLeft: 100,
  x: {label: "days"}, 
  marks: [
    Plot.barX(
      seasonsrdata.filter(d => d.city == 'City 26'), 
      Plot.groupY({x: "count"}, {
      y: "sun_interval"
    }))
  ]
})

```

I found grouping by `sun_min` a workaround to sort my bars, but then I couldn’t find the way to change the tick labels.  
The faceting issue is explained because there is also a city variable, and I wanted to have a set of grouped bars.

Thank you for your time and your patience. I’m just landing here 🙂

---

<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:** [June 7, 2023, 6:30pm UTC](https://talk.observablehq.com/t/facet-custom-sort/8002/6 "2023-06-07T18:30:03Z")

</div>

Ordinal scale domains are sorted naturally by default, and since “10.2 - 14.5” is a string, it is considered ahead of “5.05 - 7.7”. As you noted, if you use `sun_min` instead of `sun_interval`, you get the expected order because `sun_min` is defined as numbers instead of strings.

 ![untitled (17)](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/3/365c837477c8b8dade23b278873a4d419c9c5c32.png)

```js
Plot.plot({
  marginLeft: 100,
  x: { label: "days" },
  marks: [
    Plot.barX(
      seasonsrdata.filter((d) => d.city == "City 26"),
      Plot.groupY({ x: "count" }, { y: "sun_min" })
    )
  ]
})

```

So, one solution is to specify the **tickFormat** option to control how a given `sun_min` value is displayed. Here’s one way to do that by finding an arbitrary row in the data with a given `sun_min` and returning the corresponding `sun_interval`:

 ![untitled (18)](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/b/b5cd3cc45182e2c0b6de9683afbf1be364860829.png)

```js
Plot.plot({
  marginLeft: 100,
  x: { label: "days" },
  y: {
    label: "sun_interval",
    tickFormat: (y) => seasonsrdata.find((d) => d.sun_min === y).sun_interval
  },
  marks: [
    Plot.barX(
      seasonsrdata.filter((d) => d.city == "City 26"),
      Plot.groupY({ x: "count" }, { y: "sun_min" })
    )
  ]
})

```

If you had a lookup table from `sun_min` to `sun_interval`, you could use that instead.

Another approach is to group by `sun_interval`, and then specify the _y_ scale’s domain so that the order is what you expect instead of the default natural order.

 ![untitled (18)](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/b/b5cd3cc45182e2c0b6de9683afbf1be364860829.png)

```js
Plot.plot({
  marginLeft: 100,
  x: { label: "days" },
  y: { domain: ["0 - 1.3", "1.3 - 5.04", "5.04 - 7.7", "7.7 - 10.2", "10.2 - 14.5"] },
  marks: [
    Plot.barX(
      seasonsrdata.filter((d) => d.city == "City 26"),
      Plot.groupY({ x: "count" }, { y: "sun_interval" })
    )
  ]
})

```

If you don’t want to hardcode the domain, you can use the mark **sort** option to impute it from the data. Here we say that the _y_ scale domain should be imputed from the data, and we should pull out the first `sun_min` value from each group to set the order:

 ![untitled (18)](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/b/b5cd3cc45182e2c0b6de9683afbf1be364860829.png)

```js
Plot.plot({
  marginLeft: 100,
  x: { label: "days" },
  marks: [
    Plot.barX(
      seasonsrdata.filter((d) => d.city == "City 26"),
      Plot.groupY(
        { x: "count" },
        { y: "sun_interval", sort: { y: "data", reduce: ([[d]]) => d.sun_min } }
      )
    )
  ]
})

```

Another way we could write this is to extract the first number from the `sun_interval` value already used by the _y_ channel:

 ![untitled (18)](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/b/b5cd3cc45182e2c0b6de9683afbf1be364860829.png)

```js
Plot.plot({
  marginLeft: 100,
  x: { label: "days" },
  marks: [
    Plot.barX(
      seasonsrdata.filter((d) => d.city == "City 26"),
      Plot.groupY(
        { x: "count" },
        { y: "sun_interval", sort: { y: "y", reduce: ([d]) => parseFloat(d) } }
      )
    )
  ]
})

```

If you had another channel such as **fill** which was naturally ordered, you could use that too:

 ![untitled (22)](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/8/8000dde01f4a7b9599ab46baffe09753810abdb0.png)

```js
Plot.plot({
  marginLeft: 100,
  x: { label: "days" },
  marks: [
    Plot.barX(
      seasonsrdata.filter((d) => d.city == "City 26"),
      Plot.groupY(
        { x: "count" },
        { y: "sun_interval", fill: "sun_min", sort: { y: "fill" } }
      )
    )
  ]
})

```

Live examples: [Plot: Sorting bars / Observable | Observable](https://observablehq.com/d/3789126f637962a7)

---

<div class="post-metadata">

**Author:** ![Beatriz](https://avatars.discourse-cdn.com/v4/letter/b/f9ae1b/32.png) [@Beatriz](https://talk.observablehq.com/u/Beatriz)\
**Post date:** [June 8, 2023, 1:11pm UTC](https://talk.observablehq.com/t/facet-custom-sort/8002/7 "2023-06-08T13:11:46Z")

</div>

Many thanks for such a complete answer 🙂  
I also had tried to use the mark sort without success. It is much more clear now!

thanks for your time!
