Using two different text sizes in one legend in Python - python

I am using matplotlib
I have a legend on a graph in Python which is:
plt.text(sigma1+5,5,str("$\sigma$("+"%.2f"%(sigma1) + ",0)"),color='red')
I'd like the symbol sigma to be larger than the rest of the text. Is this possible? Or do I have to create two separate legends?

Unfortunately, a matplotlib.text.Text instance uses a single style (font, size, etc.) for the whole string. So yes, I'm pretty sure you're going to need to create two of them.
If you don't know how to set the font size, see the docs for matplotlib.pyplot.text: you can either pass an optional fontdict argument that specifies font properties, or you can pass extra keyword arguments like size or fontproperties that get passed on to the Text constructor.

Related

Python folium - Markercluster not iterable with GroupedLayerControl

I would like to group my 2 marker cluster layers, where one is reliant on the other by providing a separate styling. Hence the second one is set as control=False.
Nevertheless, I want to have it disappear when the first one is switched off.
Along with the new Python folium issue v.0.14 I found, that the new feature has been provided, which potentially could resolve my issue:
https://github.com/ikoojoshi/Folium-GroupedLayerControl
Allow only one layer at a time in Folium LayerControl
and I've applied the following code:
df = pd.read_csv("or_geo.csv")
fo=FeatureGroup(name="OR")
or_cluster = MarkerCluster(name="Or", overlay=True, visible=True).add_to(map)
or_status = MarkerCluster(overlay=True,
control=False,
visible=False,
disableClusteringAtZoom=16,
).add_to(map)
GroupedLayerControl(
groups={'OrB': or_cluster, 'OrC': or_status},
collapsed=False,
).add_to(map)
and the console throws the following error:
TypeError: 'MarkerCluster' object is not iterable
How could I switch off 2 layer groups at once?
UPDATE:
The answer below provides the code, which seems to work but not in the way I need.
df = pd.read_csv("or_geo.csv")
fo=FeatureGroup(name="Or",overlay = True)
or_cluster = MarkerCluster(name="Or").add_to(map)
or_status = MarkerCluster(control=False,
visible=True,
disableClusteringAtZoom=16,
).add_to(map)
# definition of or_marker
# definition of or_stat_marker
or_cluster.add_child(or_marker)
or_status.add_child(or_stat_marker)
GroupedLayerControl(
groups={"Or": [or_cluster, or_status]},
collapsed=False,
exclusive_group=False,
).add_to(map)
I have a separate box instead, but what is worst I can just switch between one layer and another whereas I would like to have them reliant on the main group. The exclusive_groups option allows me to untick both of them but I am looking for something, which would allow me to switch off two of them at once (place the thick box on the major group instead). Is it possible to have something like this?
Try passing your markerclusters as a list to the GroupedLayerControl, not one by one. This is described here:
https://nbviewer.org/github/chansooligans/folium/blob/plugins-groupedlayercontrol/examples/plugin-GroupedLayerControl.ipynb
GroupedLayerControl(
groups={'OrB': [or_cluster, or_status]},
collapsed=False,
).add_to(map)
Update I
I see what you mean, that was definitely nonsense as it splits groups instead of joining them. so, back to topic
We had a similar discussion here and I am still convinced that the FeatureSubGroup should solve this issue. I use it in exact that way that I enable/disable a MarkerCluster in the legend and multiple FeatureGroupSubGroups (which are added not to the map but to the MarkerCluster) appear/disappear. Perhaps you try that again

use custom functions in transform_calculate in altair

i have a bar chart, that i want to add approximation of each bar value, above each itself.
that value is a big number, i have a function that formats number e.g.: 10,000$ to 10k$. how can i apply that.
base = alt.Chart(target_df_hahu).mark_bar().encode(
alt.X('monthdate(date):O'),
alt.Y('value'),
color = 'variable'
)
i have try blow code .
text = base.mark_text().encode(
text = 'value:Q'
).transform_calculate(
value=custom_function(datum.value)
)
base+text
Calculate transforms are evaluated in Javascript by the Vega renderer, and thus cannot contain custom functionality defined in Python. Calculate transforms must be strings containing a well-defined subset of javascript syntax, making use of any of the functionality listed at https://vega.github.io/vega/docs/expressions/

Get all objects (artists) drawn on a figure

I'm hoping to setup a method which can convert a normal figure (dark lines, white/transparent background) to a pseudo-inverted figure (light lines, black/transparent background). I could just post-process invert the image, but directly inverted colors look awful, so I'd like to instead (try to) create a mapping from one set of colors to another, and then apply this to all artists which have been added to (all axes on) a figure.
Is there a way to access all objects (e.g. text, scatter, lines, ticklabels, etc) that have been added to a figure?
Edit: my motivation is to automatically create white-background and black-background versions of figures. White-background figures will always (I think) be required for publications (for example), while black-background figures may be better for presentations (i.e. talk slides). While it wouldn't be that much trouble to setup a flag, and change each color based on that, e.g.
if dark:
col_line = 'cyan'
col_bg = 'black'
else:
col_line = 'red'
col_bg = 'white'
# ... plot ...
It would be much cooler and more convenient (despite overhead) to do something like,
fig.savefig('dark.pdf')
invert(fig)
fig.savefig('light.pdf')
Recursively call .get_children(), stop when the returned list is empty.
You can use a different style or change an existing style to your needs instead of changing all properties of all possible artists yourself.
E.g. you might start with the "dark_background" style and then adjust some further parameters using rcParams.
import numpy as np
import matplotlib.pyplot as plt
plt.style.use("dark_background")
style = {"lines.linewidth" : 2,
"axes.facecolor" : "#410448"}
plt.rcParams.update(style)
plt.plot(np.sin(np.linspace(0, 2 * np.pi)))
plt.show()

How to change the size of individual legend label size?

As shown in the figure, the same font size for Greek letters seems smaller than normal characters. I want to make them looks the same size, how to achieve this?
The code of the graph is as follows:
import numpy as np
import math
import matplotlib.pyplot as plt
alpha=np.arange(0,1,0.01)
gamma=np.sin(2*np.pi*alpha)
x=alpha
y=np.cos(2*np.pi*x)
plt.plot(x,y,label=r'cosine function')
plt.plot(alpha,gamma,label=r'$\gamma=\sin(\alpha)$')
plt.legend(loc=0,fontsize=20)
plt.show()
There's a little bit of a trick to this. Scroll down to the end if you're just interested in the solution.
plt.legend returns a Legend object with methods that allow you to modify the appearance of the legend. So first we'll save the Legend object:
legend = plt.legend(loc=0, fontsize=20)
The method we are looking for is Legend.get_texts(). This will return a list of Text objects whose methods control the size, color, font, etc. of the legend text. We only want the second Text object:
text = legend.get_texts()[1]
The Text object has a method called Text.set_fontsize. So let's try that. Altogether, the end of your code should look like:
legend = plt.legend(loc=0,fontsize=20)
text = legend.get_texts()[1]
text.set_fontsize(40)
And this is what we get:
Hm. It looks like both of the legend entries have been made bigger. This certainly isn't what we want. What is going on here, and how do we fix it?
The short of it is that the size, color, etc. of each of the legend entries are managed by an instance of a FontProperties class. The problem is that the two entries share the same instance. So setting the size of one instance also changes the size of the other.
The workaround is to create a new, independent instance of the font properties, as follows. First, we get our text, just as before:
text = legend.get_texts()[1]
Now, instead of setting the size immediately, we get the font properties object, but then make sure to copy it:
props = text.get_font_properties().copy()
Now we make this new, independent font properties instance our text's properties:
text.set_fontproperties(props)
And we can now try setting this legend entry's size:
text.set_size(40)
Solution
The end of your code should now look like:
legend = plt.legend(loc=0,fontsize=20)
text = legend.get_texts()[1]
props = text.get_font_properties().copy()
text.set_fontproperties(props)
text.set_size(40)
Producing a plot looking like

Matplotlib adding overlay labels to an axis

In matplotlib I wish to know the cleanest and most robust means of overlaying labels onto an axis. This is probably best demonstrated with an example:
While normal axis labels/ticks are placed every 5.00 units additional labels without ticks have been overlayed onto the axis (this can be seen at 1113.75 which partially covers 1114.00 and 1105.00 which is covered entirely). The labels also have the same font and size as their normal, ticked, counterparts with the background (if any) going right up to the axis (as a tick mark would).
What is the simplest way of obtaining this effect in matplotlib?
Edit
Following on from #Ken's suggestion I have managed to obtain the effect for an existing tick/label by using ax.yaxis.get_ticklines and ax.yaxis.get_ticklabels to both remove the tick marker and change the background/font/zorder of a label. However, I am unsure how best to add a new tick/label to an axis.
In other words I am looking for a function add_tick(ax.yaxis, loc) that adds a tick at location loc and returns the tickline and ticklabel objects for me to operate on.
I haven't ever tried to do that, but I think that the Artist tutorial might be helpful for you. In particular, the last section has the following code:
for line in ax1.yaxis.get_ticklines():
# line is a Line2D instance
line.set_color('green')
line.set_markersize(25)
line.set_markeredgewidth(3)
I think that using something like line.set_markersize(0) might make the markers have size zero. The difficult part might be finding the ones that need that done. It is possible that the line.xdata or line.ydata arrays might contain enough information to isolate the ones you need. Of course, if you are manually adding the tick marks, it is possible that as you do that the instance gets returned, so you can just modify them as you create them.
The best solution I have been able to devise:
# main: axis; olocs: locations list; ocols: location colours
def overlay_labels(main, olocs, ocols):
# Append the overlay labels as ticks
main.yaxis.set_ticks(np.append(main.yaxis.get_ticklocs(), olocs))
# Perform generic formatting to /all/ ticks
# [...]
labels = reversed(main.yaxis.get_ticklabels())
markers = reversed(main.yaxis.get_ticklines()[1::2]) # RHS ticks only
glines = reversed(main.yaxis.get_gridlines())
rocols = reversed(ocols)
# Suitably format each overlay tick (colours and lines)
for label,marker,grid,colour in izip(labels, markers, glines, rocols):
label.set_color('white')
label.set_backgroundcolor(colour)
marker.set_visible(False)
grid.set_visible(False)
It is not particularly elegant but does appear to work.

Categories