Repository: ruleminer/InterpreTS (language: python)

Issue:
Pypi package configuration



Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 82f9275..367cde5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -129,6 +129,8 @@ venv/
 ENV/
 env.bak/
 venv.bak/
+/env
+/env/
 
 # Spyder project settings
 .spyderproject
@@ -159,4 +161,4 @@ cython_debug/
 #  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
 #  and can be added to the global gitignore or merged into this file.  For a more nuclear
 #  option (not recommended) you can uncomment the following to ignore the entire idea folder.
-#.idea/
+#.idea/
\ No newline at end of file
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..ee552af
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,5 @@
+{
+    "recommendations": [
+        "visualstudioexptteam.vscodeintellicode"
+    ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index d4ddca4..6f945e9 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,7 +1,12 @@
 {
     "python.analysis.extraPaths": [
-        "./src/utils",
-        "./src/core",
-        "./src"
-    ]
+        "./interpreTS/utils",
+        "./interpreTS/core",
+        "./interpreTS"
+    ],
+    "python.testing.pytestArgs": [
+        "tests"
+    ],
+    "python.testing.unittestEnabled": false,
+    "python.testing.pytestEnabled": true
 }
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..b7fcd7e
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = docs/source
+BUILDDIR      = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md
deleted file mode 100644
index bd29f0e..0000000
--- a/docs/INSTALLATION.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Installation Guide for InterpreTS
-Follow the steps below to install InterpreTS and its dependencies.
-
-## Requirements
-Python 3.8 or higher
-- [`pandas`](https://github.com/pandas-dev/pandas)
-- [`numpy`](https://github.com/numpy/numpy)
-  
-## Steps
-1. Clone the repository: Clone the InterpreTS repository to your local machine:
-   
-```python
-git clone https://github.com/ruleminer/InterpreTS.git
-cd time_series_feature_extractor
-```
-
-2. Install dependencies: Install the required packages listed in the `requirements.txt` file:
-
-```python
-pip install -r requirements.txt
-```
-
-3. Install InterpreTS: Run the following command to install InterpreTS:
-
-```python
-pip install .
-```
-
-## Verifying Installation
-Once installed, you can verify the installation by running a simple feature extraction example:
-
-```python
-from interpreTS import FeatureExtractor
-import pandas as pd
-
-data = pd.DataFrame({'value': [1, 2, 3, 4, 5]})
-extractor = FeatureExtractor(features=['length'])
-features = extractor.extract_features(data)
-print("Extracted Features:", features)
-```
-
-For any issues, please consult our [Issue Tracker](https://github.com/ruleminer/InterpreTS/issues) on GitHub.
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index ed360fe..b34c9c7 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,9 +1,9 @@
 # InterpreTS
 
-Feature extraction from time series to support the creation of interpretable and explainable predictive models.
+InterpreTS is a Python library designed for extracting meaningful and interpretable features from time series data to support the creation of interpretable and explainable predictive models.
 
 ## Overview
-InterpreTS is a Python library designed for extracting meaningful and interpretable features from time series data. With the growing importance of interpretability in machine learning and AI, InterpreTS focuses on creating feature representations that facilitate the development of interpretable and explainable predictive models.
+With the growing importance of interpretability in machine learning and AI, InterpreTS focuses on creating feature representations that facilitate the development of interpretable and explainable predictive models.
 
 ## Key Features
 - **Statistical Features**: Extract basic statistics like mean, standard deviation, minimum, and maximum values.
@@ -14,26 +14,75 @@ InterpreTS is a Python library designed for extracting meaningful and interpreta
 
 ## Requirements
 - Python 3.8 or above
-- pandas
-- numpy
-- sktime
+- `pandas`
+- `numpy`
+- `statsmodels`
 
-## Installation
-To install InterpreTS, follow the steps in [Installation Guide](docs/INSTALLATION.md).
+## Installation Guide
+Follow these steps to install InterpreTS and its dependencies:
 
-## Quickstart
-To begin using InterpreTS, see the [Usage Guide](docs/USAGE.md) or follow the example below:
+1. **Clone the Repository**  
+   Clone the InterpreTS repository to your local machine:
+   
+   ```bash
+   git clone https://github.com/ruleminer/InterpreTS.git
+   cd InterpreTS
+   ```
 
-```python
-from interpreTS import FeatureExtractor, TimeSeriesData
-import pandas as pd
+2. Install dependencies: Install the required packages listed in the `requirements.txt` file:
 
-# Sample data
-data = pd.DataFrame({'value': [1, 2, 3, 4, 5]})
+    ```python
+    pip install -r requirements.txt
+    ```
 
-# Initialize the feature extractor with desired features
-extractor = FeatureExtractor(features=['length'])
+3. Install InterpreTS: Run the following command to install InterpreTS:
 
-# Extract features
-features = extractor.extract_features(data)
-print("Extracted Features:", features)
+    ```python
+    pip install interpreTS
+    ```
+
+
+## Verifying Installation
+Once installed, you can verify the installation by running a simple feature extraction example:
+
+    ```python
+    from interpreTS.core.feature_extractor import FeatureExtractor, Features
+    import pandas as pd
+
+    # Sample time series data
+    data = pd.DataFrame({'value': [1, 2, 3, 4, 5]})
+    extractor = FeatureExtractor(features=[Features.LENGTH, Features.MEAN, Features.VARIANCE])
+    features = extractor.extract_features(data)
+    print("Extracted Features:\n", features)
+    ```
+
+## Additional Usage Example with Time Series Data
+You can also use InterpreTS with time-indexed data:
+
+    ```python
+
+    from interpreTS.core.time_series_data import TimeSeriesData
+    from interpreTS.core.feature_extractor import FeatureExtractor, Features
+    import pandas as pd
+
+    # Time-indexed data
+    data_with_date = pd.Series(
+        [5, 3, 6, 2, 7, 4, 8, 3, 9, 1],
+        index=pd.date_range("2023-01-01", periods=10, freq="D")
+    )
+    ts_data = TimeSeriesData(data_with_date)
+
+    # Feature extraction
+    extractor = FeatureExtractor(features=[Features.LENGTH, Features.MEAN, Features.VARIANCE])
+    features = extractor.extract_features(ts_data.data)
+    print("\nExtracted Features from Time Series Data:\n", features)
+    ```
+
+## Documentation
+
+Complete documentation is available in the [docs folder](./docs).
+
+
+## Issues and Support
+
+For any issues, please consult our [Issue Tracker](https://github.com/ruleminer/InterpreTS/issues) on GitHub.
\ No newline at end of file
diff --git a/docs/USAGE.md b/docs/USAGE.md
deleted file mode 100644
index c6f00c7..0000000
--- a/docs/USAGE.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# Usage Guide for InterpreTS
-
-This guide will walk you through using InterpreTS to extract features from time series data. Below are some examples demonstrating the primary functionalities of InterpreTS.
-
-## Basic Feature Extraction
-
-The following example demonstrates how to use FeatureExtractor to extract basic statistical and frequency-based features.
-
-```python
-from interpreTS import FeatureExtractor
-import pandas as pd
-
-# Sample time series data
-data = pd.DataFrame({'value': [1, 2, 3, 4, 5]})
-
-# Initialize the feature extractor with statistical and frequency features
-extractor = FeatureExtractor(features=['length'])
-
-# Extract features
-features = extractor.extract_features(data)
-print("Extracted Features:", features)
-
-```
\ No newline at end of file
diff --git a/docs/source/conf.py b/docs/source/conf.py
new file mode 100644
index 0000000..5ef0293
--- /dev/null
+++ b/docs/source/conf.py
@@ -0,0 +1,30 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Project information -----------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+import os
+import sys
+sys.path.insert(0, os.path.abspath('../../'))
+
+project = 'InterpreTS'
+copyright = '2024, Łukasz Wróbel, Sławomir Put, Martyna Żur, Martyna Kramarz, Jarosław Strzelczyk, Weronika Wołowczyk, Piotr Krupiński'
+author = 'Łukasz Wróbel, Sławomir Put, Martyna Żur, Martyna Kramarz, Jarosław Strzelczyk, Weronika Wołowczyk, Piotr Krupiński'
+release = '0.1.0'
+
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = ['sphinx.ext.autodoc']
+html_theme = 'sphinx_book_theme'
+templates_path = ['_templates']
+exclude_patterns = ['setup.py']
+suppress_warnings = ["autodoc.import_object", "autodoc"]
+
+# -- Options for HTML output -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+
+html_theme = 'alabaster'
+html_static_path = ['_static']
diff --git a/docs/source/index.rst b/docs/source/index.rst
new file mode 100644
index 0000000..c410a3f
--- /dev/null
+++ b/docs/source/index.rst
@@ -0,0 +1,20 @@
+.. InterpreTS documentation master file, created by
+   sphinx-quickstart on Wed Nov  6 22:11:49 2024.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+InterpreTS documentation
+========================
+
+Add your content using ``reStructuredText`` syntax. See the
+`reStructuredText <https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html>`_
+documentation for details.
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+
+   modules
+   interpreTS
+   tests
+   setup
diff --git a/docs/source/interpreTS.core.features.rst b/docs/source/interpreTS.core.features.rst
new file mode 100644
index 0000000..c748b14
--- /dev/null
+++ b/docs/source/interpreTS.core.features.rst
@@ -0,0 +1,93 @@
+interpreTS.core.features package
+================================
+
+Submodules
+----------
+
+interpreTS.core.features.feature\_length module
+-----------------------------------------------
+
+.. automodule:: interpreTS.core.features.feature_length
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+interpreTS.core.features.feature\_mean module
+---------------------------------------------
+
+.. automodule:: interpreTS.core.features.feature_mean
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+interpreTS.core.features.feature\_peak module
+---------------------------------------------
+
+.. automodule:: interpreTS.core.features.feature_peak
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+interpreTS.core.features.feature\_spikeness module
+--------------------------------------------------
+
+.. automodule:: interpreTS.core.features.feature_spikeness
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+interpreTS.core.features.feature\_std\_1st\_der module
+------------------------------------------------------
+
+.. automodule:: interpreTS.core.features.feature_std_1st_der
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+interpreTS.core.features.feature\_trough module
+-----------------------------------------------
+
+.. automodule:: interpreTS.core.features.feature_trough
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+interpreTS.core.features.feature\_variance module
+-------------------------------------------------
+
+.. automodule:: interpreTS.core.features.feature_variance
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+interpreTS.core.features.histogram\_dominant module
+---------------------------------------------------
+
+.. automodule:: interpreTS.core.features.histogram_dominant
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+interpreTS.core.features.seasonality\_strength module
+-----------------------------------------------------
+
+.. automodule:: interpreTS.core.features.seasonality_strength
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+interpreTS.core.features.trend\_strength module
+-----------------------------------------------
+
+.. automodule:: interpreTS.core.features.trend_strength
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Module contents
+---------------
+
+.. automodule:: interpreTS.core.features
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/docs/source/interpreTS.core.rst b/docs/source/interpreTS.core.rst
new file mode 100644
index 0000000..3b449f5
--- /dev/null
+++ b/docs/source/interpreTS.core.rst
@@ -0,0 +1,37 @@
+interpreTS.core package
+=======================
+
+Subpackages
+-----------
+
+.. toctree::
+   :maxdepth: 4
+
+   interpreTS.core.features
+
+Submodules
+----------
+
+interpreTS.core.feature\_extractor module
+-----------------------------------------
+
+.. automodule:: interpreTS.core.feature_extractor
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+interpreTS.core.time\_series\_data module
+-----------------------------------------
+
+.. automodule:: interpreTS.core.time_series_data
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Module contents
+---------------
+
+.. automodule:: interpreTS.core
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/docs/source/interpreTS.rst b/docs/source/interpreTS.rst
new file mode 100644
index 0000000..2dae6fa
--- /dev/null
+++ b/docs/source/interpreTS.rst
@@ -0,0 +1,19 @@
+interpreTS package
+==================
+
+Subpackages
+-----------
+
+.. toctree::
+   :maxdepth: 4
+
+   interpreTS.core
+   interpreTS.utils
+
+Module contents
+---------------
+
+.. automodule:: interpreTS
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/docs/source/interpreTS.utils.rst b/docs/source/interpreTS.utils.rst
new file mode 100644
index 0000000..08fe7c2
--- /dev/null
+++ b/docs/source/interpreTS.utils.rst
@@ -0,0 +1,29 @@
+interpreTS.utils package
+========================
+
+Submodules
+----------
+
+interpreTS.utils.data\_conversion module
+----------------------------------------
+
+.. automodule:: interpreTS.utils.data_conversion
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+interpreTS.utils.data\_validation module
+----------------------------------------
+
+.. automodule:: interpreTS.utils.data_validation
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Module contents
+---------------
+
+.. automodule:: interpreTS.utils
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/docs/source/modules.rst b/docs/source/modules.rst
new file mode 100644
index 0000000..d726b9a
--- /dev/null
+++ b/docs/source/modules.rst
@@ -0,0 +1,9 @@
+InterpreTS
+==========
+
+.. toctree::
+   :maxdepth: 4
+
+   interpreTS
+   setup
+   tests
diff --git a/docs/source/setup.rst b/docs/source/setup.rst
new file mode 100644
index 0000000..552eb49
--- /dev/null
+++ b/docs/source/setup.rst
@@ -0,0 +1,7 @@
+setup module
+============
+
+.. automodule:: setup
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/examples/core/basic_usage.ipynb b/examples/core/basic_usage.ipynb
index fd86a50..e981579 100644
--- a/examples/core/basic_usage.ipynb
+++ b/examples/core/basic_usage.ipynb
@@ -37,15 +37,26 @@
    "metadata": {},
    "outputs": [
     {
-     "ename": "ImportError",
-     "evalue": "attempted relative import beyond top-level package",
-     "output_type": "error",
-     "traceback": [
-      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
-      "\u001b[1;31mImportError\u001b[0m                               Traceback (most recent call last)",
-      "Cell \u001b[1;32mIn[2], line 8\u001b[0m\n\u001b[0;32m      6\u001b[0m sys\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39minsert(\u001b[38;5;241m0\u001b[39m, os\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39mabspath(os\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39mjoin(os\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39mdirname(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m__file__\u001b[39m\u001b[38;5;124m'\u001b[39m), \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m../../src\u001b[39m\u001b[38;5;124m'\u001b[39m)))\n\u001b[0;32m      7\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mcore\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mfeature_extractor\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m FeatureExtractor\n\u001b[1;32m----> 8\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mutils\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mdata_conversion\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m convert_to_time_series\n\u001b[0;32m      9\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mutils\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mdata_validation\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m validate_time_series_data\n\u001b[0;32m     11\u001b[0m \u001b[38;5;66;03m# Creating Sample Time Series Data\u001b[39;00m\n",
-      "File \u001b[1;32mc:\\Users\\slawek\\Desktop\\Praca naukowa\\InterpreTS\\src\\utils\\data_conversion.py:3\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mpandas\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mpd\u001b[39;00m\n\u001b[0;32m      2\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mnumpy\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mnp\u001b[39;00m\n\u001b[1;32m----> 3\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mcore\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mtime_series_data\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m TimeSeriesData\n\u001b[0;32m      5\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mconvert_to_time_series\u001b[39m(data):\n\u001b[0;32m      6\u001b[0m \u001b[38;5;250m    \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m      7\u001b[0m \u001b[38;5;124;03m    Convert input data to a TimeSeriesData object.\u001b[39;00m\n\u001b[0;32m      8\u001b[0m \u001b[38;5;124;03m    \u001b[39;00m\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m     28\u001b[0m \u001b[38;5;124;03m    >>> ts_data = convert_to_time_series(data)\u001b[39;00m\n\u001b[0;32m     29\u001b[0m \u001b[38;5;124;03m    \"\"\"\u001b[39;00m\n",
-      "\u001b[1;31mImportError\u001b[0m: attempted relative import beyond top-level package"
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Training Data:\n",
+      " 2023-01-01    1\n",
+      "2023-01-02    2\n",
+      "2023-01-03    3\n",
+      "2023-01-04    4\n",
+      "2023-01-05    5\n",
+      "2023-01-06    6\n",
+      "2023-01-07    7\n",
+      "Freq: D, dtype: int64\n",
+      "Test Data:\n",
+      " 2023-01-08     8\n",
+      "2023-01-09     9\n",
+      "2023-01-10    10\n",
+      "Freq: D, dtype: int64\n",
+      "\n",
+      "Extracted Features:\n",
+      "Length of the time series: 10\n"
      ]
     }
    ],
@@ -54,11 +65,11 @@
     "import sys\n",
     "import os\n",
     "\n",
-    "# Adding path to src directory so notebook can import modules\n",
-    "sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname('__file__'), '../../src')))\n",
+    "# Adding path to interpreTS directory so notebook can import modules\n",
+    "sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname('__file__'), '../../interpreTS')))\n",
     "from core.feature_extractor import FeatureExtractor\n",
-    "from src.utils.data_conversion import convert_to_time_series\n",
-    "from src.utils.data_validation import validate_time_series_data\n",
+    "from interpreTS.utils.data_conversion import convert_to_time_series\n",
+    "from interpreTS.utils.data_validation import validate_time_series_data\n",
     "\n",
     "# Creating Sample Time Series Data\n",
     "data = pd.Series([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], index=pd.date_range(\"2023-01-01\", periods=10))\n",
@@ -68,7 +79,6 @@
     "    validate_time_series_data(data)\n",
     "except (TypeError, ValueError) as e:\n",
     "    print(f\"Validation error: {e}\")\n",
-    "    sys.exit(1)  # Stops execution if data is invalid\n",
     "\n",
     "# Converting data to TimeSeriesData object\n",
     "ts_data = convert_to_time_series(data)\n",
@@ -88,6 +98,13 @@
     "print(\"\\nExtracted Features:\")\n",
     "print(\"Length of the time series:\", features['length'])\n"
    ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
   }
  ],
  "metadata": {
diff --git a/examples/core/feature_extraction.ipynb b/examples/core/feature_extraction.ipynb
index 5a3a5e2..94e1f23 100644
--- a/examples/core/feature_extraction.ipynb
+++ b/examples/core/feature_extraction.ipynb
@@ -33,34 +33,20 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": 1,
    "metadata": {},
    "outputs": [
     {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Resampled Data:\n",
-      " 2023-01-01    4.0\n",
-      "2023-01-03    4.0\n",
-      "2023-01-05    5.5\n",
-      "2023-01-07    5.5\n",
-      "2023-01-09    5.0\n",
-      "Freq: 2D, dtype: float64\n",
-      "\n",
-      "Features from Original Data:\n",
-      "{'length': 10}\n",
-      "\n",
-      "Features from Resampled Data:\n",
-      "{'length': 5}\n"
-     ]
-    },
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "c:\\Users\\slawek\\Desktop\\Praca naukowa\\InterpreTS\\src\\core\\time_series_data.py:53: FutureWarning: Index.is_all_dates is deprecated, will be removed in a future version.  check index.inferred_type instead\n",
-      "  if isinstance(self.data, pd.Series) and self.data.index.is_all_dates:\n"
+     "ename": "ModuleNotFoundError",
+     "evalue": "No module named 'interpreTS'",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[1;31mModuleNotFoundError\u001b[0m                       Traceback (most recent call last)",
+      "Cell \u001b[1;32mIn[1], line 9\u001b[0m\n\u001b[0;32m      7\u001b[0m sys\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39minsert(\u001b[38;5;241m0\u001b[39m, os\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39mabspath(os\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39mjoin(os\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39mdirname(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m__file__\u001b[39m\u001b[38;5;124m'\u001b[39m), \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m../../interpreTS\u001b[39m\u001b[38;5;124m'\u001b[39m)))\n\u001b[0;32m      8\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mcore\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mtime_series_data\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m TimeSeriesData\n\u001b[1;32m----> 9\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mcore\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mfeature_extractor\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m FeatureExtractor, Features\n\u001b[0;32m     11\u001b[0m \u001b[38;5;66;03m# Tworzenie przykładowych danych czasowych z indeksami czasowymi\u001b[39;00m\n\u001b[0;32m     12\u001b[0m data_with_date \u001b[38;5;241m=\u001b[39m pd\u001b[38;5;241m.\u001b[39mSeries(\n\u001b[0;32m     13\u001b[0m     [\u001b[38;5;241m5\u001b[39m, \u001b[38;5;241m3\u001b[39m, \u001b[38;5;241m6\u001b[39m, \u001b[38;5;241m2\u001b[39m, \u001b[38;5;241m7\u001b[39m, \u001b[38;5;241m4\u001b[39m, \u001b[38;5;241m8\u001b[39m, \u001b[38;5;241m3\u001b[39m, \u001b[38;5;241m9\u001b[39m, \u001b[38;5;241m1\u001b[39m],\n\u001b[0;32m     14\u001b[0m     index\u001b[38;5;241m=\u001b[39mpd\u001b[38;5;241m.\u001b[39mdate_range(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m2023-01-01\u001b[39m\u001b[38;5;124m\"\u001b[39m, periods\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m10\u001b[39m, freq\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mD\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m     15\u001b[0m )\n",
+      "File \u001b[1;32mc:\\Users\\slawek\\Desktop\\Praca naukowa\\InterpreTS\\interpreTS\\core\\feature_extractor.py:3\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mpandas\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mpd\u001b[39;00m\n\u001b[0;32m      2\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mnumpy\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mnp\u001b[39;00m\n\u001b[1;32m----> 3\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mfeatures\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mfeature_spikeness\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m calculate_spikeness\n\u001b[0;32m      4\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mfeatures\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mfeature_peak\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m calculate_peak\n\u001b[0;32m      5\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mfeatures\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mfeature_trough\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m calculate_trough\n",
+      "File \u001b[1;32mc:\\Users\\slawek\\Desktop\\Praca naukowa\\InterpreTS\\interpreTS\\core\\features\\feature_spikeness.py:3\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mpandas\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mpd\u001b[39;00m\n\u001b[0;32m      2\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mnumpy\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mnp\u001b[39;00m\n\u001b[1;32m----> 3\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01minterpreTS\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mutils\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mdata_validation\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m validate_time_series_data\n\u001b[0;32m      5\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcalculate_spikeness\u001b[39m(data):\n\u001b[0;32m      6\u001b[0m \u001b[38;5;250m    \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m      7\u001b[0m \u001b[38;5;124;03m    Calculate the spikeness (skewness) of a time series.\u001b[39;00m\n\u001b[0;32m      8\u001b[0m \n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m     31\u001b[0m \u001b[38;5;124;03m    0.0\u001b[39;00m\n\u001b[0;32m     32\u001b[0m \u001b[38;5;124;03m    \"\"\"\u001b[39;00m\n",
+      "\u001b[1;31mModuleNotFoundError\u001b[0m: No module named 'interpreTS'"
      ]
     }
    ],
@@ -70,33 +56,34 @@
     "import sys\n",
     "import os\n",
     "\n",
-    "# Adding path to src directory so notebook can import modules\n",
-    "sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname('__file__'), '../../src')))\n",
+    "# Adding path to interpreTS directory so notebook can import modules\n",
+    "sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname('__file__'), '../../interpreTS')))\n",
     "from core.time_series_data import TimeSeriesData\n",
-    "from core.feature_extractor import FeatureExtractor\n",
+    "from core.feature_extractor import FeatureExtractor, Features\n",
     "\n",
-    "# Creating sample time data with time index\n",
+    "# Tworzenie przykładowych danych czasowych z indeksami czasowymi\n",
     "data_with_date = pd.Series(\n",
     "    [5, 3, 6, 2, 7, 4, 8, 3, 9, 1],\n",
     "    index=pd.date_range(\"2023-01-01\", periods=10, freq=\"D\")\n",
     ")\n",
     "\n",
-    "# TimeSeriesData object initialization\n",
+    "# Inicjalizacja obiektu TimeSeriesData\n",
     "ts_data = TimeSeriesData(data_with_date)\n",
     "\n",
-    "# Resampling data to a two-day interval\n",
+    "# Resampling danych do interwału dwudniowego\n",
     "resampled_ts_data = ts_data.resample(\"2D\")\n",
     "print(\"Resampled Data:\\n\", resampled_ts_data.data)\n",
     "\n",
-    "# Extraction of several features from the original data\n",
-    "extractor = FeatureExtractor(features=['length'])  # Możesz dodać więcej cech w przyszłości\n",
+    "# Ekstrakcja kilku cech z oryginalnych danych\n",
+    "# Tutaj używamy stałych Features, zamiast ciągów znaków\n",
+    "extractor = FeatureExtractor(features=[Features.LENGTH, Features.MEAN, Features.VARIANCE])\n",
     "\n",
-    "# Invoking the feature extractor on the original data\n",
-    "features_original = extractor.extract_features(ts_data.data)\n",
+    "# Wywołanie ekstrakcji cech na oryginalnych danych\n",
+    "features_original = extractor.extract_features(data_with_date)\n",
     "print(\"\\nFeatures from Original Data:\")\n",
     "print(features_original)\n",
     "\n",
-    "# Calling the feature extractor on the resampled data\n",
+    "# Wywołanie ekstrakcji cech na danych resamplowanych\n",
     "features_resampled = extractor.extract_features(resampled_ts_data.data)\n",
     "print(\"\\nFeatures from Resampled Data:\")\n",
     "print(features_resampled)\n"
diff --git a/examples/core/features/feature_length.ipynb b/examples/core/features/feature_length.ipynb
index b70384d..0ce3b0d 100644
--- a/examples/core/features/feature_length.ipynb
+++ b/examples/core/features/feature_length.ipynb
@@ -4,31 +4,14 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Note: you may need to restart the kernel to use updated packages.\n"
-     ]
-    },
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "ERROR: Invalid requirement: '#': Expected package name at the start of dependency specifier\n",
-      "    #\n",
-      "    ^\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
-    "pip install pandas # type: ignore"
+    "pip install pandas"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 44,
+   "execution_count": null,
    "metadata": {},
    "outputs": [
     {
@@ -44,8 +27,8 @@
     "import sys\n",
     "import os\n",
     "\n",
-    "# Adding path to src directory so notebook can import modules\n",
-    "sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname('__file__'), '../../../src')))\n",
+    "# Adding path to interpreTS directory so notebook can import modules\n",
+    "sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname('__file__'), '../../../interpreTS')))\n",
     "from core.time_series_data import TimeSeriesData\n",
     "from core.feature_extractor import FeatureExtractor\n",
     "\n",
@@ -68,6 +51,13 @@
     "# Displaying results\n",
     "print(\"Length Feature:\", features['length'])"
    ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
   }
  ],
  "metadata": {
@@ -77,7 +67,15 @@
    "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",
    "version": "3.9.13"
   }
  },
diff --git a/examples/core/features/feature_mean.ipynb b/examples/core/features/feature_mean.ipynb
index 51db638..adb02a6 100644
--- a/examples/core/features/feature_mean.ipynb
+++ b/examples/core/features/feature_mean.ipynb
@@ -2,31 +2,9 @@
  "cells": [
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": null,
    "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Requirement already satisfied: pandas in c:\\users\\slawek\\appdata\\local\\programs\\python\\python39\\lib\\site-packages (1.2.0)\n",
-      "Requirement already satisfied: python-dateutil>=2.7.3 in c:\\users\\slawek\\appdata\\local\\programs\\python\\python39\\lib\\site-packages (from pandas) (2.9.0.post0)\n",
-      "Requirement already satisfied: pytz>=2017.3 in c:\\users\\slawek\\appdata\\local\\programs\\python\\python39\\lib\\site-packages (from pandas) (2024.1)\n",
-      "Requirement already satisfied: numpy>=1.16.5 in c:\\users\\slawek\\appdata\\local\\programs\\python\\python39\\lib\\site-packages (from pandas) (1.23.5)\n",
-      "Requirement already satisfied: six>=1.5 in c:\\users\\slawek\\appdata\\local\\programs\\python\\python39\\lib\\site-packages (from python-dateutil>=2.7.3->pandas) (1.16.0)\n",
-      "Note: you may need to restart the kernel to use updated packages.\n"
-     ]
-    },
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "WARNING: Ignoring invalid distribution -atplotlib (c:\\users\\slawek\\appdata\\local\\programs\\python\\python39\\lib\\site-packages)\n",
-      "WARNING: Ignoring invalid distribution -atplotlib (c:\\users\\slawek\\appdata\\local\\programs\\python\\python39\\lib\\site-packages)\n",
-      "WARNING: Ignoring invalid distribution -atplotlib (c:\\users\\slawek\\appdata\\local\\programs\\python\\python39\\lib\\site-packages)\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "pip install pandas"
    ]
@@ -49,8 +27,8 @@
     "import sys\n",
     "import os\n",
     "\n",
-    "# Adding path to src directory so notebook can import modules\n",
-    "sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname('__file__'), '../../../src')))\n",
+    "# Adding path to interpreTS directory so notebook can import modules\n",
+    "sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname('__file__'), '../../..')))\n",
     "from core.features.feature_mean import calculate_mean\n",
     "\n",
     "# Data preparation\n",
diff --git a/examples/core/features/feature_std_1st_der.ipynb b/examples/core/features/feature_std_1st_der.ipynb
new file mode 100644
index 0000000..2fd916a
--- /dev/null
+++ b/examples/core/features/feature_std_1st_der.ipynb
@@ -0,0 +1,64 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "The standard deviation of the first derivative of time series: 3.5128336140500593\n"
+     ]
+    }
+   ],
+   "source": [
+    "import pandas as pd\n",
+    "import sys\n",
+    "import os\n",
+    "\n",
+    "# Adding path to interpreTS directory so notebook can import modules\n",
+    "sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname('__file__'), '../../..')))\n",
+    "from core.features.feature_std_1st_der import calculate_std_1st_der\n",
+    "\n",
+    "# Data preparation\n",
+    "data = pd.Series([1, -1, 2, -3, 5])\n",
+    "\n",
+    "# Function call\n",
+    "std_1st_der = calculate_std_1st_der(data)\n",
+    "\n",
+    "# Displaying the result\n",
+    "print(f\"The standard deviation of the first derivative of time series: {std_1st_der}\")\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "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",
+   "version": "3.12.2"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/examples/core/features/feature_variance.ipynb b/examples/core/features/feature_variance.ipynb
new file mode 100644
index 0000000..7351093
--- /dev/null
+++ b/examples/core/features/feature_variance.ipynb
@@ -0,0 +1,57 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Variance of time series: 2.0\n"
+     ]
+    }
+   ],
+   "source": [
+    "import pandas as pd\n",
+    "import sys\n",
+    "import os\n",
+    "\n",
+    "# Adding path to interpreTS directory so notebook can import modules\n",
+    "sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname('__file__'), '../../../interpreTS')))\n",
+    "from core.features.feature_variance import calculate_variance\n",
+    "\n",
+    "# Data preparation\n",
+    "data = pd.Series([1, 2, 3, 4, 5])\n",
+    "\n",
+    "# Function call\n",
+    "variance = calculate_variance(data)\n",
+    "\n",
+    "# Displaying the result\n",
+    "print(f\"Variance of time series: {variance}\")\n"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "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",
+   "version": "3.12.2"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/github/workflows/dependabot.yml b/github/workflows/dependabot.yml
new file mode 100644
index 0000000..0619b7b
--- /dev/null
+++ b/github/workflows/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+  - package-ecosystem: "pip"
+    directory: "/" 
+    schedule:
+      interval: "weekly"
diff --git a/github/workflows/generate_docs.yml b/github/workflows/generate_docs.yml
new file mode 100644
index 0000000..1f5f3cc
--- /dev/null
+++ b/github/workflows/generate_docs.yml
@@ -0,0 +1,35 @@
+name: Generate Documentation
+
+on:
+  release:
+    types: [published]
+
+jobs:
+  build-docs:
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v3
+
+      - name: Set up Python
+        uses: actions/setup-python@v3
+        with:
+          python-version: '3.8' 
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install -r requirements.txt
+          pip install sphinx-book-theme
+
+      - name: Build documentation
+        run: |
+          sphinx-build -b html docs/source build
+
+      - name: Deploy to GitHub Pages
+        if: success()
+        uses: peaceiris/actions-gh-pages@v3
+        with:
+          github_token: ${{ secrets.GITHUB_TOKEN }}
+          publish_dir: docs/build
diff --git a/github/workflows/publish_to_pypi.yml b/github/workflows/publish_to_pypi.yml
new file mode 100644
index 0000000..bcc2726
--- /dev/null
+++ b/github/workflows/publish_to_pypi.yml
@@ -0,0 +1,33 @@
+name: Publish to PyPI
+
+on:
+  push:
+    tags:
+      - '*.*.*'
+
+jobs:
+  publish:
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v3
+
+      - name: Set up Python
+        uses: actions/setup-python@v3
+        with:
+          python-version: '3.x'
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install build twine
+
+      - name: Build package
+        run: python -m build
+
+      - name: Publish package
+        env:
+          TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
+          TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+        run: twine upload dist/*
diff --git a/github/workflows/security_check.yml b/github/workflows/security_check.yml
new file mode 100644
index 0000000..2fa56c1
--- /dev/null
+++ b/github/workflows/security_check.yml
@@ -0,0 +1,25 @@
+name: Security Check - Bandit
+
+on: [push, pull_request]
+
+jobs:
+  security:
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v3
+
+      - name: Set up Python
+        uses: actions/setup-python@v3
+        with:
+          python-version: '3.x'
+
+      - name: Install Bandit
+        run: |
+          python -m pip install --upgrade pip
+          pip install bandit
+
+      - name: Run Bandit
+        run: |
+          bandit -r .
diff --git a/src/__init__.py b/interpreTS/__init__.py
similarity index 83%
rename from src/__init__.py
rename to interpreTS/__init__.py
index 88da7a2..4d716d4 100644
--- a/src/__init__.py
+++ b/interpreTS/__init__.py
@@ -1,4 +1,5 @@
 from .core.feature_extractor import FeatureExtractor
+from .core.feature_extractor import Features
 from .core.time_series_data import TimeSeriesData
 from .utils.data_conversion import convert_to_time_series
 from .utils.data_validation import validate_time_series_data
\ No newline at end of file
diff --git a/src/core/__init__.py b/interpreTS/core/__init__.py
similarity index 100%
rename from src/core/__init__.py
rename to interpreTS/core/__init__.py
diff --git a/interpreTS/core/feature_extractor.py b/interpreTS/core/feature_extractor.py
new file mode 100644
index 0000000..f2cc130
--- /dev/null
+++ b/interpreTS/core/feature_extractor.py
@@ -0,0 +1,116 @@
+import pandas as pd
+import numpy as np
+from .features.feature_spikeness import calculate_spikeness
+from .features.feature_peak import calculate_peak
+from .features.feature_trough import calculate_trough
+from .features.feature_length import calculate_length
+from .features.feature_mean import calculate_mean
+from .features.seasonality_strength import calculate_seasonality_strength
+from .features.feature_variance import calculate_variance
+from .features.feature_std_1st_der import calculate_std_1st_der
+
+class Features:
+    LENGTH = 'length'
+    MEAN = 'mean'
+    PEAK = 'peak'
+    STD_1ST_DER = 'std_1st_der'
+    TROUGH = 'trough'
+    VARIANCE = 'variance'
+    SPIKENESS = 'spikeness'
+    CALCULATE_SEASONALITY_STRENGTH = 'seasonality_strength'
+
+class FeatureExtractor:
+    def __init__(self, features=None, feature_params=None, window_size=5, stride=1, id_column=None, sort_column=None):
+        """
+        Initialize the FeatureExtractor with a list of features to calculate and optional parameters for each feature.
+        
+        Parameters
+        ----------
+        features : list of Features constants, optional
+            A list of features to calculate. Default is None, which calculates all available features.
+        feature_params : dict, optional
+            Parameters for specific features, where keys are feature names and values are dicts of parameters.
+        window_size : int, optional
+            The size of the window for feature extraction (default is 5).
+        stride : int, optional
+            The step size for moving the window (default is 1).
+        id_column : str, optional
+            The name of the column used to identify different time series (optional).
+        sort_column : str, optional
+            The column to sort by before feature extraction (optional).
+        """
+        
+        self.features = features if features is not None else [Features.LENGTH]
+        self.feature_params = feature_params if feature_params is not None else {}
+        self.window_size = window_size
+        self.stride = stride
+        self.id_column = id_column
+        self.sort_column = sort_column
+
+        # Map of feature names to calculation functions
+        self.feature_functions = {
+            Features.LENGTH: calculate_length,
+            Features.MEAN: calculate_mean,
+            Features.PEAK: calculate_peak,
+            Features.STD_1ST_DER: calculate_std_1st_der,
+            Features.TROUGH: calculate_trough,
+            Features.VARIANCE: calculate_variance,
+            Features.SPIKENESS: calculate_spikeness,
+            Features.CALCULATE_SEASONALITY_STRENGTH: calculate_seasonality_strength,
+        }
+
+    def extract_features(self, data):
+        """
+        Extract features from a time series dataset.
+
+        Parameters
+        ----------
+        data : pd.DataFrame or pd.Series
+            The time series data for which features are to be extracted.
+
+        Returns
+        -------
+        pd.DataFrame
+            A DataFrame containing calculated features for each window.
+        """
+        # Convert Series to DataFrame with 'value' column if necessary
+        if isinstance(data, pd.Series):
+            data = data.to_frame(name='value')
+
+        # Sort data if sort_column is provided
+        if self.sort_column:
+            data = data.sort_values(by=self.sort_column)
+
+        # Group by id_column if provided``
+        grouped_data = data.groupby(self.id_column) if self.id_column else [(None, data)]
+
+        results = []
+
+        # Process each group separately
+        for _, group in grouped_data:
+            for start in range(0, len(group) - self.window_size + 1, self.stride):
+                window = group.iloc[start:start + self.window_size]
+                extracted_features = {self.id_column: group[self.id_column].iloc[0]} if self.id_column else {}
+
+                # Calculate each selected feature on the window
+                for feature_name in self.features:
+                    if feature_name in self.feature_functions:
+                        params = self.feature_params.get(feature_name, {})
+                        feature_data = window['value'] if 'value' in window else window.iloc[:, 0]
+                        extracted_features[feature_name] = self.feature_functions[feature_name](feature_data, **params)
+
+                results.append(extracted_features)
+
+        return pd.DataFrame(results) 
+
+    @staticmethod
+    def available_features():
+        """
+        Returns a list of all available features.
+        
+        Returns
+        -------
+        list
+            List of feature names.
+        """
+        return list(Features.__dict__.values())
diff --git a/src/core/features/__init__.py b/interpreTS/core/features/__init__.py
similarity index 100%
rename from src/core/features/__init__.py
rename to interpreTS/core/features/__init__.py
diff --git a/src/core/features/feature_length.py b/interpreTS/core/features/feature_length.py
similarity index 92%
rename from src/core/features/feature_length.py
rename to interpreTS/core/features/feature_length.py
index 3b1fe7b..9cd9ea7 100644
--- a/src/core/features/feature_length.py
+++ b/interpreTS/core/features/feature_length.py
@@ -1,4 +1,4 @@
-from src.utils.data_validation import validate_time_series_data
+from interpreTS.utils.data_validation import validate_time_series_data
 
 def calculate_length(data):
     """
diff --git a/src/core/features/feature_mean.py b/interpreTS/core/features/feature_mean.py
similarity index 92%
rename from src/core/features/feature_mean.py
rename to interpreTS/core/features/feature_mean.py
index 458a8f6..a31cc94 100644
--- a/src/core/features/feature_mean.py
+++ b/interpreTS/core/features/feature_mean.py
@@ -1,6 +1,6 @@
 import pandas as pd
 import numpy as np
-from src.utils.data_validation import validate_time_series_data
+from interpreTS.utils.data_validation import validate_time_series_data
 
 def calculate_mean(data):
     """
diff --git a/interpreTS/core/features/feature_peak.py b/interpreTS/core/features/feature_peak.py
new file mode 100644
index 0000000..0650ed7
--- /dev/null
+++ b/interpreTS/core/features/feature_peak.py
@@ -0,0 +1,51 @@
+import pandas as pd
+import numpy as np
+from interpreTS.utils.data_validation import validate_time_series_data
+
+
+def calculate_peak(data, start=None, end=None):
+    """
+    Calculate the local maximum of a time series within an optional range.
+
+    Parameters
+    ----------
+    data : pd.Series or np.ndarray
+        The time series data for which the maximum value is to be calculated.
+    start : int, str, or None, optional
+        The starting index, timestamp, or position for slicing the data.
+        If None, the series starts from the beginning.
+    end : int, str, or None, optional
+        The ending index, timestamp, or position for slicing the data.
+        If None, the series ends at the last value.
+
+    Returns
+    -------
+    float
+        The local maximum of the specified range in the provided time series.
+
+    Raises
+    ------
+    TypeError
+        If the data is not a valid time series type.
+    ValueError
+        If the data contains NaN values.
+
+    Examples
+    --------
+    >>> import pandas as pd
+    >>> data = pd.Series([1, 2, 5, 4, 7])
+    >>> calculate_peak(data)
+    7.0
+    >>> calculate_peak(data, start=1, end=3)
+    5.0
+    """
+
+    # Validate the time series without requiring a DateTime index
+    validate_time_series_data(data, require_datetime_index=False)
+
+    # Slice the data based on start and end, if provided
+    if start is not None or end is not None:
+        data = data[start:end]
+
+    # Calculate and return the maximum, handling empty series by returning NaN
+    return data.max() if len(data) > 0 else np.nan
diff --git a/interpreTS/core/features/feature_spikeness.py b/interpreTS/core/features/feature_spikeness.py
new file mode 100644
index 0000000..546e85d
--- /dev/null
+++ b/interpreTS/core/features/feature_spikeness.py
@@ -0,0 +1,41 @@
+import pandas as pd
+import numpy as np
+from interpreTS.utils.data_validation import validate_time_series_data
+
+def calculate_spikeness(data):
+    """
+    Calculate the spikeness (skewness) of a time series.
+
+    Parameters
+    ----------
+    data : pd.Series or np.ndarray
+        The time series data for which the spikeness is to be calculated.
+
+    Returns
+    -------
+    float
+        The spikeness (skewness) of the provided time series.
+
+    Raises
+    ------
+    TypeError
+        If the data is not a valid time series type.
+    ValueError
+        If the data contains NaN values.
+
+    Examples
+    --------
+    >>> import pandas as pd
+    >>> data = pd.Series([1, 2, 3, 4, 5])
+    >>> calculate_spikeness(data)
+    0.0
+    """
+    # Validate the time series without requiring a DateTime index
+    validate_time_series_data(data, require_datetime_index=False)
+    
+    # Ensure data is a pandas Series for compatibility with .skew()
+    if isinstance(data, np.ndarray):
+        data = pd.Series(data)
+    
+    # Return spikeness (skewness), handling empty series by returning NaN
+    return data.skew() if len(data) > 0 else np.nan
diff --git a/interpreTS/core/features/feature_std_1st_der.py b/interpreTS/core/features/feature_std_1st_der.py
new file mode 100644
index 0000000..968ccb5
--- /dev/null
+++ b/interpreTS/core/features/feature_std_1st_der.py
@@ -0,0 +1,37 @@
+import pandas as pd
+import numpy as np
+from interpreTS.utils.data_validation import validate_time_series_data
+
+def calculate_std_1st_der(data):
+    """
+    Calculate the standard deviation of the first derivative of a time series.
+    
+    Parameters
+    ----------
+    data : pd.Series or np.ndarray
+        The time series data for which the standard deviation of the first derivative is to be calculated.
+        
+    Returns
+    -------
+    float
+        The standard deviation of the first derivative of the provided time series.
+        
+    Raises
+    ------
+    TypeError
+        If the data is not a valid time series type.
+    ValueError
+        If the data contains NaN values.
+        
+    Examples
+    --------
+    >>> import pandas as pd
+    >>> data = pd.Series([1, 2, 3, 4, 5])
+    >>> calculate_std_1st_derivative(data)
+    0.0
+    """
+    # Validate the time series without requiring a DateTime index
+    validate_time_series_data(data, require_datetime_index=False)
+    
+    # Calculate and return the standard deviation of the first derivative, handling empty series by returning NaN
+    return np.std(np.gradient(data)) if len(data) > 0 else np.nan
\ No newline at end of file
diff --git a/interpreTS/core/features/feature_trough.py b/interpreTS/core/features/feature_trough.py
new file mode 100644
index 0000000..c34d600
--- /dev/null
+++ b/interpreTS/core/features/feature_trough.py
@@ -0,0 +1,51 @@
+import pandas as pd
+import numpy as np
+from interpreTS.utils.data_validation import validate_time_series_data
+
+
+def calculate_trough(data, start=None, end=None):
+    """
+    Calculate the local minimum of a time series within an optional range.
+
+    Parameters
+    ----------
+    data : pd.Series or np.ndarray
+        The time series data for which the minimum value is to be calculated.
+    start : int, str, or None, optional
+        The starting index, timestamp, or position for slicing the data.
+        If None, the series starts from the beginning.
+    end : int, str, or None, optional
+        The ending index, timestamp, or position for slicing the data.
+        If None, the series ends at the last value.
+
+    Returns
+    -------
+    float
+        The local minimum of the specified range in the provided time series.
+
+    Raises
+    ------
+    TypeError
+        If the data is not a valid time series type.
+    ValueError
+        If the data contains NaN values.
+
+    Examples
+    --------
+    >>> import pandas as pd
+    >>> data = pd.Series([1, 2, 5, 4, 3])
+    >>> calculate_trough(data)
+    1.0
+    >>> calculate_trough(data, start=1, end=3)
+    2.0
+    """
+
+    # Validate the time series without requiring a DateTime index
+    validate_time_series_data(data, require_datetime_index=False)
+
+    # Slice the data based on start and end, if provided
+    if start is not None or end is not None:
+        data = data[start:end]
+
+    # Calculate and return the minimum, handling empty series by returning NaN
+    return data.min() if len(data) > 0 else np.nan
diff --git a/interpreTS/core/features/feature_variance.py b/interpreTS/core/features/feature_variance.py
new file mode 100644
index 0000000..e8daa5a
--- /dev/null
+++ b/interpreTS/core/features/feature_variance.py
@@ -0,0 +1,41 @@
+import pandas as pd
+import numpy as np
+from interpreTS.utils.data_validation import validate_time_series_data
+
+def calculate_variance(data, ddof=0):
+    """
+    Calculate the variance value of a time series with specified degrees of freedom.
+    
+    Parameters
+    ----------
+    data : pd.Series or np.ndarray
+        The time series data for which the variance is to be calculated.
+    ddof : int, optional
+        The degrees of freedom to use when calculating the variance. Default is 0 (population variance).
+        
+    Returns
+    -------
+    float
+        The variance of the provided time series with specified degrees of freedom.
+        
+    Raises
+    ------
+    TypeError
+        If the data is not a valid time series type.
+    ValueError
+        If the data contains NaN values.
+        
+    Examples
+    --------
+    >>> import pandas as pd
+    >>> data = pd.Series([1, 2, 3, 4, 5])
+    >>> calculate_variance(data)
+    2.5
+    >>> calculate_variance(data, ddof=0)  # Population variance
+    2.0
+    """
+    # Validate the time series without requiring a DateTime index
+    validate_time_series_data(data, require_datetime_index=False)
+    
+    # Calculate and return the variance with specified ddof, handling empty series by returning NaN
+    return np.var(data, ddof=ddof) if len(data) > 0 else np.nan
\ No newline at end of file
diff --git a/interpreTS/core/features/histogram_dominant.py b/interpreTS/core/features/histogram_dominant.py
new file mode 100644
index 0000000..f8b81e0
--- /dev/null
+++ b/interpreTS/core/features/histogram_dominant.py
@@ -0,0 +1,55 @@
+import pandas as pd
+import numpy as np
+from interpreTS.utils.data_validation import validate_time_series_data
+
+def calculate_dominant(data, bins=10, return_bin_center=False):
+    """
+    Calculate the dominant value (mode) of a time series histogram.
+
+    Parameters
+    ----------
+    data : pd.Series or np.ndarray
+        The time series data for which the dominant value is to be calculated.
+    bins : int, optional
+        The number of bins to use for creating the histogram, by default 10.
+
+    Returns
+    -------
+    float
+        The dominant value (mode) of the histogram.
+        
+    Raises
+    ------
+    TypeError
+        If the data is not a valid time series type.
+    ValueError
+        If the data contains NaN values.
+        
+    Examples
+    --------
+    >>> import pandas as pd
+    >>> data = pd.Series([1, 1, 2, 3, 3, 3, 4, 5])
+    >>> calculate_dominant(data)
+    3.0
+    """
+    # Validate the time series data
+    validate_time_series_data(data, require_datetime_index=False)
+    
+    if len(data) == 0:
+        return np.nan
+    
+    # Get unique values and use them as bin edges
+    unique_values = np.unique(data)
+    bins = np.append(unique_values, unique_values[-1] + 1)  # Add an extra edge to cover the last bin
+
+    # Calculate histogram with specific bin edges
+    counts, bin_edges = np.histogram(data, bins=bins)
+    max_bin_index = np.argmax(counts)
+
+    # Calculate the center or lower bound of the bin with the highest frequency
+    if return_bin_center:
+        dominant_value = (bin_edges[max_bin_index] + bin_edges[max_bin_index + 1]) / 2
+    else:
+        dominant_value = bin_edges[max_bin_index]
+    
+    return dominant_value
\ No newline at end of file
diff --git a/interpreTS/core/features/seasonality_strength.py b/interpreTS/core/features/seasonality_strength.py
new file mode 100644
index 0000000..47cb55c
--- /dev/null
+++ b/interpreTS/core/features/seasonality_strength.py
@@ -0,0 +1,51 @@
+import pandas as pd
+import numpy as np
+from statsmodels.tsa.stattools import acf
+from interpreTS.utils.data_validation import validate_time_series_data
+
+def calculate_seasonality_strength(data, period=2, max_lag=12):
+    """
+    Calculate the strength of the seasonality in a time series based on autocorrelation.
+
+    Parameters
+    ----------
+    data : pd.Series or np.ndarray
+        The time series data for which the seasonality strength is to be calculated.
+    period : int, optional
+        The periodic interval to check for seasonality (default is 2).
+    max_lag : int, optional
+        The maximum number of lags to consider for autocorrelation (default is 12).
+
+    Returns
+    -------
+    float
+        The seasonality strength, ranging from 0 to 1, where 1 indicates strong seasonality.
+
+    Raises
+    ------
+    TypeError
+        If the data is not a valid time series type.
+    ValueError
+        If the data contains NaN values or is too short to calculate seasonality.
+
+    Examples
+    --------
+    >>> import pandas as pd
+    >>> data = pd.Series([1, 2, 3, 2, 1, 2, 3, 2, 1, 2, 3, 2], index=pd.date_range("2023-01-01", periods=12, freq="M"))
+    >>> calculate_seasonality_strength(data, period=3)
+    0.75
+    """
+    # Validate the time series data
+    validate_time_series_data(data, require_datetime_index=False)
+    
+    # Handle empty or insufficient data
+    if len(data) < period + 1:
+        return np.nan
+    
+    # Calculate the autocorrelation of the data
+    autocorr_values = acf(data, nlags=max(max_lag, period), fft=True)
+    
+    # The seasonality strength is based on the autocorrelation at the specified period
+    seasonality_strength = autocorr_values[period] if period < len(autocorr_values) else 0.0
+    
+    return seasonality_strength
diff --git a/interpreTS/core/features/trend_strength.py b/interpreTS/core/features/trend_strength.py
new file mode 100644
index 0000000..7745fd4
--- /dev/null
+++ b/interpreTS/core/features/trend_strength.py
@@ -0,0 +1,50 @@
+import pandas as pd
+import numpy as np
+from scipy.stats import linregress
+from interpreTS.utils.data_validation import validate_time_series_data
+
+def calculate_trend_strength(data):
+    """
+    Calculate the strength of the trend in a time series using linear regression.
+
+    Parameters
+    ----------
+    data : pd.Series or np.ndarray
+        The time series data for which the trend strength is to be calculated.
+        
+    Returns
+    -------
+    float
+        The R-squared value representing the strength of the trend (0 to 1).
+        
+    Raises
+    ------
+    TypeError
+        If the data is not a valid time series type.
+    ValueError
+        If the data contains NaN values.
+        
+    Examples
+    --------
+    >>> import pandas as pd
+    >>> data = pd.Series([1, 2, 3, 4, 5])
+    >>> calculate_trend_strength(data)
+    1.0
+    """
+    # Validate the time series data
+    validate_time_series_data(data, require_datetime_index=False)
+    
+    # Handle empty or insufficient data by returning NaN
+    if len(data) < 2:
+        return np.nan
+    
+    # Generate an index as a proxy for time
+    x = np.arange(len(data))
+    
+    # Fit a linear regression and calculate the R-squared value
+    slope, intercept, r_value, p_value, std_err = linregress(x, data)
+    
+    # R-squared value as trend strength
+    trend_strength = r_value ** 2
+    
+    return trend_strength
\ No newline at end of file
diff --git a/src/core/time_series_data.py b/interpreTS/core/time_series_data.py
similarity index 94%
rename from src/core/time_series_data.py
rename to interpreTS/core/time_series_data.py
index 53d26e3..e7eee0c 100644
--- a/src/core/time_series_data.py
+++ b/interpreTS/core/time_series_data.py
@@ -50,10 +50,10 @@ def resample(self, interval):
         """
         
         
-        if isinstance(self.data, pd.Series) and self.data.index.is_all_dates:
-            resampled_data = self.data.resample(interval).mean()
-        else:
+        if not isinstance(self.data.index, pd.DatetimeIndex):
             raise ValueError("Data must have a DateTime index for resampling.")
+
+        resampled_data = self.data.resample(interval).mean()
         
         return TimeSeriesData(resampled_data)
 
diff --git a/src/utils/__init__.py b/interpreTS/utils/__init__.py
similarity index 100%
rename from src/utils/__init__.py
rename to interpreTS/utils/__init__.py
diff --git a/src/utils/data_conversion.py b/interpreTS/utils/data_conversion.py
similarity index 100%
rename from src/utils/data_conversion.py
rename to interpreTS/utils/data_conversion.py
diff --git a/src/utils/data_validation.py b/interpreTS/utils/data_validation.py
similarity index 66%
rename from src/utils/data_validation.py
rename to interpreTS/utils/data_validation.py
index 04f7b7d..c1e2a1d 100644
--- a/src/utils/data_validation.py
+++ b/interpreTS/utils/data_validation.py
@@ -23,25 +23,20 @@ def validate_time_series_data(data, require_datetime_index=False):
         If data is not a pd.Series, pd.DataFrame, or np.ndarray.
     ValueError
         If the data contains NaN values or lacks a DateTime index when required.
-        
-    Examples
-    --------
-    >>> import pandas as pd
-    >>> data = pd.Series([1, 2, 3, 4, 5])
-    >>> validate_time_series_data(data)
-    True
     """
     
+    # Sprawdzenie typu danych
     if not isinstance(data, (pd.Series, pd.DataFrame, np.ndarray)):
         raise TypeError("Data must be a pandas Series, DataFrame, or numpy array.")
     
+    # Walidacja dla danych w formacie Series lub DataFrame
     if isinstance(data, (pd.Series, pd.DataFrame)):
+        # Sprawdzenie wartości NaN
         if data.isnull().any().any():
             raise ValueError("Data contains NaN values.")
-        if require_datetime_index:
-            if isinstance(data, pd.Series) and not data.index.is_all_dates:
-                raise ValueError("Series must have a DateTime index for time-based operations.")
-            elif isinstance(data, pd.DataFrame) and not isinstance(data.index, pd.DatetimeIndex):
-                raise ValueError("DataFrame must have a DateTime index for time-based operations.")
+        
+        # Sprawdzenie typu indeksu, jeśli wymagany jest DateTimeIndex
+        if require_datetime_index and not isinstance(data.index, pd.DatetimeIndex):
+            raise ValueError("Data must have a DateTime index for time-based operations.")
     
     return True
diff --git a/make.bat b/make.bat
new file mode 100644
index 0000000..7776ee5
--- /dev/null
+++ b/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=docs/source
+set BUILDDIR=build
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+	echo.
+	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+	echo.installed, then set the SPHINXBUILD environment variable to point
+	echo.to the full path of the 'sphinx-build' executable. Alternatively you
+	echo.may add the Sphinx directory to PATH.
+	echo.
+	echo.If you don't have Sphinx installed, grab it from
+	echo.https://www.sphinx-doc.org/
+	exit /b 1
+)
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/requirements.txt b/requirements.txt
index 03adf8e..1caba76 100644
Binary files a/requirements.txt and b/requirements.txt differ
diff --git a/setup.py b/setup.py
index f33c243..62a6975 100644
--- a/setup.py
+++ b/setup.py
@@ -6,20 +6,27 @@
     packages=find_packages(),
     install_requires=[
         "pandas>=1.0.0",
-        "numpy>=1.18.0"
+        "numpy",
+        "statsmodels",
     ],
     description="Feature extraction from time series to support the creation of interpretable and explainable predictive models.",
     long_description=open("docs/README.md").read(),
     long_description_content_type="text/markdown",
-    author="Your Name",
-    author_email="your.email@example.com",
+    author=["Łukasz Wróbel", "Sławomir Put", "Martyna Żur", "Martyna Kramarz", "Jarosław Strzelczyk", "Weronika Wołowczyk", "Piotr Krupiński"],
     url="https://github.com/ruleminer/InterpreTS",
     classifiers=[
+        "Development Status :: 4 - Beta",
+        "License :: OSI Approved :: GNU Affero General Public License v3",
         "Programming Language :: Python :: 3",
-        "License :: OSI Approved :: MIT License",
-        "Operating System :: OS Independent",
+        "Operating System :: Microsoft :: Windows",
+        "Operating System :: Unix",
+        "Topic :: Scientific/Engineering",
+        "Topic :: Scientific/Engineering :: Artificial Intelligence",
+        "Intended Audience :: Developers",
+        "Intended Audience :: Science/Research",
     ],
     python_requires=">=3.8",
+    test_suite="tests",
     keywords="time series feature extraction interpretability explainability machine learning",
     project_urls={
         "Documentation": "https://github.com/ruleminer/InterpreTS/docs",
diff --git a/src/core/feature_extractor.py b/src/core/feature_extractor.py
deleted file mode 100644
index c4bcc92..0000000
--- a/src/core/feature_extractor.py
+++ /dev/null
@@ -1,54 +0,0 @@
-from .features.feature_length import calculate_length
-from .features.feature_mean import calculate_mean
-
-class FeatureExtractor:
-    """
-    A class to manage and execute feature extraction on time series data.
-    """
-
-    def __init__(self, features=None):
-        """
-        Initialize the FeatureExtractor with a list of features to calculate.
-        
-        Parameters
-        ----------
-        features : list of str, optional
-            A list of features to calculate. Default is None, which calculates all available features.
-        """
-        
-        
-        self.features = features if features is not None else ['length']
-
-    def extract_features(self, data):
-        """
-        Extract features from a time series dataset.
-        
-        Parameters
-        ----------
-        data : pd.Series or np.ndarray
-            The time series data for which features are to be extracted.
-            
-        Returns
-        -------
-        dict
-            A dictionary containing calculated features and their values.
-            
-        Examples
-        --------
-        >>> import pandas as pd
-        >>> data = pd.Series([1, 2, 3, 4, 5])
-        >>> extractor = FeatureExtractor(features=['length'])
-        >>> extractor.extract_features(data)
-        {'length': 5}
-        """
-        
-        
-        extracted_features = {}
-        
-        if 'length' in self.features:
-            extracted_features['length'] = calculate_length(data)
-
-        if 'mean' in self.features:
-            extracted_features['mean'] = calculate_mean(data)
-            
-        return extracted_features


Test patch:
diff --git a/docs/source/tests.core.features.rst b/docs/source/tests.core.features.rst
new file mode 100644
index 0000000..5de257c
--- /dev/null
+++ b/docs/source/tests.core.features.rst
@@ -0,0 +1,93 @@
+tests.core.features package
+===========================
+
+Submodules
+----------
+
+tests.core.features.test\_feature\_length module
+------------------------------------------------
+
+.. automodule:: tests.core.features.test_feature_length
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+tests.core.features.test\_feature\_mean module
+----------------------------------------------
+
+.. automodule:: tests.core.features.test_feature_mean
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+tests.core.features.test\_feature\_peak module
+----------------------------------------------
+
+.. automodule:: tests.core.features.test_feature_peak
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+tests.core.features.test\_feature\_spikeness module
+---------------------------------------------------
+
+.. automodule:: tests.core.features.test_feature_spikeness
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+tests.core.features.test\_feature\_std\_1st\_der module
+-------------------------------------------------------
+
+.. automodule:: tests.core.features.test_feature_std_1st_der
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+tests.core.features.test\_feature\_trough module
+------------------------------------------------
+
+.. automodule:: tests.core.features.test_feature_trough
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+tests.core.features.test\_feature\_variance module
+--------------------------------------------------
+
+.. automodule:: tests.core.features.test_feature_variance
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+tests.core.features.test\_histogram\_dominant module
+----------------------------------------------------
+
+.. automodule:: tests.core.features.test_histogram_dominant
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+tests.core.features.test\_seasonality\_strength module
+------------------------------------------------------
+
+.. automodule:: tests.core.features.test_seasonality_strength
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+tests.core.features.test\_trend\_strength module
+------------------------------------------------
+
+.. automodule:: tests.core.features.test_trend_strength
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Module contents
+---------------
+
+.. automodule:: tests.core.features
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/docs/source/tests.core.rst b/docs/source/tests.core.rst
new file mode 100644
index 0000000..dc3ed09
--- /dev/null
+++ b/docs/source/tests.core.rst
@@ -0,0 +1,37 @@
+tests.core package
+==================
+
+Subpackages
+-----------
+
+.. toctree::
+   :maxdepth: 4
+
+   tests.core.features
+
+Submodules
+----------
+
+tests.core.test\_feature\_extractor module
+------------------------------------------
+
+.. automodule:: tests.core.test_feature_extractor
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+tests.core.test\_time\_series\_data module
+------------------------------------------
+
+.. automodule:: tests.core.test_time_series_data
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Module contents
+---------------
+
+.. automodule:: tests.core
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/docs/source/tests.rst b/docs/source/tests.rst
new file mode 100644
index 0000000..32278f3
--- /dev/null
+++ b/docs/source/tests.rst
@@ -0,0 +1,19 @@
+tests package
+=============
+
+Subpackages
+-----------
+
+.. toctree::
+   :maxdepth: 4
+
+   tests.core
+   tests.utils
+
+Module contents
+---------------
+
+.. automodule:: tests
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/docs/source/tests.utils.rst b/docs/source/tests.utils.rst
new file mode 100644
index 0000000..3570de0
--- /dev/null
+++ b/docs/source/tests.utils.rst
@@ -0,0 +1,29 @@
+tests.utils package
+===================
+
+Submodules
+----------
+
+tests.utils.test\_convert\_to\_time\_series module
+--------------------------------------------------
+
+.. automodule:: tests.utils.test_convert_to_time_series
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+tests.utils.test\_validate\_time\_series\_data module
+-----------------------------------------------------
+
+.. automodule:: tests.utils.test_validate_time_series_data
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Module contents
+---------------
+
+.. automodule:: tests.utils
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/github/workflows/python-tests.yml b/github/workflows/python-tests.yml
new file mode 100644
index 0000000..64404bb
--- /dev/null
+++ b/github/workflows/python-tests.yml
@@ -0,0 +1,29 @@
+name: Python Tests
+
+on:
+  pull_request:
+    branches:
+      - main 
+      - development 
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v3
+
+      - name: Set up Python
+        uses: actions/setup-python@v3
+        with:
+          python-version: '3.x'  # dostosuj wersję do swojego projektu
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install -r requirements.txt 
+
+      - name: Run tests
+        run: |
+          pytest tests  
diff --git a/tests/core/features/test_feature_length.py b/tests/core/features/test_feature_length.py
index 4528884..ee79be7 100644
--- a/tests/core/features/test_feature_length.py
+++ b/tests/core/features/test_feature_length.py
@@ -1,7 +1,7 @@
 import pytest
 import pandas as pd
 import numpy as np
-from src.core.features.feature_length import calculate_length
+from interpreTS.core.features.feature_length import calculate_length
 
 def test_calculate_length_series():
     """
diff --git a/tests/core/features/test_feature_mean.py b/tests/core/features/test_feature_mean.py
index 28a4faf..9cefe56 100644
--- a/tests/core/features/test_feature_mean.py
+++ b/tests/core/features/test_feature_mean.py
@@ -1,7 +1,7 @@
 import pytest
 import pandas as pd
 import numpy as np
-from src.core.features.feature_mean import calculate_mean
+from interpreTS.core.features.feature_mean import calculate_mean
 
 def test_calculate_mean():
     """
diff --git a/tests/core/features/test_feature_peak.py b/tests/core/features/test_feature_peak.py
new file mode 100644
index 0000000..d98db7b
--- /dev/null
+++ b/tests/core/features/test_feature_peak.py
@@ -0,0 +1,54 @@
+import pytest
+import pandas as pd
+import numpy as np
+from interpreTS.core.features.feature_peak import calculate_peak
+
+def test_calculate_peak():
+    """
+    Test that calculate_peak correctly calculates the peak (maximum) value of a time series.
+    """
+    data = pd.Series([1, 2, 3, 4, 5])
+    assert calculate_peak(data) == 5.0, "The peak value should be 5.0"
+
+def test_calculate_peak_empty_series():
+    """
+    Test that calculate_peak returns NaN for an empty time series.
+    """
+    data = pd.Series([])
+    assert pd.isna(calculate_peak(data)), "The peak of an empty series should be NaN"
+
+def test_calculate_peak_numpy_array():
+    """
+    Test that calculate_peak correctly calculates the peak (maximum) value of a numpy array.
+    """
+    data = np.array([1, 2, 3, 4, 5])
+    assert calculate_peak(data) == 5.0, "The peak value should be 5.0 for numpy array"
+
+def test_calculate_peak_with_negative_values():
+    """
+    Test that calculate_peak correctly calculates the peak (maximum) for a series with negative values.
+    """
+    data = pd.Series([-1, -2, -3, -4, -5])
+    assert calculate_peak(data) == -1.0, "The peak value should be -1.0"
+
+def test_calculate_peak_with_start_and_end():
+    """
+    Test that calculate_peak correctly calculates the peak within a specified start and end range.
+    """
+    data = pd.Series([1, 2, 5, 4, 3])
+    assert calculate_peak(data, start=1, end=4) == 5.0, "The peak value within index range 1 to 4 should be 5.0"
+
+def test_calculate_peak_with_out_of_range_indices():
+    """
+    Test that calculate_peak handles out-of-range start and end indices gracefully.
+    """
+    data = pd.Series([1, 2, 3, 4, 5])
+    assert np.isnan(calculate_peak(data, start=10)), "The peak with out-of-range start index should be NaN"
+    assert np.isnan(calculate_peak(data, end=-10)), "The peak with out-of-range end index should be NaN"
+
+def test_calculate_peak_with_start_greater_than_end():
+    """
+    Test that calculate_peak returns NaN if start index is greater than end index.
+    """
+    data = pd.Series([1, 2, 3, 4, 5])
+    assert pd.isna(calculate_peak(data, start=4, end=2)), "The peak should be NaN if start index is greater than end index"
diff --git a/tests/core/features/test_feature_spikeness.py b/tests/core/features/test_feature_spikeness.py
new file mode 100644
index 0000000..0ad03b5
--- /dev/null
+++ b/tests/core/features/test_feature_spikeness.py
@@ -0,0 +1,52 @@
+import pytest
+import pandas as pd
+import numpy as np
+from interpreTS.core.features.feature_spikeness import calculate_spikeness
+
+def test_spikeness_positive_skew():
+    """
+    Test calculate_spikeness on a positively skewed time series.
+    """
+    data = pd.Series([1, 2, 3, 4, 10])
+    spikeness = calculate_spikeness(data)
+    assert spikeness > 0, "Spikeness should be positive for positively skewed data"
+
+def test_spikeness_negative_skew():
+    """
+    Test calculate_spikeness on a negatively skewed time series.
+    """
+    data = pd.Series([50, 40, 30, 20, 10, 5, 2, 1, 0, -20, -30, -40]) 
+    spikeness = calculate_spikeness(data)
+    assert spikeness < 0, "Spikeness should be negative for negatively skewed data"
+    
+def test_spikeness_no_skew():
+    """
+    Test calculate_spikeness on a time series with no skew (symmetric data).
+    """
+    data = pd.Series([1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1])  # Większy symetryczny rozkład
+    spikeness = calculate_spikeness(data)
+    assert np.isclose(spikeness, 0, atol=1e-1), "Spikeness should be close to 0 for symmetric data"
+
+def test_spikeness_empty_series():
+    """
+    Test calculate_spikeness on an empty time series.
+    """
+    data = pd.Series([], dtype="float64")
+    spikeness = calculate_spikeness(data)
+    assert np.isnan(spikeness), "Spikeness should be NaN for an empty series"
+
+def test_spikeness_with_nan_values():
+    """
+    Test calculate_spikeness on a time series containing NaN values.
+    """
+    data = pd.Series([1, 2, np.nan, 4, 5])
+    with pytest.raises(ValueError, match="Data contains NaN values"):
+        calculate_spikeness(data)
+
+def test_spikeness_numpy_array():
+    """
+    Test calculate_spikeness on a numpy array (alternative data format).
+    """
+    data = np.array([1, 2, 3, 4, 10])
+    spikeness = calculate_spikeness(pd.Series(data))
+    assert spikeness > 0, "Spikeness should be positive for positively skewed data"
diff --git a/tests/core/features/test_feature_std_1st_der.py b/tests/core/features/test_feature_std_1st_der.py
new file mode 100644
index 0000000..16a6db3
--- /dev/null
+++ b/tests/core/features/test_feature_std_1st_der.py
@@ -0,0 +1,34 @@
+import pytest
+import pandas as pd
+import numpy as np
+from interpreTS.core.features.feature_std_1st_der import calculate_std_1st_der
+
+def test_calculate_std_1st_derivative_constant_series():
+    """
+    Test that the standard deviation of the first derivative is zero for a constant series.
+    """
+    data = pd.Series([5, 5, 5, 5, 5])
+    assert calculate_std_1st_der(data) == 0.0, "The std of first derivative for a constant series should be 0"
+
+def test_calculate_std_1st_derivative_linear_series():
+    """
+    Test that the standard deviation of the first derivative is zero for a linear series.
+    """
+    data = pd.Series([1, 2, 3, 4, 5])
+    assert calculate_std_1st_der(data) == 0.0, "The std of first derivative for a linear series should be 0"
+
+def test_calculate_std_1st_derivative_varying_series():
+    """
+    Test the standard deviation of the first derivative for a varying series.
+    """
+    data = pd.Series([1, 2, 1, 2, 1])
+    result = 0.63245553
+    assert calculate_std_1st_der(data) == pytest.approx(result, rel=1e-5), f"The std of first derivative should be approximately {result:.5f})"
+
+def test_calculate_std_1st_derivative_numpy_array():
+    """
+    Test that the function works with a numpy array as input.
+    """
+    data = np.array([1, -1, 2, -3, 5])
+    result = 3.5128336
+    assert calculate_std_1st_der(data) == pytest.approx(result, rel=1e-5), f"The std of first derivative should be approximately {result:.5f})"
diff --git a/tests/core/features/test_feature_trough.py b/tests/core/features/test_feature_trough.py
new file mode 100644
index 0000000..c871ac8
--- /dev/null
+++ b/tests/core/features/test_feature_trough.py
@@ -0,0 +1,54 @@
+import pytest
+import pandas as pd
+import numpy as np
+from interpreTS.core.features.feature_trough import calculate_trough
+
+def test_calculate_trough():
+    """
+    Test that calculate_trough correctly calculates the trough (minimum) value of a time series.
+    """
+    data = pd.Series([1, 2, 3, 4, 5])
+    assert calculate_trough(data) == 1.0, "The trough value should be 1.0"
+
+def test_calculate_trough_empty_series():
+    """
+    Test that calculate_trough returns NaN for an empty time series.
+    """
+    data = pd.Series([])
+    assert pd.isna(calculate_trough(data)), "The trough of an empty series should be NaN"
+
+def test_calculate_trough_numpy_array():
+    """
+    Test that calculate_trough correctly calculates the trough (minimum) value of a numpy array.
+    """
+    data = np.array([1, 2, 3, 4, 5])
+    assert calculate_trough(data) == 1.0, "The trough value should be 1.0 for numpy array"
+
+def test_calculate_trough_with_negative_values():
+    """
+    Test that calculate_trough correctly calculates the trough (minimum) for a series with negative values.
+    """
+    data = pd.Series([-1, -2, -3, -4, -5])
+    assert calculate_trough(data) == -5.0, "The trough value should be -5.0"
+
+def test_calculate_trough_with_start_and_end():
+    """
+    Test that calculate_trough correctly calculates the trough within a specified start and end range.
+    """
+    data = pd.Series([1, 2, 5, 4, 3])
+    assert calculate_trough(data, start=1, end=4) == 2.0, "The trough value within index range 1 to 4 should be 2.0"
+
+def test_calculate_trough_with_out_of_range_indices():
+    """
+    Test that calculate_trough handles out-of-range start and end indices gracefully.
+    """
+    data = pd.Series([1, 2, 3, 4, 5])
+    assert np.isnan(calculate_trough(data, start=10)), "The trough with out-of-range start index should be NaN"
+    assert np.isnan(calculate_trough(data, end=-10)), "The trough with out-of-range end index should be NaN"
+
+def test_calculate_trough_with_start_greater_than_end():
+    """
+    Test that calculate_trough returns NaN if start index is greater than end index.
+    """
+    data = pd.Series([1, 2, 3, 4, 5])
+    assert pd.isna(calculate_trough(data, start=4, end=2)), "The trough should be NaN if start index is greater than end index"
diff --git a/tests/core/features/test_feature_variance.py b/tests/core/features/test_feature_variance.py
new file mode 100644
index 0000000..0014426
--- /dev/null
+++ b/tests/core/features/test_feature_variance.py
@@ -0,0 +1,32 @@
+import pytest
+import pandas as pd
+import numpy as np
+from interpreTS.core.features.feature_variance import calculate_variance
+
+def test_calculate_variance():
+    """
+    Test that calculate_variance correctly calculates the variance of a time series.
+    """
+    data = pd.Series([1, 2, 3, 4, 5])
+    assert calculate_variance(data) == 2.0, "The variance value should be 2.0"
+
+def test_calculate_variance_empty_series():
+    """
+    Test that calculate_variance returns NaN for an empty time series.
+    """
+    data = pd.Series([])
+    assert pd.isna(calculate_variance(data)), "The variance of an empty series should be NaN"
+
+def test_calculate_variance_numpy_array():
+    """
+    Test that calculate_variance correctly calculates the variance of a numpy array.
+    """
+    data = np.array([1, 2, 3, 4, 5])
+    assert calculate_variance(data) == 2.0, "The variance should be 2.0 for numpy array"
+
+def test_calculate_variance_negative_values():
+    """
+    Test that calculate_variance correctly calculates the variance for a series with negative values.
+    """
+    data = pd.Series([-1, -2, -3, -4, -5])
+    assert calculate_variance(data) == 2.0, "The mean value should be 2.0"
diff --git a/tests/core/features/test_histogram_dominant.py b/tests/core/features/test_histogram_dominant.py
new file mode 100644
index 0000000..15b699a
--- /dev/null
+++ b/tests/core/features/test_histogram_dominant.py
@@ -0,0 +1,51 @@
+import pytest
+import pandas as pd
+import numpy as np
+from interpreTS.core.features.histogram_dominant import calculate_dominant
+
+def test_calculate_dominant_basic():
+    """
+    Test that calculate_dominant correctly identifies the dominant value
+    in a simple time series.
+    """
+    data = pd.Series([1, 2, 2, 3, 4, 4, 4, 5])
+    assert calculate_dominant(data) == 4.0, "The dominant value should be 4.0"
+
+def test_calculate_dominant_empty_series():
+    """
+    Test that calculate_dominant returns NaN for an empty time series.
+    """
+    data = pd.Series([])
+    assert pd.isna(calculate_dominant(data)), "The dominant value of an empty series should be NaN"
+
+def test_calculate_dominant_numpy_array():
+    """
+    Test that calculate_dominant correctly identifies the dominant value in a numpy array.
+    """
+    data = np.array([1, 1, 2, 3, 3, 3, 4, 5])
+    assert calculate_dominant(data) == 3.0, "The dominant value should be 3.0 for the numpy array"
+
+def test_calculate_dominant_multiple_modes():
+    """
+    Test that calculate_dominant returns one of the most frequent values
+    when there are multiple modes in the data.
+    """
+    data = pd.Series([1, 2, 2, 3, 3, 4])
+    dominant_value = calculate_dominant(data, return_bin_center=True)
+    assert dominant_value in [2.5, 3.5], "The dominant value should be one of the modes"
+
+def test_calculate_dominant_bins_parameter():
+    """
+    Test that calculate_dominant respects the 'bins' parameter for histogram granularity.
+    """
+    data = pd.Series([1, 1, 2, 2, 3, 3, 4, 4, 5, 5])
+    # With fewer bins, the dominant should approximate the most frequent bin
+    dominant_value = calculate_dominant(data, bins=3, return_bin_center=True)
+    assert dominant_value in [1.5, 4.5], "The dominant value should approximate the largest bin center"
+
+def test_calculate_dominant_negative_values():
+    """
+    Test that calculate_dominant correctly identifies the dominant value for a series with negative values.
+    """
+    data = pd.Series([-5, -4, -4, -3, -3, -3, -2, -1])
+    assert calculate_dominant(data) == -3.0, "The dominant value should be -3.0"
\ No newline at end of file
diff --git a/tests/core/features/test_seasonality_strength.py b/tests/core/features/test_seasonality_strength.py
new file mode 100644
index 0000000..f80ecd2
--- /dev/null
+++ b/tests/core/features/test_seasonality_strength.py
@@ -0,0 +1,72 @@
+import pandas as pd
+import numpy as np
+from interpreTS.core.features.seasonality_strength import calculate_seasonality_strength
+
+def test_calculate_seasonality_strength_strong_seasonality():
+    """
+    Test that calculate_seasonality_strength identifies strong seasonality.
+    This test uses a repeating seasonal pattern (e.g., a sine wave).
+    """
+    t = np.linspace(0, 10, 100)
+    data = pd.Series(np.sin(t))
+    result = calculate_seasonality_strength(data)
+    assert result > 0.7, "The seasonality strength should be strong for periodic data"
+
+def test_calculate_seasonality_strength_weak_seasonality():
+    """
+    Test that calculate_seasonality_strength identifies weak seasonality.
+    This test uses a data set with some periodicity but lower correlation.
+    """
+    data = pd.Series([1, 2, 3, 1, 2, 3, 1, 2, 2.5, 1, 1.5, 3])
+    result = calculate_seasonality_strength(data, period=3)
+    assert result > 0.3, "The seasonality strength should be moderate for periodic data"
+
+def test_calculate_seasonality_strength_no_seasonality():
+    """
+    Test that calculate_seasonality_strength identifies no seasonality.
+    This test uses random data without any periodic pattern.
+    """
+    np.random.seed(0)
+    data = pd.Series(np.random.randn(100))
+    result = calculate_seasonality_strength(data)
+    assert result < 0.2, "The seasonality strength should be low for random data"
+
+def test_calculate_seasonality_strength_empty_series():
+    """
+    Test that calculate_seasonality_strength returns NaN for an empty series.
+    """
+    data = pd.Series([])
+    result = calculate_seasonality_strength(data)
+    assert pd.isna(result), "The seasonality strength of an empty series should be NaN"
+
+def test_calculate_seasonality_strength_insufficient_data():
+    """
+    Test that calculate_seasonality_strength returns NaN for insufficient data.
+    """
+    data = pd.Series([5])
+    result = calculate_seasonality_strength(data)
+    assert pd.isna(result), "The seasonality strength should be NaN for insufficient data"
+
+def test_calculate_seasonality_strength_with_short_periodicity():
+    """
+    Test that calculate_seasonality_strength detects seasonality with short periodicity.
+    """
+    data = pd.Series([1, 2, 3, 4, 5] * 3)
+    result = calculate_seasonality_strength(data, period=5) 
+    assert result > 0.5, "The seasonality strength should be significant for short periodicity data"
+
+def test_calculate_seasonality_strength_with_long_periodicity():
+    """
+    Test that calculate_seasonality_strength detects seasonality with longer periodicity.
+    """
+    data = pd.Series([1, 0, 1, 0, 1, 0, 1, 0]) 
+    result = calculate_seasonality_strength(data)
+    assert result > 0.5, "The seasonality strength should be significant for longer periodicity data"
+
+def test_calculate_seasonality_strength_numpy_array():
+    """
+    Test that calculate_seasonality_strength works with a numpy array.
+    """
+    data = np.array([1, 2, 3, 2, 1, 2, 3, 2, 1])
+    result = calculate_seasonality_strength(data, period=4)
+    assert result > 0.5, "The seasonality strength should be significant for periodic data"
diff --git a/tests/core/features/test_trend_strength.py b/tests/core/features/test_trend_strength.py
new file mode 100644
index 0000000..ebb8789
--- /dev/null
+++ b/tests/core/features/test_trend_strength.py
@@ -0,0 +1,54 @@
+import pytest
+import pandas as pd
+import numpy as np
+from interpreTS.core.features.trend_strength import calculate_trend_strength
+
+def test_calculate_trend_strength_increasing():
+    """
+    Test that calculate_trend_strength identifies a strong positive trend.
+    """
+    data = pd.Series([1, 2, 3, 4, 5])
+    assert calculate_trend_strength(data) == pytest.approx(1.0), "The trend strength should be close to 1.0 for a perfect increasing trend"
+
+def test_calculate_trend_strength_decreasing():
+    """
+    Test that calculate_trend_strength identifies a strong negative trend.
+    """
+    data = pd.Series([5, 4, 3, 2, 1])
+    assert calculate_trend_strength(data) == pytest.approx(1.0), "The trend strength should be close to 1.0 for a perfect decreasing trend"
+
+def test_calculate_trend_strength_no_trend():
+    """
+    Test that calculate_trend_strength returns a low value for a series with no trend.
+    """
+    data = pd.Series([1, 1, 1, 1, 1])
+    assert calculate_trend_strength(data) == pytest.approx(0.0), "The trend strength should be 0.0 for no trend"
+
+def test_calculate_trend_strength_random_data():
+    """
+    Test that calculate_trend_strength returns a low value for random data.
+    """
+    np.random.seed(0)
+    data = pd.Series(np.random.randn(100))
+    assert calculate_trend_strength(data) < 0.2, "The trend strength should be low for random data"
+
+def test_calculate_trend_strength_empty_series():
+    """
+    Test that calculate_trend_strength returns NaN for an empty series.
+    """
+    data = pd.Series([])
+    assert pd.isna(calculate_trend_strength(data)), "The trend strength of an empty series should be NaN"
+
+def test_calculate_trend_strength_insufficient_data():
+    """
+    Test that calculate_trend_strength returns NaN for a single data point.
+    """
+    data = pd.Series([5])
+    assert pd.isna(calculate_trend_strength(data)), "The trend strength should be NaN for insufficient data"
+
+def test_calculate_trend_strength_numpy_array():
+    """
+    Test that calculate_trend_strength works with a numpy array.
+    """
+    data = np.array([1, 2, 3, 4, 5])
+    assert calculate_trend_strength(data) == pytest.approx(1.0), "The trend strength should be close to 1.0 for a perfect trend"
\ No newline at end of file
diff --git a/tests/core/test_feature_extractor.py b/tests/core/test_feature_extractor.py
index b8d63d5..3772900 100644
--- a/tests/core/test_feature_extractor.py
+++ b/tests/core/test_feature_extractor.py
@@ -1,21 +1,112 @@
-import pytest
 import pandas as pd
-from src.core.feature_extractor import FeatureExtractor
+import numpy as np
+from interpreTS.core.feature_extractor import FeatureExtractor, Features
 
 def test_extract_length_feature():
     """
     Test that FeatureExtractor correctly extracts the 'length' feature.
     """
     data = pd.Series([1, 2, 3, 4, 5])
-    extractor = FeatureExtractor(features=['length'])
+    extractor = FeatureExtractor(features=[Features.LENGTH])
     features = extractor.extract_features(data)
-    assert features['length'] == 5, "The 'length' feature should be 5"
+    length_value = features[Features.LENGTH].iloc[0] if isinstance(features[Features.LENGTH], pd.Series) else features[Features.LENGTH]
+    assert length_value == 5, "The 'length' feature should be 5"
+    
+def test_extract_mean_feature():
+    """
+    Test that FeatureExtractor correctly extracts the 'mean' feature.
+    """
+    data = pd.Series([1, 2, 3, 4, 5])
+    extractor = FeatureExtractor(features=[Features.MEAN])
+    features = extractor.extract_features(data)
+    assert np.isclose(features[Features.MEAN], 3.0, atol=1e-4), "The 'mean' feature should be 3.0"
+
+def test_extract_spikeness_feature():
+    """
+    Test that FeatureExtractor correctly extracts the 'spikeness' feature.
+    """
+    data = pd.Series([1, 2, 3, 4, 5, 4, 3, 2, 1])
+    extractor = FeatureExtractor(features=[Features.SPIKENESS])
+    features = extractor.extract_features(data)
+    spikeness_value = features[Features.SPIKENESS].iloc[0] if isinstance(features[Features.SPIKENESS], pd.Series) else features[Features.SPIKENESS]
+    assert np.isclose(spikeness_value, 0, atol=0.2), "The 'spikeness' feature should be close to 0 for symmetric data"
 
-def test_extract_no_features():
+def test_extract_std_1st_der_feature():
     """
-    Test that FeatureExtractor returns an empty dictionary when no features are specified.
+    Test that FeatureExtractor correctly extracts the 'std_1st_der' feature.
     """
     data = pd.Series([1, 2, 3, 4, 5])
-    extractor = FeatureExtractor(features=[])
+    extractor = FeatureExtractor(features=[Features.STD_1ST_DER])
+    features = extractor.extract_features(data)
+    assert np.isclose(features[Features.STD_1ST_DER], 0, atol=1e-2), "The 'std_1st_der' feature should be approximately 0 for a linearly increasing sequence"
+
+def test_extract_seasonality_strength_feature():
+    """
+    Test that FeatureExtractor correctly extracts the 'seasonality_strength' feature.
+    """
+    data = pd.Series([1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2])
+    extractor = FeatureExtractor(features=[Features.CALCULATE_SEASONALITY_STRENGTH],
+                                 feature_params={Features.CALCULATE_SEASONALITY_STRENGTH: {'period': 2}})
+    features = extractor.extract_features(data)
+    
+    assert features[Features.CALCULATE_SEASONALITY_STRENGTH].iloc[0] > 0.5, "The 'seasonality_strength' feature should be high for repeating patterns"
+
+def test_extract_with_window_size_and_stride():
+    """
+    Test that FeatureExtractor correctly applies window_size and stride.
+    """
+    data = pd.DataFrame({'value': range(10)})
+    extractor = FeatureExtractor(features=[Features.MEAN], window_size=3, stride=2)
+    features = extractor.extract_features(data)    
+    expected_means = [1, 3, 5, 7]
+    actual_means = features[Features.MEAN].tolist()
+    assert actual_means == expected_means, f"Expected means {expected_means}, got {actual_means}"
+
+
+def test_extract_with_id_column():
+    """
+    Test that FeatureExtractor groups data by id_column before feature extraction.
+    """
+    data = pd.DataFrame({
+        'id': [1, 1, 1, 2, 2, 2],
+        'value': [1, 2, 3, 4, 5, 6]
+    })
+    extractor = FeatureExtractor(features=[Features.MEAN], id_column='id', window_size=3)
+    features = extractor.extract_features(data)
+    
+    assert len(features) == 2, "Powinny być 2 grupy dla id_column 'id'"
+    assert features['id'].nunique() == 2, "Każda grupa powinna mieć unikalne id"
+
+def test_extract_variance_feature():
+    """
+    Test that FeatureExtractor correctly extracts the 'variance' feature.
+    """
+    data = pd.DataFrame({'value': [1, 2, 3, 4, 5]})
+    extractor = FeatureExtractor(features=[Features.VARIANCE])
+    features = extractor.extract_features(data)
+    assert np.isclose(features[Features.VARIANCE].iloc[0], 2.0, atol=1e-4), "The 'variance' feature should be 2.0"
+
+def test_extract_peak_and_trough_features():
+    """
+    Test that FeatureExtractor correctly extracts the 'peak' and 'trough' features.
+    """
+    data = pd.DataFrame({'value': [1, 3, 2, 4, 1]})
+    extractor = FeatureExtractor(features=[Features.PEAK, Features.TROUGH])
+    features = extractor.extract_features(data)
+    
+    assert features[Features.PEAK].iloc[0] == 4, "The peak feature should be 4"
+    assert features[Features.TROUGH].iloc[0] == 1, "The trough feature should be 1"
+
+def test_extract_with_sort_column():
+    """
+    Test that FeatureExtractor sorts data by sort_column before feature extraction.
+    """
+    data = pd.DataFrame({
+        'id': [1, 1, 1],
+        'time': [3, 1, 2],
+        'value': [10, 20, 30]
+    })
+    extractor = FeatureExtractor(features=[Features.MEAN], sort_column='time', window_size=3, stride=1)
     features = extractor.extract_features(data)
-    assert features == {}, "No features should be extracted when an empty feature list is provided"
+    assert Features.MEAN in features.columns, "The 'mean' feature should be in the features DataFrame"
+    assert features[Features.MEAN].iloc[0] == 20, "The mean should be calculated after sorting by 'time'"
diff --git a/tests/core/test_time_series_data.py b/tests/core/test_time_series_data.py
index 0f6f480..73cb5d5 100644
--- a/tests/core/test_time_series_data.py
+++ b/tests/core/test_time_series_data.py
@@ -1,6 +1,6 @@
 import pytest
 import pandas as pd
-from src.core.time_series_data import TimeSeriesData
+from interpreTS.core.time_series_data import TimeSeriesData
 
 def test_time_series_initialization():
     """
diff --git a/tests/utils/test_convert_to_time_series.py b/tests/utils/test_convert_to_time_series.py
index 7da0623..2e98fd0 100644
--- a/tests/utils/test_convert_to_time_series.py
+++ b/tests/utils/test_convert_to_time_series.py
@@ -1,8 +1,8 @@
 import pytest
 import pandas as pd
 import numpy as np
-from src.utils.data_conversion import convert_to_time_series
-from src.core.time_series_data import TimeSeriesData
+from interpreTS.utils.data_conversion import convert_to_time_series
+from interpreTS.core.time_series_data import TimeSeriesData
 
 def test_convert_pandas_series():
     """
diff --git a/tests/utils/test_validate_time_series_data.py b/tests/utils/test_validate_time_series_data.py
index 2b54e9d..77c16fc 100644
--- a/tests/utils/test_validate_time_series_data.py
+++ b/tests/utils/test_validate_time_series_data.py
@@ -1,7 +1,7 @@
 import pytest
 import pandas as pd
 import numpy as np
-from src.utils.data_validation import validate_time_series_data
+from interpreTS.utils.data_validation import validate_time_series_data
 
 def test_validate_pandas_series():
     """
@@ -24,7 +24,7 @@ def test_validate_without_datetime_index():
     """
     data = pd.Series([1, 2, 3, 4, 5])
     with pytest.raises(ValueError):
-        validate_time_series_data(data)
+        validate_time_series_data(data, require_datetime_index=True)
 
 def test_validate_numpy_array():
     """