Why don't different bars share the same y-axis in plotly.express group bars - plot

I made these attached group bars with plotly.express:
And my code is like this:
import pandas as pd
df=pd.DataFrame([['C-1','C-11','111'],['C-1','C-12','121'],['C-1','C-13','31'],['C-1','C-14','120'],['C-1','C-15','100'],
['C-2','C-21','150'],['C-2','C-22','168'],['C-2','C-23','173'],['C-2','C-24','111'],['C-2','C-25','121'],
['C-3','C-31','123'],['C-3','C-32','100'],['C-3','C-33','111'],['C-3','C-34','111'],['C-3','C-35','163'],
['C-4','C-41','174'],['C-4','C-42','174'],['C-4','C-43','173'],['C-4','C-44','135'],['C-4','C-45','102'],
['C-5','C-51','118'],['C-5','C-52','122'],['C-5','C-53','113'],['C-5','C-54','178'],['C-5','C-55','142']
],columns=['Case group','Case number','Average revenue'])
import plotly.express as px
fig = px.bar(df,
x='Case number',y='Average revenue',
color='Case group', barmode='group',text="Average revenue",
height=500,width=700,
color_discrete_sequence=[px.colors.qualitative.Dark2[0],px.colors.qualitative.Set2[1],px.colors.qualitative.Pastel[0],px.colors.qualitative.Set2[5],px.colors.qualitative.Set2[4],])
)
fig.update_traces(textposition='outside')
fig.show()
My question is, how can I make the y-axis labels normally ascend so that all groups share the same y-axis scale? e.g., C-55 should be lower than C-54 in my ideal thought.
Also, the annotations above each bar are too small to read. I tried to change the font size with fig.update.layout(). but it didn't work.
Please let me know if I did anything wrong.
I would really appreciate any help you can provide.

You only need to convert the column type to float as follows:
df["Average revenue"] = df["Average revenue"].astype(float)
The full code:
import pandas as pd
import plotly.express as px
df=pd.DataFrame([['C-1','C-11','111'],['C-1','C-12','121'],['C-1','C-13','31'],['C-1','C-14','120'],['C-1','C-15','100'],
['C-2','C-21','150'],['C-2','C-22','168'],['C-2','C-23','173'],['C-2','C-24','111'],['C-2','C-25','121'],
['C-3','C-31','123'],['C-3','C-32','100'],['C-3','C-33','111'],['C-3','C-34','111'],['C-3','C-35','163'],
['C-4','C-41','174'],['C-4','C-42','174'],['C-4','C-43','173'],['C-4','C-44','135'],['C-4','C-45','102'],
['C-5','C-51','118'],['C-5','C-52','122'],['C-5','C-53','113'],['C-5','C-54','178'],['C-5','C-55','142']
],columns=['Case group','Case number','Average revenue'])
df["Average revenue"] = df["Average revenue"].astype(float)
fig = px.bar(df,
x="Case number",y='Average revenue',
color='Case group', barmode='group',text="Average revenue",
height=500,width=700,
color_discrete_sequence=[px.colors.qualitative.Dark2[0],px.colors.qualitative.Set2[1],px.colors.qualitative.Pastel[0],px.colors.qualitative.Set2[5],px.colors.qualitative.Set2[4],])
fig.update_traces(textposition='outside', width=0.8)
fig.show()
Output

Related

Reverse range colorscale plotly

I was wondering if it is possible to make my colorscale bar ascending (from 1 to 4) instead of descending (from 4 to 1). Does anyone have clue? The picture of my current bar is underneath the code.
import pandas as pd
import plotly.graph_objects as go
fig = go.Figure(go.Densitymapbox(lat=df_dropped.latitude, lon=df_dropped.longitude, z=df_dropped.propextent,
radius=10))
fig.update_layout(mapbox_style="stamen-terrain", mapbox_center_lon=180)
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.show()
Just add reversescale=True as below:
import pandas as pd
import plotly.graph_objects as go
fig = go.Figure(go.Densitymapbox(lat=df_dropped.latitude,
lon=df_dropped.longitude, z=df_dropped.propextent,radius=10,reversescale=True
))
fig.update_layout(mapbox_style="stamen-terrain", mapbox_center_lon=180)
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.show()
For more details, please visit the reversescale section in the documentation

matplotlib bar plot add legend from categories dataframe column

I try to add the legend which should, according to my example, output:
a red square with the word fruit and
a green square with the word
veggie.
I tried several things (the example below is just 1 of the many trials), but I can't get it work.
Can someone tell me how to solve this problem?
import pandas as pd
from matplotlib import pyplot as plt
data = [['apple', 'fruit', 10], ['nanaba', 'fruit', 15], ['salat','veggie', 144]]
data = pd.DataFrame(data, columns = ['Object', 'Type', 'Value'])
colors = {'fruit':'red', 'veggie':'green'}
c = data['Type'].apply(lambda x: colors[x])
bars = plt.bar(data['Object'], data['Value'], color=c, label=colors)
plt.legend()
The usual way to create a legend for objects which are not in the axes would be to create proxy artists as shown in the legend guide
Here,
colors = {'fruit':'red', 'veggie':'green'}
labels = list(colors.keys())
handles = [plt.Rectangle((0,0),1,1, color=colors[label]) for label in labels]
plt.legend(handles, labels)
So this is a hacky solution and I'm sure there are probably better ways to do this. What you can do is plot individual bar plots that are invisible using width=0 with the original plot colors and specify the labels. You will have to do this in a subplot though.
import pandas as pd
from matplotlib import pyplot as plt
data = [['apple', 'fruit', 10], ['nanaba', 'fruit', 15], ['salat','veggie', 144]]
data = pd.DataFrame(data, columns = ['Object', 'Type', 'Value'])
colors = {'fruit':'red', 'veggie':'green'}
c = data['Type'].apply(lambda x: colors[x])
ax = plt.subplot(111) #specify a subplot
bars = ax.bar(data['Object'], data['Value'], color=c) #Plot data on subplot axis
for i, j in colors.items(): #Loop over color dictionary
ax.bar(data['Object'], data['Value'],width=0,color=j,label=i) #Plot invisible bar graph but have the legends specified
ax.legend()
plt.show()

histogram2d example for bokeh

Surprisingly nobody took the pain to make an example in the bokeh gallery for 2D histogram plotting
histogram2d of numpy gives the raw material, but would be nice to have an example as it happens for matplotlib
Any idea for a short way to make one?
Following up a proposed answer let me attach a case in which hexbin does not the job because exagons are not a good fit for the job. Also check out matplotlib result.
Of course I am not saying bokeh cannot do this, but it seem not straightfoward. Would be enough to change the hexbin plot into a square bin plot, but quad(left, right, top, bottom, **kwargs) seems not to do this, nor hexbin to have an option to change "tile" shapes.
You can make something close with relatively few lines of code (comapring with this example from the matplotib gallery). Note bokeh has some examples for hex binning in the gallery here and here. Adapting those and the example provided in the numpy docs you can get the below:
import numpy as np
from bokeh.plotting import figure, show
from bokeh.layouts import row
# normal distribution center at x=0 and y=5
x = np.random.randn(100000)
y = np.random.randn(100000) + 5
H, xe, ye = np.histogram2d(x, y, bins=100)
# produce an image of the 2d histogram
p = figure(x_range=(min(xe), max(xe)), y_range=(min(ye), max(ye)), title='Image')
p.image(image=[H], x=xe[0], y=ye[0], dw=xe[-1] - xe[0], dh=ye[-1] - ye[0], palette="Spectral11")
# produce hexbin plot
p2 = figure(title="Hexbin", match_aspect=True)
p.grid.visible = False
r, bins = p2.hexbin(x, y, size=0.1, hover_color="pink", hover_alpha=0.8, palette='Spectral11')
show(row(p, p2))

How to add permanent name labels (not interactive ones) on nodes for a networkx graph in bokeh?

I am trying to add a permanent label on nodes for a networkx graph using spring_layout and bokeh library. I would like for this labels to be re-positioned as the graph scales or refreshed like what string layout does, re-positioning the nodes as the graph scales or refreshed.
I tried to create the graph, and layout, then got pos from the string_layout. However, as I call pos=nx.spring_layout(G), it will generated a set of positions for the nodes in graph G, which I can get coordinates of to put into the LabelSet. However, I have to call graph = from_networkx(G, spring_layout, scale=2, center=(0,0)) to draw the network graph. This will create a new set of position for the node. Therefore, the positions of the nodes and the labels will not be the same.
How to fix this issues?
Thanks for asking this question. Working through it, I've realized that it is currently more work than it should be. I'd very strongly encourage you to open a GitHub issue so that we can discuss what improvements can best make this kind of thing easier for users.
Here is a complete example:
import networkx as nx
from bokeh.io import output_file, show
from bokeh.models import CustomJSTransform, LabelSet
from bokeh.models.graphs import from_networkx
from bokeh.plotting import figure
G=nx.karate_club_graph()
p = figure(x_range=(-3,3), y_range=(-3,3))
p.grid.grid_line_color = None
r = from_networkx(G, nx.spring_layout, scale=3, center=(0,0))
r.node_renderer.glyph.size=15
r.edge_renderer.glyph.line_alpha=0.2
p.renderers.append(r)
So far this is all fairly normal Bokeh graph layout code. Here is the additional part you need to add permanent labels for each node:
from bokeh.transform import transform
# add the labels to the node renderer data source
source = r.node_renderer.data_source
source.data['names'] = [str(x*10) for x in source.data['index']]
# create a transform that can extract the actual x,y positions
code = """
var result = new Float64Array(xs.length)
for (var i = 0; i < xs.length; i++) {
result[i] = provider.graph_layout[xs[i]][%s]
}
return result
"""
xcoord = CustomJSTransform(v_func=code % "0", args=dict(provider=r.layout_provider))
ycoord = CustomJSTransform(v_func=code % "1", args=dict(provider=r.layout_provider))
# Use the transforms to supply coords to a LabelSet
labels = LabelSet(x=transform('index', xcoord),
y=transform('index', ycoord),
text='names', text_font_size="12px",
x_offset=5, y_offset=5,
source=source, render_mode='canvas')
p.add_layout(labels)
show(p)
Basically, since Bokeh (potentially) computes layouts in the browser, the actual node locations are only available via the "layout provider" which is currently a bit tedious to access. As I said, please open a GitHub issue to suggest making this better for users. There are probably some very quick and easy things we can do to make this much simpler for users.
The code above results in:
similar solution as #bigreddot.
#Libraries for this solution
from bokeh.plotting import figure ColumnDataSource
from bokeh.models import LabelSet
#Remove randomness
import numpy as np
np.random.seed(1337)
#Load positions
pos = nx.spring_layout(G)
#Dict to df
labels_df = pd.DataFrame.from_dict(pos).T
#Reset index + column names
labels_df = labels_df.reset_index()
labels_df.columns = ["names", "x", "y"]
graph_renderer = from_networkx(G, pos, center=(0,0))
.
.
.
plot.renderers.append(graph_renderer)
#Set labels
labels = LabelSet(x='x', y='y', text='names', source=ColumnDataSource(labels_df))
#Add labels
plot.add_layout(labels)
Fixed node positions
From the networkx.spring_layout() documentation: you can add a list of nodes with a fixed position as a parameter.
import networkx as nx
import matplotlib.pyplot as plt
g = nx.Graph()
g.add_edges_from([(0,1),(1,2),(0,2),(1,3)])
pos = nx.spring_layout(g)
nx.draw(g,pos)
plt.show()
Then you can plot the nodes at a fixed position:
pos = nx.spring_layout(g, pos=pos, fixed=[0,1,2,3])
nx.draw(g,pos)
plt.show()

how to plot more than two plots using for loop in python?

I'm trying to do 4 plots using for loop.But I'm not sure how to do it.how can I display the plots one by one orderly?or save the figure as png?
Here is my code:
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from astropy.io import fits
import pyregion
import glob
# read in the image
xray_name = glob.glob("*.fits")
for filename in xray_name:
f_xray = fits.open(filename)
#name = file_name[:-len('.fits')]
try:
from astropy.wcs import WCS
from astropy.visualization.wcsaxes import WCSAxes
wcs = WCS(f_xray[0].header)
fig = plt.figure()
ax = plt.subplot(projection=wcs)
fig.add_axes(ax)
except ImportError:
ax = plt.subplot(111)
ax.imshow(f_xray[0].data, cmap="summer", vmin=0., vmax=0.00038, origin="lower")
reg_name=glob.glob("*.reg")
for i in reg_name:
r =pyregion.open(i).as_imagecoord(header=f_xray[0].header)
from pyregion.mpl_helper import properties_func_default
# Use custom function for patch attribute
def fixed_color(shape, saved_attrs):
attr_list, attr_dict = saved_attrs
attr_dict["color"] = "red"
kwargs = properties_func_default(shape, (attr_list, attr_dict))
return kwargs
# select region shape with tag=="Group 1"
r1 = pyregion.ShapeList([rr for rr in r if rr.attr[1].get("tag") == "Group 1"])
patch_list1, artist_list1 = r1.get_mpl_patches_texts(fixed_color)
r2 = pyregion.ShapeList([rr for rr in r if rr.attr[1].get("tag") != "Group 1"])
patch_list2, artist_list2 = r2.get_mpl_patches_texts()
for p in patch_list1 + patch_list2:
ax.add_patch(p)
#for t in artist_list1 + artist_list2:
# ax.add_artist(t)
plt.show()
the aim of the code is to plot a region on fits file image,if there is a way to change the color of the background image to white and the brighter (centeral region) as it is would be okay.Thanks
You are using colormap "summer" with provided limits. It is not clear to me what you want to achieve since the picture you posted looks more or less digital black and white pixelwise.
In matplotlib there are built in colormaps, and all of those have a reversed twin.
'summer' has a reversed twin with 'summer_r'
This can be picked up in the mpl docs at multiple spots, like colormap example, or SO answers like this.
Hope that is what you are looking for. For the future, when posting code like this, try to remove all non relevant portions as well as at minimum provide a description of the data format/type. Best is to also include a small sample of the data and it's structure. A piece of code only works together with a set of data, so only sharing one is only half the problem formulation.

Resources