plotly | An interactive graphing library for R | Data Visualization library
kandi X-RAY | plotly Summary
Support
Quality
Security
License
Reuse
Currently covering the most popular Java, JavaScript and Python libraries. See a Sample Here
plotly Key Features
plotly Examples and Code Snippets
Trending Discussions on plotly
Trending Discussions on plotly
QUESTION
I'm trying to run a simple dash app in a conda environment in Pycharm, however I'm running into the error in the title. Weirdly enough, I couldn't find a place on the internet which has a mention of this bug, except for here. The code is simple, as all I'm trying to run is a simple dashapp; code obtained the code from here. I have tried switching between python versions in conda (back and forth between python 3.9, 3.8 and 3.7) but the error seems to be persistent. I know I have also correctly installed all its dependencies as I'm not getting any import error. Would appreciate if anyone could help with this.
Edit: Versions of Dash installed, as requested by @coralvanda :
Basically, I just did a pip install of everything so all the versions of packages are the latest.
ANSWER
Answered 2022-Mar-29 at 03:40I've been in the same problem.
Uninstall the wrong version with:
pip uninstall werkzeug
Install the right one with:
pip install -v https://github.com/pallets/werkzeug/archive/refs/tags/2.0.1.tar.gz
QUESTION
I want to produce a plot via R plotly with independent legends while respecting the colorscale.
This is what I have:
library(plotly)
X <- data.frame(xcoord = 1:6,
ycoord = 1:6,
score = 1:6,
gender = c("M", "M", "M", "F", "F", "F"),
age = c("young", "old", "old", "old", "young", "young"))
plot_ly(data = X, x = ~xcoord, y = ~ycoord, split = ~interaction(age, gender),
type = "scatter", mode = "markers",
marker = list(color = ~score,
colorbar = list(len = .5, y = .3)))
As you can see, the colorbar is messed up and the two categories are entangled.
I need to have separate legends for age
(young vs old) and gender
(M vs F), that can be clicked independently from one another. This would be the expected outcome:
Edit 1
This is the equivalent with ggplot2
:
gg <- ggplot(X, aes(x = xcoord, y = ycoord)) +
geom_point(aes(color = score, shape = gender, alpha = age), size = 5) +
scale_shape_manual(values = c("M" = 19, "F" = 19)) +
scale_alpha_manual(values = c("young" = 1, "old" = 1))
ggplotly(gg)
It does display correctly in ggplot, but breaks when applying ggplotly()
.
Please note that I would favor a solution with the native plotly
plot, rather than a post hoc ggplotly()
fix as has been proposed in other posts.
Edit 2
Although the current answers do disentangle the two legends (age
and gender
), they are not functional. For instance, if you click on the young
level, the whole age
legend will be toggled on/off. The objective here is that each sub level of each legend can be toggled independently from the others, and that by clicking on the legend's levels, the dot will show/hide accordingly.
ANSWER
Answered 2022-Mar-19 at 15:21This isn't exactly what you're looking for. I was able to create a meaningful color bar, though.
I removed the call for interaction between the groups and created a separate trace. Then I created legend groups and named them to create separate legends for gender
and age
. When I pull color =
out of the call to create a colorbar, this synced the color scales.
However, it assigns colors to the labels for age and gender and that's not meaningful! There are a few things that don't line up with your request, but someone may be able to build on this information.
plot_ly(data = X, x = ~xcoord, y = ~ycoord,
split = ~age,
legendgroup = 'age', # create first split and name it
legendgrouptitle = list(text = "Age"),
type = "scatter", mode = "markers",
color = ~score,
marker = list(colorbar = list(len = .5, y = .3))) %>%
add_trace(split = ~gender,
legendgroup = 'gender', # create second split and name it
color = ~score,
legendgrouptitle = list(text = "Gender")) %>%
colorbar(title = 'Score')
QUESTION
I have an Rmarkdown with a simple scatter plot (a map for instance), and I would like users to be able to provide some arbitrary x
and y
coordinates via an input and have those plotted on the graph (in red in the example below). The problem is, I don't have a shiny server so I cannot rely on that option. Is there a implement this, for instance, via javascript or something?
This is what I have:
---
title: "Untitled"
output: html_document
---
```{r setup, include=FALSE}
library(ggplot2)
library(plotly)
```
```{r fig.height=4, fig.width=4}
X <- data.frame(x = 1:10, y = 1:10)
gg <- ggplot(X, aes(x, y)) + geom_point()
ggplotly(gg)
```
This is what I am looking for:
Edit
The example above is a simplification. In reality, the grid is 360x240 and the coordinates can only be integers.
Edit 2 @JohanRosa already provided a nice answer by rebuilding the plot entirely on plotly.js. However, my ggplot is in fact quite complexe and I have many of them. It would therefore be quite complicated for me to rebuild each of them into plotly.js. This is the reason I am looking for an solution that can work directly on the ggplot(ly) that I have.
ANSWER
Answered 2022-Mar-04 at 19:18This may not be what you want but you can do this by adding a runtime of shiny in your yaml
---
title: "Untitled"
output: html_document
runtime: shiny
---
```{r setup, include=FALSE}
library(ggplot2)
library(plotly)
library(shiny)
```
```{r shinyInputs}
shiny::numericInput('someInput', "Some Number", value = 5)
shiny::numericInput('someInput2', "Some Number2", value = 2)
plotlyOutput('gg')
```
```{r fig.height=4, fig.width=4}
X <- data.frame(x = 1:10, y = 1:10)
output$gg <- renderPlotly({
temp <- tibble::tibble(x = input$someInput, y = input$someInput2)
ggplotly(ggplot(X, aes(x, y)) + geom_point() + geom_point(data = temp, aes(x =
x, y = y), color = 'red'))
})
```
QUESTION
I am working with the R programming language. I made the following 3 Dimensional Plot using the "plotly" library:
library(dplyr)
library(plotly)
my_function <- function(x,y) {
final_value = (1 - x)^2 + 100*((y - x^2)^2)
}
input_1 <- seq(-1.5, 1.5,0.1)
input_2 <- seq(-1.5, 1.5,0.1)
z <- outer(input_1, input_2, my_function)
plot_ly(x = input_1, y = input_2, z = z) %>% add_surface()
I am now trying to add "contour lines" to the above plot as shown below: https://plotly.com/r/3d-surface-plots/
I am trying to adapt the code from the "plotly website" to make these contours, but I am not sure how to do this:
Graph 1:
# This might have worked?
fig <- plot_ly(z = ~z) %>% add_surface(
contours = list(
z = list(
show=TRUE,
usecolormap=TRUE,
highlightcolor="#ff0000",
project=list(z=TRUE)
)
)
)
fig <- fig %>% layout(
scene = list(
camera=list(
eye = list(x=1.87, y=0.88, z=-0.64)
)
)
)
# I don't think this worked?
fig <- plot_ly(
type = 'surface',
contours = list(
x = list(show = TRUE, start = 1.5, end = 2, size = 0.04, color = 'white'),
z = list(show = TRUE, start = 0.5, end = 0.8, size = 0.05)),
x = ~x,
y = ~y,
z = ~z)
fig <- fig %>% layout(
scene = list(
xaxis = list(nticks = 20),
zaxis = list(nticks = 4),
camera = list(eye = list(x = 0, y = -1, z = 0.5)),
aspectratio = list(x = .9, y = .8, z = 0.2)))
fig
ANSWER
Answered 2022-Mar-04 at 17:52You were almost there.
The contours on z
should be defined according to min
-max
values of z
:
plot_ly(x = input_1, y = input_2, z = z,
contours = list(
z = list(show = TRUE, start = round(min(z),-2),
end = round(max(z),-2),
size = 100))) %>%
add_surface()
plot_ly(x = input_1, y = input_2, z = z,
colors = 'Oranges',
contours = list(
z = list(show = TRUE))) %>%
add_surface()
QUESTION
I am trying to do a regular import in Google Colab.
This import worked up until now.
If I try:
import plotly.express as px
or
import pingouin as pg
I get an error:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()
----> 1 import plotly.express as px
9 frames
/usr/local/lib/python3.7/dist-packages/plotly/express/__init__.py in ()
13 )
14
---> 15 from ._imshow import imshow
16 from ._chart_types import ( # noqa: F401
17 scatter,
/usr/local/lib/python3.7/dist-packages/plotly/express/_imshow.py in ()
9
10 try:
---> 11 import xarray
12
13 xarray_imported = True
/usr/local/lib/python3.7/dist-packages/xarray/__init__.py in ()
1 import pkg_resources
2
----> 3 from . import testing, tutorial, ufuncs
4 from .backends.api import (
5 load_dataarray,
/usr/local/lib/python3.7/dist-packages/xarray/tutorial.py in ()
11 import numpy as np
12
---> 13 from .backends.api import open_dataset as _open_dataset
14 from .backends.rasterio_ import open_rasterio as _open_rasterio
15 from .core.dataarray import DataArray
/usr/local/lib/python3.7/dist-packages/xarray/backends/__init__.py in ()
4 formats. They should not be used directly, but rather through Dataset objects.
5
----> 6 from .cfgrib_ import CfGribDataStore
7 from .common import AbstractDataStore, BackendArray, BackendEntrypoint
8 from .file_manager import CachingFileManager, DummyFileManager, FileManager
/usr/local/lib/python3.7/dist-packages/xarray/backends/cfgrib_.py in ()
14 _normalize_path,
15 )
---> 16 from .locks import SerializableLock, ensure_lock
17 from .store import StoreBackendEntrypoint
18
/usr/local/lib/python3.7/dist-packages/xarray/backends/locks.py in ()
11
12 try:
---> 13 from dask.distributed import Lock as DistributedLock
14 except ImportError:
15 DistributedLock = None
/usr/local/lib/python3.7/dist-packages/dask/distributed.py in ()
1 # flake8: noqa
2 try:
----> 3 from distributed import *
4 except ImportError:
5 msg = (
/usr/local/lib/python3.7/dist-packages/distributed/__init__.py in ()
1 from __future__ import print_function, division, absolute_import
2
----> 3 from . import config
4 from dask.config import config
5 from .actor import Actor, ActorFuture
/usr/local/lib/python3.7/dist-packages/distributed/config.py in ()
18
19 with open(fn) as f:
---> 20 defaults = yaml.load(f)
21
22 dask.config.update_defaults(defaults)
TypeError: load() missing 1 required positional argument: 'Loader'
I think it might be a problem with Google Colab or some basic utility package that has been updated, but I can not find a way to solve it.
ANSWER
Answered 2021-Oct-15 at 21:11Found the problem.
I was installing pandas_profiling
, and this package updated pyyaml
to version 6.0 which is not compatible with the current way Google Colab imports packages.
So just reverting back to pyyaml
version 5.4.1 solved the problem.
For more information check versions of pyyaml
here.
See this issue and formal answers in GitHub
##################################################################
For reverting back to pyyaml
version 5.4.1 in your code, add the next line at the end of your packages installations:
!pip install pyyaml==5.4.1
It is important to put it at the end of the installation, some of the installations will change the pyyaml
version.
QUESTION
I am trying to convert a geom_tile
plot built with ggplot
to ggplotly
. However, the tiles are distorted in plotly. The same issues takes place with geom_raster
.
Showcase:
library(ggplot2)
library(plotly)
set.seed(1)
n <- 10
X <- data.frame(xcoord = sample(1:10, n, replace = TRUE),
ycoord = sample(1:10, n, replace = TRUE),
value = runif(n))
gg <- ggplot(X) + geom_tile(aes(x = xcoord, y = ycoord, fill = value))
ggplotly(gg)
ANSWER
Answered 2022-Feb-22 at 17:27Looking at the plotly code here (excerpt below), it seems that the raster is only defined for any values of x and y available in the dataset - and whatever happens in between is up the the rest of the plotly code.
geom2trace.GeomTile <- function(data, params, p) {
x <- sort(unique(data[["x"]]))
y <- sort(unique(data[["y"]]))
# make sure we're dealing with a complete grid
g <- expand.grid(x = x, y = y)
g$order <- seq_len(nrow(g))
g <- merge(g, data, by = c("x", "y"), all.x = TRUE)
g <- g[order(g$order), ]
...
For the example data this code generates the following data, where gray areas are NA, and blank areas are simply undefined. And all the distortions/stretching happens in the undefined areas.
ggplot(g, aes(x = x, y = y, fill = value)) + geom_tile()
With this, one possible workaround (outside the plotly package) would be to manually ensure there are (NA) data available across the whole x/y range, so expand.grid generates a sufficiently dense grid when the plot is translated to plotly.
set.seed(1)
X1 <- data.frame(xcoord = c(sample(1:10, n, replace = TRUE), 1:10),
ycoord = c(sample(1:10, n, replace = TRUE), 1:10),
value = c(runif(n), rep(NA, 10)))
gg1 <- ggplot(X1) + geom_tile(aes(x = xcoord, y = ycoord, fill = value))
ggplotly(gg1)
While the example above demonstrates it is sufficient to have a single value for any x and y in the dataset, I'll also add an arguably cleaner solution as suggested by Waldi in the comments. By (automatically) generating the full grid in advance, there is less reliance on quirks from the plotly translation. For grid spacing different than 1, the sequence of course needs to be adjusted.
# X: original dataframe as defined in the question
X2 <- tidyr::expand_grid(
xcoord = seq(min(X$xcoord), max(X$xcoord)),
ycoord = seq(min(X$ycoord),max(X$ycoord))
) %>%
dplyr::left_join(X, by=c('xcoord','ycoord'))
gg2 <- ggplot(X2) + geom_tile(aes(x = xcoord, y = ycoord, fill = value))
ggplotly(gg2)
QUESTION
i am currently working with plotly i have a function called plotChart that takes a dataframe as input and plots a candlestick chart. I am trying to figure out a way to pass a list of dataframes to the function plotChart and use a plotly dropdown menu to show the options on the input list by the stock name. The drop down menu will have the list of dataframe and when an option is clicked on it will update the figure in plotly is there away to do this. below is the code i have to plot a single dataframe
def make_multi_plot(df):
fig = make_subplots(rows=2, cols=2,
shared_xaxes=True,
vertical_spacing=0.03,
subplot_titles=('OHLC', 'Volume Profile'),
row_width=[0.2, 0.7])
for s in df.name.unique():
trace1 = go.Candlestick(
x=df.loc[df.name.isin([s])].time,
open=df.loc[df.name.isin([s])].open,
high=df.loc[df.name.isin([s])].high,
low=df.loc[df.name.isin([s])].low,
close=df.loc[df.name.isin([s])].close,
name = s)
fig.append_trace(trace1,1,1)
fig.append_trace(go.Scatter(x=df.loc[df.name.isin([s])].time, y=df.loc[df.name.isin([s])].BbandsMid, mode='lines',name='MidBollinger'),1,1)
fig.append_trace(go.Scatter(x=df.loc[df.name.isin([s])].time, y=df.loc[df.name.isin([s])].BbandsUpp, mode='lines',name='UpperBollinger'),1,1)
fig.append_trace(go.Scatter(x=df.loc[df.name.isin([s])].time, y=df.loc[df.name.isin([s])].BbandsLow, mode='lines',name='LowerBollinger'),1,1)
fig.append_trace(go.Scatter(x=df.loc[df.name.isin([s])].time, y=df.loc[df.name.isin([s])].vwap, mode='lines',name='VWAP'),1,1)
fig.append_trace(go.Scatter(x=df.loc[df.name.isin([s])].time, y=df.loc[df.name.isin([s])].STDEV_1, mode='lines',name='UPPERVWAP'),1,1)
fig.append_trace(go.Scatter(x=df.loc[df.name.isin([s])].time, y=df.loc[df.name.isin([s])].STDEV_N1, mode='lines',name='LOWERVWAP'),1,1)
fig.append_trace(go.Scatter(x=df.loc[df.name.isin([s])].time, y=df.loc[df.name.isin([s])].KcMid, mode='lines',name='KcMid'),1,1)
fig.append_trace(go.Scatter(x=df.loc[df.name.isin([s])].time, y=df.loc[df.name.isin([s])].KcUpper, mode='lines',name='KcUpper'),1,1)
fig.append_trace(go.Scatter(x=df.loc[df.name.isin([s])].time, y=df.loc[df.name.isin([s])].KcLow, mode='lines',name='KcLow'),1,1)
trace2 = go.Bar(
x=df.loc[df.name.isin([s])].time,
y=df.loc[df.name.isin([s])].volume,
name = s)
fig.append_trace(trace2,2,1)
# fig.update_layout(title_text=s)
graph_cnt=len(fig.data)
tr = 11
symbol_cnt =len(df.name.unique())
for g in range(tr, graph_cnt):
fig.update_traces(visible=False, selector=g)
#print(g)
def create_layout_button(k, symbol):
start, end = tr*k, tr*k+2
visibility = [False]*tr*symbol_cnt
visibility[start:end] = [True,True,True,True,True,True,True,True,True,True,True]
return dict(label = symbol,
method = 'restyle',
args = [{'visible': visibility[:-1],
'title': symbol,
'showlegend': False}])
fig.update(layout_xaxis_rangeslider_visible=False)
fig.update_layout(
updatemenus=[go.layout.Updatemenu(
active = 0,
buttons = [create_layout_button(k, s) for k, s in enumerate(df.name.unique())]
)
])
fig.show()
i am trying to add annotations to the figure it will be different for each chart below is how i had it setup for the single chart df['superTrend'] is a Boolean column
for i in range(df.first_valid_index()+1,len(df.index)):
prev = i - 1
if df['superTrend'][i] != df['superTrend'][prev] and not np.isnan(df['superTrend'][i]) :
#print(i,df['inUptrend'][i])
fig.add_annotation(x=df['time'][i], y=df['open'][i],
text= 'Buy' if df['superTrend'][i] else 'Sell',
showarrow=True,
arrowhead=6,
font=dict(
#family="Courier New, monospace",
size=20,
#color="#ffffff"
),)
ANSWER
Answered 2022-Feb-18 at 07:18I adapted an example from the plotly community to your example and created the code. The point of creation is to create the data for each subplot and then switch between them by means of buttons. The sample data is created using representative companies of US stocks. one issue is that the title is set but not displayed. We are currently investigating this issue.
import yfinance as yf
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
symbols = ['AAPL','GOOG','TSLA']
stocks = pd.DataFrame()
for s in symbols:
data = yf.download(s, start="2021-01-01", end="2021-12-31")
data['mean'] = data['Close'].rolling(20).mean()
data['std'] = data['Close'].rolling(20).std()
data['upperBand'] = data['mean'] + (data['std'] * 2)
data.reset_index(inplace=True)
data['symbol'] = s
stocks = stocks.append(data, ignore_index=True)
def make_multi_plot(df):
fig = make_subplots(rows=2, cols=1,
shared_xaxes=True,
vertical_spacing=0.03,
subplot_titles=('OHLC', 'Volume Profile'),
row_width=[0.2, 0.7])
for s in df.symbol.unique():
trace1 = go.Candlestick(
x=df.loc[df.symbol.isin([s])].Date,
open=df.loc[df.symbol.isin([s])].Open,
high=df.loc[df.symbol.isin([s])].High,
low=df.loc[df.symbol.isin([s])].Low,
close=df.loc[df.symbol.isin([s])].Close,
name=s)
fig.append_trace(trace1,1,1)
trace2 = go.Scatter(
x=df.loc[df.symbol.isin([s])].Date,
y=df.loc[df.symbol.isin([s])].upperBand,
name=s)
fig.append_trace(trace2,1,1)
trace3 = go.Bar(
x=df.loc[df.symbol.isin([s])].Date,
y=df.loc[df.symbol.isin([s])].Volume,
name=s)
fig.append_trace(trace3,2,1)
# fig.update_layout(title_text=s)
# Calculate the total number of graphs
graph_cnt=len(fig.data)
# Number of Symbols
symbol_cnt =len(df.symbol.unique())
# Number of graphs per symbol
tr = 3
# Hide setting for initial display
for g in range(tr, graph_cnt):
fig.update_traces(visible=False, selector=g)
def create_layout_button(k, symbol):
start, end = tr*k, tr*k+2
visibility = [False]*tr*symbol_cnt
# Number of graphs per symbol, so if you add a graph, add True.
visibility[start:end] = [True,True,True]
return dict(label = symbol,
method = 'restyle',
args = [{'visible': visibility[:-1],
'title': symbol,
'showlegend': True}])
fig.update(layout_xaxis_rangeslider_visible=False)
fig.update_layout(
updatemenus=[go.layout.Updatemenu(
active = 0,
buttons = [create_layout_button(k, s) for k, s in enumerate(df.symbol.unique())]
)
])
fig.show()
return fig.layout
make_multi_plot(stocks)
QUESTION
I'm trying to make a dash table based on input data but I'm stucking in add more rows to add new inputs. Actually I read this docs and I know that I can directly input in dash table but I want to update dash table from input.
Below is my code:
import pandas as pd
import numpy as np
from datetime import datetime as dt
import plotly.express as px
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output, State
import dash_table
import dash_bootstrap_components as dbc
from dash_extensions import Download
from dash_extensions.snippets import send_data_frame
import glob
import os
from pandas.tseries.offsets import BDay
import plotly.graph_objects as go
app = dash.Dash(__name__)
MD23 = pd.DataFrame({'Number':[],
'PW':[],
'Name 1':[],
'Name 2':[],
'Email':[],
'Web':[],
'Abc':[]})
# ------------------------------------------------------------------------
input_types = ['number', 'password', 'text', 'tel', 'email', 'url', 'search']
app.layout = html.Div([
html.Div([
dcc.Input(
id='my_{}'.format(x),
type=x,
placeholder="insert {}".format(x), # A hint to the user of what can be entered in the control
minLength=0, maxLength=50, # Ranges for character length inside input box
autoComplete='on',
disabled=False, # Disable input box
readOnly=False, # Make input box read only
required=False, # Require user to insert something into input box
size="20", # Number of characters that will be visible inside box
) for x in input_types
]),
html.Br(),
html.Button('Add Row',id='add_row',n_clicks=0),
dbc.Row([
dbc.Col([html.H5('List',className='text-center'),
dash_table.DataTable(
id='table-container_3',
data=[],
columns=[{"name":i_3,"id":i_3,'type':'numeric'} for i_3 in MD23.columns],
style_table={'overflow':'scroll','height':600},
style_cell={'textAlign':'center'},
row_deletable=True,
editable=True)
],width={'size':12,"offset":0,'order':1})
]),
])
@app.callback(Output('table-container_3', 'data'),
[Input('my_{}'.format(x),'value')for x in input_types])
def update_data(selected_number, selected_pw,
selected_text, selected_tel,
selected_email,selected_url,
selected_search):
data = pd.DataFrame({'Number':[selected_number],
'PW':[selected_pw],
'Name 1':[selected_text],
'Name 2':[selected_tel],
'Email':[selected_email],
'Web':[selected_url],
'Abc':[selected_search]})
return data.to_dict(orient='records')
# ------------------------------------------------------------------------
if __name__ == '__main__':
app.run_server(debug=False)
I tried to add rows as below but it's not worked:
@app.callback(
Output('table-container_3', 'data'),
Input('add_row', 'n_clicks'),
State('table-container_3', 'data'),
State('table-container_3', 'columns'))
def add_row(n_clicks, rows, columns):
if n_clicks > 0:
rows.append()
return rows
I really need suggestions to solve this problem. Thank you so much.
ANSWER
Answered 2022-Feb-15 at 05:25tran Try to replace your callback with this callback:
@app.callback(
Output('table-container_3', 'data'),
Input('add_row', 'n_clicks'),
[State('table-container_3', 'data'),
State('table-container_3', 'columns')]+
[State('my_{}'.format(x), 'value') for x in input_types])
def add_row(n_clicks, rows, columns, selected_number, selected_pw,
selected_text, selected_tel,
selected_email, selected_url,
selected_search):
if n_clicks > 0:
rows.append({c['id']: r for c,r in zip(columns, [selected_number, selected_pw, selected_text, selected_tel, selected_email, selected_url, selected_search])})
return rows
QUESTION
Consider the plot produced by the following reprex. Note that the ggplot has sensible legends, while in plotly, the legend is heavily duplicated, with one entry for each time the same category ("manufacturer") appears in each facet. How do I make the plotly legend better match that of the ggplot2 one?
library(plotly)
library(ggplot2)
p <- mpg %>%
ggplot(aes(year)) +
geom_ribbon(aes(ymin=cty, ymax=hwy, fill = manufacturer), alpha=0.2) +
geom_line(aes(y = hwy, col=manufacturer)) +
facet_wrap(~class)
p
plotly::ggplotly(p)
ANSWER
Answered 2021-Sep-22 at 19:29Adapting my answer on this post to your case (which draws on this answer) one option would be to manipulate the plotly
object.
The issue is that with facetting we end up with one legend entry for each facet in which a group is present, i.e. the numbers in the legend entries correspond to the number of the facet or panel.
In plotly
one could prevent the duplicated legend entries via the legendgroup
argument. One option to achieve the same result when using ggplotly
would be to assign the legendgroup
manually like so:
library(plotly)
library(ggplot2)
p <- mpg %>%
ggplot(aes(year)) +
geom_ribbon(aes(ymin=cty, ymax=hwy, fill = manufacturer), alpha=0.2) +
geom_line(aes(y = hwy, col=manufacturer)) +
facet_wrap(~class)
gp <- ggplotly(p = p)
# Get the names of the legend entries
df <- data.frame(id = seq_along(gp$x$data), legend_entries = unlist(lapply(gp$x$data, `[[`, "name")))
# Extract the group identifier
df$legend_group <- gsub("^\\((.*?),\\d+\\)", "\\1", df$legend_entries)
# Add an indicator for the first entry per group
df$is_first <- !duplicated(df$legend_group)
for (i in df$id) {
# Is the layer the first entry of the group?
is_first <- df$is_first[[i]]
# Assign the group identifier to the name and legendgroup arguments
gp$x$data[[i]]$name <- df$legend_group[[i]]
gp$x$data[[i]]$legendgroup <- gp$x$data[[i]]$name
# Show the legend only for the first layer of the group
if (!is_first) gp$x$data[[i]]$showlegend <- FALSE
}
gp
QUESTION
I have a react app which generates images on the front end dynamically using Plotly.js. I'd like to add image sharing functionality. I am trying to use react-share for this. Social platforms require image URL for image sharing and do not support images in base64 encoding or alike. Backend was implemented so it can receive images in base64, store in the database and return URL to the image, which is then used for sharing with react-share
.
As the image is generated dynamically (it changes each time user resizes the chart, for instance), everything should be done when user clicks on Share icon.
So after the user has clicked on the Share icon, the image generated on the front end should be saved to back end
let imgURI;
const handleClick = () => {
Plotly.toImage('chartContainer', {
format: 'png',
width: 1000,
height: 600
})
.then(dataUrl => api.post('/image/base64ToPng', { image: dataUrl })
.then(
(response) => {
imgURI = response.data.imgURI;
},
failure => console.error(failure)
));
};
after the response is received, passed down to the sharing component like this
The code sample is not asynchronous, so the image URI is not passed to the sharing component, therefore sharing does not work. I tried to pass the prop down using conditional depending on whether it's defined or not and did not come up with a solution. I also looked up some issues in react-share repo that dealt with async urls, but seems like none of them deals with the dynamic image sharing on click.
I'd very appreciate a hint on how to complete this task.
ANSWER
Answered 2021-Nov-19 at 20:27This is serious hack territory, and the whole thing would be a lot simpler if this PR had been completed.
However, the code below should work (see codesandbox).
The key steps are:
- Have a bit of state that keeps track of whether you have a url from the service or not.
- When this state is "none", disable the facebook button's default behavior (i.e.
openShareDialogOnClick
=false
) - Add an
onClick
handler to the facebook button that asynchronously fetches the url and sets the state (triggering a re-render) - Use an effect + ref so that when the url is set to something real, you manually call the click event on the button (which now has a real address in its
url
prop), and then re-sets the url to "none"
import { useEffect, useRef, useState } from "react";
import { FacebookIcon, FacebookShareButton } from "react-share";
async function getUrFromService(): Promise {
// The real implementation would make a network call here.
await new Promise((resolve) => setTimeout(resolve, 1000));
return "https://via.placeholder.com/150";
}
export default function App() {
const shareButton = useRef(null);
const [url, setUrl] = useState("none"); // Unfortunately, we have to have a dummy string here, or FacebookShareButton will blow up.
// Provide an onClick handler that asyncronously fetches the url and sets it in the state.
const onClick = async () => {
// Be sure to check for the "none" state, so we don't trigger an infinite loop.
if (url === "none") {
const newUrl = await getUrFromService();
setUrl(newUrl);
}
};
// Whenever "url" changes and we re-render, we manually fire the click event on the button, and then re-set the url.
useEffect(() => {
if (url !== "none") {
shareButton.current?.click();
setUrl("none");
}
}, [url, shareButton]);
return (
);
}
Community Discussions, Code Snippets contain sources that include Stack Exchange Network
Vulnerabilities
No vulnerabilities reported
Install plotly
Support
Find, review, and download reusable Libraries, Code Snippets, Cloud APIs from over 650 million Knowledge Items
Find more librariesExplore Kits - Develop, implement, customize Projects, Custom Functions and Applications with kandi kits
Save this library and start creating your kit
Share this Page