2

I'd like to add quivers to a plotly subplot. But I can't find any way to do this.

Her's what I'd like to do:

fig = plotly.subplots.make_subplots(rows=1, cols=2)
    
    
x,y = np.meshgrid(np.arange(0, 2, .2), np.arange(0, 2, .2))
u = np.cos(x)*y
v = np.sin(x)*y
    
quivers = ff.create_quiver(x, y, u, v)
    
fig.add_trace(data=quivers.data,col=1,row=1)

The problem is add_trace doesn't accept the data argument, and add_traces doesn't accept col and row arguments.

When launching this piece of code, I get the following error:

TypeError: add_trace() got an unexpected keyword argument 'data'

Thanks!

vestland
  • 55,229
  • 37
  • 187
  • 305
hulyce
  • 438
  • 3
  • 15
  • as in [the answer](https://stackoverflow.com/a/64082500/6692898) you were provided with yesterday, the parameter should be `data=quivers.data`... if that solves your problem please close this question as a duplicate of your own question from yesterday – RichieV Sep 27 '20 at 14:52
  • 1
    Of course I tried, but I had this error: TypeError: add_trace() got an unexpected keyword argument 'data' – hulyce Sep 27 '20 at 14:58
  • so you want a chart similar to yesterday's answer, but separated in subplots? – RichieV Sep 27 '20 at 15:07
  • Yes, that's what I want! – hulyce Sep 27 '20 at 15:13
  • @hulyce How did my suggestion work out for you? – vestland Sep 28 '20 at 19:25

2 Answers2

2

It appears that complete figure objects can not be used to set up subplots using make_subplots(). And as you've shown yourself, fig.add_trace() can't be used directly with the data from a ff.create_quiver() figure. What will work though, is to include a unique go.Scatter for each and every x and y element in fig1.data:

'x': [0.0, 0.0, None, ..., 1.7591036229552444, 1.7526465527333175, None],
'y': [0.0, 0.0, None, ..., 1.9752925735580753, 1.9216800167812427, None]

That may sound a little complicated, but really isn't at all. Just make two ff.create_quiver() figures and use this for each of them:

# add all fig1.data as individual traces in fig at row=1, col=1
for d in fig1.data:
    subplots.add_trace(go.Scatter(x=d['x'], y=d['y']),
                  row=1, col=1)

Using the snippet below will produce the following subplot setup with 1 row and two columns. Even the arrow shapes for all lines are included.

Plot

enter image description here

Complete code

import numpy as np
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import plotly.figure_factory as ff

# data
x,y = np.meshgrid(np.arange(0, 2, .2), np.arange(0, 2, .2))
u = np.cos(x)*y
v = np.sin(x)*y

# quiver plots
fig1 = ff.create_quiver(x, y, u, v)
fig2 = ff.create_quiver(x, y, u*0.9, v*1.1)
    
# subplot setup
subplots = make_subplots(rows=1, cols=2)

# add all fig1.data as individual traces in fig at row=1, col=1
for d in fig1.data:
    subplots.add_trace(go.Scatter(x=d['x'], y=d['y']),
                  row=1, col=1)

# add all fig2.data as individual traces in fig at row=1, col=2
for d in fig1.data:
    subplots.add_trace(go.Scatter(x=d['x'], y=d['y']),
                  row=1, col=2)
subplots.show()
vestland
  • 55,229
  • 37
  • 187
  • 305
  • Interesting, did you get an idea of the impact in memory of adding separate traces? just started using plotly so I really have no idea of how that is managed backstage, although if it is only a matter of adding dictionaries to the data list then it probably doesn't matter that much – RichieV Sep 28 '20 at 19:29
  • also, how did you manage to include the labels `trace 0` and `trace 1` ? – RichieV Sep 28 '20 at 19:33
  • 1
    @RichieV *No idea* about the memory footprint. I was just focusing on getting it done with `make_subplots()`. Either way I don't think it matters much. Even if it's included in a Dash app. I've produced stud that's *way* more clunky and it still runs pretty fast. – vestland Sep 28 '20 at 19:33
  • 1
    @RichieV Regarding the labels, that may seem a little funky at first. But as far as I know that's the default behaviour. One trace => no label. More than one trace => labels. – vestland Sep 28 '20 at 19:35
1

I found a solution in the plotly webpage by manually adjusting the axes instead of using make_subplots.

import numpy as np
import plotly.figure_factory as ff
import plotly.graph_objects as go

x,y = np.meshgrid(np.arange(0, 2, .2), np.arange(0, 2, .2))
u = np.cos(x)*y
v = np.sin(x)*y

fig1 = ff.create_quiver(x, y, u, v, name='trace1')
fig2 = ff.create_quiver(x, y, u*0.9, v*2, name='trace2')


for i in range(len(fig1.data)):
    fig1.data[i].xaxis = 'x1'
    fig1.data[i].yaxis = 'y1'

for i in range(len(fig2.data)):
    fig2.data[i].xaxis = 'x2'
    fig2.data[i].yaxis = 'y2'

fig1.layout.xaxis1.update({'anchor': 'y1', 'domain': [0.55, 1]})
# apparently [0.55, 1] is relative to the full chart's dimensions
fig1.layout.yaxis1.update({'anchor': 'x1'})

fig2['layout']['xaxis2'] = {'anchor': 'y2', 'domain': [0, 0.45]}
fig2['layout']['yaxis2'] = {'anchor': 'x2'}

fig = go.Figure()
fig.add_traces([fig1.data[0], fig2.data[0]])
fig.layout.update(fig1.layout)
fig.layout.update(fig2.layout)

enter image description here

RichieV
  • 5,103
  • 2
  • 11
  • 24
  • I would suggest not accepting this answer so other users are motivated to find a better solution by using `make_subplots` which seems like it should be the preferred option – RichieV Sep 27 '20 at 15:36