How to switch axes (x and y) of a Plotly Figure Object - python

I'm trying to create a button that allows to switch x and y axes from a plotly.js figure so that x =becomes=> y axis and y =becomes=> x
Reading the documentation the only thing I could find regards reversing the range using the autorange attribute.
Is there a way to simply switch x and y without having to create a new figure from scratch?

This is tagged as python. Below works for flipping x & y in python. Similar approach could be used in javascript for structure of updatemenus
import pandas as pd
import numpy as np
import plotly.express as px
df = pd.DataFrame(
{"var1": np.random.uniform(1, 5, 30), "var2": np.random.uniform(4, 10, 30)}
)
fig = px.scatter(df, x="var1", y="var2")
fig.update_layout(
updatemenus=[
{
"buttons": [
{
"label": combi,
"method": "restyle",
"args": [
{"x": [fig.data[0][combi[0]]], "y": [fig.data[0][combi[1]]]}
],
}
for combi in ["xy", "yx"]
]
}
]
)

Related

How to use respective colorbar when using facet_col with plotly.express?

I have a data which has three variables whose magnitudes are different.
I'm trying to apply animation_frame and facet_col to make them animate at the same time.
Here's the code:
import plotly.express as px
import xarray as xr
# Load xarray from dataset included in the xarray tutorial
ds = xr.tutorial.open_dataset('eraint_uvz').sel(level=500)
# convert Dataset to DataArray for animation
data = ds.to_array().transpose('month', ...)
# fix the bug
# TypeError: %d format: a real number is required, not str
plot_data = data.assign_coords({'variable': range(len(data['variable']))})
fig = px.imshow(plot_data, animation_frame='month', facet_col='variable', color_continuous_scale='viridis')
# set variable back to string
# https://community.plotly.com/t/cant-set-strings-as-facet-col-in-px-imshow/60904
for k in range(len(data['variable'])):
fig.layout.annotations[k].update(text = data['variable'].values[k])
fig.show()
By default, they share the same colorbar like below.
Is it possible to make three colorbars (even different cmaps) with manual zmin/zmax values?
I found the similar question and modified it.
It looks nice to me. Only the titles are in the wrong order.
fig = px.imshow(plot_data, animation_frame='month',
facet_col='variable', facet_col_wrap=1,
color_continuous_scale='viridis')
# set variable back to string
# https://community.plotly.com/t/cant-set-strings-as-facet-col-in-px-imshow/60904
for k in range(len(data['variable'])):
fig.layout.annotations[k].update(text = data['variable'].values[k])
# update traces to use different coloraxis
for i, t in enumerate(fig.data):
t.update(coloraxis=f"coloraxis{i+1}")
for fr in fig.frames:
# update each of the traces in each of the animation frames
for i, t in enumerate(fr.data):
t.update(coloraxis=f"coloraxis{i+1}")
# position / config all coloraxis
fig.update_layout(
coloraxis={"colorbar": {"x": 1, "len": 0.3, "y": 0.85}},
coloraxis2={
"colorbar": {
"x": 1,
"len": 0.3,
"y": 0.5,
},
"colorscale": 'Thermal',
},
coloraxis3={
"colorbar": {"x": 1, "len": 0.3, "y": 0.15},
"colorscale": 'Blues',
},
)
fig.show()

Dropdown buttons for figures with multiple lines per plot/trace

I'm trying to create a dropdown button in plotly that would allow for plotting multiple vectors at once for a subset of data (or, to have multiple traces for the same dropdown button). These subsets would be chosen via the above-mentioned button.
Here's a toy example in plotly.express:
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
df = pd.DataFrame(
dict(
x=[1.0, 2.0, 3.0, 4.0],
y1=[1.0, 2.0, 3.0, 4.0],
y2=[2.0, 4.0, 6.0, 8.0],
z=["a", "a", "b", "b"],
)
)
px.scatter(df, x="x", y=["y1", "y2"], symbol="z")
plt.show()
What I'd like to achieve is a dropdown button that would select a subset for distinct z values ("a" or "b").
Unfortunately, go.Scatter does not seem like to have multiple y arrays and I end up with a complete mess.
zs = df.z.unique()
dropdown_buttons = []
fig = go.Figure()
for i, val in enumerate(zs):
df_ = df.query(f'z=="{val}"')
fig.add_trace(
go.Scatter(
x=df_["x"],
y=df_[["y1", "y2"]],
name=val,
)
)
dropdown_buttons.append(
{
"label": val,
"method": "update",
"args": [
{"visible": [x == i for x in range(len(zs))]},
{"title": val},
],
}
)
fig.update_layout(
{
"updatemenus": [
{
"type": "dropdown",
"showactive": True,
"active": 0,
"buttons": dropdown_buttons,
}
]
}
)
fig.show()

Remove left and right labels and paint values 1 by 1 in x-axis with Plotly that use facet_row or instead use make_subplots in Python

I'm using Plotly.express, plotly.subplots and plotly.graph_objs to visualize some graphs that vary according to some data (data) that I pass, but that here I put in hard code to save code, it is more readable and simplified.
What I want to achieve is a graph like the one I show next (it is an edited image), with a single label on the y-axes "Value", no labels on the right side, the x-axis would stay as-is with the label " Timeline" and that the x-axis does not separate the values ​​into multiples of 2, but 1 by 1 (and integrate it with the lines from -1 to the last value of the x-axis, I explain at the end):
1st. When I use plotly.express this is the code:
import plotly.express as px
import pandas as pd
import numpy as np
data = {
"Name": [
"Lamp_D_Rq", "Status", "Status", "HMI",
"Lck_D_RqDrv3", "Lck_D_RqDrv3", "Lck_D_RqDrv3",
"Lck_D_RqDrv3", "Lamp_D_Rq", "Lamp_D_Rq",
"Lamp_D_Rq", "Lamp_D_Rq",
],
"Value": [0, 4, 4, 2, 1, 1, 2, 2, 1, 1, 3, 3],
"Gage": [
"F1", "H1", "H3", "H3", "H3",
"F1", "H3", "F1", "F1", "H3",
"F1", "H3",
],
"Id_Par": [0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0],
}
signals_df = pd.DataFrame(data)
signals_df['Count'] = signals_df.groupby('Id_Par').cumcount().add(1).mask(signals_df['Id_Par'].eq(0), 0)
signals_df['Sub'] = signals_df.index - signals_df['Count']
id_par_prev = signals_df['Id_Par'].unique()
id_par = np.delete(id_par_prev, 0)
signals_df['Prev'] = [1 if x in id_par else 0 for x in signals_df['Id_Par']]
print(signals_df)
fig = px.line(
signals_df,
y="Value",
x="Sub",
color="Name",
hover_data=["Gage"],
custom_data=["Gage"],
markers=True,
height=500,
render_mode="svg",
facet_row="Name"
)
fig.update_traces(line={"shape": 'hv'})
fig.update_traces(
hovertemplate="<br>".join([
"Gage: %{customdata[0]}",
]))
fig.update_layout(
hovermode="x",
title="Saving/Loss diagram",
legend_title="CAN Singals",)
fig.update_xaxes(matches='x')
fig.show(config={'displaylogo': False})
In the execution of this first code, I get the following, the Value tag repeated by subplot and with the names spliced ​​on the right side, even these values ​​have the smallest names that I found, there are much longer names, that is why the I want to delete, but I can't find the option, however, I was thinking to remove Name and change the orientation to horizontal and the x-axis values ​​separated by multiples of 2, I would like to present them 1 by 1, but I cannot find any of these parameters or options for Plotly:
2nd. When I use plotly.subplots and plotly.graph_objs the code is:
from plotly.subplots import make_subplots
import plotly.graph_objs as go
import pandas as pd
import numpy as np
data = {
"Name": [
"Lamp", "Status", "Status", "M1",
"Lock", "Lock", "Lock",
"Lock", "Lamp", "Lamp",
"Lamp", "Lamp",
],
"Value": [0, 4, 4, 2, 1, 1, 2, 2, 1, 1, 3, 3],
"Gage": [
"A1", "B1", "B3", "B3", "B3",
"A1", "B3", "A1", "A1", "B3",
"A1", "B3",
],
"Id_Par": [0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0],
}
signals_df = pd.DataFrame(data)
signals_df['Count'] = signals_df.groupby('Id_Par').cumcount().add(1).mask(signals_df['Id_Par'].eq(0), 0)
signals_df['Sub'] = signals_df.index - signals_df['Count']
id_par_prev = signals_df['Id_Par'].unique()
id_par = np.delete(id_par_prev, 0)
signals_df['Prev'] = [1 if x in id_par else 0 for x in signals_df['Id_Par']]
print(signals_df)
names_signals = signals_df['Name'].unique()
fig = make_subplots(rows=len(names_signals), cols=1,
shared_xaxes=True,
vertical_spacing=0.02)
for i, name_signal in enumerate(names_signals):
fig.add_trace(go.Scatter(x=signals_df["Sub"],
y=signals_df["Value"],
line_shape='hv',
# facet_row="Name")
))
fig.update_layout(
hovermode="x",
title="Saving/Loss diagram",
legend_title="CAN Singals",)
fig.update_xaxes(matches='x')
fig.show(config={'displaylogo': False})
In the execution of the second code, which is a test of what I have been doing to see if it is easier for me to visualize the data, but it gives me an error that it does not find the facet_row parameter, when I uncomment it and yes, I already looked in it help file from Scatter and I can't find something similar:
In this case, I'm using this because when reviewing the Plotly documentation I realized that the update_trace, update_layout, etc is where you can edit and update these graph parameters, and here how you use add_trace seems to be a little different, but I need first separate the graph by subplots and I can't find how.
In both cases, the objective is, of some signals from a circuit, to separate the values ​​and graphs by names and subplots, that is, to graph how they vary and the values ​​are presented in a timeline according to their name and painted in different subplots.
Regarding the lines that start from -1 and end up to the last value of the signals, I tried the following, and it does not accept it because it necessarily wants me to pass a column of the signals_df dataframe, but first I want to find the parameters that allow me graph the subplots with a single label on the left y-axis and remove the names on the right side and the x-axis separate it 1x1, then continue with this, which actually served me very well, in a desktop application that I made with Matplotlib, but in this case I would not know how to call these values, since I have tried to put it in variables and assign it to "x" and "y" of px.line and it does not work:
x= np.hstack([-1, data.index.values, len(signals_df) - 1])
y= np.hstack([0, data.values, data.iloc[-1]])
I hope I have been explicit and can help me, I thank you very much.
there are multiple questions embedded in this
px annotations can be removed from layout if required. I don't believe this is the question
how to use go to dynamically generate sub-plots. Simple, loop over subsets of data with enumerate() to define the row. Details below
from plotly.subplots import make_subplots
import plotly.graph_objects as go
names_signals = signals_df['Name'].unique()
fig = make_subplots(rows=len(names_signals), cols=1,
shared_xaxes=True,
vertical_spacing=0.02)
for r, (n, d) in enumerate(signals_df.groupby("Name")):
# gemerate -1 point
d = d.merge(pd.Series(np.arange(-1, d["Sub"].max()+1), name="Sub"), on="Sub", how="right").fillna(0, limit=1).dropna()
fig.add_trace(go.Scatter(name=n, x=d["Sub"], y=d["Value"], line_shape="hv"), row=r+1, col=1)
# finally label axes and set tick sizes
fig.add_annotation(x=-0.05, y=.5, text="Value", xref="paper", yref="paper", textangle=270, showarrow=False)
fig.add_annotation(x=.5, y=-0.2, text="Timeline", xref="paper", yref="paper", showarrow=False)
fig.update_xaxes(dtick=1, tick0=-1)
fig.update_yaxes(dtick=1)

Hide plotly sankey nodes and links while preserving total node value

I would like to hide specific nodes (in my case, the rightmost) while preserving the size of intermediate nodes. As a simplistic example:
import plotly.graph_objects as go
link_data = dict(
source = [0,1,1],
target = [1,2,2],
value = [1,1,1]
)
node_data = dict(
label = ['a','b','c'],
)
fig = go.Figure(
data = [go.Sankey(
node = node_data,
link = link_data
)]
)
fig.show()
Results in:
But I want something more like this:
Some approaches I've tried:
I can remove the extra b-to-c connection and feed it back to b. This preserves the height of node b, but adds a circular link (which I don't want). This might be ok if I could remove the loop.
I can specify link colors as ['grey','white','white] (or 'rgba(0,0,0,0) in place of 'white') and node colors as ['blue','blue','white'], but this isn't the best looking: it adds a large pad of space to the right. And this seems like it adds unnecessary elements to the figure (more important to me for performance when I my figure is complex).
-Python 3.8, Plotly 5.3.1
re-using this approach to creating a sankey plot plotly sankey graph data formatting
I used a slightly more sophisticated approach that is similar to your second approach. This as you have noted does mean two things
there is space to right of chart
hover info still there !
have extended sample data to show node d is invisible as well as it's an end node with no flows going out of it
import pandas as pd
import numpy as np
import plotly.graph_objects as go
import plotly.express as px
links = [
{"source": "a", "target": "b", "value": 1},
{"source": "b", "target": "c", "value": 1},
{"source": "b", "target": "c", "value": 1},
{"source": "b", "target": "d", "value": 1}
]
df = pd.DataFrame(links)
nodes = np.unique(df[["source", "target"]], axis=None)
nodes = pd.Series(index=nodes, data=range(len(nodes)))
invisible = set(df["target"]) - set(df["source"])
fig = go.Figure(
go.Sankey(
node={
"label": [n if not n in invisible else "" for n in nodes.index],
"color": [
px.colors.qualitative.Plotly[i%len(px.colors.qualitative.Plotly)]
if not n in invisible
else "rgba(0,0,0,0)"
for i, n in enumerate(nodes.index)
],
"line": {"width": 0},
},
link={
"source": nodes.loc[df["source"]],
"target": nodes.loc[df["target"]],
"value": df["value"],
"color": [
"lightgray" if not n in invisible else "rgba(0,0,0,0)"
for n in df["target"]
],
},
)
)
fig

Size of y axis in faceted px.line graph not working

I am trying to modify the size of my y axis labels for each facet subgraph
fig.update_yaxes(matches=None, title_font=dict(size=17), row=5, title='abc', title_standoff=0)
fig.update_yaxes(matches=None, title_font=dict(size=7), row=6, title='abc', title_standoff=0)
But the size doesn't seem to change :
have generated sample data to create a faceted line figure
key point is that each facet will have it's own yaxis
used update_layout() to unify modification as per your code example. Every thing is constant except font size.
df = pd.concat(
[
pd.DataFrame(
{"x": np.linspace(0, 99, 100), "y": np.random.uniform(1, 3, 100)}
).assign(facet=f)
for f in range(2)
]
)
px.line(df, x="x", y="y", facet_row="facet").update_layout(
{
ax: {"matches": None, "title": {"text": "abc", "font": {"size": s}, "standoff":0}}
for ax, s in zip(["yaxis", "yaxis2"], [17, 7])
}
)

Categories