plots#

Visualizations for energy time-series data and psychrometric charts.

See the Examples section for interactive usage examples.

Tip

Reading the comfort plots: The h,x diagram in five minutes (reading the diagram, comfort, saving energy) for plot_mollier_hx / plot_mollier_hx_fast, and Reading the comfort compass (reading the compass, comfort, saving energy) for plot_comfort_compass. The comfort-zone and warning-band ideas apply to the other comfort plots too.

Daily Profiles#

Daily profile analysis plots.

pyedautils.plots.profiles.plot_daily_profiles_overview(data: DataFrame, title: str = 'Daily Profiles Overview by Weekday and Season', ylab: str = 'Value', confidence: float = 95.0, colors: Dict[str, str] | None = None, seasons: List[str] | None = None, weekdays: List[str] | None = None) → Figure[source]#

Create a 4x7 subplot grid showing daily profiles by season and weekday.

Each subplot shows the median line with a confidence band based on quantiles. Rows represent seasons (Spring, Summer, Fall, Winter) and columns represent weekdays (Monday through Sunday).

Parameters:
  • data – DataFrame with two columns: timestamp and value.

  • title – Plot title.

  • ylab – Y-axis label (shown on first column).

  • confidence – Confidence level for quantile bands (0-100). Default 95.

  • colors – Optional color overrides. Keys: “median”, “bounds”, “fill”.

  • seasons – Custom season names in row order. Default: Spring, Summer, Fall, Winter.

  • weekdays – Custom weekday names in column order. Default: Monday through Sunday.

Returns:

Plotly figure with the subplot grid.

Return type:

go.Figure

pyedautils.plots.profiles.plot_daily_profiles(data: DataFrame, method: str = 'mean', loc_time_zone: str = 'UTC', title: str | None = None, ylab: str | None = None, k: int = 672, digits: int = 1)[source]#

Create a daily profile plot with one line per weekday.

Supports three methods:

  • "mean": Simple mean per weekday and time-of-day.

  • "decomposed": Detrend with a rolling mean, extract the seasonal component, and normalise each weekday to start at 0 at midnight.

  • "overlayed": All individual daily profiles overlaid in blue. Hovering highlights the entire day-line in orange (requires JS).

Parameters:
  • data – DataFrame with two columns: timestamp and value.

  • method – Aggregation method — "mean", "decomposed", or "overlayed".

  • loc_time_zone – Timezone for localization (e.g. “Europe/Zurich”). Default “UTC”.

  • title – Plot title. Auto-set per method if None.

  • ylab – Y-axis label. Auto-set per method if None.

  • k – Rolling window size for trend (only used with "decomposed"). Default 672 (7 days at 15-min resolution).

  • digits – Number of decimal places for rounding. Default 1.

Returns:

go.Figure for "mean" / "decomposed", or str (self-contained HTML) for "overlayed".

Heatmaps#

Heatmap visualization plots.

pyedautils.plots.heatmaps.plot_heatmap_median_weeks(data: DataFrame, title: str = 'Heatmap Median per Hour by Weekday and Season', ylab: str = 'Energy Consumption (kWh/h)', seasons: List[str] | None = None, colorscale: str = 'Magma') → Figure[source]#

Create a heatmap of median values per hour, grouped by weekday and season.

Each season is shown as a separate subplot column. The y-axis shows weekdays (Monday at top, Sunday at bottom), the x-axis shows the hour of day (0-23), and the color intensity represents the median value.

Parameters:
  • data – DataFrame with two columns: timestamp and value.

  • title – Plot title.

  • ylab – Colorbar title / value label.

  • seasons – Custom season names in column order. Default: Spring, Summer, Fall, Winter.

  • colorscale – Plotly colorscale name. Default “Magma” (≈ viridis option B).

Returns:

Plotly figure with one heatmap subplot per season.

Return type:

go.Figure

pyedautils.plots.heatmaps.plot_heatmap_calendar(data: DataFrame, title: str = 'Calendar Heatmap', ylab: str = 'Energy Consumption (kWh/d)', colorscale: str = 'Magma') → Figure[source]#

Create a calendar heatmap of daily aggregated values.

Each cell represents one day, coloured by the aggregated daily value. Rows are weekdays (Mon–Sun), columns are calendar weeks, and each year is shown as a separate subplot row.

Parameters:
  • data – DataFrame with two columns: timestamp and value.

  • title – Plot title.

  • ylab – Colorbar title / value label.

  • colorscale – Plotly colorscale name. Default “Magma” (≈ viridis option B).

Returns:

Plotly figure with one subplot row per year.

Return type:

go.Figure

Energy Signature#

Energy signature plots.

pyedautils.plots.energy.plot_energy_signature(data: DataFrame, title: str = 'Building Energy Signature', xlab: str = 'Outside Temperature [°C]', ylab: str = 'Energy Consumption [kWh/d]', colors: Dict[str, str] | None = None) → Figure[source]#

Create a scatter plot of daily energy consumption vs outside temperature.

Each point represents one day, colored by season. The input DataFrame is aggregated to daily resolution (mean temperature, sum of value).

Parameters:
  • data – DataFrame with columns [timestamp, temperature, value].

  • title – Plot title.

  • xlab – X-axis label.

  • ylab – Y-axis label.

  • colors – Season color overrides keyed by season name. Default uses DEFAULT_SEASON_COLORS.

Returns:

Plotly figure with the scatter plot.

Return type:

go.Figure

pyedautils.plots.energy.plot_energy_signature_pes(data: DataFrame, p_ihg: float = 0.0, title: str = 'Proposed Energy Signature', xlab: str = 'Outside Temperature [°C]', ylab: str = 'Power [kW]', colors: Dict[str, str] | None = None) → Figure[source]#

Create a Proposed Energy Signature plot with regression lines.

Computes the PES parameters (balance temperature, heat loss coefficient, standby and hot-water power) and overlays the characteristic lines on a daily scatter plot colored by season.

Parameters:
  • data – DataFrame with columns [timestamp, outside_temp, power, room_temp].

  • p_ihg – Internal heat gains [kW]. Default 0.

  • title – Plot title.

  • xlab – X-axis label.

  • ylab – Y-axis label.

  • colors – Season color overrides keyed by season name.

Returns:

Plotly figure with scatter and regression lines.

Return type:

go.Figure

Seasonal Patterns#

Seasonal and temporal pattern plots.

pyedautils.plots.seasonal.plot_seasonal_overlapping(data: DataFrame, title: str = 'Seasonal Plot — Overlapping', ylab: str = 'Value') → Figure[source]#

Seasonal plot with one line per year overlaid on the same x-axis (month).

Parameters:
  • data – DataFrame with columns [timestamp, value]. Monthly data; value should be normalised (e.g. kWh/day).

  • title – Plot title.

  • ylab – Y-axis label.

Returns:

go.Figure

pyedautils.plots.seasonal.plot_seasonal_miniplots(data: DataFrame, title: str = 'Seasonal Miniplots', ylab: str = 'Value') → Figure[source]#

Seasonal subseries plot — one mini panel per month showing values across years.

A horizontal blue line shows the per-month mean.

Parameters:
  • data – DataFrame with columns [timestamp, value].

  • title – Plot title.

  • ylab – Y-axis label.

Returns:

go.Figure

pyedautils.plots.seasonal.plot_seasonal_before_after(data: DataFrame, date_optimization: str, title: str = 'Seasonal — Before/After Optimization', ylab: str = 'Value', confidence: float = 95.0) → Figure[source]#

Seasonal plot highlighting before/after an optimization date.

Years before the optimization are shown in grey with a confidence band; years after are shown in colour.

Parameters:
  • data – DataFrame with columns [timestamp, value].

  • date_optimization – Date string (e.g. "2017-09-01"). Years starting after this date are “after”.

  • title – Plot title.

  • ylab – Y-axis label.

  • confidence – Confidence level for the “before” band (0-100).

Returns:

go.Figure

pyedautils.plots.seasonal.plot_seasonal_polar(data: DataFrame, title: str = 'Seasonal Plot — Polar', ylab: str = 'Value') → Figure[source]#

Seasonal plot in polar coordinates — one line per year.

Parameters:
  • data – DataFrame with columns [timestamp, value].

  • title – Plot title.

  • ylab – Radial axis label.

Returns:

go.Figure

pyedautils.plots.seasonal.plot_density_seasons(data: DataFrame, title: str = 'Density Plot by Season', xlab: str = 'Value', ylab: str = 'Density', colors: Dict[str, str] | None = None) → Figure[source]#

Create a kernel density plot of a value column, one curve per season.

Parameters:
  • data – DataFrame with two columns: timestamp and value.

  • title – Plot title.

  • xlab – X-axis label.

  • ylab – Y-axis label.

  • colors – Season color overrides keyed by season name. Default uses DEFAULT_SEASON_COLORS.

Returns:

Plotly figure with one density trace per season.

Return type:

go.Figure

Thermal Comfort#

Comfort compass (pyedautils.plots.comfort.plot_comfort_compass()): the dashed circle is the area of 100 % of the days; the green disc (share in range) and the eight arms (deviation directions, mild / moderate / severe from the inside out) always add up to exactly that area.

  • The green disc says what it means: share plus the in-range word (names["ok"]). When the disc is too small only the share is printed, or nothing; without the legend a caption below the glyph gives it instead (● 8% in range).

  • Each direction label sits at the end of its own arm, anchored outward.

  • scale="fixed" (default, same as fixed_scale=True): the dashed 100 % circle has the same size in every chart, so rooms can be compared by area. scale="fit": zoomed to this glyph and its labels; compare such charts by their percentages only. scale takes precedence over fixed_scale.

  • Every visible text can be passed: title, names, direction_labels, stage_names, count_label / count_label_singular, stats_text. German presets: _COMPASS_NAMES_DE, _COMPASS_LABELS_DE, _COMPASS_STAGE_NAMES_DE in pyedautils.plots.comfort.

Thermal comfort and psychrometric chart plots.

pyedautils.plots.comfort.plot_comfort_sia180(data_outdoor: DataFrame, data_room: DataFrame, title: str = 'Thermal Comfort according to SIA 180:2014', colors: Dict[str, str] | None = None) → Figure[source]#

SIA 180:2014 thermal comfort plot.

Scatter of room temperature vs 48-hour rolling mean outdoor temperature, colored by season, with SIA 180 comfort boundaries.

Parameters:
  • data_outdoor – DataFrame [timestamp, value] with outdoor temp.

  • data_room – DataFrame [timestamp, value] with room temp.

  • title – Plot title.

  • colors – Season color overrides.

Returns:

go.Figure

See also

Why temperature and humidity belong together, and how to save energy without losing comfort: https://retomarek.github.io/pyedautils/guides/mollier_hx.html#hx-comfort

pyedautils.plots.comfort.plot_comfort_temp_humidity(data: DataFrame, title: str = 'Temperature vs Humidity Comfort Plot', colors: Dict[str, str] | None = None) → Figure[source]#

Scatter of daily mean temperature vs humidity with comfort zones.

Shows two comfort zone polygons: “comfortable” (green) and “still comfortable” (orange), based on common building standards.

Parameters:
  • data – DataFrame with timestamp, temperature and humidity columns (read by name, so the order does not matter). Humidity in %rH, temperature in °C.

  • title – Plot title.

  • colors – Season color overrides.

Returns:

go.Figure

See also

The same data as an h,x diagram (plot_mollier_hx()), and how to read comfort zone, mould risk and energy saving from it: https://retomarek.github.io/pyedautils/guides/mollier_hx.html#hx-reading-the-diagram

pyedautils.plots.comfort.MOLLIER_DEFAULT_CAPTIONS: Dict[str, str] = {'density': 'Density ρ [kg/m³]', 'enthalpy': 'Enthalpy [kJ/kg]', 'rel_humidity': 'rel. humidity φ [%]', 'temperature': 'Temperature ϑ [°C]', 'x_axis': 'x [g/kg]', 'x_axis_title': 'absolute water content x [g/kg]'}#

Captions of the h,x-diagram grid (English defaults) for the captions argument of plot_mollier_hx() / plot_mollier_hx_fast(). Copy and translate as needed; "" hides a caption. x_axis_title defaults to the x_axis_title argument.

pyedautils.plots.comfort.MOLLIER_DEFAULT_TEXTS: Dict[str, str] = {'altitude': '{alt} m a.s.l.', 'comfort_zone': 'Comfort zone', 'freq_days': '{n} days/year outside', 'freq_hours': '{n} h/year outside', 'latest': 'Latest', 'now': 'Now', 'outdoor': 'Outside air', 'pressure': '{hpa} hPa', 'room': 'Room', 'season_fall': 'Fall', 'season_spring': 'Spring', 'season_summer': 'Summer', 'season_winter': 'Winter', 'state_value': '{t} °C · {rh} %', 'tip_h': 'h', 'tip_phi': 'φ', 'tip_t': 'T', 'tip_td': 'Td', 'tip_x': 'x', 'ventilation_dries': 'ventilating dries', 'ventilation_humidifies': 'ventilating adds moisture', 'ventilation_neutral': 'ventilating: no change'}#

All other visible texts of plot_mollier_hx() / plot_mollier_hx_fast() (English defaults) for their texts argument. Placeholders in braces are filled in: {t} °C, {rh} %, {x} g/kg, {n} count, {alt} m, {hpa} hPa. "" hides the altitude / pressure line and the ventilation note.

pyedautils.plots.comfort.plot_mollier_hx(data: DataFrame | None = None, pressure: float | None = None, altitude: float | None = None, domain_x: Tuple[float, float] = (0.0, 0.02), domain_y: Tuple[float, float] = (-20.0, 50.0), comfort_zone: Dict[str, Tuple[float, float]] | None = None, height: int = 700, convention: str = 'classical', highlight_latest: bool = True, highlight_color: str | None = 'black', states: Sequence | None = None, labels: List[str] | None = None, comfort_label: str = 'Comfort zone', comfort_zone_orange: Dict[str, object] | None = None, comfort_zone_red: Dict[str, object] | None = None, show_temperature: bool = True, show_density: bool = True, show_rel_humidity: bool = True, show_enthalpy: bool = True, show_abs_humidity: bool = True, x_axis_title: str = 'absolute water content x [g/kg]', season_labels: Dict[str, str] | None = None, show_frequency: bool = False, frequency_unit: str = 'hours', frequency_smoothing: float = 4.0, outdoor: Tuple[float, float] | None = None, current: Tuple[float, float] | None = None, outdoor_color: str = '#2563eb', ventilation_line: bool = True, ventilation_note: bool = True, ventilation_threshold: float = 0.0003, captions: Dict[str, str] | None = None, texts: Dict[str, str] | None = None) → str[source]#

Create a Mollier h,x-diagram (psychrometric chart) as self-contained HTML.

Uses D3.js (v7) for fast SVG rendering with iso-lines for temperature, enthalpy, relative humidity and density, a comfort zone, and optional measured data points colour-coded by season with interactive hover tooltips. Optionally shows the outside air with the ventilation (mixing) line from the room point and a note on whether airing dries the room.

Data points, comfort zone, warning bands and frequency lines are clipped to the axes. The room point and the outside point are drawn on top; a point outside the axes becomes an arrow on the edge pointing towards it, labelled with its value (e.g. 6.8 °C · 89 %).

Every visible text can be replaced through captions (axis captions) and texts (everything else); the defaults are English. See MOLLIER_DEFAULT_CAPTIONS and MOLLIER_DEFAULT_TEXTS for the keys.

Parameters:
  • data – Optional DataFrame with columns [timestamp, humidity, temperature]. humidity in %, temperature in °C.

  • pressure – Air pressure in Pa used for all psychrometric relations and shown in the top-right corner in hPa. When None (default), it is derived from altitude via the ISA barometric formula (pyedautils.psychrometrics.pressure_from_altitude()), or 101325 Pa (sea level) without an altitude. An explicit pressure always wins; altitude is then only the label.

  • altitude – Optional altitude in metres above sea level. Sets the pressure when pressure is not given (altitude=450 gives 960 hPa) and is shown together with the pressure in the top-right corner. When None, the shown altitude is derived from pressure via the inverse ISA barometric formula.

  • domain_x – Range of absolute humidity [kg/kg] for the x-axis.

  • domain_y – Range of the y-coordinate (≈ temperature at x=0) for the y-axis.

  • comfort_zone – Dict with keys “temperature”, “rel_humidity”, “abs_humidity”, each a (min, max) tuple. Defaults: T=[20, 26], phi=[0.30, 0.65], x=[0, 0.0115]. "abs_humidity": None (or a None upper bound, e.g. (0, None)) means no abs-humidity limit — the zone is bounded by T and φ only, like hum_abs_band=None of pyedautils.comfort.comfort_compass_distribution(); a missing key keeps the default 0.0115 kg/kg cap. Pass False to disable the comfort zone.

  • height – Diagram height in pixels. Default 700.

  • convention – Mollier coordinate convention. 'classical' (default) normalises enthalpy per kg of dry air (Recknagel/Sprenger style, isotherms tilt slightly up with x). 'glueck' normalises per kg of moist air (per the Glück book, isotherms tilt slightly down).

  • highlight_latest – When True (default), the row with the most recent timestamp is overlaid as a circle on top of the seasonal scatter points (the room point, also the start of the ventilation line). Set to False to disable. Ignored when current is given.

  • highlight_color – CSS colour of the room point (latest row or current). Default 'black'. Pass None to use the latest row’s season colour (so the highlight is visible only by size/order, not by colour; black for current).

  • states – Optional sequence of MoistAirState objects (from pyedautils.psychrometrics.state()). When supplied, each state is drawn as a numbered point and consecutive states are connected by arrows — a psychrometric process chain in the spirit of psychrosim.com. Works alongside data (historical scatter is drawn underneath).

  • labels – Optional sequence of strings used as point labels (default is the number "1", "2", …). Length must match states.

  • comfort_label – Legend caption for the comfort-zone polygon. Default "Comfort zone". Override e.g. to "Komfortzone" for German output or to a custom designation like "DIN EN 16798 Cat II". Same as texts["comfort_zone"] (which wins if both are given).

  • comfort_zone_orange –

    Optional warning band drawn as a dashed orange outline (no fill). Off by default (None). A dict with keys "temperature", "rel_humidity", "abs_humidity", each either

    • a (min, max) tuple — absolute bounds in °C, 0–1 and kg/kg, e.g. {"temperature": (19, 27), "rel_humidity": (0.25, 0.70)}; or

    • a scalar — a ± offset around the comfort_zone bounds in K, rel.-humidity fraction and kg/kg, e.g. {"temperature": 1, "rel_humidity": 0.05, "abs_humidity": 0.001} for ±1 K / ±5 %-points / ±1 g/kg.

    Both forms may be mixed per key. A dict without any offset keeps the absolute defaults T=[20, 26], φ=[0.30, 0.65] for missing keys and leaves the abs. humidity unclipped (the band follows its T/φ bounds). A dict with at least one offset fills missing keys with the default offsets of the band (orange: 1 K / 0.05 / 0.001 kg/kg, red: 2.5 K / 0.10 / 0.0025 kg/kg). True uses all default offsets. "abs_humidity": None means no upper limit (as does an offset band around a comfort zone without abs-humidity limit). Optional "label" (legend caption) and "color" (CSS colour). The default offsets are the severity-stage limits of pyedautils.comfort.comfort_compass_distribution(); use pyedautils.comfort.comfort_warning_bands() to derive both bands from the same stage limits as the comfort compass. Offsets need a comfort zone (ValueError with comfort_zone=False).

  • comfort_zone_red – Like comfort_zone_orange but drawn as a dashed red outline — e.g. the ±2.5 K / ±10 % band. Keys given as offsets are widened where needed to enclose the orange band, so the stages never overlap.

  • show_temperature – Draw the iso-temperature lines. Default True. The temperature y-axis itself is always shown (it is the chart’s coordinate base) — this only toggles the blue iso-lines.

  • show_density – Draw the iso-density curves together with their value labels and the "Density ρ [kg/m³]" caption. Default True. Set False to hide everything density-related.

  • show_rel_humidity – Draw the relative-humidity (φ) curves and their value labels. Default True. The 100 % saturation curve (the chart boundary) is always drawn regardless of this flag.

  • show_enthalpy – Draw the iso-enthalpy lines together with their value labels and the "Enthalpy [kJ/kg]" caption. Default True. Set False to hide everything enthalpy-related.

  • show_abs_humidity – Draw the vertical iso-lines of constant absolute humidity (at the bottom-axis tick values). Default True.

  • x_axis_title – Caption centred below the bottom axis. Default "absolute water content x [g/kg]". Pass "" to omit it. Same as captions["x_axis_title"] (which wins if both are given).

  • season_labels – Optional mapping from the English season keys "Winter", "Spring", "Summer", "Fall" to the labels shown in the seasonal scatter tooltips and legend. Defaults to the English keys themselves. Pass pyedautils.plots._constants._SEASON_LABELS_DE (or any custom dict) for German output. Same as texts["season_winter"] etc. (which win if both are given).

  • show_frequency – Draw cumulative-frequency contour lines (“risk hours/days”) over the scatter. Each line labelled N encloses all but N units of the year, so the outer lines mark rare conditions and the inner lines the typical core. Requires data. Default False.

  • frequency_unit – Unit counted by the frequency lines — "hours" (default; line N = N hours/year outside) or "days" (line N = N distinct calendar days/year with at least one hour outside).

  • frequency_smoothing – Line-rounding amount for the frequency lines, ~0.6 (angular) to ~9 (very round). Default 4.0. Purely geometric — it rounds the curves but never enlarges the enclosed region.

  • outdoor – Optional outside air as (temperature [°C], relative humidity [%]) — the same units as the data columns, e.g. (4.0, 85). Drawn as a hollow circle (an edge arrow when outside the axes). With a room point (current or the latest row) a dashed ventilation line joins the two: airing moves the room air along it. A note above the axes says whether airing dries the room (outside air holds more than ventilation_threshold less water), adds moisture (more), or makes no noticeable change — see pyedautils.comfort.ventilation_effect().

  • current – Optional room point as (temperature [°C], relative humidity [%]), e.g. a live reading. Replaces the latest-row highlight as the room point; works without data.

  • outdoor_color – CSS colour of the outside point, the ventilation line and the note. Default '#2563eb' (blue).

  • ventilation_line – Draw the ventilation line (and the note) between the room point and outdoor. Default True.

  • ventilation_note – Show the ventilation note above the axes. Default True; needs the ventilation line.

  • ventilation_threshold – Difference in absolute humidity [kg/kg] up to which airing counts as “no change”. Default 0.0003 (0.3 g/kg).

  • captions – Optional dict overriding the axis captions, merged over MOLLIER_DEFAULT_CAPTIONS (keys temperature, rel_humidity, density, enthalpy, x_axis, x_axis_title). "" hides a caption. The right-edge captions shrink (8–12 px) to fit the chart height. Unknown keys raise ValueError.

  • texts –

    Optional dict overriding any other visible text, merged over MOLLIER_DEFAULT_TEXTS: outside air and ventilation note, edge-arrow value label, tooltips, frequency-line tooltip, altitude / pressure line, legend (comfort zone, seasons, room point). "" hides the altitude / pressure line or the note. Unknown keys raise ValueError. Example (German):

    texts={"outdoor": "Aussenluft", "room": "Raum",
           "ventilation_dries": "Lüften trocknet",
           "ventilation_humidifies": "Lüften befeuchtet",
           "ventilation_neutral": "Lüften: keine Änderung",
           "altitude": "{alt} m ü. M.", "latest": "Letzter Wert"}
    

Returns:

Self-contained HTML string with inline D3.js rendering.

Can be saved to a file, used with streamlit.components.v1.html(), or displayed in a Jupyter notebook via IPython.display.HTML().

Return type:

str

See also

How to read the h,x diagram correctly, what the comfort zone and warning bands mean, and how to save energy: https://retomarek.github.io/pyedautils/guides/mollier_hx.html

pyedautils.plots.comfort.plot_comfort_donuts(data: DataFrame, temp_range: Tuple[float, float] = (20.0, 26.0), hum_range: Tuple[float, float] = (30.0, 65.0), title: str | None = None, temp_colors: Tuple[str, str, str] = ('#3498DB', '#2ECC71', '#E74C3C'), hum_colors: Tuple[str, str, str] = ('#F39C12', '#2ECC71', '#3498DB'), count_label: str = 'days', show_center_stats: bool = True) → Figure[source]#

Two donut charts showing time spent below / within / above comfort.

The left donut splits temperature into too cold / in range / too warm, the right donut splits humidity into too dry / in range / too humid. Each slice shows its share inside the ring; the centre shows the average with the min–max range; and each donut has its own vertical bullet-point legend listing the slice counts and percentages (e.g. number of days when the input is daily data). Hover tooltips show the count and the percentage.

Parameters:
  • data – DataFrame with columns temperature [°C] and humidity [%rH] (extra columns are ignored).

  • temp_range – (min, max) comfort temperature band [°C].

  • hum_range – (min, max) comfort humidity band [%rH].

  • title – Overall figure title. Default None.

  • temp_colors – Colors for (cold, in range, warm).

  • hum_colors – Colors for (dry, in range, humid).

  • count_label – Unit word for the per-slice counts, used in the legend and tooltips (e.g. "days" -> "In range — 42 days").

  • show_center_stats – Draw the average and min–max range in the centre of each donut. Default True.

Returns:

Plotly figure with two donut traces and a vertical bullet-point legend under each.

Return type:

go.Figure

See also

plot_comfort_compass() for the direction of the deviations, and what the comfort zone and warning bands mean: https://retomarek.github.io/pyedautils/guides/comfort_compass.html#compass-comfort

pyedautils.plots.comfort.plot_comfort_compass(distribution, title: str | None = None, show_direction_labels: bool = True, show_legend: bool = True, count_label: str = 'days', direction_labels: List[str] | None = None, names: Dict[str, str] | None = None, stage_names: Dict[str, str] | None = None, fixed_scale: bool = True, stats_text: str | None = None, count_label_singular: str | None = None, height: int = 460, scale: str | None = None, width: int | None = None) → Figure[source]#

Area-true “comfort compass” glyph for one room / group.

A single equal-area polar glyph summarising how a room’s days split across the nine comfort states (see pyedautils.comfort.comfort_compass_distribution()):

  • Centre — a green disc whose area is the share of days in range. It says what green means: the share and the in-range word (names["ok"], default "in range"), e.g. 52% / in range. When the disc is too small for both lines only the share is printed inside, and when even that does not fit, nothing. Without the legend (show_legend=False) a caption below the glyph then gives the share instead, e.g. ● 8% in range; with the legend its first row does.

  • Eight wedges — one per deviation direction; the wedge area is the share of days, and the three radial sub-segments (inner → outer) split that share by severity. Severity is encoded as lightness on three fixed planes shared by all directions — mild is always the lightest shade in the glyph, severe always the darkest — so the shade alone identifies the stage. Orientation matches an h,x diagram: warm top, cold bottom, humid right, dry left; mixed directions blend the hues of their two poles.

  • Direction labels — each label sits just beyond the end of its own arm (at least the dashed circle), anchored outward and moved out until its text box clears the whole glyph (disc and neighbouring arms). Label lines longer than 14 characters are wrapped at spaces; a label that would still run off the figure is drawn smaller (down to 7 px).

  • Every glyph has the same total area (= 100 % of days) — only the shape tells the story. With scale="fixed" (the default, same as fixed_scale=True) the radial axis is framed to the worst-case extent, so the dashed reference circle is drawn at the same size in every chart and glyphs stay directly comparable by area. With scale="fit" the glyph is zoomed so that it and its labels fill the panel; the circle — and thus the scale — then differs from chart to chart, so compare such charts by their percentages only.

The percentages are listed in a donut-style bullet legend on the right — each deviation row shows the count, the total share and its mild/moderate/severe split (e.g. (19% → 5/4/10%)) — followed by a grey severity key (mild / moderate / severe swatches on the same three lightness planes). Hovering a segment shows only that segment: direction · stage, its count and share.

Parameters:
  • distribution – Mapping of comfort-compass categories to counts, as returned by pyedautils.comfort.comfort_compass_distribution() (keys "ok" and "<direction>_<stage>"; missing keys count as 0). Percentages and areas are derived from the counts.

  • title – Figure title; a muted subtitle underneath shows the total count (e.g. 265 days). Default None.

  • show_direction_labels – Draw the eight direction labels around the rose. Default True.

  • show_legend – Draw the bullet legend with the per-direction counts and percentages. Default True.

  • count_label – Unit word for the counts in legend, hover and title subtitle (e.g. "days", "hours"). Default "days".

  • direction_labels – Optional 8 labels for the rose (order: humid, warm+humid, warm, warm+dry, dry, cold+dry, cold, cold+humid). Use "<br>" for line breaks. Default English; pass pyedautils.plots.comfort._COMPASS_LABELS_DE for German.

  • names – Optional mapping overriding the direction names in legend and hover and the in-range word in the green disc / caption (keys "ok" and "f","wf","w","wt","t","ct","c","kf"; missing keys keep the English default). Pass _COMPASS_NAMES_DE for German.

  • stage_names – Optional mapping overriding the severity-stage names shown in the hover and the severity key (keys "l" mild, "d" moderate, "s" severe). Default English; pass _COMPASS_STAGE_NAMES_DE for German.

  • fixed_scale – Keep the radial axis fixed to the worst-case extent so the dashed 100 % reference circle is the same size in every chart, keeping glyphs from different rooms/periods directly comparable. Default True. False is the same as scale="fit". Ignored when scale is given.

  • stats_text – Optional extra text block (e.g. min/mean/max readings) rendered left-aligned below the legend (<br> for line breaks, inline HTML spans allowed). With show_legend=False it is drawn on its own in the legend column. Default None (off).

  • count_label_singular – Optional singular unit word used in the legend, hover and title subtitle when a count is exactly 1 (e.g. pass count_label="Tage", count_label_singular="Tag" for German). Default None (always use count_label).

  • height – Figure height in pixels. Default 460.

  • scale – "fixed" (same 100 % circle in every chart) or "fit" (zoomed to this glyph and its labels). Takes precedence over fixed_scale; default None uses fixed_scale.

  • width – Optional figure width in pixels. Default None (Plotly’s default / container width). Whether the texts fit into the green disc is estimated for this width (700 px when None).

All visible texts can be set by the caller: title, count_label / count_label_singular, names (incl. the in-range word), direction_labels, stage_names and stats_text; the remaining characters are numbers, % and symbols. Example for German:

from pyedautils.plots.comfort import (
    _COMPASS_LABELS_DE, _COMPASS_NAMES_DE, _COMPASS_STAGE_NAMES_DE)
plot_comfort_compass(dist, title="Schlafzimmer",
                     names=_COMPASS_NAMES_DE,
                     direction_labels=_COMPASS_LABELS_DE,
                     stage_names=_COMPASS_STAGE_NAMES_DE,
                     count_label="Tage", count_label_singular="Tag")
Returns:

go.Figure

Raises:

ValueError – If scale is not None, "fixed" or "fit".

See also

How to read the compass, compare rooms (scale), and what the arms mean for comfort and energy: https://retomarek.github.io/pyedautils/guides/comfort_compass.html

pyedautils.plots.comfort.plot_overheating_timeseries(aligned: DataFrame, method: str = 'adaptive', summer_only: bool = True, business_hours_only: bool = False, title: str = 'Overheating — temperature curve', ylab: str = 'Temperature [°C]', height: int = 400) → Figure[source]#

Room-temperature curve with the comfort threshold and overheating marks.

Mirrors the temperature plot of the Streamlit overheating page: the room temperature as a line, the (adaptive or fixed) comfort threshold as a dashed line, and the samples above the threshold highlighted as red markers.

Parameters:
  • aligned – DataFrame indexed by timestamp with columns [t_room, t_oa, t_oa_48h] (see pyedautils.comfort.align_hourly()).

  • method – "adaptive" (SIA 180 curve) or "fixed" (26.5 °C), passed to pyedautils.comfort.overheating_hours().

  • summer_only – Restrict the threshold/markers to the SIA 180 summer half-year. Default True.

  • business_hours_only – Restrict to 07:00–22:00. Default False.

  • title – Plot title.

  • ylab – Y-axis label.

  • height – Figure height in pixels. Default 400.

Returns:

go.Figure

pyedautils.plots.comfort.plot_overheating_bar(monthly: DataFrame, month_col: str = 'month', hours_col: str = 'hours', title: str = 'Overheating hours per month', ylab: str = 'Hours [h]', color: str = '#f59e0b', height: int = 400) → Figure[source]#

Bar chart of overheating hours per calendar month.

Mirrors the monthly bar of the Streamlit overheating page: one amber bar per month (Jan–Dec, missing months shown as zero) with the hour count labelled above each bar.

Parameters:
  • monthly – DataFrame with a month column (1–12) and an hours column, e.g. the output of pyedautils.comfort.overheating_per_month().

  • month_col – Month column name (values 1–12). Default "month".

  • hours_col – Overheating-hours column name. Default "hours".

  • title – Plot title.

  • ylab – Y-axis label.

  • color – Bar colour.

  • height – Figure height in pixels. Default 400.

Returns:

go.Figure

pyedautils.plots.comfort.plot_temp_humidity_timeseries(data: DataFrame, temp_band: Tuple[float, float] | None = (20.0, 26.0), hum_band: Tuple[float, float] | None = (30.0, 65.0), temp_band_orange: Tuple[float, float] | None = (19.0, 27.0), hum_band_orange: Tuple[float, float] | None = (25.0, 70.0), temp_band_red: Tuple[float, float] | None = (17.5, 28.5), hum_band_red: Tuple[float, float] | None = (20.0, 75.0), show_hourly: bool = True, show_daily_mean: bool = True, temp_title: str | None = None, hum_title: str | None = None, title: str = 'Temperature & humidity over time', height: int = 600) → Figure[source]#

Temperature and humidity time series with comfort and warning limits.

Two stacked subplots (temperature on top, humidity below, shared time axis). Each shows the hourly readings as faint dots and the daily-mean as a bold line, plus — when given — the green comfort target band (filled, with a solid edge line top and bottom) and the moderate and severe warning limits as dashed threshold lines (the same levels used by the Mollier comfort zone and the kg-zh comfort compass). Hover labels use T / φ and the %Y-%m-%d %H:%M date format of the Mollier h,x chart.

The daily-mean line is reindexed onto a continuous daily grid, so gaps in the data (missing days) appear as breaks rather than straight connecting lines (connectgaps=False).

Parameters:
  • data – DataFrame with timestamp, temperature [°C] and humidity [%rH] columns (read by name, order-independent).

  • hum_band (temp_band /) – (low, high) comfort target band (green fill + solid edge lines). Defaults to the Mollier h,x comfort zone (T 20–26 °C, φ 30–65 %). None to omit.

  • hum_band_orange (temp_band_orange /) – (low, high) moderate warning limits, drawn as dashed orange lines. Defaults to ± 1 K / ± 5 % around the comfort band. None to omit.

  • hum_band_red (temp_band_red /) – (low, high) severe warning limits, drawn as dashed red lines. Defaults to ± 2.5 K / ± 10 % around the comfort band. None to omit.

  • show_hourly – Overlay the raw readings as faint dots. Default True.

  • show_daily_mean – Draw the daily-mean line. Default True.

  • hum_title (temp_title /) – Optional title shown above the temperature / humidity subplot (e.g. "Temperature", "abs. Humidity"). None to omit.

  • title – Figure title.

  • height – Figure height in pixels. Default 600.

Returns:

go.Figure

pyedautils.plots.comfort.plot_mollier_hx_fast(data: DataFrame | None = None, pressure: float | None = None, altitude: float | None = None, domain_x: Tuple[float, float] = (0.0, 0.02), domain_y: Tuple[float, float] = (-20.0, 50.0), comfort_zone: Dict[str, Tuple[float, float]] | None = None, height: int = 700, convention: str = 'classical', highlight_latest: bool = True, highlight_color: str | None = 'black', states: Sequence | None = None, labels: List[str] | None = None, comfort_label: str = 'Comfort zone', comfort_zone_orange: Dict[str, object] | None = None, comfort_zone_red: Dict[str, object] | None = None, show_temperature: bool = True, show_density: bool = True, show_rel_humidity: bool = True, show_enthalpy: bool = True, show_abs_humidity: bool = True, x_axis_title: str = 'absolute water content x [g/kg]', season_labels: Dict[str, str] | None = None, show_frequency: bool = False, frequency_unit: str = 'hours', frequency_smoothing: float = 4.0, show_points: bool = True, mean_enthalpy: bool = False, mean_temperature: bool = False, outdoor: Tuple[float, float] | None = None, current: Tuple[float, float] | None = None, outdoor_color: str = '#2563eb', ventilation_line: bool = True, ventilation_note: bool = True, ventilation_threshold: float = 0.0003, captions: Dict[str, str] | None = None, texts: Dict[str, str] | None = None) → str[source]#

Stateful, fast variant of plot_mollier_hx().

Renders with the d3-mollierhx MollierChart class: the coordinate grid is drawn once and the overlays (measurement points, comfort zone, warning bands, frequency lines, process chain, room and outside point) update in place. The created chart is exposed on window["mollier_<uid>"] so an embedding app can call its update methods (setData, setComfort, setFrequency, setCurrent, setOutdoor, setTexts …) without re-rendering — the basis for fast, interactive dashboards.

Same inputs as plot_mollier_hx() (including altitude setting the pressure when pressure is not given, offset warning bands, "abs_humidity": None for no abs-humidity limit, outdoor / current with the ventilation line and note, and captions / texts), plus:

Parameters:
  • show_points – Draw the measurement points. False keeps only the frequency contour lines. Default True.

  • mean_enthalpy – Draw the mean-enthalpy curve (h_m, binned by temperature). Default False.

  • mean_temperature – Draw the mean-temperature curve (t_m, binned by enthalpy). Default False.

Differences to plot_mollier_hx(): the chart has no legend, so comfort_label, season_labels and the legend keys of texts (comfort_zone, season_*, room) are accepted but not shown; the comfort zone is drawn only when comfort_zone is given. Data, comfort zone, bands and frequency lines are clipped to the axes; room and outside points outside the axes become edge arrows with their value.

Returns:

Self-contained HTML (loads D3 v7 from the CDN + an inline bundle).

Return type:

str

See also

plot_mollier_hx() for the full-featured variant. How to read the diagram, comfort and saving energy: https://retomarek.github.io/pyedautils/guides/mollier_hx.html

Statistical / Exploratory#

Statistical and exploratory plots.

pyedautils.plots.stat.plot_sum_frequency(data: DataFrame, resolution: str = 'hourly', year: int | None = None, reverse: bool = False, title: str | None = None, xlab: str | None = None, ylab: str = 'Value') → Figure[source]#

Create a sum frequency (duration curve) plot.

Values are sorted and plotted against their rank (frequency). Useful for temperature duration curves or load duration curves.

Parameters:
  • data – DataFrame with two columns: timestamp and value.

  • resolution – "hourly" or "daily". Controls aggregation.

  • year – Filter to a specific year. If None, use all data.

  • reverse – If True, the highest value is at x=0 (classic duration curve / Jahresdauerlinie). If False (default), lowest value at x=0 (R default).

  • title – Plot title. Auto-generated if None.

  • xlab – X-axis label. Auto-generated if None.

  • ylab – Y-axis label.

Returns:

go.Figure

pyedautils.plots.stat.plot_decomposition(data: DataFrame, period: int | None = None, s_window: int | None = None, title: str = 'Time Series Decomposition', ylab: str = 'Value', digits: int = 1, max_gap: int = 6) → Figure[source]#

Decompose a time series into trend, seasonal, and remainder using STL.

Works for both long-term (e.g. monthly over years, period=12) and short-term data (e.g. 15-min over days, period=96).

STL needs a regular series without missing values (the period is a number of observations), so the data is prepared first:

  1. The timestamps are sorted and aligned to the sampling step (the inferred frequency, else the most common step between timestamps), so missing timestamps become explicit gaps instead of shifting the phase of the seasonal pattern.

  2. Gaps of at most max_gap consecutive steps are linearly interpolated (in time); leading and trailing gaps are trimmed.

  3. If longer gaps remain, only the longest contiguous segment without gaps is decomposed and a UserWarning names the segment. Fill or shorten the data yourself (or raise max_gap) to decompose a different stretch.

Raises a ValueError when the series (or that segment) is shorter than two full periods.

Parameters:
  • data – DataFrame with two columns: timestamp and value.

  • period – Seasonal period in number of observations. If None, auto-detected from the data frequency (e.g. 12 for monthly, 96 for 15-min, 24 for hourly).

  • s_window – Seasonal smoothing window for STL. If None, defaults to 2 * period + 1 (robust default).

  • title – Plot title.

  • ylab – Y-axis label for the raw data panel.

  • digits – Decimal places for rounding. Default 1.

  • max_gap – Longest gap (in sampling steps) that is interpolated. Default 6. 0 disables interpolation.

Returns:

Plotly figure with 4 subplot rows (observed, trend, seasonal, remainder).

Return type:

go.Figure

pyedautils.plots.stat.plot_timeseries(data: DataFrame, columns: List[str] | None = None, title: str = 'Time Series', ylab: str = 'Value', range_slider: bool = True, height: int = 400) → Figure[source]#

Create an interactive line plot with an optional range slider.

Supports plotting one or multiple columns from a DataFrame with a DatetimeIndex.

Parameters:
  • data – DataFrame with a DatetimeIndex and one or more value columns.

  • columns – Columns to plot. If None, plots all columns.

  • title – Plot title.

  • ylab – Y-axis label.

  • range_slider – Show a range slider below the plot. Default True.

  • height – Figure height in pixels. Default 400.

Returns:

Plotly figure with line traces and optional range slider.

Return type:

go.Figure

pyedautils.plots.stat.plot_distribution(data: DataFrame, columns: List[str] | None = None, nbins: int = 50, opacity: float = 0.6, title: str = 'Distribution', xlab: str = 'Value') → Figure[source]#

Create overlaid histograms for one or more columns.

Parameters:
  • data – DataFrame with a DatetimeIndex and one or more value columns.

  • columns – Columns to include. If None, uses all columns.

  • nbins – Number of histogram bins. Default 50.

  • opacity – Bar opacity. Default 0.6.

  • title – Plot title.

  • xlab – X-axis label.

Returns:

Plotly histogram figure.

Return type:

go.Figure

pyedautils.plots.stat.plot_boxplot(data: DataFrame, column: str | None = None, groupby: str = 'month', title: str | None = None, ylab: str = 'Value') → Figure[source]#

Create a box plot grouped by a time unit extracted from the DatetimeIndex.

Parameters:
  • data – DataFrame with a DatetimeIndex and one or more value columns.

  • column – Column to plot. If None, uses the first column.

  • groupby – Time grouping — "month", "hour", "weekday", or "quarter". Default "month".

  • title – Plot title. Auto-generated if None.

  • ylab – Y-axis label.

Returns:

Plotly box plot figure.

Return type:

go.Figure

pyedautils.plots.stat.plot_outliers(data: DataFrame, column: str | None = None, multiplier: float = 1.5, title: str | None = None, ylab: str = 'Value', height: int = 400) → Figure[source]#

Visualize IQR-based outliers on a time series plot.

Plots the time series as a line with outlier points highlighted in red and dashed horizontal lines at the IQR fences.

Parameters:
  • data – DataFrame with a DatetimeIndex and one or more value columns.

  • column – Column to analyse. If None, uses the first column.

  • multiplier – IQR multiplier for the fence. Default 1.5.

  • title – Plot title. Auto-generated if None.

  • ylab – Y-axis label.

  • height – Figure height in pixels. Default 400.

Returns:

Plotly figure with time series, outlier markers, and fences.

Return type:

go.Figure

pyedautils.plots.stat.plot_correlation(data: DataFrame, color_scale: str = 'RdBu_r', text_format: str = '.2f', title: str = 'Correlation Matrix', height: int = 500, width: int = 600) → Figure[source]#

Create a correlation matrix heatmap.

Parameters:
  • data – DataFrame with numeric columns (DatetimeIndex is ignored).

  • color_scale – Plotly colorscale name. Default "RdBu_r".

  • text_format – Number format for cell annotations. Default ".2f".

  • title – Plot title.

  • height – Figure height in pixels. Default 500.

  • width – Figure width in pixels. Default 600.

Returns:

Plotly heatmap figure of the correlation matrix.

Return type:

go.Figure

pyedautils.plots.stat.plot_scatter(data: DataFrame, x_column: str | None = None, y_column: str | None = None, opacity: float = 0.2, title: str | None = None, xlab: str | None = None, ylab: str | None = None) → Figure[source]#

Create a scatter plot of two variables.

Low default opacity helps reveal density in large datasets.

Parameters:
  • data – DataFrame with a DatetimeIndex and at least two value columns.

  • x_column – Column for the x-axis. If None, uses the first column.

  • y_column – Column for the y-axis. If None, uses the second column.

  • opacity – Marker opacity. Default 0.2.

  • title – Plot title. Auto-generated if None.

  • xlab – X-axis label. Defaults to the column name.

  • ylab – Y-axis label. Defaults to the column name.

Returns:

Plotly scatter plot figure.

Return type:

go.Figure

pyedautils.plots.stat.plot_autocorrelation(data: DataFrame, column: str | None = None, lags: int = 168, title: str | None = None, height: int = 300, max_gap: int = 6) → Figure[source]#

Create an autocorrelation plot with a confidence band using Plotly.

A lag is a fixed number of sampling steps, so the series is first put on a regular time grid before the ACF is computed:

  1. The DatetimeIndex is sorted and aligned to its sampling step (the inferred frequency, else the most common step between timestamps), so missing timestamps become explicit gaps instead of silently shifting all later values by one or more lags.

  2. Gaps of at most max_gap consecutive steps are linearly interpolated (in time). Longer gaps are left empty completely; they are not partly filled. Leading and trailing gaps are trimmed.

  3. The ACF is computed on the regular series with statsmodels’ missing="conservative": each autocovariance uses only the pairs of observations where both values are present, so the remaining long gaps neither break the lag alignment nor are replaced by invented values. As with the usual (n - k) / n factor of the ACF, values at lags with fewer available pairs are shrunk towards zero (never inflated). Without remaining gaps this is the ordinary ACF.

Parameters:
  • data – DataFrame with a DatetimeIndex and one or more value columns. A non-datetime index is taken as already regular (positional).

  • column – Column to analyse. If None, uses the first column.

  • lags – Number of lags to compute. Default 168 (one week at hourly).

  • title – Plot title. Auto-generated if None.

  • height – Figure height in pixels. Default 300.

  • max_gap – Longest gap (in sampling steps) that is interpolated. Default 6. 0 disables interpolation.

Returns:

Plotly bar chart of autocorrelation values with a 95% confidence band.

Return type:

go.Figure

Gradients#

Plots for heating/cooling gradients (see pyedautils.data_prep.gradients).

pyedautils.plots.gradients.plot_gradient_boxplots(gradients: DataFrame, groupby: str = 'season', direction_labels: Tuple[str, str] = ('heating', 'cooling'), title: str = 'Temperature Gradients', ylab: str = 'Gradient [K/h]', colors: Tuple[str, str] = ('#E74C3C', '#3498DB'), season_order: list | None = None) → Figure[source]#

Grouped boxplots of gradients, split by direction.

Draws one box per group and direction (e.g. heating vs cooling per season or per quarter), so heating and cooling spreads are directly comparable.

Parameters:
  • gradients – Output of pyedautils.data_prep.gradients.compute_gradients() (columns [timestamp, gradient, direction, season]).

  • groupby – Grouping on the x-axis. "season" uses the season column; "quarter", "month", "weekday" and "hour" are derived from timestamp.

  • direction_labels – (rising, falling) labels matching compute_gradients().

  • title – Plot title.

  • ylab – Y-axis label.

  • colors – (rising_color, falling_color).

  • season_order – Optional ordered season labels for the x-axis when groupby="season".

Returns:

Plotly grouped boxplot figure.

Return type:

go.Figure

Degree Days#

Degree-day and climate-correction plots.

pyedautils.plots.degree_days.plot_degree_days(actual, reference=None, value_col: str | None = None, title: str = 'Heating Degree Days', xlab: str = 'Month', ylab: str = 'HGT [Kd]', colors: Dict[str, str] | None = None) → Figure[source]#

Grouped bar chart of degree days per period, optionally against a reference.

Parameters:
  • actual – Monthly (or yearly) degree days: output of pyedautils.degree_days.heating_degree_days() / accumulated_temperature_difference / cooling_degree_days (freq="MS" or "YS") or a Series with a DatetimeIndex.

  • reference – Reference degree days: a pyedautils.degree_days.ReferenceDegreeDays, a Series indexed by calendar month 1–12 (matched by month), or a Series on the same DatetimeIndex. Default None (no reference bars).

  • value_col – Column of actual to plot; auto-detected (hgt/atd/cdd/dd) if None.

  • title – Plot title.

  • xlab – X-axis label.

  • ylab – Y-axis label.

  • colors – Color overrides keyed "actual" and "reference".

Returns:

Plotly figure with one bar trace per series.

Return type:

go.Figure

pyedautils.plots.degree_days.plot_degree_day_regression(energy, temperature, base: float = 12.0, freq: str = 'D', title: str = 'Degree-Day Regression', xlab: str = 'Outside Temperature [°C]', ylab: str = 'Energy [kWh/d]', colors: Dict[str, str] | None = None) → Figure[source]#

Scatter of energy per day vs outside temperature with a fixed-base degree-day regression line.

Fits pyedautils.degree_days.fit_degree_day_line() and draws the heating line (intercept + slope · (base − T) below base) and the flat base load above it. Points are colored by season (daily) or shown as a single series (monthly).

Parameters:
  • energy – Consumption [kWh], see fit_degree_day_line.

  • temperature – Outdoor temperature [°C], daily or finer.

  • base – Base temperature [°C] of the heating degree days. Default 12; e.g. compute_pes(...).tb.

  • freq – "D" (daily points) or "MS" (monthly means per day).

  • title – Plot title.

  • xlab – X-axis label.

  • ylab – Y-axis label.

  • colors – Season color overrides keyed by season name (daily) or "points" (monthly).

Returns:

Plotly figure with scatter, regression line and annotation of base temperature, slope and fit metrics.

Return type:

go.Figure

Solar Influence#

Solar-influence plot (see pyedautils.data_prep.solar_influence).

pyedautils.plots.solar.plot_solar_influence(data: DataFrame, room_col: str = 't_room', radiation_col: str = 'solar', event_col: str | None = None, title: str = 'Solar Influence', temp_label: str = 'Room temperature (°C)', radiation_label: str = 'Global radiation (W/m²)', height: int = 360) → Figure[source]#

Dual-axis time series of room temperature and global radiation.

Room temperature is drawn on the left axis, global radiation on the right axis. Optional steep-rise events are marked with triangles.

Parameters:
  • data – DataFrame indexed by timestamp (or with a timestamp column) containing the room-temperature and radiation columns.

  • room_col – Room-temperature column. Default "t_room".

  • radiation_col – Global-radiation column. Default "solar".

  • event_col – Optional boolean column flagging steep-rise events; flagged rows are overlaid as triangles. Default None.

  • title – Plot title.

  • temp_label – Left y-axis label.

  • radiation_label – Right y-axis label.

  • height – Figure height in pixels. Default 360.

Returns:

Plotly dual-axis figure.

Return type:

go.Figure