简体   繁体   中英

How to place geom_text labels outside the plot boundary in plotnine

Using plotnine I am trying to plot line segments and annotate each segment on the right hand side of the plot. I am following examples on plotnine online documentation , however the text on the right are being cutoff. I can display the text if I use ha='right' but that's not what I want, and it moves text into the plots. I also tried adding + theme(subplots_adjust={'right': 2}) but that only expanded the width of the plot and didn't include the text.

Please note that I do not want to manually specify x positions because the range of the x values in my dataframe will vary and I need to make many plots in a script. Being able to get xlimits using some plotnine function and pass them to x positions ok though, but I could not figure out how to do that.

Even better would be to have the text placed out of the plot area.

My question is: is there a way to place the geom_text() text out of the plot's main box, on the margin in the right (and also to the left)? How can I achieve this?

A code example is provided below.

from plotnine import *
from plotnine.data import mtcars
import numpy as np

mtcars_plot_df = mtcars[:20]
mtcars_plot_df.loc[:, 'wt_l'] = mtcars_plot_df['wt'] - np.random.uniform(1)
mtcars_plot_df.loc[:, 'wt_h'] = mtcars_plot_df['wt'] + np.random.uniform(1)
mtcars_plot_df.loc[:, 'wt_str'] = mtcars_plot_df['wt'].round(2).astype(str) + ' (' + mtcars_plot_df['wt_l'].round(2).astype(str) + ' - ' + mtcars_plot_df['wt_h'].round(2).astype(str) + ')'

nrows = mtcars_plot_df.shape[0] * 0.2

(ggplot()
 # Range strip
 + geom_segment(
     mtcars_plot_df,
     aes(x='wt_l', xend='wt_h', y='name', yend='name'),
     size=1,
     color='red'
 )
 # Point markers
 + geom_point(
     mtcars_plot_df,
     aes('wt', 'name'),
     size=1.5,
     stroke=0.7,
     color='blue'
 )
 # Range label
 + geom_text(
     # mtcars_plot_df, => This generates error
     aes(x=np.inf, y='name', label="wt_str"),
     data = mtcars_plot_df, # => This gets rid of the error
     size=8,
     ha='left'
 )
 + theme(figure_size=(4, nrows*1))  # here you define the plot size
)

When I use ha='right' in geom_text() , I get a plot as shown in the attached image. As you can see, the text interferes with the plot. When I use ha='left' , the text is not visible at all. I would like the text to be outside the plot area, in the white area right of the plot similar to how the car names are printed on the left of the plot.

在此处输入图像描述


A side question about something I came across while following plotnine documentation is: if I do not specify the dataframe as data = mtcars_plot_df after the aes line, then I get this error: ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all() ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all() . See my two comments in the code above.

This is old, but you can use the draw method of the ggplot object to return the matplotlib object and use the matplotlib ax.text method to draw your annotations

p = (ggplot()
 # Range strip
 + geom_segment(
     mtcars_plot_df,
     aes(x='wt_l', xend='wt_h', y='name', yend='name'),
     size=1,
     color='red'
 )
 # Point markers
 + geom_point(
     mtcars_plot_df,
     aes('wt', 'name'),
     size=1.5,
     stroke=0.7,
     color='blue'
 )
 # Range label
 + geom_text(
     # mtcars_plot_df, => This generates error
     aes(x=np.inf, y='name', label="wt_str"),
     data = mtcars_plot_df, # => This gets rid of the error
     size=8,
     ha='right'
 )
 + theme(figure_size=(4, nrows*1))  # here you define the plot size
)

fig = p.draw()
# get the figure axis
ax = fig.axes[0]
# info for where to draw the annotations
far_right_x = mtcars_plot_df['wt_h'].max()
y_tick_labels = list(ax.get_yticklabels())
# loop through the y labels and plot the text at right location
for y_tick in y_tick_labels:
    y_text = y_tick.get_text()
    y_loc = y_tick.get_position()[1]
    ax.text(far_right_x,y_loc,
            mtcars_plot_df.loc[mtcars_plot_df['name']==y_text,'wt_str'].values[0],
            ha='left',va='center',color='green')
plt.title("orig in black...new in green")

You can see your original text in black and the new text in green

plot_with_new_text

Regarding your side question, I don't get that error when removing or placing the data= line above the aes. But since you don't define the data within the ggplot constructor then you need to provide it in all of the geom_* that you call.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM