Altair: fix title position for interactive() chart - python

I am using Altair to create a mark_point plot with the slightly strange combination (as far as I can tell via searching) of clip=False and .interactive(). This allows me to pan and zoom the axes while the points themselves are allowed to leave the axes bounding box and stay visible.
An unintuitive side effect of this is that the plot title moves its position to get out of the way of the points as they leave the axes - to the point where the title will in fact leave the screen altogether if I pan down far enough. I would like to have the title stay in a fixed position regardless of the contents of a mark moving around outside the axes.
It seems like this should be possible via configure_title but I can't figure it out. The frame keyword controls the reference for the anchor, but there is no option for using screen pixels instead of the data. I checked the Vega documentation for Title and it does not appear like there are any relevant properties that Altair is not controlling. Weirdly, the axis labels don't have this problem: they stay in their place even as the points move over them. I would like the title to behave the same way.
Here is a very simple MRE that creates a plot with this problem:
import altair as alt
import pandas as pd
import numpy as np
df = pd.DataFrame({'x': np.linspace(0, 4, 100)})
df['y'] = np.sin(2 * np.pi * df['x'])
chart = alt.Chart(df, title='TITLE HERE').mark_point(filled=True, size=100, clip=False).encode(
x='x:Q', y='y:Q').interactive()
chart = chart.configure_title(frame='group')
The obvious workaround is to not use a chart title and instead use a mark_text with clip=False and fixed pixel coordinates at the top of the window to manually make my own title, but I would rather not have to jump through that hoop every time I make a plot.

Related

Label text position in Bokeh

I would like to stick MyText Label to the bottom right part of my figure
for a given text and a given font size (as shown on the picture for 'this is super fun', font size of '20px' and with tiny characters. I found the good position by dichotomy ).
What is the function position I need to pass to x ?
This should depends on len(MyText), text_font_size and figure width ...
from bokeh.models import ColumnDataSource, Label, LabelSet, Range1d
from bokeh.plotting import figure, output_file, show
width,height=400,300
p = figure(plot_width=width, plot_height=height)
MyText='this is super fun'
my_font_size = "20px"
labels = Label(x=width/2+25, y=0,x_units='screen', y_units='screen', text=MyText,text_font_size=my_font_size)
p.add_layout(labels)
show(p)
I don't think there is any 100% robust way to do this, actually.
You can set the text_align to "right" which helps:
p = figure(plot_width=width, plot_height=height)
labels = Label(x=width-50, y=0,
x_units="screen", y_units='screen', text_align="right",
text=MyText,text_font_size=my_font_size)
Note the -50 above is to account (roughly) for the width of the space to the right of the "plot area" (i.e where the toolbar is). However if you add a y-axis on the left side, you'd need to account for that too, and if you allow zooming, then left space can grow and shrink to accommodate bigger or smaller axis labels, which means you can't reliably account for that space with a single constant up front. You could set min_border values to be larger, which might mitigate the problem for some range of zooming/panning.
Also the above assumes the plot sizing mode is not "responsive". If the plot itself can resize then no constant value in screen units will ever work.
If you can fix your x range start/end (or add an "extra" range), then you could right-align to the range end value using "data" units. But if you allow zooming or panning then the label will move to stay fixed at that data position.
The main issue is that the "inner_width" is only computed in the browser. It's not available to the Python code because it doesn't exist outside the browser. What's really needed is some special convention or confguration to designate "inner_width" as a symbolic concept that updates to whatever is necessary, regardless of panning or zooming or resizing. I'd suggest making a GitHub issue to propose this feature.
In the mean time, I think any solution will involve some trial and error with a fixed font size in "px" and also ideally limiting panning/zooming if possible.

Grouped bar chart of multiindex

first of all: I'm completely new to python.
I'm trying to visualize some measured data. Each entry has a quadrant, number and sector. The original data lies in a .xlsx file. I've managed to use a .pivot_table to sort the data according to its sector. Due to overlapping, number and quadrant also have to be indexed. Now I want to plot it as a bar chart, where the bars are grouped by sector and the colors represent the quadrant.
But because number also has to be indexed, it shows up in the bar chart as a separate group. There should only be three groups, 0, i and a.
MWE:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
d = {'quadrant': ["0","0","0","0","0","0","I","I","I","I","I","I","I","I","I","I","I","I","II","II","II","II","II","II","II","II","II","II","II","II","III","III","III","III","III","III","III","III","III","III","III","III","IV","IV","IV","IV","IV","IV","IV","IV","IV","IV","IV","IV"], 'sector': [0,"0","0","0","0","0","a","a","a","a","a","a","i","i","i","i","i","i","a","a","a","a","a","a","i","i","i","i","i","i","a","a","a","a","a","a","i","i","i","i","i","i","a","a","a","a","a","a","i","i","i","i","i","i"], 'number': [1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6], 'Rz_m': [67.90,44.17,44.30,63.43,49.87,39.33,61.17,69.37,66.20,44.20,64.77,39.93,44.33,50.97,55.90,51.33,58.23,44.53,50.03,47.40,58.67,71.57,57.60,70.77,63.93,47.37,46.90,34.73,41.27,48.23,58.30,47.07,50.53,51.20,32.67,50.37,37.50,55.50,41.20,48.07,56.80,49.77,40.87,44.43,44.00,60.03,63.73,72.80,51.60,45.53,60.27,71.00,59.63,48.70]}
df = pd.DataFrame(data=d)
B = df.pivot_table(index=['sector','number', 'quadrant'])
B.unstack().plot.bar(y='Rz_m')
The data viz ecosystem in Python is pretty diverse and there are multiple libraries you can use to produce the same chart. Matplotlib is a very powerful library, but it's also quite low-level, meaning you often have to do a lot of preparatory work before getting to the chart, so usually you'll find people use seaborn for static visualisations, especially if there is a scientific element to them (it has built-in support for things like error bars, etc.)
Out of the box, it has a lot of chart types to support exploratory data analysis and is built on top of matplotlib. For your example, if I understood it right, it would be as simple as:
import seaborn as sns
sns.catplot(x="sector", y="Rz_m", hue="quadrant", data=df, ci=None,
height=6, kind="bar", palette="muted")
And the output would look like this:
Note that in your example, you missed out "" for one of the zeroes and 0 and "0" are plotted as separate columns. If you're using seaborn, you don't need to pivot the data, just feed it the df as you've defined it.
For interactive visualisations (with tooltips, zoom, pan, etc.), you can also check out bokeh.
There is an interesting wrinkle to this - how to center the nested bars on the label. By default the bars are drawn with center alignment which works fine for an odd number of columns. However, for an even number, you'd want them to be centered on the right edge. You can make a small alteration in the source code categorical.py, lines beginning 1642 like so:
# Draw the bars
offpos = barpos + self.hue_offsets[j]
barfunc(offpos, self.statistic[:, j], -self.nested_width,
color=self.colors[j], align="edge",
label=hue_level, **kws)
Save the .png and then change it back, but it's not ideal. Probably worth flagging up to the library maintainers.

Hover tooltip with stacked Area chart in holoviews

I am creating a stacked area chart in holoviews with bokeh backend, similarly to the example here:
http://holoviews.org/reference/elements/matplotlib/Area.html
I would like to have a Hover tooltip but if I add it to the code, the resulting chart shows the hover cross but no data is displayed in the tooltip.
My code:
import holoviews as hv
values = np.random.rand(5, 20)
percentages = (values/values.sum(axis=0)).T*100
overlay = hv.Overlay([hv.Area(percentages[:, i], vdims=[hv.Dimension('value', unit='%')]).opts(tools=["hover"]) for i in range(5)])
stackA = hv.Area.stack(overlay)
I also tried putting the hover option in the hv.Stack step instead:
stackA = hv.Area.stack(overlay).opts(tools=["hover"])
but this does nothing.
I would like the hover tooltip to show the area value below the cursor and potentially other dimensions of my dataset.
This is a known issue: https://github.com/pyviz/holoviews/issues/3187. The same is valid for the Spread element.
The reason is (my guess) that bokeh has no hovertool for Patch, which is the glyph used to render Area and Spread elements: https://stackoverflow.com/a/53384398. So at the moment your best bet is probably trying to implement the vectorized workaround proposed in that stackoverflow answer in holoviews/plotting/bokeh/chart.py.

How can i set the location of an axis label in terms of locations on said axis?

I would like to move my axis label Number of States to the left, so that it is actually over the numbers i have. Other similar questions/answers have suggested using labelpad, but this shifts the text up or down, not left/right. How can i move my title to the right?
i've also tried the horizontalalignment kwarg, which a. seems to have the right and left alignments reversed, and also does not move the title far enough, nor offer any actual control on where exactly it goes.
i see that i can set the _x and _y properties of the Text instance, using set_[xy](), but it seems a bit hacky. Is there a convenient way i can set the location of hte title relative to a value on the xaxis?
You can grap the title object and set its respective position property to whatever you like.
A simple example could be:
%matplotlib inline
import matplotlib.pyplot as plt
plt.plot([1,2,3])
ti=plt.title('foo')
ti.set_position((0.2,1))
which creates a plot like
Note that the position is set in relative coordinates.
The position argument suggested by #ThePredator (loc : {‘center’, ‘left’, ‘right’}, see docs also works in a similar fashion.
Update
To set the position of the text in the data coordinate system instead of the axis coordinates, a simple transformation can be used. For details have a look at the Transformation Documentation. A minimal example could look like:
%matplotlib inline
import matplotlib.pyplot as plt
f,ax = plt.subplots(1)
ax.plot([1,2,3])
ti=ax.set_title('foo')
ti.set_position(ax.transLimits.transform((0.5,3)))
This places the title centered at (0.5, 3) as shown in the following plot

Python graph- change axis marker colours and legend border colours

I'm using Python to plot a couple of graphs and I'm trying to change the formatting and essentially 'brand' the graph. I've managed to change most things using pylab.rcParams[...], but I can't work out how to change the colour of the markers on the axes and the border around the legend. Any help would be much appreciated. The line below is an example of the type of code I've been using to edit other parts. Basically just lines taken from matplotlibrc, but I can't find them to change everything I want.
pylab.rcParams[axes.labelcolor' = '#031F73'
If you just want to use rcParams, the proper parameters are xticks.color and yticks.color. I can't seem to find a key for the legend frame color. You can set that (along with the tick colors) programmatically though.
import pylab
pylab.plot([1,2,3],[4,5,6], label ='test')
lg = pylab.legend()
lg.get_frame().set_edgecolor('blue')
ax = pylab.axes()
for line in ax.yaxis.get_ticklines():
line.set_color('blue')
for line in ax.xaxis.get_ticklines():
line.set_color('blue')
for label in ax.yaxis.get_ticklabels():
label.set_color('blue')
for label in ax.xaxis.get_ticklabels():
label.set_color('blue')
pylab.show()

Categories