How to plot candlestick hourly with bokeh? - python

I have try to plot the candlestick chart.
From above figure you will see that the x-axis is sequences of data,
I just want to replace it by use date from df['date']
I try to use df['date'] instead df['seq'] but it doesn't work because time format.
How should i solve it?
Here is my code:
import pandas
import datetime
import random
import numpy
from bokeh.models import ColumnDataSource
from bokeh.models.tools import *
from bokeh.plotting import figure
from bokeh.io import gridplot, show
lenght = 20
df = pandas.DataFrame()
date = []
for i in range(1,5):
for j in range(10,15):
date.append(datetime.datetime(2018, 1, i, j, 0))
df['date'] = pandas.to_datetime(date).strftime('%Y-%m-%d %H:%M')
df['open'] = [random.randint(40,50) for p in range(lenght)]
df['high'] = [random.randint(40,50) for p in range(lenght)]
df['low'] = [random.randint(40,50) for p in range(lenght)]
df['close'] = [random.randint(40,50) for p in range(lenght)]
df['rsi'] = [random.randint(0,100) for p in range(lenght)]
seqs=numpy.arange(df.shape[0])
df["seq"]=pandas.Series(seqs)
df['mid']=df.apply(lambda x:(x['open']+x['close'])/2,axis=1)
df['height']=df.apply(lambda x:abs(x['close']-x['open'] if x['close']!=x['open'] else 0.001),axis=1)
df["seq"] = df['date']
inc = df.close > df.open
dec = df.open > df.close
w=0.3
#use ColumnDataSource to pass in data for tooltips
sourceInc=ColumnDataSource(ColumnDataSource.from_df(df.loc[inc]))
sourceDec=ColumnDataSource(ColumnDataSource.from_df(df.loc[dec]))
#the values for the tooltip come from ColumnDataSource
hover = HoverTool(
tooltips=[
("date", "#date"),
("close", "#close"),
]
)
TOOLS = [CrosshairTool(), hover, BoxZoomTool(), WheelZoomTool()]
ohlc = figure(plot_width=1000, plot_height=500, tools=TOOLS, x_axis_type='datetime')
ohlc.grid.grid_line_alpha=0.3
ohlc.segment(df.seq[inc], df.high[inc], df.seq[inc], df.low[inc], color="red")
ohlc.segment(df.seq[dec], df.high[dec], df.seq[dec], df.low[dec], color="green")
ohlc.rect(x='date', y='mid', width=w, height='height', fill_color="red", line_color="red", source=sourceInc)
ohlc.rect(x='date', y='mid', width=w, height='height', fill_color="green", line_color="green", source=sourceDec)
ohlc.xaxis.major_label_orientation = 3.14/4
rsi = figure(plot_width=1000, plot_height=100, y_range=(0,100))
rsi.xaxis.visible = False
rsi.multi_line(xs=[df.seq]*3, ys=[df.rsi, [30]*df.shape[0], [70]*df.shape[0]], line_color=['brown','grey','grey'], line_width=1)
chart = gridplot([[ohlc, None],[rsi, None]],toolbar_location='left')
print(df.date)
show(chart)

Try to replace the w=0.3 constant to w = 0.5 * 60 * 60 * 1000 # half hour in ms.
w means candlestick_width

Related

How to add labels to subplots in plotly?

I am trying to plot a candlestick with volume, using the plotly. However I can not get the proper x and yaxis label.please help.I need y labels for both plot but xlabel for just the bottom one, also one title for both. Bellow is the code.
** one more question, how can I change the line color in the volume plot.Thank you
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from plotly import tools
stock = 'AAPL'
df = web.DataReader(stock, data_source='yahoo', start='01-01-2019')
def chart_can_vol(df):
fig = tools.make_subplots(
rows=3, cols=1,
specs=[[{"rowspan": 2}],
[None],
[{}]],
shared_xaxes=True,
vertical_spacing=0.1)
fig.add_trace(go.Candlestick(x = df.index,
open = df['Open'],
close = df['Close'],
low = df['Low'],
high = df['High']),
row = 1, col = 1)
fig.update_layout(xaxis_rangeslider_visible = False)
fig.update_layout(
yaxis_title = 'Apple Stock Price USD ($)'
)
fig.add_trace(go.Scatter(x = df.index,
y = df['Volume']),
row = 3, col = 1)
fig.update_layout(
yaxis_title = 'Volume',
xaxis_title = 'Date'
)
fig.update_layout(title_text="Apple Stock")
fig.update_layout(width=900, height=900)
return fig
chart_can_vol(df)
When you make your subplots, you can add the subplot_titles attribute. In the code below, I used the titles "test1" and "test2". When you change your axis labels, you can use update_xaxes and update_yaxes, just make sure that the row and column values are the same for the update_axes method and the subplot.
To change the color of the line, you can add the line attribute within the scatterplot method and set it equal to a dictionary with a hex value of the color you want.
P.S. You should update plotly, because the tools.make_subplots was deprecated. Once you update, you can simply use make_subplots. Also, you are using pandas, when you should use pandas-datareader. See import statements.
Code:
import numpy as np
import pandas as pd
import pandas_datareader.data as web
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from plotly import tools
stock = 'AAPL'
df = web.DataReader(stock, data_source='yahoo', start='01-01-2019')
def chart_can_vol(df):
subplot_titles=["test1", "test2"]
rows = 2
cols = 2
height = 300 * rows
fig = make_subplots(
rows=3, cols=1,
specs=[[{"rowspan": 2}],
[None],
[{}]],
shared_xaxes=True,
subplot_titles=("test1", "test2"),
vertical_spacing=0.1)
fig.add_trace(go.Candlestick(x = df.index,
open = df['Open'],
close = df['Close'],
low = df['Low'],
high = df['High']),
row = 1, col = 1)
fig.update_layout(xaxis_rangeslider_visible = False)
fig.update_layout(
yaxis_title = 'Apple Stock Price USD ($)'
)
fig.add_trace(go.Scatter(x = df.index,
y = df['Volume'],
line= dict(color="#ffe476")),
row = 3, col = 1)
fig.update_xaxes(title_text="Date", row = 3, col = 1)
fig.update_yaxes(title_text="Volume", row = 3, col = 1)
fig.update_layout(title_text="Apple Stock")
fig.update_layout(width=900, height=900)
return fig
chart_can_vol(df).show()

Skipping certain values in Python with Matplotlib

I am currently working on an intra-day stock chart using the Alpha Vantage API. The data frame contains values from 4:00 to 20:00. In my matplotlib.pyplot chart however, the x-Axis also includes values from 20:00 to 4:00 over night. I dont want this as it messes up the aesthetics and also the Volume subplot.
Q: Is there any way to skip x-Axis values which dont exist in the actual Data Frame (the values from 20:00 to 04:00)?
As you can see, the Data Frame clearly jumps from 20:00 to 04:00
However in the Matplotlib chart, the x-Axis contains the values from 20:00 to 4:00, messing with the chart
Code so far. I believe so far everything is right:
import pandas as pd
import matplotlib.pyplot as plt
from alpha_vantage.timeseries import TimeSeries
import time
import datetime as dt
from datetime import timedelta as td
from dateutil.relativedelta import relativedelta
#Accessing and Preparing API
ts = TimeSeries(key=api_key, output_format='pandas')
ticker_input = "TSLA"
interval_input = "15min"
df, meta_data = ts.get_intraday(symbol = ticker_input, interval = interval_input, outputsize = 'full')
slice_date = 16*4*5
df = df[0:slice_date]
df = df.iloc[::-1]
df["100ma"] = df["4. close"].rolling(window = 50, min_periods = 0).mean()
df["Close"] = df["4. close"]
df["Date"] = df.index
#Plotting all as 2 different subplots
ax1 = plt.subplot2grid((7,1), (0,0), rowspan = 5, colspan = 1)
ax1.plot(df["Date"], df['Close'])
ax1.plot(df["Date"], df["100ma"], linewidth = 0.5)
plt.xticks(rotation=45)
ax2 = plt.subplot2grid((6,1), (5,0), rowspan = 2, colspan = 2, sharex = ax1)
ax2.bar(df["Date"], df["5. volume"])
ax2.axes.xaxis.set_visible(False)
plt.tight_layout()
plt.show()
It would be great if anybody could help. Im still a complete beginner and only started Python 2 weeks ago.
We got the data from the same place, although the data acquisition method is different. After extracting it in 15 units, I created a graph by excluding the data after 8pm and before 4pm. I created the code with the understanding that your skip would open up the pause. What you want it to skip is skipped once the NaN is set.
import datetime
import pandas as pd
import numpy as np
import pandas_datareader.data as web
import mplfinance as mpf
# import matplotlib.pyplot as plt
with open('./alpha_vantage_api_key.txt') as f:
api_key = f.read()
now_ = datetime.datetime.today()
start = datetime.datetime(2019, 1, 1)
end = datetime.datetime(now_.year, now_.month, now_.day - 1)
symbol = 'TSLA'
df = web.DataReader(symbol, 'av-intraday', start, end, api_key=api_key)
df.columns = ['Open', 'High', 'Low', 'Close', 'Volume']
df.index = pd.to_datetime(df.index)
df["100ma"] = df["Close"].rolling(window = 50, min_periods = 0).mean()
df["Date"] = df.index
df_15 = df.asfreq('15min')
df_15 = df_15[(df_15.index.hour >= 4)&(df_15.index.hour <= 20) ]
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,4.5),dpi=144)
#Plotting all as 2 different subplots
ax1 = plt.subplot2grid((7,1), (0,0), rowspan = 5, colspan = 1)
ax1.plot(df_15["Date"], df_15['Close'])
ax1.plot(df_15["Date"], df_15["100ma"], linewidth = 0.5)
plt.xticks(rotation=20)
ax2 = plt.subplot2grid((6,1), (5,0), rowspan = 2, colspan = 2, sharex = ax1)
ax2.bar(df_15["Date"], df_15["Volume"])
ax2.axes.xaxis.set_visible(False)
# plt.tight_layout()
plt.show()
I fixed it using matplotlib.ticker.formatter.
I first created a class and using:
class MyFormatter(Formatter):
def __init__(self, dates, fmt='%Y-%m-%d %H:%M'):
self.dates = dates
self.fmt = fmt
def __call__(self, x, pos=0):
'Return the label for time x at position pos'
ind = int(np.round(x))
if ind >= len(self.dates) or ind < 0:
return ''
return self.dates[ind].strftime(self.fmt)
formatter = MyFormatter(df.index)
ax1 = plt.subplot2grid((7,1), (0,0), rowspan = 5, colspan = 1)
ax1.xaxis.set_major_formatter(formatter)
ax1.plot(np.arange(len(df)), df["Close"])
ax1.plot(np.arange(len(df)), df["100ma"], linewidth = 0.5)
ax1.xticks(rotation=45)
ax1.axis([xmin,xmax,ymin,ymax])
ax2 = plt.subplot2grid((6,1), (5,0), rowspan = 2, colspan = 2, sharex = ax1)
ax2.bar(np.arange(len(df)), df["5. volume"])
plt.show()
This gave me a smoother graph than the one before and also that recommended by r-beginner.
The only issue that I have is that if I zoom in the x-axis doesnt really change. it always has teh year, month, date, hour, and minute. Obviously I only want hour and minute when Im zoomed in further. I am yet to figure out how to do that

Unsure how to use colormap with Folium marker plot

I have a dataframe with latitude, longitude, and power percentage. I want to do something very simple but not sure how: apply a colormap to color the data points based on their percentage. So 90% is red and 100% is blue. I have created both a successful map and colormap, but not sure how to proceed next.
import folium
import pandas as pd
import folium.plugins
import branca
import branca.colormap as cm
data = [
[33.823400, -118.12194, 99.23],
[33.823500, -118.12294, 95.23],
[33.823600, -118.12394, 91.23],
[33.823700, -118.12494, 90.00]
]
df = pd.DataFrame(data, columns=['latitude','longitude','power'])
x_start = (df['latitude'].max() + df['latitude'].min()) / 2
y_start = (df['longitude'].max() + df['longitude'].min()) / 2
start_coord = (x_start, y_start)
map = folium.Map(location=start_coord, zoom_start=12)
lat = list(df.latitude)
lon = list(df.longitude)
for loc in zip(lat, lon):
folium.Circle(
location=loc,
radius=10,
#fill=True,
#color='blue',
#fill_opacity=0.7
).add_to(map)
display(map)
colormap = cm.LinearColormap(colors=['red','lightblue'], index=[90,100],vmin=90,vmax=100)
colormap
I'm in a rush, but this is how I've done it in the past. Create the CM and then call it like so colormap(.9)
import folium
import pandas as pd
import folium.plugins
import branca
import branca.colormap as cm
data = [
[33.823400, -118.12194, 99.23],
[33.823500, -118.12294, 95.23],
[33.823600, -118.12394, 91.23],
[33.823700, -118.12494, 90.00]
]
df = pd.DataFrame(data, columns=['latitude','longitude','power'])
x_start = (df['latitude'].max() + df['latitude'].min()) / 2
y_start = (df['longitude'].max() + df['longitude'].min()) / 2
start_coord = (x_start, y_start)
colormap = cm.LinearColormap(colors=['red','lightblue'], index=[90,100],vmin=90,vmax=100)
map = folium.Map(location=start_coord, zoom_start=12)
lat = list(df.latitude)
lon = list(df.longitude)
pow = list(df.power)
for loc, p in zip(zip(lat, lon), pow):
folium.Circle(
location=loc,
radius=10,
fill=True,
color=colormap(p),
#fill_opacity=0.7
).add_to(map)
map.add_child(colormap)
display(map)

Set maximum of datapoints per plot

Im using the following code:
import matplotlib.pyplot as pyplot
import pandas as pandas
from datetime import datetime
dataset = pandas.read_csv("HugLog_17.01.11.csv", sep=",", header=0)
print('filter data for SrcAddr')
dataset_filtered = dataset[dataset['SrcAddr']=='0x1FD3']
print('get Values')
varY = dataset_filtered.Battery_Millivolt.values
varX = dataset_filtered.Timestamp.values
print('Convert the date-strings in date-objects.')
dates_list = [datetime.strptime(date, '%y-%m-%d %H:%M:%S') for date in varX]
fig = pyplot.figure()
ax1 = fig.add_subplot(1,1,1)
ax1.set_xlabel('Time')
ax1.set_ylabel('Millivolt')
ax1.bar(dates_list, varY)
pyplot.locator_params(axis='x',nbins=10)
pyplot.show()
The problem i have is, its a large datacollection with 180k datapoints.
And pyplot displays all points an the graph which makes it slow and the bars overlap. Is there a way to set a maximum-limit on how much datapoints a displayed at a "view".
What i mean by that is, that as soon as the graph is render ther are only 50 datapoints and when i zoomm in i only get a maximum of 50 datapoints again.
Resampling can be done with the resample function from pandas.
Note that the resample syntax has changed between version 0.17 and 0.19 of pandas. The example below uses the old style. See e.g. this tutorial for the new style.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# generate some data for every second over a whole day
times = pd.date_range(start='2017-01-11',periods=86400, freq='1S')
df = pd.DataFrame(index = times)
df['data'] = np.sort(np.random.randint(low=1300, high=1600, size=len(df.index)) )[::-1] + \
np.random.rand(len(df.index))*100
# resample the data, taking the mean over 1 hours ("H")
t = "H" # for hours, try "T" for minutes as well
width=1./24 #matplotlib default uses a width of 1 day per bar
# try width=1./(24*60) for minutes
df_resampled = pd.DataFrame()
df_resampled['data'] = df.data.resample(t, how="mean")
fig, ax = plt.subplots()
#ax.bar(df.index, df['data'], width=1./(24*60*60)) # original data, takes too long to plot
ax.bar(df_resampled.index, df_resampled['data'], width=width)
ax.xaxis_date()
plt.show()
Automatic adaption of the resampling when zooming would indeed require some manual work. There is a resampling example on the matplotlib event handling page, which does not work out of the box but could be adapted accordingly.
This is how it would look like:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import datetime
import matplotlib.dates
class Sampler():
def __init__(self,df):
self.df = df
def resample(self, limits):
print limits
dt = limits[1] - limits[0]
if (type(dt) != pd.tslib.Timedelta) and (type(dt) != datetime.timedelta):
dt = datetime.timedelta(days=dt)
print dt
#see #http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases
if dt > datetime.timedelta(hours=5):
t = "H"; width=1./24
elif dt > datetime.timedelta(minutes=60):
t = "15T"; width=15./(24.*60)
elif dt > datetime.timedelta(minutes=5):
t = "T"; width=1./(24.*60)
elif dt > datetime.timedelta(seconds=60):
t = "15S"; width=15./(24.*60*60)
else:
#dt < datetime.timedelta(seconds=60):
t = "S"; width=1./(24.*60*60)
self.resampled = pd.DataFrame()
self.resampled['data'] = self.df.data.resample(t, how="mean")
print t, len(self.resampled['data'])
print "indextype", type(self.resampled.index[0])
print "limitstype", type(limits[1])
if type(limits[1]) == float or type(limits[1]) == np.float64 :
dlowlimit = matplotlib.dates.num2date(limits[0])
duplimit = matplotlib.dates.num2date(limits[1])
print type(duplimit), duplimit
self.resampled = self.resampled.loc[self.resampled.index <= duplimit]
self.resampled = self.resampled.loc[self.resampled.index >= dlowlimit]
else:
self.resampled = self.resampled.loc[self.resampled.index <= limits[1]]
self.resampled = self.resampled.loc[self.resampled.index >= limits[0]]
return self.resampled.index,self.resampled['data'],width
def update(self, ax):
print "update"
lims = ax.viewLim
start, stop = lims.intervalx
ax.clear()
x,y,width = self.resample([start, stop])
ax.bar(x,y, width=width)
ax.set_xlim([start, stop])
ax.callbacks.connect('xlim_changed', self.update)
ax.figure.canvas.draw()
times = pd.date_range(start='2017-01-11',periods=86400, freq='1S')
df = pd.DataFrame(index = times)
df['data'] = np.sort(np.random.randint(low=1300, high=1600, size=len(df.index)) )[::-1] + \
np.random.rand(len(df.index))*500
sampler = Sampler(df)
x,y,width = sampler.resample( [df.index[0],df.index[-1] ] )
fig, ax = plt.subplots()
ax.bar(x,y, width=width)
ax.xaxis_date()
# connect to limits changes
ax.callbacks.connect('xlim_changed', sampler.update)
plt.show()
One thing you can do is plot a random subset of the data by using the sample method on your pandas DataFrame. Use the frac argument to determine the fraction of points you want to use. It ranges from 0 to 1.
After you get your dataset_filtered DataFrame, take a sample of it like this
dataset_filtered_sample = dataset_filtered.sample(frac=.001)

Plotting a Heat Table Based on bokeh

I am trying to make a heat map like this one from bokeh:
Where all the code is here: http://docs.bokeh.org/en/latest/docs/gallery/unemployment.html
I got pretty close, but for some reason it is only printing the values in a diagonal order.
I tried to format my data the same way and just substitute it, but it got a little more complicated than that. Here is my data:
from collections import OrderedDict
import numpy as np
import pandas as pd
from bokeh.plotting import ColumnDataSource, figure, show, output_file
from bokeh.models import HoverTool
import pandas.util.testing as tm; tm.N = 3
df = pd.read_csv('MYDATA.csv', usecols=[1, 16])
df = df.set_index('recvd_dttm')
df.index = pd.to_datetime(df.index, format='%m/%d/%Y %H:%M')
result = df.groupby([lambda idx: idx.month, 'CompanyName']).agg(len).reset_index()
result.columns = ['Month', 'CompanyName', 'NumberCalls']
pivot_table = result.pivot(index='Month', columns='CompanyName', values='NumberCalls').fillna(0)
s = pivot_table.sum().sort(ascending=False,inplace=False)
pivot_table = pivot_table.ix[:,s.index[:46]]
pivot_table = pivot_table.transpose()
pivot_table.to_csv('pivot_table.csv')
pivot_table = pivot_table.reset_index()
pivot_table['CompanyName'] = [str(x) for x in pivot_table['CompanyName']]
Companies = list(pivot_table['CompanyName'])
months = ["1","2","3","4","5","6","7","8","9","10","11","12"]
pivot_table = pivot_table.set_index('CompanyName')
# this is the colormap from the original plot
colors = [
"#75968f", "#a5bab7", "#c9d9d3", "#e2e2e2", "#dfccce",
"#ddb7b1", "#cc7878", "#933b41", "#550b1d"
]
# Set up the data for plotting. We will need to have values for every
# pair of year/month names. Map the rate to a color.
month = []
company = []
color = []
rate = []
for y in pivot_table.index:
for m in pivot_table.columns:
month.append(m)
company.append(y)
num_calls = pivot_table.loc[y,m]
rate.append(num_calls)
color.append(colors[min(int(num_calls)-2, 8)])
source = ColumnDataSource(
data=dict(months=months, Companies=Companies, color=color, rate=rate)
)
output_file('heatmap.html')
TOOLS = "resize,hover,save,pan,box_zoom,wheel_zoom"
p = figure(title="Customer Calls This Year",
x_range=Companies, y_range=list(reversed(months)),
x_axis_location="above", plot_width=1400, plot_height=900,
toolbar_location="left", tools=TOOLS)
p.rect("Companies", "months", 1, 1, source=source,
color="color", line_color=None)
p.grid.grid_line_color = None
p.axis.axis_line_color = None
p.axis.major_tick_line_color = None
p.axis.major_label_text_font_size = "10pt"
p.axis.major_label_standoff = 0
p.xaxis.major_label_orientation = np.pi/3
hover = p.select(dict(type=HoverTool))
hover.tooltips = OrderedDict([
('Company Name', '#Companies'),
('Number of Calls', '#rate'),
])
show(p) # show the plot
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# just following your previous post to simulate your data
np.random.seed(0)
dates = np.random.choice(pd.date_range('2015-01-01 00:00:00', '2015-06-30 00:00:00', freq='1h'), 10000)
company = np.random.choice(['company' + x for x in '1 2 3 4 5'.split()], 10000)
df = pd.DataFrame(dict(recvd_dttm=dates, CompanyName=company)).set_index('recvd_dttm').sort_index()
df['C'] = 1
df.columns = ['CompanyName', '']
result = df.groupby([lambda idx: idx.month, 'CompanyName']).agg({df.columns[1]: sum}).reset_index()
result.columns = ['Month', 'CompanyName', 'counts']
pivot_table = result.pivot(index='CompanyName', columns='Month', values='counts')
x_labels = ['Month'+str(x) for x in pivot_table.columns.values]
y_labels = pivot_table.index.values
fig, ax = plt.subplots()
x = ax.imshow(pivot_table, cmap=plt.cm.winter)
plt.colorbar(mappable=x, ax=ax)
ax.set_xticks(np.arange(len(x_labels)))
ax.set_yticks(np.arange(len(y_labels)))
ax.set_xticklabels(x_labels)
ax.set_yticklabels(y_labels)
ax.set_xlabel('Month')
ax.set_ylabel('Company')
ax.set_title('Customer Calls This Year')
The answer was in this line:
source = ColumnDataSource(
data=dict(months=months, Companies=Companies, color=color, rate=rate)
)
It should have been:
source = ColumnDataSource(
data=dict(month=months, company=company, color=color, rate=rate)
)

Categories