Plotly Charts in a for Loop

Plotly charts in a for loop

It turns out the problem is solved here
https://github.com/ropensci/plotly/issues/273

knitr has known issues printing plotly charts in a loop.
Quoting the final answer cpsievert from here

```{r}
l <- htmltools::tagList()
for (i in 1:3) {
l[[i]] <- as.widget(plot_ly(x = rnorm(10)))
}
l
```

This solved my problem. Though I ended up porting everything from ggplot2 to plotly R api before I found this solution.

MLavoie, thanks for your help. I had generated a dummy dataset just to create a reproducible example. Though your solution probably solves the dummy example, it is not all that relevant to me in the current problem. I think the solution provided here should be generic.

Thanks,
Kaustubh

Unable to make multiple plotly graphs in for loop

  • have coded placeholders for two functions used in your code create_model() and assign_model()
  • you create fig = make_subplots(rows=len(float_range_list), cols=1) then in loop overwrite it with fig = px.line(). Changed to use variable name fig_ for figure created within loop
  • also then added traces from fig_ to fig within loop
import plotly.express as px
import plotly.graph_objs as go
from plotly.subplots import make_subplots
import pandas as pd
import numpy as np

def create_model(a,fraction=.1):
return 1

def assign_model(n):
return pd.DataFrame({"timestamp":pd.date_range("1-mar-2022", freq="1H", periods=100),
"value":np.random.uniform(1,10,100),
"Anomaly":np.full(100, 1)})

start = 0.01
stop = 0.26
step = 0.05
float_range_array = np.arange(start, stop, step)
float_range_list = list(float_range_array)
fig = make_subplots(rows=len(float_range_list), cols=1)
for x1, i in enumerate(float_range_list):
iforest1 = create_model("pca", fraction=i)
iforest_results = assign_model(iforest1)
fig_ = px.line(
iforest_results,
x="timestamp",
y="value",
title="Principal Component Analysis: Fraction={}".format(round(i, 2)),
template="plotly",
labels={"timestamp": "Stay Date", "value": "Number of Bookings"},
)
outlier_dates = iforest_results[iforest_results["Anomaly"] == 1].index
outlier_dates1 = iforest_results.iloc[outlier_dates]["timestamp"]
y_values = [iforest_results.loc[i]["value"] for i in outlier_dates]
fig.add_trace(
go.Scatter(
x=outlier_dates1,
y=y_values,
mode="markers",
name="Anomaly",
marker=dict(color="red", size=6),
),
row=x1 + 1,
col=1,
)
for t in fig_.data:
fig.add_trace(t, row=x1+1,col=1)

fig.show()

Sample Image

how to loop to create subplots in Plotly, where each subplot has a few curves on it?

To use subplots in plotly you need to:

  1. use make_subplots to initialize the layout specifying the row and column
  2. then use row and col as arguments to fig.add_trace. NOTE: subplots row and columns start at 1 (not zero)

In your case, step2 is where you are getting stuck. Initially this part was missing (first post), but now in your update it's added in as an argument to go.Scatter. Carefully look over the examples here as the differences are just commas and parentheses and their placement.

To clarify, this:

fig.add_trace(go.Scatter(x=month, 
y=workingGasVolume_peryear,
name=f'workingGasVolume{year}',
row=i,
col=1,
line=dict(width=4,dash='dash')))

should be:

fig.add_trace(go.Scatter(x=month, 
y=workingGasVolume_peryear,
name=f'workingGasVolume{year}',
line=dict(width=4,dash='dash')),
row=i+1,
col=1)

I'm having difficulty with your code and data, which could be on my end as I do not use dictionaries like this, but here is a working example with your data in a csv and the use of pandas. Also, I changed one of the years to a different country so that there would be another plot.

import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots

df = pd.read_csv('someData.csv')
countries = df.country.unique()

# STEP 1
fig = make_subplots(
rows=len(countries), cols=1,
subplot_titles=(countries))

for i, country in enumerate(countries): #enumerate here to get access to i
years = df.year[df.country==country].unique()
for yrs in years:
focus = (df.country==country) & (df.year==yrs)
month = df.month[focus]
workingGasVolume_peryear = df.workingGasVolume[focus]
gasInStorage_peryear = df.gasInStorage[focus]

# STEP 2, notice position of arguments!
fig.add_trace(go.Scatter(x=month,
y=workingGasVolume_peryear,
name=f'workingGasVolume{yrs}',
line=dict(width=4,dash='dash')
),
row=i+1, #index for the subplot, i+1 because plotly starts with 1
col=1)
fig.add_trace(go.Scatter(x=month,
y=gasInStorage_peryear,
name=f'gasInStorage{yrs}',
line = dict(width=4)),
row=i+1,
col=1)
fig.show()

Sample Image

Plotly: How to use for loop or list for name attribute in bar charts?

Expected graph is like the legends(ie the name attribute 'Domain A'
,'Domain B', 'Domain C' etc should come in x-axis

and the status such
as 'Completed' , 'Not completed' etc should come in the legends or the
name attribute for the bar chart.

Like this?

Sample Image

I would like to achieve this within the for loop, is that possible ?

It probably is. But there are much easier ways to get what you want. Take a look at one of the many great answer to the question Pandas: How to find percentage of group members type per subgroup? to see how you can cut your code down to one or two lines. And then build a complete plotly figure with a few additional lines like this:

# data restructuring
df_ply = pd.crosstab(df['Domain'], df['Course1 completion'], normalize='index')

# plotly setup
fig = go.Figure()

# add trace for eat
for col in df_ply.columns:
#print(col)
fig.add_trace(go.Bar(x=df_ply.index, y=df_ply[col], name = col))

fig.update_layout(title=dict(text='Completion status per domain'))
fig.show()

This loops through the columns of the re-structured dataframe and adds a column per Course1 Completion status and displays them per Domain.

Complete code with data:

# imports
import pandas as pd
import plotly.graph_objects as go
from plotly.graph_objs import Pie, Layout,Figure
import plotly.offline as py

# data
df = pd.DataFrame({'HC No.': {0: 1,
1: 2,
2: 3,
3: 4,
4: 5,
5: 6,
6: 7,
7: 8,
8: 9,
9: 10,
10: 11,
11: 12,
12: 13,
13: 14,
14: 15,
15: 16,
16: 17,
17: 18,
18: 19,
19: 20,
20: 21,
21: 22,
22: 23,
23: 24,
24: 25,
25: 26},
'Domain': {0: 'Domain A',
1: 'Domain A',
2: 'Domain A',
3: 'Domain A',
4: 'Domain A',
5: 'Domain B',
6: 'Domain B',
7: 'Domain B',
8: 'Domain B',
9: 'Domain B',
10: 'Domain B',
11: 'Domain C',
12: 'Domain C',
13: 'Domain C',
14: 'Domain C',
15: 'Domain C',
16: 'Domain D',
17: 'Domain D',
18: 'Domain D',
19: 'Domain D',
20: 'Domain D',
21: 'Others',
22: 'Others',
23: 'Others',
24: 'Others',
25: 'Others'},
'Project': {0: 'Dog',
1: 'Dog',
2: 'Cat',
3: 'Cat',
4: 'Bird',
5: 'Tree',
6: 'Tree',
7: 'Plant',
8: 'Seed',
9: 'Seed',
10: 'Soil',
11: 'Liquid',
12: 'Solid',
13: 'Solid',
14: 'Solid',
15: 'Gas',
16: 'Gas',
17: 'Gas',
18: 'Gas',
19: 'Slime',
20: 'Slime',
21: 'Metal',
22: 'Metal',
23: 'wood',
24: 'wood',
25: 'Plastic'},
'Sub Project\n': {0: '',
1: '',
2: '',
3: '',
4: '',
5: '',
6: '',
7: '',
8: '',
9: '',
10: '',
11: '',
12: '',
13: '',
14: '',
15: '',
16: '',
17: '',
18: '',
19: '',
20: '',
21: '',
22: '',
23: '',
24: '',
25: ''},
'Emp Name': {0: 'Associate 1',
1: 'Associate 2',
2: 'Associate 3',
3: 'Associate 4',
4: 'Associate 5',
5: 'Associate 6',
6: 'Associate 7',
7: 'Associate 8',
8: 'Associate 9',
9: 'Associate 10',
10: 'Associate 11',
11: 'Associate 12',
12: 'Associate 13',
13: 'Associate 14',
14: 'Associate 15',
15: 'Associate 16',
16: 'Associate 17',
17: 'Associate 18',
18: 'Associate 19',
19: 'Associate 20',
20: 'Associate 21',
21: 'Associate 22',
22: 'Associate 23',
23: 'Associate 24',
24: 'Associate 25',
25: 'Associate 26'},
'Education mark': {0: '1,46975374',
1: '0,4285622',
2: '1,13064316',
3: '1,29683695',
4: '1,18009194',
5: '1,99',
6: '0,73110463',
7: '1,08737382',
8: '1,72600086',
9: '0,35357572',
10: '0,19593062',
11: '1,96790904',
12: '1,02216422',
13: '1,92464914',
14: '1,57124406',
15: '1,65805295',
16: '0,19593062',
17: '0',
18: '0,93860653',
19: '0,41443375',
20: '0,90421186',
21: '1,54062763',
22: '1,3367975',
23: '0,41977105',
24: '1,99',
25: '1,99'},
'Course1 completion': {0: 'Completed',
1: 'Completed',
2: 'Completed',
3: 'Not Completed',
4: 'Completed',
5: 'Not Completed',
6: 'Completed',
7: 'Completed',
8: 'Not Completed',
9: 'Completed',
10: "Planned in Q4 FY'20",
11: 'Completed',
12: 'Completed',
13: 'Not Completed',
14: 'Not Required',
15: 'Completed',
16: 'Completed',
17: 'Not Required',
18: 'Completed',
19: 'Completed',
20: 'Completed',
21: 'Not Completed',
22: 'Not Completed',
23: 'Completed',
24: 'Completed',
25: 'Not Completed'},
'Course2 completion': {0: 'Completed',
1: 'Completed',
2: '',
3: '',
4: '',
5: '',
6: '',
7: '',
8: '',
9: '',
10: '',
11: '',
12: '',
13: '',
14: '',
15: '',
16: 'Completed',
17: '',
18: '',
19: 'Completed',
20: '',
21: '',
22: '',
23: '',
24: '',
25: ''}})

# data restructuring
df_ply = pd.crosstab(df['Domain'], df['Course1 completion'], normalize='index')

# plotly setup
fig = go.Figure()

# add trace for eat
for col in df_ply.columns:
#print(col)
fig.add_trace(go.Bar(x=df_ply.index, y=df_ply[col], name = col))

fig.update_layout(title=dict(text='Completion status per domain'))
fig.show()

Create Plotly plot for each column in df with a 'for' loop

You can add the plots to a dict like

plots = {i: px.scatter(df, x="A", y=i) for i in df.columns}

which is equivalent but shorter than

plots = {}
for i in df.columns:
plots[i] = px.scatter(df, x="A", y=i)

and then you can show each plot with plots['A'], plots['B'] and plots['C']

How do I loop over multiple figures in plotly?

Approach

There are a number of ways you can do this. The real challenge is rather how to reference them later. Here's how you do it:

  1. Slice your dataframe using frames = df['a'].unique(),
  2. loop through your subsets using for i, f in enumerate(frames):,
  3. build figures as normal using fig=go.Figure()
  4. add each new figure to a dictionary using figs['fig'+str(i)] = fig

Now you can reference and show, for example, figure 1 using:

figs['fig1'].show()

Plot

Sample Image

Complete code

import pandas as pd
from plotly import graph_objects as go
df = pd.DataFrame({
"a": ["a1", "a1", "a1", "a2", "a2", "a2", "a3", "a3", "a3"],
"b": ["b1", "b2", "b3", "b1", "b2", "b3", "b1", "b2", "b3"],
"c": [10, 20, 30, 40, 50, 60, 80, 90, 100],
"d": [100, 200, 300, 400, 500, 600, 1000, 2000, 3000]
})

frames = df['a'].unique() # use to slice dataframe
figs = {} # container for figures
for i, f in enumerate(frames, start = 1):
di = df[df['a']==f]
fig = go.Figure()
fig.add_bar(name = "ccc", x=di.b, y=di.c)
fig.add_bar(name = "ddd", x=di.b, y=di.d)
fig.update_layout(barmode='group', title="fig" + str(i) + " for a" + str(i))
figs['fig'+str(i)] = fig
figs['fig1'].show()

Plotly displayed nothing when using for loop in a notebook

I don't know why first version shows without fig.show() but sencond not but mostly using matplotlib or plotly or other module (in script, python's shell or notebook) I have to use fig.show() (or plt.show() for matplotlib) to see plot.

After changing few mistakes - y = data[i] instead of y = data[:, i] and range(3) instead ofrange(0, 2) - this code works for me in notebook

from random import randint
import plotly.graph_objects as go

x1 = [randint(0, 9) for p in range(10)]
x2 = [randint(0, 9) for p in range(10)]
x3 = [randint(0, 9) for p in range(10)]

time = list(range(len(x1)))

data = [x1, x2, x3]

fig = go.Figure()

for i in range(3):
fig.add_trace(go.Scatter(x=time, y=data[i], mode='markers+lines', name='X{}'.format(i)))

fig.show()


Related Topics



Leave a reply



Submit