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

Update installation, fix some warning move to src layout #93

Merged
merged 3 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
6 changes: 1 addition & 5 deletions .github/workflows/daily_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
pip install pytest-cov
pip install codecov
pip install -r requirements.txt
pip install --no-cache-dir -e .
pip install --no-cache-dir -e .[dev]
- name: Test with pytest
working-directory: ./test/
Expand Down
12 changes: 2 additions & 10 deletions .github/workflows/test_on_push_and_pull.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
pip install pytest-cov
pip install codecov
pip install -r requirements.txt
pip install --no-cache-dir -e .
pip install --no-cache-dir -e .[dev]
- name: Test with pytest
working-directory: ./test/
Expand All @@ -58,11 +54,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install numpy==${{matrix.python-numpy-version.numpy}} --upgrade pip
pip install pytest
pip install pytest-cov
pip install codecov
pip install -r requirements.txt
pip install --no-cache-dir -e .
pip install --no-cache-dir -e .[dev]
- name: Test with pytest
working-directory: ./test/
Expand Down
1 change: 1 addition & 0 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ build:

python:
install:
- requirements: requirements.txt
- requirements: requirements_dev.txt
system_packages: false
40 changes: 31 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,50 @@ The documentation of the tsam code can be found [**here**](https://tsam.readthed


## Installation
It is recommended to install tsam within its own environment. If you are no familiar with python environments, plaese consider to read some [external documentation](https://realpython.com/python-virtual-environments-a-primer/). In the following we assume you have a [mamba](https://mamba.readthedocs.io/en/latest/installation/mamba-installation.html) or [conda](https://www.anaconda.com/) installation. All conda and mamba command are interchangeable.

### Direct Installations from Package Manager Repositories

If you want to prevent any possible dependency conflicts create a new environment using the following command:

mamba create -n tsam_env python pip

Activate an existing or the newly create environment afterward

mamba activate tsam_env

Directly install via pip from pypi as follows:

pip install tsam

of install from conda forge with the following command:
or install from conda forge with the following command:

conda install tsam -c conda-forge

### Local Installation for Development
Alternatively, clone a local copy of the repository to your computer

git clone https://github.com/FZJ-IEK3-VSA/tsam.git
Then install tsam via pip as follow

Change the directory of your shell into the root folder of the repository

cd tsam
pip install .

Or install directly via python as

python setup.py install
For development, it is recommended to install tsam into its own environment using conda e.g.

conda env create --file=requirement.yml

Afterward activate the environment

conda activate tsam_env

Then install tsam via pip as follows


pip install -e .[dev]

In order to use the k-medoids clustering, make sure that you have installed a MILP solver. As default [HiGHS](https://github.com/ERGO-Code/HiGHS) is used. Nevertheless, in case you have access to a license we recommend commercial solvers (e.g. Gurobi or CPLEX) since they have a better performance.
### Installation of MILP Solver for k-medoids
In order to use the k-medoids clustering, make sure that you have installed a MILP solver. As default [HiGHS](https://github.com/ERGO-Code/HiGHS) is installed and used. Nevertheless, in case you have access to a license we recommend commercial solvers (e.g. Gurobi or CPLEX) since they have a better performance.

### Developer installation

Expand Down
50 changes: 50 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
[build-system]
requires = ["setuptools>=64.0.0", "wheel"]
build-backend = "setuptools.build_meta"


[project]
name = "tsam"
version = "2.3.6"
description = "Time series aggregation module (tsam) to create typical periods"
authors = [
{ name = "Leander Kotzur", email = "[email protected]" },
{ name = "Maximilian Hoffmann", email = "[email protected]" },
]
license = { file = "LICENSE.txt" }
keywords = ["clustering", "optimization"]
requires-python = ">=3.9,<3.13"
readme = "README.md"
classifiers=[
"Development Status :: 4 - Beta",
"Intended Audience :: End Users/Desktop",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Natural Language :: English",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 3",
"Topic :: Scientific/Engineering :: Mathematics",
"Topic :: Software Development :: Libraries :: Python Modules",
]
dynamic = ["dependencies", "optional-dependencies"]


[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools.dynamic]
dependencies = {file = ["requirements.txt"]}
optional-dependencies = {dev = { file = ["requirements_dev.txt"] }}


#Configureation options
# https://docs.pytest.org/en/7.1.x/reference/reference.html#configuration-options

[tool.pytest.ini_options]
testpaths = ["test"] # Sets the path where to look for tests
pythonpath =["test"] # Sets the path which should be prepended to pythonpath relative to the root folder
console_output_style = "count"


4 changes: 1 addition & 3 deletions requirements.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
name: tsam
name: tsam_env
channels:
- conda-forge
dependencies:
- python>=3.9,<3.13
- pip
- pip:
- -r requirements.txt
3 changes: 1 addition & 2 deletions requirements_dev.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
-r requirements.txt

# Testing
pytest
pytest-cov
codecov

# Documentation
sphinx
Expand Down
11 changes: 0 additions & 11 deletions requirements_dev.yml

This file was deleted.

37 changes: 0 additions & 37 deletions setup.py

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,18 @@ def durationRepresentation(
# get all the values of a certain attribute and cluster
candidateValues = candidates.loc[indice[0], a]
# sort all values
sortedAttr = candidateValues.stack(future_stack=True,).sort_values()
sortedAttr = candidateValues.stack(
future_stack=True,
).sort_values()
# reindex and arrange such that every sorted segment gets represented by its mean
sortedAttr.index = pd.MultiIndex.from_tuples(clean_index)
representationValues = sortedAttr.unstack(level=0).mean(axis=1)
# respect max and min of the attributes
if representMinMax:
representationValues.loc[0] = sortedAttr.values[0]
representationValues.loc[
representationValues.index[-1]
] = sortedAttr.values[-1]

representationValues.loc[representationValues.index[-1]] = (
sortedAttr.values[-1]
)

# get the order of the representation values such that euclidean distance to the candidates is minimized
order = candidateValues.mean().sort_values().index
Expand Down Expand Up @@ -97,8 +98,12 @@ def durationRepresentation(
# concat centroid values and cluster weights for all clusters
meansAndWeights = pd.concat(
[
pd.DataFrame(np.array(meanVals)).stack(future_stack=True,),
pd.DataFrame(np.array(clusterLengths)).stack(future_stack=True,),
pd.DataFrame(np.array(meanVals)).stack(
future_stack=True,
),
pd.DataFrame(np.array(clusterLengths)).stack(
future_stack=True,
),
],
axis=1,
)
Expand All @@ -107,7 +112,14 @@ def durationRepresentation(
# save order of the sorted centroid values across all clusters
order = meansAndWeightsSorted.index
# sort all values of the original time series
sortedAttr = candidates.loc[:, a].stack(future_stack=True,).sort_values().values
sortedAttr = (
candidates.loc[:, a]
.stack(
future_stack=True,
)
.sort_values()
.values
)
# take mean of sections of the original duration curve according to the cluster and its weight the
# respective section is assigned to
representationValues = []
Expand All @@ -124,8 +136,7 @@ def durationRepresentation(
keepSum=True,
)


# transform all representation values to a data frame and arrange it
# transform all representation values to a data frame and arrange it
# according to the order of the sorted
# centroid values
representationValues = pd.DataFrame(np.array(representationValues))
Expand All @@ -139,9 +150,9 @@ def durationRepresentation(
return clusterCenters



def _representMinMax(representationValues, sortedAttr, meansAndWeightsSorted,
keepSum=True):
def _representMinMax(
representationValues, sortedAttr, meansAndWeightsSorted, keepSum=True
):
"""
Represents the the min and max values of the original time series in the
duration curve representation such that the min and max values of the
Expand All @@ -164,8 +175,8 @@ def _representMinMax(representationValues, sortedAttr, meansAndWeightsSorted,
if np.any(np.array(representationValues) < 0):
raise ValueError("Negative values in the duration curve representation")

# first retrieve the change of the values to the min and max values
# of the original time series and their duration in the original
# first retrieve the change of the values to the min and max values
# of the original time series and their duration in the original
# time series
delta_max = sortedAttr.max() - representationValues[-1]
appearance_max = meansAndWeightsSorted[1].iloc[-1]
Expand All @@ -174,31 +185,36 @@ def _representMinMax(representationValues, sortedAttr, meansAndWeightsSorted,

if delta_min == 0 and delta_max == 0:
return representationValues

if keepSum:

# now anticipate the shift of the sum of the time series
# now anticipate the shift of the sum of the time series
# due to the change of the min and max values
# of the duration curve
delta_sum = delta_max * appearance_max + delta_min * appearance_min
# and derive how much the other values have to be changed to preserve
# and derive how much the other values have to be changed to preserve
# the mean of the duration curve
correction_factor = - delta_sum / (meansAndWeightsSorted[1].iloc[1:-1]
* representationValues[1:-1]).sum()

correction_factor = (
-delta_sum
/ (meansAndWeightsSorted[1].iloc[1:-1] * representationValues[1:-1]).sum()
)

if correction_factor < -1 or correction_factor > 1:
warnings.warn("The cluster is to small to preserve the sum of the duration curve and additionally the min and max values of the original cluster members. The min max values of the cluster are not preserved. This does not necessarily mean that the min and max values of the original time series are not preserved.")
warnings.warn(
"The cluster is too small to preserve the sum of the duration curve and additionally the min and max values of the original cluster members. The min max values of the cluster are not preserved. This does not necessarily mean that the min and max values of the original time series are not preserved."
)
return representationValues

# correct the values of the duration curve such
# correct the values of the duration curve such
# that the mean of the duration curve is preserved
# since the min and max values are changed
representationValues[1:-1] = np.multiply(representationValues[1:-1], (
1+ correction_factor))

# change the values of the duration curve such that the min and max
representationValues[1:-1] = np.multiply(
representationValues[1:-1], (1 + correction_factor)
)

# change the values of the duration curve such that the min and max
# values are preserved
representationValues[-1] += delta_max
representationValues[0] += delta_min

return representationValues
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading
Loading