I am trying to reproduce this example (Mr. @Jhsmit 's version) using a pandas.DataFrame
where I can not assume that each consecutive widget filters down the DataFrame
. Suppose you have a DataFrame
that has lat
, lon
, value_1
and value_2
as well as these columns:
Unique values for column ‘station_id’:
[‘NOAA15’ ‘METOP-3’ ‘METOP-1’ ‘NOAA19’ ‘NOAA18’]
Unique values for column ‘channel’:
[28 29 30 31 32 33 34 35 36 37 38 39 40 41 42]
Unique values for column ‘orbit_number’:
[30434 30433 23820 55663 73903 93066 93065 23821 73904 55664 30436 30435
23822 55665 73905 30437 93068]
Unique values for column ‘scan_line_number’:
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
25 26 27 28 29 30]
Example can be created using
import pandas as pd
import numpy as np
# Define the unique values for each column
channels = np.arange(28, 43)
station_ids = ['NOAA15', 'METOP-3', 'METOP-1', 'NOAA19', 'NOAA18']
orbit_numbers = np.array([30434, 30433, 23820, 55663, 73903, 93066, 93065, 23821, 73904, 55664,
30436, 30435, 23822, 55665, 73905, 30437, 93068])
scan_line_numbers = np.arange(1, 31)
# Generate all combinations of the unique values
channel_combinations = np.repeat(channels, len(station_ids) * len(orbit_numbers) * len(scan_line_numbers))
station_id_combinations = np.tile(np.repeat(station_ids, len(orbit_numbers) * len(scan_line_numbers)), len(channels))
orbit_number_combinations = np.tile(np.repeat(orbit_numbers, len(scan_line_numbers)), len(channels) * len(station_ids))
scan_line_number_combinations = np.tile(scan_line_numbers, len(channels) * len(station_ids) * len(orbit_numbers))
# Generate random float values for value_1 and value_2
value_1 = np.random.rand(len(channel_combinations))
value_2 = np.random.rand(len(channel_combinations))
# Create the example dataframe
data = {
'channel': channel_combinations,
'station_id': station_id_combinations,
'orbit_number': orbit_number_combinations,
'scan_line_number': scan_line_number_combinations,
'value_1': value_1,
'value_2': value_2
}
df = pd.DataFrame(data)
And i have written the following class in an attempt to filter it down but it does not work correctly :
class ObservationVisualizer(param.Parameterized):
def __init__(self, **params):
self.plot_button = pnw.Button(name="Plot")
self.ON_Values = []
self.CHANNEL_Values = []
self.hvplot_object = None
self.SLN_Values = []
self.df = merged_df.copy()
self.Specie_Select = pnw.Select(
name="Specie Select",
options=[
"brightness_temperature",
"altitude",
"satellite_zenith_angle",
"solar_zenith_angle",
"solar_azimuth_angle",
"satellite_azimuth_angle",],
value="brightness_temperature",
)
self.SID_Values = sorted(list(self.df.station_id.unique()))
self.SID_Select = pnw.CheckButtonGroup(
name="Station ID Select",
options=self.SID_Values,
value=[self.SID_Values[0]],
)
self.subset = self.df.loc[self.df.station_id == self.SID_Values[0]]
self.cmap = pnw.Select(
name="Select Color Map", options=CMAP_OPTIONS, value="jet"
)
self.alpha = pnw.FloatSlider(
name="Opacity",
start=0.0,
end=1.0,
step=0.1,
value=0.8,
value_throttled=True,
)
self.SID_Values = sorted(list(self.df.station_id.unique()))
self.SID_Select = pnw.CheckButtonGroup(
name="Station ID Select",
options=self.SID_Values,
value=[self.SID_Values[0]],
)
self.SLN_Values = sorted(list(self.subset.scan_line_number.unique()))
self.SLN_Slider = pn.widgets.IntRangeSlider(
name="Scan Line Numbers Range Slider",
start=self.SLN_Values[0].item(),
end=self.SLN_Values[-1].item(),
step=1,
)
self.CHANNEL_Values = sorted(list(self.subset.orbit_number.unique()))
self.CHANNEL_SELECT = pn.widgets.IntRangeSlider(
name="Channel Range Slider",
start=self.CHANNEL_Values[0].item(),
end=self.CHANNEL_Values[-1].item(),
step=1,
)
self.ON_Values = sorted(list(self.subset.orbit_number.unique()))
self.ON_Select = pnw.MultiChoice(
name="Orbit Number Select", options=self.ON_Values, value=self.ON_Values
)
super().__init__(**params)
@param.depends("SID_Select.value", watch=True)
def on_station_id_update(self):
self.subset = self.df.loc[self.df["station_id"].isin(self.SID_Select.value)]
self.SLN_Values = self.subset.scan_line_number.unique()
self.SLN_Slider.start = int(self.SLN_Values.min())
self.SLN_Slider.end = int(self.SLN_Values.max())
self.SLN_Slider.value = (int(self.SLN_Values.min()), int(self.SLN_Values.max()))
self.CHANNEL_Values = self.subset.channel.unique()
self.CHANNEL_SELECT.start = int(self.CHANNEL_Values.min())
self.CHANNEL_SELECT.end = int(self.CHANNEL_Values.max())
self.CHANNEL_SELECT.value = (
int(self.CHANNEL_Values.min()),
int(self.CHANNEL_Values.max()),
)
self.ON_Values = sorted(list(self.subset.orbit_number.unique()))
self.ON_Select.options = self.ON_Values
self.ON_Select.value = self.ON_Values
@param.depends("ON_Select.value", watch=True)
def on_orbit_number_update(self):
self.subset = self.df.loc[self.df["orbit_number"].isin(self.ON_Select.value)]
self.SLN_Values = self.subset.scan_line_number.unique()
self.SLN_Slider.start = int(self.SLN_Values.min())
self.SLN_Slider.end = int(self.SLN_Values.max())
self.SLN_Slider.value = (int(self.SLN_Values.min()), int(self.SLN_Values.max()))
self.CHANNEL_Values = self.subset.channel.unique()
self.CHANNEL_SELECT.start = int(self.CHANNEL_Values.min())
self.CHANNEL_SELECT.end = int(self.CHANNEL_Values.max())
self.CHANNEL_SELECT.value = (
int(self.CHANNEL_Values.min()),
int(self.CHANNEL_Values.max()),
)
@param.depends(
"SID_Select.value",
"SLN_Slider.value",
"CHANNEL_SELECT.value",
"ON_Select.value",
"Specie_Select.value",
watch=True,
)
def update_subset(self):
try:
print(["lon", "lat", self.Specie_Select.value])
self.subset = self.df.loc[
self.df["station_id"].isin(self.SID_Select.value)
& self.df["orbit_number"].isin(self.ON_Select.value)
& self.df["scan_line_number"].isin(
list(
range(
int(self.SLN_Slider.value[0]),
int(self.SLN_Slider.value[1]) + 1,
)
)
)
& self.df["channel"].isin(
list(
range(
int(self.CHANNEL_SELECT.value[0]),
int(self.CHANNEL_SELECT.value[1]) + 1,
)
)
),
["lon", "lat", self.Specie_Select.value],
]
print(self.subset.shape)
except:
print('oups')
pn.state.notifications.send(
"Subset invalid!!!",
background="red",
icon='<i class="fas fa-burn"></i>',
duration=4000,
)
pn.state.notifications.success("update_subset")
@param.depends("plot_button.value", watch=True)
def plot_button_on_click(self):
self.hvplot_object = self.subset.hvplot.points(
x="lon",
y="lat",
rasterize=True,
dynspread=True,
height=800,
coastline=True,
cmap="jet",
title=f"{self.subset.shape[0]:,d} observations!!!",
)
return self.hvplot_object
app = ObservationVisualizer()
sidebar_components = Column(
app.SID_Select,
app.ON_Select,
app.SLN_Slider,
app.CHANNEL_SELECT,
app.Specie_Select,
app.plot_button,
loading_indicator=True,
).servable(target="main")
main_components = Column(
app.plot_button_on_click,
loading_indicator=True,
).servable(target="main")
Row(sidebar_components, main_components)
I just want to understand how to use Param to filter the DataFrame
and use the new unique values in each widget’s respective columns to populate them so that I can filter it down even further. I have seen the Continents and Countries example but I am not sure how to implement it without hardcoding some stuff. Thank you for your help everyone.