Hi Jack,
I’m using a custom environment and I ran conda update --all just in case to see if it would do anything for the situation; unfortunately it had no effect. My package list is as follows:
# packages in environment at /home/83472bac-e5ce-4c84-8b24-88894596b663/.conda/envs/pandapipes-RHN:
#
# Name Version Build Channel
_libgcc_mutex 0.1 main
_openmp_mutex 5.1 1_gnu
asttokens 2.0.5 pyhd3eb1b0_0
attrs 24.1.0 pypi_0 pypi
backcall 0.2.0 pyhd3eb1b0_0
beautifulsoup4 4.12.3 pypi_0 pypi
blas 1.0 mkl
bleach 6.1.0 pypi_0 pypi
bokeh 3.4.3 pypi_0 pypi
bottleneck 1.3.7 py39ha9d4c09_0
brotli 1.0.9 h5eee18b_8
brotli-bin 1.0.9 h5eee18b_8
brotli-python 1.0.9 py39h6a678d5_8
ca-certificates 2024.7.2 h06a4308_0
certifi 2024.8.30 py39h06a4308_0
charset-normalizer 3.3.2 pyhd3eb1b0_0
colorcet 3.1.0 py39h06a4308_0
comm 0.2.1 py39h06a4308_0
contourpy 1.2.1 pypi_0 pypi
cycler 0.12.1 pypi_0 pypi
debugpy 1.6.7 py39h6a678d5_0
decorator 5.1.1 pyhd3eb1b0_0
deepdiff 7.0.1 pypi_0 pypi
defusedxml 0.7.1 pypi_0 pypi
et-xmlfile 1.1.0 pypi_0 pypi
exceptiongroup 1.2.0 py39h06a4308_0
executing 0.8.3 pyhd3eb1b0_0
fastjsonschema 2.20.0 pypi_0 pypi
fonttools 4.53.1 pypi_0 pypi
freetype 2.12.1 h4a9f257_0
holoviews 1.19.1 py39h06a4308_0
hvplot 0.10.0 py39h06a4308_0
idna 3.7 py39h06a4308_0
importlib-metadata 7.0.1 py39h06a4308_0
importlib_metadata 7.0.1 hd3eb1b0_0
importlib_resources 6.4.0 py39h06a4308_0
intel-openmp 2023.1.0 hdb19cb5_46306
ipykernel 6.28.0 py39h06a4308_0
ipython 8.15.0 py39h06a4308_0
ipywidgets 8.1.3 pypi_0 pypi
jedi 0.19.1 py39h06a4308_0
jinja2 3.1.4 py39h06a4308_0
jpeg 9e h5eee18b_3
jsonschema 4.23.0 pypi_0 pypi
jsonschema-specifications 2023.12.1 pypi_0 pypi
jupyter-bokeh 4.0.5 pypi_0 pypi
jupyter_client 8.6.0 py39h06a4308_0
jupyter_core 5.7.2 py39h06a4308_0
jupyterlab-pygments 0.3.0 pypi_0 pypi
jupyterlab-widgets 3.0.11 pypi_0 pypi
kiwisolver 1.4.5 pypi_0 pypi
lcms2 2.12 h3be6417_0
ld_impl_linux-64 2.40 h12ee557_0
lerc 3.0 h295c915_0
libbrotlicommon 1.0.9 h5eee18b_8
libbrotlidec 1.0.9 h5eee18b_8
libbrotlienc 1.0.9 h5eee18b_8
libdeflate 1.17 h5eee18b_1
libffi 3.4.4 h6a678d5_1
libgcc-ng 11.2.0 h1234567_1
libgomp 11.2.0 h1234567_1
libpng 1.6.39 h5eee18b_0
libsodium 1.0.18 h7b6447c_0
libstdcxx-ng 11.2.0 h1234567_1
libtiff 4.5.1 h6a678d5_0
libwebp-base 1.3.2 h5eee18b_0
linkify-it-py 2.0.3 pypi_0 pypi
lz4-c 1.9.4 h6a678d5_1
markdown 3.6 pypi_0 pypi
markdown-it-py 3.0.0 pypi_0 pypi
markupsafe 2.1.5 pypi_0 pypi
matplotlib 3.9.0 pypi_0 pypi
matplotlib-base 3.9.2 py39hbfdbfaf_0
matplotlib-inline 0.1.6 py39h06a4308_0
mdit-py-plugins 0.4.1 pypi_0 pypi
mdurl 0.1.2 pypi_0 pypi
mistune 3.0.2 pypi_0 pypi
mkl 2023.1.0 h213fc3f_46344
mkl-service 2.4.0 py39h5eee18b_1
mkl_fft 1.3.10 py39h5eee18b_0
mkl_random 1.2.7 py39h1128e8f_0
nbclient 0.10.0 pypi_0 pypi
nbconvert 7.16.4 pypi_0 pypi
nbformat 5.10.4 pypi_0 pypi
ncurses 6.4 h6a678d5_0
nest-asyncio 1.6.0 py39h06a4308_0
networkx 3.2.1 pypi_0 pypi
numexpr 2.8.7 py39h85018f9_0
numpy 1.26.4 py39h5f9d8c6_0
numpy-base 1.26.4 py39hb5e798b_0
openjpeg 2.5.2 he7f1fd0_0
openpyxl 3.1.5 pypi_0 pypi
openssl 3.0.15 h5eee18b_0
ordered-set 4.1.0 pypi_0 pypi
packaging 24.1 py39h06a4308_0
pandapipes 0.10.0 pypi_0 pypi
pandapower 2.14.9 pypi_0 pypi
pandas 2.2.2 py39h6a678d5_0
pandocfilters 1.5.1 pypi_0 pypi
panel 1.4.5 pypi_0 pypi
param 2.1.1 py39h06a4308_0
parso 0.8.3 pyhd3eb1b0_0
pexpect 4.8.0 pyhd3eb1b0_3
pickleshare 0.7.5 pyhd3eb1b0_1003
pillow 10.4.0 py39h5eee18b_0
pip 24.2 py39h06a4308_0
platformdirs 3.10.0 py39h06a4308_0
prompt-toolkit 3.0.43 py39h06a4308_0
psutil 5.9.0 py39h5eee18b_0
ptyprocess 0.7.0 pyhd3eb1b0_2
pure_eval 0.2.2 pyhd3eb1b0_0
pygments 2.15.1 py39h06a4308_1
pyparsing 3.1.2 py39h06a4308_0
pysocks 1.7.1 py39h06a4308_0
python 3.9.19 h955ad1f_1
python-dateutil 2.9.0post0 py39h06a4308_2
python-tzdata 2023.3 pyhd3eb1b0_0
pytz 2024.1 py39h06a4308_0
pyviz-comms 3.0.3 pypi_0 pypi
pyviz_comms 3.0.2 py39h06a4308_0
pyyaml 6.0.1 py39h5eee18b_0
pyzmq 25.1.2 py39h6a678d5_0
readline 8.2 h5eee18b_0
referencing 0.35.1 pypi_0 pypi
requests 2.32.3 py39h06a4308_0
rpds-py 0.19.1 pypi_0 pypi
scipy 1.13.1 pypi_0 pypi
setuptools 75.1.0 py39h06a4308_0
shapely 2.0.5 pypi_0 pypi
six 1.16.0 pyhd3eb1b0_1
soupsieve 2.5 pypi_0 pypi
sqlite 3.45.3 h5eee18b_0
stack_data 0.2.0 pyhd3eb1b0_0
tbb 2021.8.0 hdb19cb5_0
tinycss2 1.3.0 pypi_0 pypi
tk 8.6.14 h39e8969_0
tornado 6.4.1 py39h5eee18b_0
tqdm 4.66.5 py39h2f386ee_0
traitlets 5.14.3 py39h06a4308_0
typing_extensions 4.11.0 py39h06a4308_0
tzdata 2024.1 pypi_0 pypi
uc-micro-py 1.0.3 pypi_0 pypi
unicodedata2 15.1.0 py39h5eee18b_0
urllib3 2.2.2 py39h06a4308_0
wcwidth 0.2.5 pyhd3eb1b0_0
webencodings 0.5.1 pypi_0 pypi
wheel 0.44.0 py39h06a4308_0
widgetsnbextension 4.0.11 pypi_0 pypi
xyzservices 2024.6.0 pypi_0 pypi
xz 5.4.6 h5eee18b_1
yaml 0.2.5 h7b6447c_0
zeromq 4.3.5 h6a678d5_0
zipp 3.17.0 py39h06a4308_0
zlib 1.2.13 h5eee18b_1
zstd 1.5.5 hc292b87_2
The dataset(s) I’m working to visualize are Pandapipes fluid network simulation results. I’ll post a couple of screenshots after the code (as a new user I can only attach a single image per post).
Here’s the code that handles HoloViews and Panel elements (some of this will be depreciated as I proceed with implementing interactivity features, and the tap stream feature with the topmost, dynamic line graph is not in working condition yet):
## Grouped, interactive HoloViz visualisation
# Create tabbed interactive visualisations for different node and data types.
# TODO: tabs
# Explode data for heatmap so each massflow state corresponds to own row
cons_massflow_expanded = heat_consumer_data.explode('Massflow')
# Create an x-axis range for each value in Massflow
cons_massflow_expanded['x'] = list(range(96)) * len(heat_consumer_data['Name'])
# Reset index after explosion to clean up the DataFrame
cons_massflow_expanded = cons_massflow_expanded.reset_index(drop=True)
# Define a function to filter the DataFrame and create the heatmap
def create_heatmap(selected_types):
# Filter the DataFrame based on the selected Type values
filtered_df = cons_massflow_expanded[cons_massflow_expanded['Type'].isin(selected_types)]
# Create a HoloViews dataset
dataset = hv.Dataset(filtered_df, kdims=['x', 'Name'], vdims=['Massflow', 'Type'])
# Calculate number of unique Name for Heatmap vertical height
num_names = filtered_df['Name'].nunique()
# Calculate the minimum and maximum of the filtered Massflow data
min_massflow = filtered_df['Massflow'].min()
max_massflow = filtered_df['Massflow'].max()
# Add 5% padding to the min and max values
range_padding = (max_massflow - min_massflow) * 0.05
min_massflow -= range_padding
max_massflow += range_padding
# Customize the hover tool to show the Type
hover_tool = [
('Time Step', '@x'),
('Consumer', '@Name'),
('mdot kg / s', '@Massflow'),
('Type', '@Type') # Add Type information to the hover tooltip
]
# Declare HeatMap and specify options
massflow_hm = hv.HeatMap(dataset, label='Consumer Mass Flow').opts(
title="Consumer Mass Flow Profiles",
width=960, height=10*num_names,
xlabel="Time Step", ylabel="Heat Consumer",
xlim=(-0.5,n_ts),
ylim=(0,len(heat_consumer_data)),
clim=(min_massflow, max_massflow),
shared_axes=False,
cmap="magma",
toolbar='above',
tools=['hover']+['crosshair'],
hover_tooltips=hover_tool,
backend_opts={"plot.toolbar.active_scroll": None,"plot.toolbar.active_drag":None}
)
return massflow_hm
# Declare CrossSelector for consumer type filtering
type_selector = pn.widgets.CrossSelector(name='Filter by Consumer Type',
value=heat_consumer_data['Type'].unique().tolist(),
options=heat_consumer_data['Type'].unique().tolist()
)
# Create the dynamic heatmap using pn.depends on type_selector
@pn.depends(type_selector.param.value)
def dynamic_heatmap(selected_types):
return create_heatmap(selected_types)
# Create a Tap stream for the heatmap to capture clicks
heatmap_tapsource = hv.DynamicMap(dynamic_heatmap)
tap_stream = hv.streams.Tap(source=heatmap_tapsource, x=None, y=None)
# Function to create a line plot when a Name is tapped on the heatmap
def create_line_plot(x, y):
# If no name is selected, return default consumer
if y is None:
return hv.Curve(consumer_massflow[1,:]).opts(width=400, height=400, tools=['hover'])
# Get the selected Name
selected_name = y
# Filter the DataFrame based on the selected Type values
filtered_df = heat_consumer_data[heat_consumer_data['Name'] == selected_name]
# Create a HoloViews dataset
dataset_curve = hv.Dataset(cons_massflow_expanded, kdims=['Name'], vdims=['Massflow'])
# If the name is not found, return default consumer
if filtered_df.empty:
return hv.Curve([]).opts(width=400, height=400, xlabel='Time step', ylabel='Massflow')
# Get the massflow values and plot them against the time steps (0-96)
massflow_values = filtered_df['Massflow']
# Create a line plot
line_plot = hv.Curve(dataset_curve.select(Name=y), kdims=['x'], vdims=['Massflow']).opts(
width=400, height=400, tools=['hover'], xlabel='Time step', ylabel='Massflow'
)
print(filtered_df['Massflow'].values[0])
return line_plot
# Create a DynamicMap for the line plot that updates when the Tap stream changes
dynamic_line_plot = hv.DynamicMap(create_line_plot, streams=[tap_stream])
# Declare line graphs
massflow_curve = hv.Curve(consumer_massflow[1,:])
t_from_curve = hv.Curve(consumer_t_from[1,:])
t_to_curve = hv.Curve(consumer_t_to[1,:])
# Specify line graph options
massflow_curve.opts(title=str(net.heat_consumer.name[1])+" Mass Flow Profile",
xlabel="Time Step", ylabel="mdotkg / s",
xlim=(-0.5,n_ts),
ylim=(np.min(consumer_massflow[1,:])
- np.mean(consumer_massflow[1,:])/20,
np.max(consumer_massflow[1,:])
+ np.mean(consumer_massflow[1,:])/20
),
shared_axes=False,
tools=['hover'],
framewise=True,
backend_opts={"plot.toolbar.active_scroll": None,"plot.toolbar.active_drag":None}
)
t_from_curve.opts(title=str(net.heat_consumer.name[1])+" Flow Side Temp",
xlabel="Time Step", ylabel="K",
xlim=(-0.5,n_ts),
ylim=(np.min(consumer_t_from[1,:])
- np.mean(consumer_t_from[1,:])/20,
np.max(consumer_t_from[1,:])
+ np.mean(consumer_t_from[1,:])/20
),
shared_axes=False,
tools=['hover'],
framewise=True,
backend_opts={"plot.toolbar.active_scroll": None,"plot.toolbar.active_drag":None}
)
t_to_curve.opts(title=str(net.heat_consumer.name[1])+" Return Side Temp",
xlabel="Time Step", ylabel="K",
xlim=(-0.5,n_ts),ylim=(np.min(consumer_t_to[1,:])-np.mean(consumer_t_to[1,:])/20,np.max(consumer_t_to[1,:])+np.mean(consumer_t_to[1,:])/20),
shared_axes=False,
tools=['hover'],
framewise=True,
backend_opts={"plot.toolbar.active_scroll": None,"plot.toolbar.active_drag":None}
)
# Define layout
right_column = pn.Column(dynamic_line_plot,t_from_curve,t_to_curve)
layout = pn.Column(pn.Row(pn.pane.Str(width=130, height=220),type_selector,max_height=300),
pn.Row(pn.panel(dynamic_heatmap),right_column))
layout.servable()
Here’s the head of the data in use for now (the lists have 96 values each, so essentially this is a time series simulation with 96 time steps):