has2k1 / plotnine

A Grammar of Graphics for Python
https://plotnine.org
MIT License
3.92k stars 210 forks source link

geom_text cannot change vertical or horizontal alignment #726

Closed CopyOfA closed 8 months ago

CopyOfA commented 8 months ago

I am using Python 3.11.5, with plotnine 0.12.1. I am trying to place numerical labels above geom_col bars using the following code:

(
  pn.ggplot(
      data=plot_data, mapping=pn.aes(x="variable", y="value", fill="team1", label="value")
  )
  + pn.geom_col(position="dodge")
  + pn.geom_text(
      position=pn.position_dodge(width=1, preserve="single"),
      mapping=pn.aes(size=6, group="team1", va="top"),
      format_string="{:.1f}",
      show_legend=False
  )
  + pn.facet_wrap(facets="team2")
  + pn.labs(x="", y="")
  + pn.coord_flip()
)

Here plot_data is a Pandas dataframe with four columns: "variable", "value", "team1", "team2". When I call save on this plotnine object, I get the following error:

Traceback (most recent call last):
  File "~/miniconda3/envs/aws_env/lib/python3.11/site-packages/plotnine/mapping/evaluation.py", line 225, in evaluate
    new_val = env.eval(col, inner_namespace=data)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/miniconda3/envs/aws_env/lib/python3.11/site-packages/patsy/eval.py", line 169, in eval
    return eval(code, {}, VarLookupDict([inner_namespace]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<string>", line 1, in <module>
NameError: name 'top' is not defined

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "~/Documents/git/my_project/analysis/phase1_trial1_has_eda.py", line 263, in <module>
    plot_metric_barchart(metrics, data_directory)
  File "~/Documents/git/my_project/analysis/phase1_trial1_has_eda.py", line 199, in plot_metric_barchart
    ).save(
      ^^^^^
  File "~/miniconda3/envs/aws_env/lib/python3.11/site-packages/plotnine/ggplot.py", line 644, in save
    sv = self.save_helper(
         ^^^^^^^^^^^^^^^^^
  File "~/miniconda3/envs/aws_env/lib/python3.11/site-packages/plotnine/ggplot.py", line 593, in save_helper
    figure = self.draw(show=False)
             ^^^^^^^^^^^^^^^^^^^^^
  File "~/miniconda3/envs/aws_env/lib/python3.11/site-packages/plotnine/ggplot.py", line 224, in draw
    self._build()
  File "~/miniconda3/envs/aws_env/lib/python3.11/site-packages/plotnine/ggplot.py", line 314, in _build
    layers.compute_aesthetics(self)
  File "~/miniconda3/envs/aws_env/lib/python3.11/site-packages/plotnine/layer.py", line 467, in compute_aesthetics
    l.compute_aesthetics(plot)
  File "~/miniconda3/envs/aws_env/lib/python3.11/site-packages/plotnine/layer.py", line 260, in compute_aesthetics
    evaled = evaluate(self.mapping._starting, self.data, plot.environment)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/miniconda3/envs/aws_env/lib/python3.11/site-packages/plotnine/mapping/evaluation.py", line 227, in evaluate
    raise PlotnineError(_TPL_EVAL_FAIL.format(ae, col, str(e)))
plotnine.exceptions.PlotnineError: "Could not evaluate the 'va' mapping: 'top' (original error: name 'top' is not defined)"
TyberiusPrime commented 8 months ago

I presume you do not want to read va from a column 'top' of your dataframe. If so, you need to pass it outside of the aes.

CopyOfA commented 8 months ago

The docs on geom_text specify to put it in the aes field: https://plotnine.readthedocs.io/en/v0.12.4/generated/plotnine.geoms.geom_text.html#plotnine.geoms.geom_text

If I put the va="top" in the geom_text call, then all the text is place at the default location: "center".

TyberiusPrime commented 8 months ago

Each mapping can be either sourced from the underlying dataframe (with an 'aesthetic' = aes() call), or set to a constant value by being passed 'on the outside'.

See for example the label being set to the constant 'name' (below).

At least in my version (0.12.2), the text does shift:

image

CopyOfA commented 8 months ago

I see, I was confused about the meaning of the positions (i.e., "top" puts the label below the point). Thanks for helping out!