pythonplotnine

how to set accuracy within mizani.labels.percent()


Can I use mizani.label.percent or another mizani formatter to present the geom_label with one decimal place? The code below works but rounds to an integer.

import polars as pl
from plotnine import * 
import mizani.labels as ml

df = pl.DataFrame({
  "group": ["A", "B"], 
  "rate": [.511, .634]
})

(
    ggplot(df, aes(x="group", y="rate", label="ml.percent(rate)"))
    + geom_col()
    + geom_label()
    + scale_y_continuous(labels=ml.percent)
)

enter image description here

(
    ggplot(df, aes(x="group", y="rate", label="ml.percent(rate, accuracy=.1)"))
    + geom_col()
    + geom_label()
    + scale_y_continuous(labels=ml.percent)
)

When doing this, I get the following error:

PlotnineError: "Could not evaluate the 'label' mapping: 'ml.percent(rate, accuracy=.1)' (original error: __call__() got an unexpected keyword argument 'accuracy')"

Solution

  • After experimenting with it for quite a bit, I found a solution:

    You can directly format the labels within geom_label to achieve the desired percentage display:

    import polars as pl
    from plotnine import * 
    import mizani.labels as ml
    
    df = pl.DataFrame({
      "group": ["A", "B"], 
      "rate": [.511, .634]
    })
    
    (
        ggplot(df, aes(x="group", y="rate", label="rate"))
        + geom_col()
        + geom_label(format_string='{:.1%}')
        + scale_y_continuous(labels=ml.percent)
    )
    

    Result:

    Bar chart showing rates for two groups A and B, with group B having a higher rate of 63.4% compared to group A's 51.1%.

    Additionally, you could replace the mizani formatting on the y-axis like this to achieve the same result:

    import polars as pl
    from plotnine import * 
    
    df = pl.DataFrame({
      "group": ["A", "B"], 
      "rate": [.511, .634]
    })
    
    (
        ggplot(df, aes(x="group", y="rate", label="rate"))
        + geom_col()
        + geom_label(format_string='{:.1%}')
        + scale_y_continuous(labels=lambda l: ["{:.1f}%".format(v * 100) for v in l])
    )