I want to plot some time series data in plotly where the historic portion of the data has a daily resolution and the data for the current day has minute resolution. Is there a way to somehow "split" the x axis so that for the historic data it only shows the date and for the current data it shows time as well?
Currently it looks like this which is not really that readable
I think the only viable approach would be to put together two subplots. But using the correct setup should make the subplots reach pretty much 100% of what you're describing. You'll only need to adjust a few details like:
fig = make_subplots(rows=1, cols=2,
horizontal_spacing = 0,
shared_yaxes=True,
shared_xaxes=True)
Complete code:
# import pandas as pd
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
from plotly.subplots import make_subplots
import plotly.graph_objects as go
# custom function to set the first
# minute dataset to contiunue from
# the last day in the day dataset
def next_day(date):
s = date
date = datetime.strptime(s, "%Y-%m-%d")
next_date = date + timedelta(days=1)
return(datetime.strftime(next_date, "%Y-%m-%d"))
# data
np.random.seed(10)
n_days = 5
n_minutes = (2*24)
dfd = pd.DataFrame({'time':[t for t in pd.date_range('2020', freq='D', periods=n_days).format()],
'y':np.random.uniform(low=-1, high=1, size=n_days).tolist()})
dfm = pd.DataFrame({'time':[t for t in pd.date_range(next_day(dfd['time'].iloc[-1]), freq='min', periods=n_minutes).format()],
'y':np.random.uniform(low=-1, high=1, size=n_minutes).tolist()})
dfm['y'] = dfm['y'].cumsum()
# subplot setup
fig = make_subplots(rows=1, cols=2,
horizontal_spacing = 0,
shared_yaxes=True,
shared_xaxes=True)
# trace for days
fig.add_trace(
go.Scatter(x=dfd['time'], y=dfd['y'], name = 'days'),
row=1, col=1
)
# trace for minutes
fig.add_trace(
go.Scatter(x=dfm['time'], y=dfm['y'], name = 'minutes'),
row=1, col=2
)
# some x-axis aesthetics
fig.update_layout(xaxis1 = dict(tickangle=0))
fig.update_layout(xaxis2 = dict(tickangle=90))
fig.add_shape( dict(type="line",
x0=dfd['time'].iloc[-1],
y0=dfd['y'].iloc[-1],
x1=dfm['time'].iloc[0],
y1=dfm['y'].iloc[0],
xanchor = 'middle',
xref = 'x1',
yref = 'y1',
line=dict(dash = 'dash',
color="rgba(0,0,255,0.9)",
width=1
)))
fig.update_xaxes(showgrid=False)
fig.update_layout(template = 'plotly_dark')
fig.show()
Related
I saw this 3d plot. it was animated and added a new value every day. i have not found an example to recreate it with plotly in python.
the plot should start with the value from the first row (100). The start value should remain (no rolling values). The plot should be animated in such a way that each row value is added one after the other and the x-axis expands. the following data frame contains the values (df_stocks) and Dates to plot. assigning the colors would be a great addition. the more positive the deeper the green, the more negative the darker red.
import yfinance as yf
import pandas as pd
stocks = ["AAPL", "MSFT"]
df_stocks = pd.DataFrame()
for stock in stocks:
df = yf.download(stock, start="2022-01-01", end="2022-07-01", group_by='ticker')
df['perct'] = df['Close'].pct_change()
df_stocks[stock] = df['perct']
df_stocks.iloc[0] = 0
df_stocks += 1
df_stocks = df_stocks.cumprod()*100
df_stocks -= 100
You can use a list of go.Frame objects as shown in this example. Since you want the line plot to continually extend outward, each frame needs to include data that's one row longer than the previous frame, so we can use a list comprehension like:
frames = [go.Frame(data=
## ...extract info from df_stocks.iloc[:i]
for i in range(len(df_stocks))]
To add colors to your lines depending on their value, you can use binning and labels (as in this answer) to create new columns called AAPL_color and MSFT_color that contain the string of the css color (like 'darkorange' or 'green'). Then you can pass the information from these columns using the argument line=dict(color=...) in each go.Scatter3d object.
import yfinance as yf
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
stocks = ["AAPL", "MSFT"]
df_stocks = pd.DataFrame()
for stock in stocks:
df = yf.download(stock, start="2022-01-01", end="2022-07-01", group_by='ticker')
df['perct'] = df['Close'].pct_change()
df_stocks[stock] = df['perct']
df_stocks.iloc[0] = 0
df_stocks += 1
df_stocks = df_stocks.cumprod()*100
df_stocks -= 100
df_min = df_stocks[['AAPL','MSFT']].min().min() - 1
df_max = df_stocks[['AAPL','MSFT']].max().max() + 1
labels = ['firebrick','darkorange','peachpuff','palegoldenrod','palegreen','green']
bins = np.linspace(df_min,df_max,len(labels)+1)
df_stocks['AAPL_color'] = pd.cut(df_stocks['AAPL'], bins=bins, labels=labels).astype(str)
df_stocks['MSFT_color'] = pd.cut(df_stocks['MSFT'], bins=bins, labels=labels).astype(str)
frames = [go.Frame(
data=[
go.Scatter3d(
y=df_stocks.iloc[:i].index,
z=df_stocks.iloc[:i].AAPL.values,
x=['AAPL']*i,
name='AAPL',
mode='lines',
line=dict(
color=df_stocks.iloc[:i].AAPL_color.values, width=3,
)
),
go.Scatter3d(
y=df_stocks.iloc[:i].index,
z=df_stocks.iloc[:i].MSFT.values,
x=['MSFT']*i,
name='MSFT',
mode='lines',
line=dict(
color=df_stocks.iloc[:i].MSFT_color.values, width=3,
)
)]
)
for i in range(len(df_stocks))]
fig = go.Figure(
data=list(frames[1]['data']),
frames=frames,
layout=go.Layout(
# xaxis=dict(range=[0, 5], autorange=False),
# yaxis=dict(range=[0, 5], autorange=False),
# zaxis=dict(range=[0, 5], autorange=False),
template='plotly_dark',
legend = dict(bgcolor = 'grey'),
updatemenus=[dict(
type="buttons",
font=dict(color='black'),
buttons=[dict(label="Play",
method="animate",
args=[None])])]
),
)
fig.show()
I want to use plotly to show 2 sinuse waves
I want to use slider to show the progress from time=0 to current slider step.
I tried to write the following code:
import numpy as np
import pandas as pd
if __name__ == "__main__":
time = np.arange(0, 10, 0.1)
val1 = np.sin(time)
val2 = np.sin(time) * np.sin(time)
df = pd.DataFrame(val1, columns=['val-1'])
df['val-2'] = val2
fig = px.scatter(df, animation_frame=df.index)
fig.update_layout(xaxis_range=[-100, 100])
fig.update_layout(yaxis_range=[-1.1, 1.1])
fig.show()
but I can see the current value of the 2 sinuse waves (and not the whole waves from step=0 to current step)
How can I change my code and see the whole sinuse waves from step=0 to current step ?
I don't think it is possible to animate a line chart in Express, so I would have to use a graph object. There is an example in the reference, which I will adapt to your assignment. As for the graph structure, create the initial graph data and the respective frames in the animation, add them to the layout by creating steps and sliders.
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
if __name__ == "__main__":
time = np.arange(0, 10, 0.1)
val1 = np.sin(time)
val2 = np.sin(time) * np.sin(time)
df = pd.DataFrame(val1, columns=['val-1'])
df['val-2'] = val2
data = [go.Scatter(mode='lines', x=df.index, y=df['val-1'], name='val-1'),
go.Scatter(mode='lines', x=df.index, y=df['val-2'], name='val-2')]
steps = []
for i in df.index:
step = dict(method="animate", args=[[i], {"title": f'step:{i}'}], label=f'{i}')
steps.append(step)
sliders = [dict(active=0, currentvalue={"prefix": "Step: "}, steps=steps)]
layout = go.Layout(dict(xaxis=dict(range=[-100,100]),
yaxis=dict(range=[-1.1,1.1]),
updatemenus=[dict(type='buttons',
buttons=[dict(label='Start', method='animate', args=[None]),
dict(label='Pause',
method='animate',
args=[[None], dict(frame=dict(
duration=0,
redraw=False),
mode="immediate",
formcurrent=True,
transition=dict(duration=0, easing="linear")
)])],
direction="left",
pad=dict(r=10, t=40),
showactive=False,
x=0.00,
xanchor="right",
y=0,
yanchor="top")],
sliders=sliders
))
frames = []
for i in df.index:
frame = go.Frame(data=[go.Scatter(x=df.index[0:i], y=df.loc[0:i,'val-1']),
go.Scatter(x=df.index[0:i], y=df.loc[0:i,'val-2'])],
layout=go.Layout(title_text=f'Step:{i}'),
name=i)
frames.append(frame)
fig = go.Figure(data=data, layout=layout, frames=frames)
fig.show()
I essentially have two different data frames, one for calculating weekly data (df) and a second one (df1) that has the plot values of the stock/crypto. On df, I have created a pandas column 'pivot' ((open+high+low)/3) using the weekly data to create a set of values containing the weekly pivot values.
Now I want to plot these weekly data (as lines) onto df1 which has the daily data. Therefore the x1 would be the start of the week and x2 be the end of the week. the y values being the pivot value from the df(weekly).
Here is what I would want it to look like:
My Approach & Problem:
First of all, I am a beginner in Python, this is my second month of learning. My apologies if this was asked before.
I know the pivot values can be calculated using a single data frame & pandas group-by but I want to take the issue after this is done, so both ways should be fine if you are approaching this issue. What I would like to have is those final lines with OHLC candlesticks. I would like to plot these results using Plotly OHLC and go Shapes. What I am stuck with is iterating through the pivot weekly data frame and adding the lines as traces on top of the OHLC data daily data.
Here's my code so far:
import yfinance as yf
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime, timedelta
df = yf.download( tickers = 'BTC-USD',
start = '2021-08-30',
end = datetime.today().strftime('%Y-%m-%d'),
interval = '1wk',
group_by = 'ticker',
auto_adjust = True).reset_index()
#daily df for plot
df2 = yf.download( tickers = 'BTC-USD',
start = '2021-08-30',
end = datetime.today().strftime('%Y-%m-%d'),
interval = '1d',
group_by = 'ticker',
auto_adjust = True).reset_index()
#small cap everything
df = df.rename(columns={'Date':'date',
'Open': 'open',
'High': 'high',
'Low' : 'low',
'Close' : 'close'})
df['pivot'] = (df['high']+ df['low'] + df['close'])/3
result = df.copy()
fig = go.Figure(data = [go.Candlestick(x= df['date'],
open = df['open'],
high = df['high'],
low = df['low'],
close = df['close'],
name = 'Price Candle')])
This would be for plotting until the candlesticks OHLC, however, the rest iteration is what is troubling me. You can plot it on a line chart or on an OHLC chart and iterate it.
fig = px.line(df, x='time', y='close')
result = df.copy()
for i, pivot in result.iterrows():
fig.add_shape(type="line",
x0=pivot.date, y0=pivot, x1=pivot.date, y1=pivot,
line=dict(
color="green",
width=3)
)
fig
When I print this no pivot lines appear the way I want them to show.Only the original price line graph shows
Thanks in advance for taking the time to read this so far.
There are two ways to create a line segment: add a shape or use line mode on a scatter plot. I think the line mode of scatter plots is more advantageous because it allows for more detailed settings. For the data frame, introduce a loop process on a line-by-line basis to get the next line using the idx of the data frame. y-axis values are pivot values. I wanted to get Yokohama, so I moved the legend position up. Also, since we are looping through the scatter plot, we will have many legends for the pivot values, so we set the legend display to True for the first time only.
import yfinance as yf
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime, timedelta
df = yf.download( tickers = 'BTC-USD',
start = '2021-08-30',
end = datetime.today().strftime('%Y-%m-%d'),
interval = '1wk',
group_by = 'ticker',
auto_adjust = True).reset_index()
#daily df for plot
df2 = yf.download( tickers = 'BTC-USD',
start = '2021-08-30',
end = datetime.today().strftime('%Y-%m-%d'),
interval = '1d',
group_by = 'ticker',
auto_adjust = True).reset_index()
#small cap everything
df = df.rename(columns={'Date':'date',
'Open': 'open',
'High': 'high',
'Low' : 'low',
'Close' : 'close'})
df['pivot'] = (df['high']+ df['low'] + df['close'])/3
fig = go.Figure()
fig.add_trace(go.Candlestick(x= df['date'],
open = df['open'],
high = df['high'],
low = df['low'],
close = df['close'],
name = 'Price Candle',
legendgroup='one'
)
)
#fig.add_trace(go.Scatter(mode='lines', x=df['date'], y=df['pivot'], line=dict(color='green'), name='pivot'))
for idx, row in df.iterrows():
#print(idx)
if idx == len(df)-2:
break
fig.add_trace(go.Scatter(mode='lines',
x=[row['date'], df.loc[idx+1,'date']],
y=[row['pivot'], row['pivot']],
line=dict(color='blue', width=1),
name='pivot',
showlegend=True if idx == 0 else False,
)
)
fig.update_layout(
autosize=False,
height=600,
width=1100,
legend=dict(
orientation="h",
yanchor="bottom",
y=1.02,
xanchor="right",
x=1)
)
fig.update_xaxes(rangeslider_visible=False)
fig.show()
i am looking for a solution to plot a dataframe with a datetimeindex as a "carpet plot". i prefer plotly, but I also would use other libs. maybe "carpet plot" is not the correct name for the plot?!
i expect the index as xaxis label and for every column a "bucket". maybe a stacked area plot is a solution. i am not able to figure it out.
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
date_today = datetime.now()
days = pd.date_range(date_today, date_today + timedelta(7), freq='D')
np.random.seed(seed=1111)
data = [1,1,1,0,1,1,0,1]
data1 = [1,1,1,0,0,1,0,1]
data2 = [1,0,1,0,1,1,0,1]
df = pd.DataFrame({'test': days, 'col1': data, 'col2': data1, 'col3': data2})
df = df.set_index('test')
as example:
You can simply use imshow from plotly:
# using your data sample:
fig = px.imshow(df)
fig.show()
this gives you:
With a little bit more styling:
fig = px.imshow(df, width=300, height=500,
labels=dict(x="Columns", y="Days"),
x=df.columns,
y=df.index,
)
fig.show()
you can get:
Or horizontal layout, with no coloraxis and another colorscale (Viridis), if you like his better. For further details see the API reference
fig = px.imshow(df.T, width=500, height=300,
labels=dict(x="Columns", y="Days"),
x=df.T.columns,
y=df.T.index,
color_continuous_scale = 'Viridis',
)
fig.update_layout(coloraxis_showscale=False)
fig.show()
I have the following code:
from plotly.subplots import make_subplots
import requests
import json
import datetime as dt
import pandas as pd
import plotly.graph_objects as go
def get_candles(symbol, window, interval='1h'):
url = "https://api.binance.com/api/v1/klines"
end_time = dt.datetime.utcnow()
delta = dt.timedelta(hours = window)
start_time = end_time - delta
start_date = str(int(start_time.timestamp() * 1000))
end_date = str(int(end_time.timestamp() * 1000))
limit = '1000'
market = symbol + 'BUSD'
req_param = {"symbol": market, "interval": interval, "startTime": start_date, "endTime": end_date, "limit": limit}
text = requests.get(url, params = req_param).text
data = json.loads(text)
df = pd.DataFrame(data)
df.columns = ['open_time',
'o', 'h', 'l', 'c', 'v',
'close_time', 'qav', 'num_trades',
'taker_base_vol', 'taker_quote_vol', 'ignore']
df.index = [dt.datetime.fromtimestamp(x/1000.0) for x in df.close_time]
return df
def chart(symbol, interval='1h'):
windows = {'1m': 1, '5m': 5, '15m': 15, '30m': 30, '1h': 60, '2h': 120, '4h': 240, '6h': 360, '12h': 720, '1d': 1440}
chart = get_candles(symbol.upper(), windows[interval], interval)
fig = make_subplots(specs=[[{"secondary_y": True}]])
print(chart['v'].max())
fig.add_trace(go.Candlestick(x=chart.index,
open=chart['o'],
high=chart['h'],
low=chart['l'],
close=chart['c'],
name="yaxis1 data",
yaxis='y1'), secondary_y=True)
fig.add_trace(go.Bar(x=chart.index, y=chart['v'], name="yaxis2 data", yaxis="y2"), secondary_y=False)
fig.layout.yaxis2.showgrid=False
fig.update_yaxes(type="linear")
fig.update_layout(xaxis_rangeslider_visible=False)
# fig.show()
fig.write_image("figure.png", engine="kaleido")
chart('bnb')
And it produces the following image:
Now I want it so that the volume bars go to only to 1/3 of the total height of the chart and I tried doing this like this:
fig.update_layout(yaxis1=dict(title="bars", domain=[0, int(2* chart['v'].max())]))
But this does nothing.
How do I give a certain y-axis a name and change the range of that?
This is the way:
fig.update_layout(yaxis2 = dict(range=[<from_value>, <to_value>]))
Your code sample does not work on my end. But in this example, the following setup:
fig.update_layout(yaxis2 = dict(range=[0, 300*10**6]))
... will turn this:
... into this:
Complete code:
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
# data
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')
# Create figure with secondary y-axis
fig = make_subplots(specs=[[{"secondary_y": True}]])
# include candlestick with rangeselector
fig.add_trace(go.Candlestick(x=df['Date'],
open=df['AAPL.Open'], high=df['AAPL.High'],
low=df['AAPL.Low'], close=df['AAPL.Close']),
secondary_y=False)
# include a go.Bar trace for volumes
fig.add_trace(go.Bar(x=df['Date'], y=df['AAPL.Volume']),
secondary_y=True)
f = fig.full_figure_for_development(warn=False)
fig.layout.yaxis2.showgrid=False
fig.update_layout(yaxis2 = dict(range=[0, 300*10**6]))
fig.show()
I don't use plotly, but looking at the docs, I see the following for setting the range of the y-axis:
Code: fig.update_yaxes(range=<VALUE>)
Type: list
https://plotly.com/python/reference/layout/yaxis/#layout-xaxis-range
For a label/title:
Code: fig.update_yaxes(title=dict(...))
Type: dict containing one or more of the keys listed below.
fig.update_layout(title_text=<VALUE>)
https://plotly.com/python/reference/layout/yaxis/#layout-yaxis-title
I had an issue similar to this but the answers above didn't help as my chart is dynamic. Dependant on the timeframe the max volume can be anywhere from 300 - 10 million, so fixed ranges were no good.
It seemed that the best solution was to use the max() function to find the highest value in the volume column of my dataframe (also dynamic):
maxVol = max(df['Volume'], key=float)
ymax = float(maxVol)
print(ymax)
Then simply use the following to scale the max range by a factor of 3
fig.update_layout(yaxis=dict(range=[0, ymax*3]))