{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"source": [
"# Plotting traces (collections)\n",
"This Tutorial explains how to customize network plots in pandapower using plotly. Each pandapower network element can be translated into a plotly trace with all corresponding properties."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-26T11:20:31.842639Z",
"start_time": "2025-11-26T11:20:26.800990Z"
}
},
"outputs": [],
"source": [
"from pandapower.create import (\n",
" create_empty_network,\n",
" create_bus,\n",
" create_line,\n",
" create_transformer3w,\n",
" create_transformer,\n",
" create_ext_grid,\n",
" create_load,\n",
")\n",
"import pandapower.networks as nw\n",
"import pandapower.plotting.plotly as pplotly\n",
"from pandas import Series\n",
"import numpy as np\n",
"\n",
"# Prevent Dark Background on plots\n",
"import plotly.io as pio\n",
"pio.templates.default = \"plotly_white\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We created a network along with geodata in the buses."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-26T11:20:32.045746Z",
"start_time": "2025-11-26T11:20:31.851890Z"
}
},
"outputs": [],
"source": [
"net = create_empty_network()\n",
"x = np.array([0, -2, 2, -3, -2, 2])\n",
"y = np.array([4, 3, 3, -3, -2, -2])\n",
"for i in range(6):\n",
" if i < 3:\n",
" v = 110\n",
" elif i == 3:\n",
" v = 20\n",
" else:\n",
" v = 10\n",
" create_bus(net, vn_kv=v, geodata=(x[i], y[i]))\n",
"\n",
"create_line(net, 0, 1, 5, \"149-AL1/24-ST1A 110.0\", geodata=None, name=\"l1\")\n",
"create_line(net, 0, 2, 5, \"149-AL1/24-ST1A 110.0\", geodata=None, name=\"l2\")\n",
"create_transformer3w(net, 1, 3, 4, \"63/25/38 MVA 110/20/10 kV\", name=\"tr1\")\n",
"\n",
"create_transformer(net, 2, 5, \"0.25 MVA 20/0.4 kV\", name=\"tr2\")\n",
"\n",
"create_ext_grid(net, 0)\n",
"create_load(net, 4, p_mw=20.0, q_mvar=10.0, name=\"load1\")\n",
"create_load(net, 5, p_mw=20.0, q_mvar=10.0, name=\"load1\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you want to have full control over the layout of your plot, you can individually create and plot collections with the pandapower plotting module."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-26T11:20:33.645802Z",
"start_time": "2025-11-26T11:20:32.055753Z"
},
"scrolled": true
},
"outputs": [],
"source": [
"lc = pplotly.create_line_trace(net, net.line.index, color=\"black\", infofunc=net.line.name)\n",
"bc = pplotly.create_bus_trace(net, net.bus.index, size=10, color=\"orange\", infofunc=net.bus.vn_kv)\n",
"tc3 = pplotly.create_trafo_trace(\n",
" net,\n",
" net.trafo3w.index,\n",
" trafotype=\"3W\",\n",
" color=\"green\",\n",
" infofunc=net.trafo3w.name,\n",
" trace_name=\"trafo3ws\",\n",
" cmin=None,\n",
" cmax=None,\n",
" cmap_vals=None,\n",
" use_line_geo=None,\n",
")\n",
"tc = pplotly.create_trafo_trace(\n",
" net,\n",
" net.trafo.index,\n",
" trafotype=\"2W\",\n",
" color=\"blue\",\n",
" infofunc=net.trafo.name,\n",
" trace_name=\"trafos\",\n",
" cmin=None,\n",
" cmax=None,\n",
" cmap_vals=None,\n",
" use_line_geo=None,\n",
")\n",
"_ = pplotly.draw_traces(bc + lc + tc + tc3, figsize=1, aspectratio=(8, 4));"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-26T11:20:37.543880Z",
"start_time": "2025-11-26T11:20:33.966953Z"
}
},
"outputs": [],
"source": [
"net = nw.mv_oberrhein()\n",
"\n",
"lc = pplotly.create_line_trace(net, net.line.index, color=\"black\")\n",
"bc = pplotly.create_bus_trace(\n",
" net,\n",
" net.bus.index,\n",
" size=10,\n",
" color=\"orange\",\n",
" infofunc=Series(index=net.bus.index, data=net.bus.name + \"
\" + net.bus.vn_kv.astype(str) + \" kV\"),\n",
")\n",
"pplotly.draw_traces(bc + lc, figsize=1, aspectratio=(8, 6));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Order of plotting traces is as ordered in the `draw_traces` function. So, in order to plot buses in front of lines first lines and then buses need to be set in the input traces list:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-26T11:20:38.035214Z",
"start_time": "2025-11-26T11:20:37.788856Z"
}
},
"outputs": [],
"source": [
"pplotly.draw_traces(lc + bc, figsize=1, aspectratio=(8, 6));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Highlighting\n",
"Specific lines or buses can be highlighted by creating extra line collections in different colors.\n",
"\n",
"In this example, we plot lines that are longer than 2 km green and buses with a voltage below 0.98 p.u. red.\n",
"\n",
"First, we create a line collection for all lines in grey and a line collection for only the long lines in green:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-26T11:20:39.804905Z",
"start_time": "2025-11-26T11:20:38.478010Z"
}
},
"outputs": [],
"source": [
"long_lines = net.line[net.line.length_km > 2.0].index\n",
"lc = pplotly.create_line_trace(net, net.line.index, color=\"grey\")\n",
"lcl = pplotly.create_line_trace(\n",
" net,\n",
" long_lines,\n",
" color=\"green\",\n",
" width=2,\n",
" infofunc=Series(\n",
" index=net.line.index,\n",
" data=net.line.name[long_lines] + \"
\" + net.line.length_km[long_lines].astype(str) + \" km\",\n",
" ),\n",
")\n",
"\n",
"low_voltage_buses = net.res_bus[net.res_bus.vm_pu < 0.98].index\n",
"bc = pplotly.create_bus_trace(net, net.bus.index, size=10, color=\"blue\")\n",
"bch = pplotly.create_bus_trace(net, low_voltage_buses, size=10, color=\"red\")\n",
"\n",
"pplotly.draw_traces(bc + bch + lc + lcl);"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true,
"jupyter": {
"outputs_hidden": true
}
},
"source": [
"## Highlighting with the Topology Package\n",
"Colors palette for plotly can be obtained using function `plotting.plotly.get_plotly_color_palette(n)` where argument `n` defines number of colors that will be returned in a list."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-26T11:20:41.377690Z",
"start_time": "2025-11-26T11:20:40.081365Z"
}
},
"outputs": [],
"source": [
"from pandapower.plotting.plotly import get_plotly_color_palette\n",
"from pandapower.topology import create_nxgraph, connected_components\n",
"\n",
"net = nw.mv_oberrhein()\n",
"\n",
"mg = create_nxgraph(net, nogobuses=set(net.trafo.lv_bus.values) | set(net.trafo.hv_bus.values))\n",
"collections = []\n",
"ai = 0\n",
"islands = list(connected_components(mg)) # getting connected components of a graph\n",
"colors = get_plotly_color_palette(len(islands)) # getting a color for each connected component\n",
"for color, area in zip(colors, islands):\n",
" collections += pplotly.create_bus_trace(net, area, size=5, color=color, trace_name=\"feeder {0}\".format(ai))\n",
" ai += 1\n",
"collections += pplotly.create_line_trace(net, net.line.index, color=\"grey\")\n",
"collections += pplotly.create_bus_trace(net, net.ext_grid.bus.values, patch_type=\"square\", size=10, color=\"yellow\")\n",
"pplotly.draw_traces(collections);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Collection on mapbox\n",
"Plots on Mapbox maps are available only considering you have a Mapbox account and a Mapbox Access Token. After getting a mabox token it can be written set to pandapower as the following (where `''` needs to be replaced with provided mapbox token).\n",
"If network geo-data are not in lat/long form, there is a function `geo_data_to_latlong` which can be used to transform entire network geodata from a specific projection to lat/long:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-26T11:20:43.441138Z",
"start_time": "2025-11-26T11:20:41.656355Z"
}
},
"outputs": [],
"source": [
"net = nw.mv_oberrhein()\n",
"# convert_crs(net, epsg_out=31467) #transforming geodata to Gauß Krüger Zone 3\n",
"\n",
"lc = pplotly.create_line_trace(net, net.line.index, color=\"purple\")\n",
"bc = pplotly.create_bus_trace(\n",
" net,\n",
" net.bus.index,\n",
" size=10,\n",
" color=\"orange\",\n",
" infofunc=Series(index=net.bus.index, data=net.bus.name + \"
\" + net.bus.vn_kv.astype(str) + \" kV\"),\n",
")\n",
"_ = pplotly.draw_traces(lc + bc, on_map=True, map_style=\"dark\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Colormaps\n",
"Colormaps plots are available for plotly by setting `cmap=True` in create functions. By default, line loading and bus voltage magnitudes will be used for coloring."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-26T11:20:44.769454Z",
"start_time": "2025-11-26T11:20:43.669112Z"
}
},
"outputs": [],
"source": [
"net = nw.mv_oberrhein()\n",
"bt = pplotly.create_bus_trace(net, cmap=True)\n",
"lt = pplotly.create_line_trace(net, cmap=True)\n",
"pplotly.draw_traces(lt + bt, showlegend=False);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Some customization is possible in regard to: limits, choosing a colormap, colorbar title, etc."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-26T11:20:45.368409Z",
"start_time": "2025-11-26T11:20:45.093158Z"
}
},
"outputs": [],
"source": [
"lt = pplotly.create_line_trace(net, cmap=\"Blues\", cmin=0, cmax=100, cbar_title=\"Line loading [%]\")\n",
"pplotly.draw_traces(lt, showlegend=False);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Alternatively, any other values can be used for colormaps, with also some customization in regard to limits, choosing a colormap, colorbar title, etc. An example with coloring according to line length:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-26T11:20:45.989452Z",
"start_time": "2025-11-26T11:20:45.578423Z"
}
},
"outputs": [],
"source": [
"lt = pplotly.create_line_trace(net, cmap_vals=net.line.length_km, cmap=True, cbar_title=\"Line length [km]\")\n",
"pplotly.draw_traces(lt, showlegend=False);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Customized hover-info\n",
"Information that pop-ups when a bus or a line is pointed with mouse-cursor is called hover-info. By default, it is set to bus/line name, but it can be customized by setting a list of strings to `infofunc` argument. A newline is defined using `
`. See a representative example hereafter (try getting hoverinfo by pointing a cursor above each bus):"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-26T11:20:46.381044Z",
"start_time": "2025-11-26T11:20:46.196803Z"
}
},
"outputs": [],
"source": [
"bc = pplotly.create_bus_trace(\n",
" net,\n",
" [1, 2, 3, 4, 5],\n",
" size=15,\n",
" infofunc=Series(\n",
" index=net.bus.index,\n",
" data=net.bus.name + \"
\" + net.bus.vn_kv.astype(str) + \" kV\" + \"
\" + \"this is one of the 5 buses...\",\n",
" ),\n",
")\n",
"pplotly.draw_traces(bc);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Weighted Marker Traces\n",
"A trace with markers of value-dependent size can be created by the `create_weighted_marker_trace` function. Like other traces, this trace can be passed to `draw_traces` directly, or it can be passed as \"additional trace\" to `simple_plotly`. By default, the latter will also add a reference marker to indicate the value for one size."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-26T11:20:47.224355Z",
"start_time": "2025-11-26T11:20:46.694026Z"
}
},
"outputs": [],
"source": [
"net.sgen.scaling = 1\n",
"marker_trace = pplotly.create_weighted_marker_trace(\n",
" net, \"sgen\", column_to_plot=\"p_mw\", marker_scaling=200, color=\"green\", patch_type=\"circle-open\"\n",
")\n",
"_ = pplotly.simple_plotly(net, bus_size=0.25, additional_traces=[marker_trace])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2025-11-26T11:20:47.930266Z",
"start_time": "2025-11-26T11:20:47.405120Z"
}
},
"outputs": [],
"source": [
"marker_trace = pplotly.create_weighted_marker_trace(\n",
" net, \"sgen\", column_to_plot=\"p_mw\", marker_scaling=400, color=\"green\", patch_type=\"circle-open\", show_scale_legend=True, scale_legend_unit='MW', scale_marker_size=[0., 1, 2, 5]\n",
")\n",
"pplotly.simple_plotly(net, bus_size=0.25, additional_traces=[marker_trace]);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"More tutorials about interactive plots using plotly:\n",
"* [built-in interactive plots](./plotly_built-in.ipynb)\n",
"* [interactive plots on maps](./plotly_maps.ipynb)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}