Setting TextPath length and width independently in matplotlib

Viewed 185

Below is the plot I generated using axes.text option,

ax[0].text(row.TIMESTAMP, row.HIGH+(0.1*width),row['candlestick_pattern'], fontsize=5, rotation='vertical') enter image description here

I'm trying to achieve the same output using TextPath and PathPatch, in order to increase/decrease the font size when I zoom in/out of the plot, and below is the code I have (taken from here and here )

textPath = TextPath((data_coord[1], -data_coord[0]), row['candlestick_pattern'], size=2)
pathPatch = PathPatch(textPath, color="black")
transform = mpl.transforms.Affine2D().rotate_deg(90) + ax[0].transData
pathPatch.set_transform(transform)
ax[0].add_patch(pathPatch)

Output with this is enter image description here

You could see that the text is cramped into a very small region and its not what I want. I would want to set the font size to a smaller value and increase the width (in vertical mode - height) of the TextPath. Is that possible?

Below is the complete code with which we can reproduce the problem for the dataset here

import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.textpath import TextPath
from matplotlib.patches import PathPatch
from mplfinance.original_flavor import candlestick_ohlc
from matplotlib import transforms as tf

import pandas as pd

plotDf = pd.read_csv("data.csv")
plotDf.reset_index(inplace=True)
del plotDf['TIMESTAMP']
del plotDf['TOTTRDQTY']

fig, ax = plt.subplots(1)

candlestick_ohlc(ax,plotDf.values,width=0.6, \
                 colorup='green', colordown='red', alpha=0.8)

maxHigh = plotDf['HIGH'].max()
minLow = plotDf['LOW'].min()
width = maxHigh - minLow
threshold = (width)*0.6

for idx, row in plotDf.iterrows():
    if (row['candlestick_pattern'] != 'NO_PATTERN'):
        if (row.HIGH < (threshold+minLow)):
            data_coord = (idx, row.HIGH+(0.1*width))
            #ax.text(idx, row.HIGH+(0.1*width), row['candlestick_pattern'], fontsize=5, rotation='vertical')
        else:
            data_coord = (idx, row.LOW-(0.4*width))
            #ax.text(idx, row.LOW-(0.4*width), row['candlestick_pattern'], fontsize=5, rotation='vertical')

        textPath = TextPath((data_coord[1], -data_coord[0]), row['candlestick_pattern'], size=2)
        pathPatch = PathPatch(textPath, color="black")
        transform = mpl.transforms.Affine2D().rotate_deg(90) + ax.transData
        pathPatch.set_transform(transform)
        ax.add_patch(pathPatch)

fig.autofmt_xdate()
fig.tight_layout()
fig.suptitle("test", fontsize=16)
fig.set_size_inches(10.5, 10.5)

plt.subplots_adjust(top=0.95) 
plt.show()
1 Answers

Apparently, your problem is a scaling problem. Messing around with .scale(x,y), ax.set_xlim and ax.set_ylim might allow you to "unsqueeze" the text. You can also try to set an anchor for your plot like done here:

ts = ax.transData
coords = ts.transform([0,0]) #<-anchor
tr = mpl.transforms.Affine2D().rotate_deg_around(coords[0],coords[1],90).scale(1,3) #<- scale
t = ts + tr
#<extra code>
pathPatch = PathPatch(textPath, color="black", transform = t)

EDIT

I tried many things, but I couldn't find a good way of doing it. I'll leave below what I tried and some resources that might help.

The way to properly use .rotate_deg_around would be like such:

ts = ax.transData
# ts = fig.dpi_scale_trans #this guy uses the fig scale, if you're interested
coords = ts.transform([data_coord[0],data_coord[1]])
converter = (coords[0]/data_coord[0], coords[1]/data_coord[1])
#plot the anchor points for visualization:
plt.plot(coords[0]/converter[0], coords[1]/converter[1],'xk') 
tr = mpl.transforms.Affine2D().rotate_deg_around(coords[0]/converter[0],coords[1]/converter[1],90).scale(converter[0],converter[1])

pathPatch = PathPatch(textPath, color="black", transform = tr)
ax.add_patch(pathPatch)

Nonetheless, the results are still similar to what you had at the beginning: enter image description here

It appears that TextPath does not behave like it should when using transform. Here .get_offset_transform is used, and it apparently fixes this sort of issue, but I was unable to use it since the plt has a Line type.

Also, you will see that if you increase the y axis in .scale, you can start to see the text, but it spreads the coordinates as well. One idea you can try is setting a good readable y scale (use ax.set_ylim to see your text) and then use that value as a divisor when setting the coordinates for your plot.

There are also some ideas here that might serve you.

Related