I have a bunch of pictures I'd like to plot with hvplot.rgb.
The user should be able to switch between the pictures with a widget like a slider.
My problem is, that i don't know how to use the hvplot.rgb()-function for interactive dataframes.
here's what i have so far:
import numpy as np
import pandas as pd
import hvplot
import hvplot.pandas
import hvplot.xarray
import panel as pn
######################## generating a Dataframe with random rgb-arrays
height = 2
length = 2
count = 3 # number of pictures in dataframe
def rgb(height,lenght):
image = np.random.randint(255, size=(height,length,3))
x = np.arange(length)
y = np.arange(height)
channel = np.arange(len(image[0][0]))
im_xr = xr.DataArray(image,coords={'y': y,'x': x,'channel': channel},dims=["y", "x", "channel"])
return im_xr # returning an xarray in order to use hv.plot.rgb()
list_rgb = []
for i in range(count):
list_rgb.append(rgb(height,length))
df = pd.DataFrame([list_rgb]).T
df.rename(columns = {0:'rgb'}, inplace = True)
dfi = df.interactive()
slider = pn.widgets.IntSlider(name='slider', start=0, end=count-1, step=1)
pipe = dfi[(dfi.index == slider.param.value)]
so at this point i can already plot the generated pictures in the DataFrame with
df.rgb[0].hvplot.rgb(x='x',
y='y',
bands='channel',
data_aspect=1,
flip_yaxis=True,
xaxis=False,
yaxis=None,
widget_type='scrubber',
widget_location='bottom'
)
How can I call the .hvplot.rgb() function to plot the "pipe"-interactive dataframe?
so here's how to do it:
import numpy as np
import pandas as pd
import hvplot
import hvplot.pandas
import hvplot.xarray
import panel as pn
######################## generating a Dataframe with random rgb-arrays
height = 2 # height of the generated picture
width = 2 # width of the generated picture
count = 3 # number of pictures in dataframe
def rgb(height,width):
image = np.random.randint(255, size=(height,width,3))
return image
list_rgb = []
for i in range(count):
list_rgb.append(rgb(height,width))
x = np.arange(width)
y = np.arange(height)
channel = np.arange(3)
time = np.arange(count)
xar = xr.DataArray(list_rgb,
dims=("time", "y", "x", "channel")
,
coords={"x": x,
"y": y,
"channel": channel,
"time" : time},
name="rgb")
xar.hvplot.rgb(x='x',
y='y',
bands='channel',
data_aspect=1,
flip_yaxis=True,
xaxis=False,
yaxis=None
)
additionally the scrubber tool is a pretty nice thing to create movies out of pictures with hvplot, like for example timelaps-shows...
xar.hvplot.rgb(x='x',
y='y',
bands='channel',
data_aspect=1,
flip_yaxis=True,
xaxis=False,
yaxis=None,
widget_type='scrubber',
widget_location='bottom'
)
Related
I'm struggling to get a Bokeh map. The cell runs but does not show anything. It takes about 50s. I can get a blank map to display, but nothing I have tried has worked.
Jupyter version 6.4.12 run through Anaconda 2.3.2
import pandas as pd
import numpy as np
from bokeh.plotting import figure, show, output_notebook
from bokeh.tile_providers import CARTODBPOSITRON, get_provider
from bokeh.models import ColumnDataSource, LinearColorMapper, ColorBar, NumeralTickFormatter
from bokeh.palettes import PRGn, RdYlGn
from bokeh.transform import linear_cmap, factor_cmap
from bokeh.layouts import row, column
from bokeh.resources import INLINE
pd.set_option('display.max_columns', None)
output_notebook(INLINE)
I have Lat & Lon coordinates in my dataset, which I discovered I need to convert to mercator coordinates.
# Define function to switch from lat/long to mercator coordinates
def x_coord(x, y):
lat = x
lon = y
r_major = 6378137.000
x = r_major * np.radians(lon)
scale = x/lon
y = 180.0/np.pi * np.log(np.tan(np.pi/4.0 + lat * (np.pi/180.0)/2.0)) * scale
return (x, y)
# Define coord as tuple (lat,long)
df['coordinates'] = list(zip(df['LATITUDE'], df['LONGITUDE']))
# Obtain list of mercator coordinates
mercators = [x_coord(x, y) for x, y in df['coordinates'] ]
# Create mercator column in our df
df['mercator'] = mercators
# Split that column out into two separate columns - mercator_x and mercator_y
df[['mercator_x', 'mercator_y']] = df['mercator'].apply(pd.Series)
From there, this is my code cell for the plot:
tile = get_provider('CARTODBPOSITRON')
source = ColumnDataSource(data = df)
palette = PRGn[11]
color_mapper = linear_cmap(field_name = 'FIRE_SIZE', palette = palette,
low=df['FIRE_SIZE'].min(), high = df['FIRE_SIZE'].max())
tooltips = [('Fire Year', '#FIRE_YEAR'),('State','#STATE')]
p = figure(title = 'Fire Locations',
x_axis_type = 'mercator',
y_axis_type = 'mercator',
x_axis_label = 'Longitude',
y_axis_label = 'Latitude',
tooltips = tooltips)
p.add_tile(tile)
p.circle(x = 'mercator_x',
y = 'mercator_y',
color = color_mapper,
size = 10,
fill_alpha = 0.7,
source = source)
color_bar = ColorBar(color_mapper = color_mapper['transform'],
formatter = NumeralTickFormatter(format='0.0[0000]'),
`your text` label_standoff = 13, width = 8, location = (0,0))
p.add_layout(color_bar, 'right')
show(p)
The cell runs, but nothing shows. There are no errors. I confirmed that I can get a plot to display using this code:
#Test
tile = get_provider('CARTODBPOSITRON')
p = figure(x_range = (-2000000, 2000000),
y_range = (1000000, 7000000),
x_axis_type = 'mercator',
y_axis_type = 'mercator')
p.add_tile(tile)
show(p)
This is a large dataset, with 2,303,566 entries. I have checked that I have no null values in any of the columns that I am using, as well as verifying the correct data types (lat/lon are float64).
Returning to answer my own question here. After doing some more testing based on helpful comments I received from #mosc9575 and #bigreddot, I determined that the size of my dataset is the reason for Bokeh failing to display the map. I used a single point first, and then a small slice of my dataframe - and the map displayed just fine.
I hope this is helpful to someone else at some point!
Thanks to everyone who assisted.
I am attempting to produce an animated histogram that uses rows of data from a data frame I created. The code I am using to produce the histogram is below. The code works with data = np.random.randn(1000) but does not animate the histogram when I replace it with data = df['GDP'] instead it outputs a non-animated histogram. I am trying to fit a column from a data frame into this code:
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.path as path
import matplotlib.animation as animation
fig, ax = plt.subplots()
# histogram our data with numpy
data = np.random.randn(1000)
n, bins = np.histogram(data, 100)
# get the corners of the rectangles for the histogram
left = np.array(bins[:-1])
right = np.array(bins[1:])
bottom = np.zeros(len(left))
top = bottom + n
nrects = len(left)
# here comes the tricky part -- we have to set up the vertex and path
# codes arrays using moveto, lineto and closepoly
# for each rect: 1 for the MOVETO, 3 for the LINETO, 1 for the
# CLOSEPOLY; the vert for the closepoly is ignored but we still need
# it to keep the codes aligned with the vertices
nverts = nrects*(1+3+1)
verts = np.zeros((nverts, 2))
codes = np.ones(nverts, int) * path.Path.LINETO
codes[0::5] = path.Path.MOVETO
codes[4::5] = path.Path.CLOSEPOLY
verts[0::5,0] = left
verts[0::5,1] = bottom
verts[1::5,0] = left
verts[1::5,1] = top
verts[2::5,0] = right
verts[2::5,1] = top
verts[3::5,0] = right
verts[3::5,1] = bottom
barpath = path.Path(verts, codes)
patch = patches.PathPatch(barpath, facecolor='green', edgecolor='yellow', alpha=0.5)
ax.add_patch(patch)
ax.set_xlim(left[0], right[-1])
ax.set_ylim(bottom.min(), top.max())
def animate(i):
# simulate new data coming in
data = np.random.randn(1000)
n, bins = np.histogram(data, 100)
top = bottom + n
verts[1::5,1] = top
verts[2::5,1] = top
ani = animation.FuncAnimation(fig, animate, 100, repeat=False)
from IPython.display import HTML
HTML(ani.to_jshtml())
To fit my own data I am replacing :
# histogram our data with numpy
data = np.random.randn(1000)
and:
# simulate new data coming in
data = np.random.randn(1000)
with a column in my data frame that has 247 rows:
data = df['GDP']
The output is a histogram with my own data however it is not animated as is with data = np.random.randn(1000)
See example below. What I would like to happen is that the barchart (on right) always shows all xlabels (A to Z), regardless of whether they exist in the selection (in left chart). I find a lot of information online about formatting labels, but not about setting them. Can I manually set the xlabels somehow, or use the not-filtered data as a base for the xlabels?
import altair as alt
import pandas as pd
import random
import string
random.seed(42)
consultation_dates = pd.date_range(start='1/1/2018', end='1/08/2020')
disease_codes = random.choices(string.ascii_uppercase, k=len(consultation_dates))
consultation_weights = [random.randint(1, 4) for i in range(len(consultation_dates))]
df = pd.DataFrame({'date': consultation_dates,
'disease_code': disease_codes,
'consultation_weight': consultation_weights})
selected_range = alt.selection_interval(encodings=['x'])
use_over_time = alt.Chart(df).mark_area().encode(
x='yearquarter(date):Q',
y='sum(consultation_weight):Q'
).add_selection(
selected_range
)
use_by_disease = alt.Chart(df).mark_bar().encode(
x='disease_code:N',
y='sum(consultation_weight):Q'
).transform_filter(
selected_range
)
use_over_time | use_by_disease
Found my own answer. It can be done with layering!
import altair as alt
import pandas as pd
import random
import string
random.seed(42)
consultation_dates = pd.date_range(start='1/1/2018', end='1/08/2020')
disease_codes = random.choices(string.ascii_uppercase, k=len(consultation_dates))
consultation_weights = [random.randint(1, 4) for i in range(len(consultation_dates))]
df = pd.DataFrame({'date': consultation_dates,
'disease_code': disease_codes,
'consultation_weight': consultation_weights})
selected_range = alt.selection_interval(encodings=['x'])
use_over_time = alt.Chart(df).mark_area().encode(
x='yearquarter(date):Q',
y='sum(consultation_weight):Q'
).add_selection(
selected_range
)
use_by_disease_base = alt.Chart(df).mark_bar(opacity=0.3).encode(
x='disease_code:N',
y='sum(consultation_weight):Q',
color=alt.value('lightgray')
)
use_by_disease = alt.Chart(df).mark_bar(opacity=0.3).encode(
x='disease_code:N',
y='sum(consultation_weight):Q'
).transform_filter(
selected_range
)
use_over_time | (use_by_disease_base + use_by_disease)
I hava data that looks like:
Name X Y
A HIGH MID
B LOW LOW
C MID LOW
D HIGH MID
How to plot this data in a 2-D diagram with a 3x3 grid adding a random variation to place each data point including its name with enough spacing between each other.
So it should look somewhat like that:
The following i tried, but i dont know how to plot the values not exactly on the grid, but in between, so they do nbot overlap.
import pandas as pd
import matplotlib.pyplot as plt
### Mock Data ###
data = """A0,LOW,LOW
A,MID,MID
B,LOW,MID
C,MID,HIGH
D,LOW,MID
E,HIGH,HIGH"""
df = pd.DataFrame([x.split(',') for x in data.split('\n')])
df.columns = ['name','X','Y']
### Plotting ###
fig,axs = plt.subplots()
axs.scatter(df.X,df.Y,label=df.name)
axs.set_xlabel('X')
axs.set_ylabel('Y')
for i,p in enumerate(df.name):
axs.annotate(p, (df.X[i],df.Y[i]))
axs.grid()
axs.set_axisbelow(True)
fig.tight_layout()
plt.show()
resulting:
You can control directly the positions and change the labels on the axis. There are a few problems with your drawing because you are not taking into account some issue such as "what label will you have if you have more than one point at the same location?".
In any case here is a possible solution:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
### Mock Data ###
data = """A0,LOW,LOW
A,MID,MID
B,LOW,MID
C,MID,HIGH
D,LOW,MID
E,HIGH,HIGH"""
df = pd.DataFrame([x.split(',') for x in data.split('\n')])
df.columns = ['name','X','Y']
pos = [0, 1, 2]
lbls = ["LOW", "MID", "HIGH"]
trans = {lbls[i]:pos[i] for i in range(len(pos))}
mat = np.zeros((3, 3), dtype="U10") # This is limited to 10 characters
xxs = []
yys = []
offset = 0.05
for i in range(df.shape[0]):
xc, yc = trans[df.X[i]], trans[df.Y[i]]
if mat[xc, yc]=="":
mat[xc, yc] = df.name[i]
else:
mat[xc, yc] = mat[xc, yc] + ";" + df.name[i]
xxs.append(xc)
yys.append(yc)
fig,axs = plt.subplots()
axs.scatter(xxs, yys)
for i in range(df.shape[0]):
name = mat[xxs[i], yys[i]]
axs.text(xxs[i]+offset, yys[i]+offset, name)
axs.set_xticks(pos)
axs.set_xticklabels(lbls)
axs.set_yticks(pos)
axs.set_yticklabels(lbls)
for i in pos:
axs.axhline(pos[i]-0.5, color="black")
axs.axvline(pos[i]-0.5, color="black")
axs.set_xlim(-0.5, 2.5)
axs.set_ylim(-0.5, 2.5)
plt.show()
This result in the following image:
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)