Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intersect #79

Merged
merged 23 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 0 additions & 42 deletions dev_scripts/checks.py

This file was deleted.

1 change: 1 addition & 0 deletions docs/core/pfline.rst
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ Another slicing method is implemented with the ``.slice[]`` property. The improv




Concatenation
=============

Expand Down
5 changes: 4 additions & 1 deletion docs/core/toplevel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ Work on pandas objects
Work on portfolyo objects
-------------------------

* ``portfolyo.concat()`` Concatenates PfLines into one PfLine.
* ``portfolyo.concat()`` Concatenates PfLines (or PfStates) into one PfLine (or PfState).

* ``portfolyo.plot_pfstates()`` Plots several PfStates in one figure.

* ``portfolyo.intersection()`` Intersect several dataframes and/or series and/or Pflines and/or PfStates.



14 changes: 0 additions & 14 deletions docs/requirements-docs.txt

This file was deleted.

Binary file modified docs/savefig/fig_hedge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/savefig/fig_offtake.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 5 additions & 2 deletions portfolyo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
from . import _version, dev, testing, tools
from .core import extendpandas # extend functionalty of pandas
from .core import suppresswarnings
from .tools2.plot import plot_pfstates
from .core.pfline import Kind, PfLine, Structure, create
from .core.pfstate import PfState
from .core.shared.concat import general as concat
from .core.shared.plot import plot_pfstates
from .tools2.concat import general as concat
from .tools2.plot import plot_pfstates
from .prices.hedge import hedge
from .prices.utils import is_peak_hour
from .tools.changefreq import averagable as asfreq_avg
Expand All @@ -17,6 +18,8 @@
from .tools.tzone import force_agnostic, force_aware
from .tools.unit import Q_, Unit, ureg
from .tools.wavg import general as wavg
from .tools2.concat import general as concat
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Pizza2Pizza this line is already on line 9. But let's not stop the PR for this; just remove it from the plot_children branch after merging the new develop into it.

from .tools2.intersect import indexable as intersection

# from .core.shared.concat import general as concat

Expand Down
183 changes: 2 additions & 181 deletions portfolyo/core/shared/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

from __future__ import annotations

from typing import TYPE_CHECKING, Dict
from typing import TYPE_CHECKING

import matplotlib
import numpy as np

from matplotlib import pyplot as plt

from ... import tools
Expand Down Expand Up @@ -115,51 +115,6 @@ def plot(self: PfLine, cols: str = None) -> plt.Figure:


class PfStatePlot:
# def plot_to_ax(
# self: PfState, ax: plt.Axes, line: str = "offtake", col: str = None, **kwargs
# ) -> None:
# """Plot a timeseries of a PfState in the portfolio state to a specific axes.

# Parameters
# ----------
# ax : plt.Axes
# The axes object to which to plot the timeseries.
# line : str, optional
# The pfline to plot. One of {'offtake' (default), 'sourced', 'unsourced',
# 'netposition', 'procurement', 'sourcedfraction'}.
# col : str, optional
# The column to plot. Default: plot volume `w` [MW] (if available) or else
# price `p` [Eur/MWh].
# Any additional kwargs are passed to the pd.Series.plot function.
# """
# if line == "offtake":
# how = DEFAULTHOW.get(col, "step")
# (-self.offtake).plot_to_ax(ax, col, how)
# ax.bar_label(
# ax.containers[0], label_type="edge", fmt="%,.0f".replace(",", " ")
# )

# elif line.endswith("sourcedfraction"): # (un)sourcedfraction
# fractions = getattr(self, line)
# vis.plot_timeseries(ax, fractions, how="bar", color="grey")
# ax.bar_label(
# ax.containers[0],
# label_type="edge",
# labels=fractions.apply("{:.0%}".format),
# ) # print labels on top of each bar

# elif line == "sourced":
# self.sourced.plot_to_ax(
# ax,
# col,
# )
# if col == "p":

# vis.plot_timeseries(ax, self.unsourcedprice["p"], how="bar", alpha=0.0)
# ax.bar_label(
# ax.containers[0], label_type="center", fmt="%.2f"
# ) # print labels on top of each bar

def plot(self: PfState) -> plt.Figure:
"""Plot the portfolio state.

Expand Down Expand Up @@ -225,137 +180,3 @@ def plot(self: PfState) -> plt.Figure:

fig.tight_layout()
return fig


def plot_pfstates(dic: Dict[str, PfState], freq: str = "MS") -> plt.Figure:
"""Plot multiple PfState instances.

Parameters
----------
dic : Dict[str, PfState]
Dictionary with PfState instances as values, and their names as the keys.

Returns
-------
plt.Figure
The figure object to which the instances were plotted.
"""

gridspec = {"width_ratios": [0.3, 1, 1], "height_ratios": [4, 1] * len(dic)}
figsize = (15, 5 * len(dic))
fig, axes = plt.subplots(len(dic) * 2, 3, gridspec_kw=gridspec, figsize=figsize)
axesgroups = axes.flatten().reshape((len(dic), 6))

# Share x axes.
sharex = axesgroups[:, (1, 2, 4)].flatten()
for ax1, ax2 in zip(sharex[1:], sharex[:-1]):
ax1.sharex(ax2)
# Share y axes.
sharey = axesgroups[:, 2]
for ax1, ax2 in zip(sharey[1:], sharey[:-1]):
ax1.sharey(ax2)

# TODO: resample all to have same index (frequency and length).

for i, ((pfname, pfs), axes) in enumerate(zip(dic.items(), axesgroups)):
# If freq is MS or longer: use categorical axes. Plot volumes in MWh.
# If freq is D or shorter: use time axes. Plot volumes in MW.
is_category = tools.freq.shortest(pfs.index.freq, "MS") == "MS"

# Portfolio name.
axes[0].text(
0,
1,
pfname.replace(" ", "\n"),
fontsize=12,
fontweight="bold",
verticalalignment="top",
horizontalalignment="left",
)
axes[0].axis("off")

# Volumes.
if is_category:
s, kwargs = -1 * pfs.offtakevolume.q, defaultkwargs("q", is_category)
else:
s, kwargs = -1 * pfs.offtakevolume.w, defaultkwargs("w", is_category)
vis.plot_timeseries(axes[1], s, **kwargs)

# Sourced fraction.
vis.plot_timeseries(
axes[2], pfs.sourcedfraction, **defaultkwargs("f", is_category)
)

# Empty.
axes[3].axis("off")

# Procurement Price.
vis.plot_timeseries(axes[4], pfs.pnl_cost.p, **defaultkwargs("p", is_category))

# Empty.
axes[5].axis("off")

# Tick formatting.
axes[2].yaxis.set_major_formatter(matplotlib.ticker.PercentFormatter(1.0))
axes[1].yaxis.set_major_formatter(
matplotlib.ticker.FuncFormatter(
lambda x, p: "{:,.0f}".format(x).replace(",", " ")
)
)

for a, ax in enumerate(axes):
if i == 0 and a in [1, 2]:
ax.xaxis.set_tick_params(labelbottom=False, labeltop=True, pad=25)
else:
ax.xaxis.set_tick_params(labelbottom=False, labeltop=False)

if i == 0:
axes[1].set_title("Offtake Volume &\nprocurement price", y=1.27)
axes[2].set_title("Sourced fraction", y=1.27)

return
draw_horizontal_lines(fig, axes) # draw horizontal lines between portfolios


def draw_horizontal_lines(fig, axes):
"""Function to draw horizontal lines between multiple portfolios.
This function does not return anything, but tries to plot a 2D line after every 2 axes, eg.
after (0,2), (0,4),... beacuse each portfolio requires 2x4 axes in the fig (where rows=2, columns=4).

Parameters
----------
fig : plt.subplots()
axes : plt.subplots()
"""
# rearange the axes for no overlap
fig.tight_layout()

# Get the bounding boxes of the axes including text decorations
r = fig.canvas.get_renderer()
bboxes = np.array(
[
ax.get_tightbbox(r).transformed(fig.transFigure.inverted())
for ax in axes.flat
],
matplotlib.transforms.Bbox,
).reshape(axes.shape)

"""TO CORRECT: the horizontal line is not exactly in the middle of two graphs.
It is more inclined towards the second or next graph in the queue.
Each pftstate has 4x4 grid and this is plotted in the same graph, but as subgraphs.
"""

# Get the minimum and maximum extent, get the coordinate half-way between those
ymax = (
np.array(list(map(lambda b: b.y1, bboxes.flat))).reshape(axes.shape).max(axis=1)
)
ymin = (
np.array(list(map(lambda b: b.y0, bboxes.flat))).reshape(axes.shape).min(axis=1)
)
ys = np.c_[ymax[2:-1:2], ymin[1:-2:2]].mean(axis=1)
ys = [ymax[0], *ys]

# Draw a horizontal lines at those coordinates
for y in ys:
line = plt.Line2D([0, 1], [y, y], transform=fig.transFigure, color="black")
fig.add_artist(line)
Loading
Loading