labda 0.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- labda-0.0.2/CHANGELOG.md +16 -0
- labda-0.0.2/PKG-INFO +59 -0
- labda-0.0.2/README.md +26 -0
- labda-0.0.2/labda/__init__.py +1 -0
- labda-0.0.2/labda/assets/__init__.py +2 -0
- labda-0.0.2/labda/assets/counts_cut_points.py +35 -0
- labda-0.0.2/labda/assets/transportation_cut_points.py +16 -0
- labda-0.0.2/labda/expanders/__init__.py +8 -0
- labda-0.0.2/labda/expanders/accelerometer.py +29 -0
- labda-0.0.2/labda/expanders/extras.py +33 -0
- labda-0.0.2/labda/expanders/spatial.py +242 -0
- labda-0.0.2/labda/parsers/__init__.py +4 -0
- labda-0.0.2/labda/parsers/actigraph.py +232 -0
- labda-0.0.2/labda/parsers/bulk.py +31 -0
- labda-0.0.2/labda/parsers/gadgetbridge.py +232 -0
- labda-0.0.2/labda/parsers/garmin.py +89 -0
- labda-0.0.2/labda/parsers/qstarz.py +366 -0
- labda-0.0.2/labda/processing/__init__.py +1 -0
- labda-0.0.2/labda/processing/accelerometer/__init__.py +2 -0
- labda-0.0.2/labda/processing/accelerometer/activity_intensity.py +57 -0
- labda-0.0.2/labda/processing/accelerometer/steps.py +24 -0
- labda-0.0.2/labda/processing/accelerometer/wear.py +38 -0
- labda-0.0.2/labda/processing/bouts.py +85 -0
- labda-0.0.2/labda/processing/domains.py +32 -0
- labda-0.0.2/labda/processing/manipulations.py +22 -0
- labda-0.0.2/labda/processing/spatial/__init__.py +4 -0
- labda-0.0.2/labda/processing/spatial/environment.py +50 -0
- labda-0.0.2/labda/processing/spatial/manipulations.py +186 -0
- labda-0.0.2/labda/processing/spatial/timeline.py +99 -0
- labda-0.0.2/labda/processing/spatial/transportation.py +80 -0
- labda-0.0.2/labda/processing/spatial/trips.py +551 -0
- labda-0.0.2/labda/processing/wear_validity.py +69 -0
- labda-0.0.2/labda/structure/__init__.py +6 -0
- labda-0.0.2/labda/structure/collection.py +103 -0
- labda-0.0.2/labda/structure/domains.py +29 -0
- labda-0.0.2/labda/structure/linkage.py +28 -0
- labda-0.0.2/labda/structure/merging.py +133 -0
- labda-0.0.2/labda/structure/resampling.py +82 -0
- labda-0.0.2/labda/structure/subject.py +294 -0
- labda-0.0.2/labda/structure/validation/__init__.py +0 -0
- labda-0.0.2/labda/structure/validation/domains.py +37 -0
- labda-0.0.2/labda/structure/validation/linkage.py +28 -0
- labda-0.0.2/labda/structure/validation/subject.py +323 -0
- labda-0.0.2/labda/utils.py +240 -0
- labda-0.0.2/labda/visualisation/__init__.py +0 -0
- labda-0.0.2/pyproject.toml +64 -0
labda-0.0.2/CHANGELOG.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Parser: Qstarz
|
|
13
|
+
- Basic subject and collection manipulaton.
|
|
14
|
+
- Expanding data with timedelta, speed, acceleration, distance, direction.
|
|
15
|
+
- Getting sampling frequency, CRS and timezone automatically from dataset.
|
|
16
|
+
- Initial documentation with mkdocs and material theme.
|
labda-0.0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: labda
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Library for Advanced Behavioural Data Analysis
|
|
5
|
+
Home-page: https://labda.josefheidler.cz
|
|
6
|
+
License: CC-BY-SA-4.0
|
|
7
|
+
Keywords: analysis,health,behaviour,data,spatial,temporal
|
|
8
|
+
Author: Josef Heidler
|
|
9
|
+
Author-email: jheidler@health.sdu.dk
|
|
10
|
+
Maintainer: Josef Heidler
|
|
11
|
+
Maintainer-email: jheidler@health.sdu.dk
|
|
12
|
+
Requires-Python: >=3.11,<4.0
|
|
13
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Education
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: License :: Other/Proprietary License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering
|
|
22
|
+
Requires-Dist: geopandas (>=0.14.2,<0.15.0)
|
|
23
|
+
Requires-Dist: openpyxl (>=3.1.2,<4.0.0)
|
|
24
|
+
Requires-Dist: pandas (>=2.2.0,<3.0.0)
|
|
25
|
+
Requires-Dist: pandera (>=0.18.0,<0.19.0)
|
|
26
|
+
Requires-Dist: pyarrow (>=15.0.0,<16.0.0)
|
|
27
|
+
Requires-Dist: pydeck (>=0.8.0,<0.9.0)
|
|
28
|
+
Requires-Dist: sqlalchemy (>=2.0.25,<3.0.0)
|
|
29
|
+
Requires-Dist: timezonefinder (>=6.2.0,<7.0.0)
|
|
30
|
+
Project-URL: Documentation, https://labda.josefheidler.cz
|
|
31
|
+
Project-URL: Repository, https://github.com/josefheidler/labda
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# LABDA: Library for Advanced Behavioural Data Analysis
|
|
35
|
+
|
|
36
|
+
**LABDA is currently under development and may undergo significant API changes that could result in breaking changes.**
|
|
37
|
+
|
|
38
|
+
## What is it?
|
|
39
|
+
|
|
40
|
+
TO-DO...
|
|
41
|
+
|
|
42
|
+
## Main Features
|
|
43
|
+
|
|
44
|
+
TO-DO...
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
<p xmlns:cc="http://creativecommons.org/ns#" xmlns:dct="http://purl.org/dc/terms/"><a property="dct:title" rel="cc:attributionURL" href="https://labda.josefheidler.cz/">Library for Advanced Behavioural Data Analysis</a> by <a rel="cc:attributionURL dct:creator" property="cc:attributionName" href="https://josefheidler.cz/">Josef Heidler</a> is licensed under <a href="http://creativecommons.org/licenses/by/4.0/?ref=chooser-v1" target="_blank" rel="license noopener noreferrer" style="display:inline-block;">CC BY 4.0<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1"><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1"></a></p>
|
|
48
|
+
|
|
49
|
+
## Documentation
|
|
50
|
+
|
|
51
|
+
TO-DO...
|
|
52
|
+
|
|
53
|
+
## Getting Help, Discussion and Development
|
|
54
|
+
|
|
55
|
+
TO-DO...
|
|
56
|
+
|
|
57
|
+
## Contributing to labda
|
|
58
|
+
|
|
59
|
+
TO-DO..
|
labda-0.0.2/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# LABDA: Library for Advanced Behavioural Data Analysis
|
|
2
|
+
|
|
3
|
+
**LABDA is currently under development and may undergo significant API changes that could result in breaking changes.**
|
|
4
|
+
|
|
5
|
+
## What is it?
|
|
6
|
+
|
|
7
|
+
TO-DO...
|
|
8
|
+
|
|
9
|
+
## Main Features
|
|
10
|
+
|
|
11
|
+
TO-DO...
|
|
12
|
+
|
|
13
|
+
## License
|
|
14
|
+
<p xmlns:cc="http://creativecommons.org/ns#" xmlns:dct="http://purl.org/dc/terms/"><a property="dct:title" rel="cc:attributionURL" href="https://labda.josefheidler.cz/">Library for Advanced Behavioural Data Analysis</a> by <a rel="cc:attributionURL dct:creator" property="cc:attributionName" href="https://josefheidler.cz/">Josef Heidler</a> is licensed under <a href="http://creativecommons.org/licenses/by/4.0/?ref=chooser-v1" target="_blank" rel="license noopener noreferrer" style="display:inline-block;">CC BY 4.0<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1"><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1"></a></p>
|
|
15
|
+
|
|
16
|
+
## Documentation
|
|
17
|
+
|
|
18
|
+
TO-DO...
|
|
19
|
+
|
|
20
|
+
## Getting Help, Discussion and Development
|
|
21
|
+
|
|
22
|
+
TO-DO...
|
|
23
|
+
|
|
24
|
+
## Contributing to labda
|
|
25
|
+
|
|
26
|
+
TO-DO..
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .structure import Collection, Column, Subject, merge_collections, merge_subjects
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from ..structure.validation.subject import Column
|
|
2
|
+
|
|
3
|
+
CUT_POINTS = [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"name": "Freedson Adult (1998)",
|
|
7
|
+
"reference": "https://journals.lww.com/acsm-msse/fulltext/1998/05000/calibration_of_the_computer_science_and.21.aspx",
|
|
8
|
+
"sampling_frequency": 60,
|
|
9
|
+
"category": "adult",
|
|
10
|
+
"placement": "hip",
|
|
11
|
+
"required_data": Column.VERTICAL_COUNTS,
|
|
12
|
+
"cut_points": [
|
|
13
|
+
{"name": "sedentary", "max": 99},
|
|
14
|
+
{"name": "light", "max": 1951},
|
|
15
|
+
{"name": "moderate", "max": 5724},
|
|
16
|
+
{"name": "vigorous", "max": 9498},
|
|
17
|
+
{"name": "very_vigorous", "max": float("inf")},
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": 2,
|
|
22
|
+
"name": "Freedson Adult VM3 (2011)",
|
|
23
|
+
"reference": "https://doi.org/10.1016/j.jsams.2011.04.003",
|
|
24
|
+
"sampling_frequency": 60,
|
|
25
|
+
"category": "adult",
|
|
26
|
+
"placement": "hip",
|
|
27
|
+
"required_data": Column.VM_COUNTS,
|
|
28
|
+
"cut_points": [
|
|
29
|
+
{"name": "light", "max": 2689},
|
|
30
|
+
{"name": "moderate", "max": 6166},
|
|
31
|
+
{"name": "vigorous", "max": 9642},
|
|
32
|
+
{"name": "very_vigorous", "max": float("inf")},
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from ..structure.validation.subject import Column
|
|
2
|
+
|
|
3
|
+
CUT_POINTS = [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"name": "Carlson PALMS Validation (2015)",
|
|
7
|
+
"reference": "https://doi.org/10.1249/MSS.0000000000000446",
|
|
8
|
+
"required_data": Column.SPEED,
|
|
9
|
+
"units": "km/h",
|
|
10
|
+
"cut_points": [
|
|
11
|
+
{"name": "walking/running", "max": 9},
|
|
12
|
+
{"name": "bicycle", "max": 35},
|
|
13
|
+
{"name": "vehicle", "max": float("inf")},
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
|
|
4
|
+
from ..structure.validation.subject import Column
|
|
5
|
+
from ..utils import columns_exists, columns_not_exists
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def add_counts_vector_magnitude(
|
|
9
|
+
df: pd.DataFrame,
|
|
10
|
+
*,
|
|
11
|
+
name: str = Column.VM_COUNTS,
|
|
12
|
+
overwrite: bool = False,
|
|
13
|
+
) -> pd.DataFrame:
|
|
14
|
+
counts_cols = [
|
|
15
|
+
Column.VERTICAL_COUNTS,
|
|
16
|
+
Column.HORIZONTAL_COUNTS,
|
|
17
|
+
Column.PERPENDICULAR_COUNTS,
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
columns_exists(df, counts_cols) # type: ignore
|
|
21
|
+
columns_not_exists(df, [name], overwrite=overwrite)
|
|
22
|
+
|
|
23
|
+
df = df.copy()
|
|
24
|
+
df[name] = np.linalg.norm(
|
|
25
|
+
df[counts_cols].astype(float),
|
|
26
|
+
axis=1,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
return df
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
from ..structure.validation.subject import Column
|
|
4
|
+
from ..utils import columns_not_exists
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def add_timedelta(
|
|
8
|
+
df: pd.DataFrame,
|
|
9
|
+
*,
|
|
10
|
+
name: str = Column.TIMEDELTA,
|
|
11
|
+
overwrite: bool = False,
|
|
12
|
+
) -> pd.DataFrame:
|
|
13
|
+
"""
|
|
14
|
+
Adds a timedelta column to the DataFrame.
|
|
15
|
+
|
|
16
|
+
This function calculates the time difference between each row and the previous row and adds it as a new column to the DataFrame.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
df (pd.DataFrame): The DataFrame to which to add the timedelta column.
|
|
20
|
+
name (str, optional): The name of the new timedelta column. Defaults to "timedelta".
|
|
21
|
+
overwrite (bool, optional): Whether to overwrite the existing timedelta column if it exists. Defaults to False.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
pd.DataFrame: The DataFrame with the added timedelta column.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
columns_not_exists(df, [name], overwrite=overwrite)
|
|
28
|
+
|
|
29
|
+
df = df.copy()
|
|
30
|
+
|
|
31
|
+
df[name] = df.index.diff() # type: ignore
|
|
32
|
+
|
|
33
|
+
return df
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
|
|
4
|
+
from ..structure.validation.subject import Column
|
|
5
|
+
from ..utils import (
|
|
6
|
+
columns_exists,
|
|
7
|
+
columns_not_exists,
|
|
8
|
+
convert_to_geodataframe,
|
|
9
|
+
get_crs,
|
|
10
|
+
get_crs_units,
|
|
11
|
+
get_unique_column_name,
|
|
12
|
+
)
|
|
13
|
+
from .extras import add_timedelta
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"add_speed",
|
|
17
|
+
"add_distance",
|
|
18
|
+
"add_acceleration",
|
|
19
|
+
"add_direction",
|
|
20
|
+
"add_elevation",
|
|
21
|
+
# "add_weather",
|
|
22
|
+
# "add_address",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def add_speed(
|
|
27
|
+
df: pd.DataFrame,
|
|
28
|
+
*,
|
|
29
|
+
crs: str | None = None,
|
|
30
|
+
name: str = Column.SPEED,
|
|
31
|
+
overwrite: bool = False,
|
|
32
|
+
) -> pd.DataFrame:
|
|
33
|
+
"""
|
|
34
|
+
Adds a speed column to the DataFrame by calculating the speed based on the distance and time.
|
|
35
|
+
Units are based on the CRS. If CRS is not specified, the CRS will be guessed based on the coordinates (WGS84 by default).
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
df (pd.DataFrame): The input DataFrame.
|
|
39
|
+
crs (str | None, optional): The coordinate reference system. Defaults to None.
|
|
40
|
+
name (str, optional): The name of the speed dataframe.Column. Defaults to "speed".
|
|
41
|
+
overwrite (bool, optional): Whether to overwrite the existing speed column if it already exists.
|
|
42
|
+
Defaults to False.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
pd.DataFrame: The DataFrame with the speed column added.
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
ValueError: If the speed column already exists in the DataFrame and overwrite is set to False.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
columns_not_exists(df, [name], overwrite=overwrite)
|
|
52
|
+
|
|
53
|
+
df = df.copy()
|
|
54
|
+
|
|
55
|
+
timedelta_colname = get_unique_column_name(df)
|
|
56
|
+
df = add_timedelta(df, name=timedelta_colname)
|
|
57
|
+
|
|
58
|
+
distance_colname = get_unique_column_name(df)
|
|
59
|
+
df = add_distance(df, crs=crs, name=distance_colname)
|
|
60
|
+
|
|
61
|
+
df[name] = df[distance_colname] / df[timedelta_colname].dt.total_seconds()
|
|
62
|
+
|
|
63
|
+
# TODO: This is temporary fix for changing m/s to km/h for now.
|
|
64
|
+
if crs:
|
|
65
|
+
units = get_crs_units(crs)
|
|
66
|
+
|
|
67
|
+
if units == "metre":
|
|
68
|
+
df[name] = df[name] * 3.6
|
|
69
|
+
|
|
70
|
+
return df.drop(columns=[timedelta_colname, distance_colname])
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def add_distance(
|
|
74
|
+
df: pd.DataFrame,
|
|
75
|
+
*,
|
|
76
|
+
crs: str | None = None,
|
|
77
|
+
name: str = Column.DISTANCE,
|
|
78
|
+
overwrite: bool = False,
|
|
79
|
+
) -> pd.DataFrame:
|
|
80
|
+
"""
|
|
81
|
+
Adds a column to the DataFrame with the distance between consecutive points.
|
|
82
|
+
Units are based on the CRS. If CRS is not specified, the CRS will be guessed based on the coordinates (WGS84 by default).
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
df (pd.DataFrame): The input DataFrame.
|
|
86
|
+
crs (str | None, optional): The coordinate reference system of the DataFrame. Defaults to None.
|
|
87
|
+
name (str, optional): The name of the new dataframe.Column. Defaults to "distance".
|
|
88
|
+
overwrite (bool, optional): Whether to overwrite the existing column with the same name. Defaults to False.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
pd.DataFrame: The DataFrame with the added distance dataframe.Column.
|
|
92
|
+
|
|
93
|
+
Raises:
|
|
94
|
+
ValueError: If the column with the specified name already exists in the DataFrame and overwrite is False.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
columns_exists(df, [Column.LATITUDE, Column.LONGITUDE])
|
|
98
|
+
columns_not_exists(df, [name], overwrite=overwrite)
|
|
99
|
+
|
|
100
|
+
df = df.copy()
|
|
101
|
+
gdf = convert_to_geodataframe(df, crs=crs)
|
|
102
|
+
if gdf.crs is None:
|
|
103
|
+
crs = get_crs(gdf)
|
|
104
|
+
gdf = gdf.set_crs(crs)
|
|
105
|
+
|
|
106
|
+
df[name] = gdf.distance(gdf.shift(1)) # type: ignore
|
|
107
|
+
|
|
108
|
+
return df
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def add_acceleration(
|
|
112
|
+
df: pd.DataFrame,
|
|
113
|
+
*,
|
|
114
|
+
crs: str | None = None,
|
|
115
|
+
name: str = Column.ACCELERATION,
|
|
116
|
+
overwrite: bool = False,
|
|
117
|
+
) -> pd.DataFrame:
|
|
118
|
+
"""
|
|
119
|
+
Adds a column to the DataFrame with the acceleration between consecutive points.
|
|
120
|
+
Acceleration is calculated based on the change in speed over time.
|
|
121
|
+
Units are based on the CRS. If CRS is not specified, the CRS will be guessed based on the coordinates (WGS84 by default).
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
df (pd.DataFrame): The input DataFrame.
|
|
125
|
+
crs (str | None, optional): The coordinate reference system of the DataFrame. Defaults to None.
|
|
126
|
+
name (str, optional): The name of the new dataframe.Column. Defaults to "acceleration".
|
|
127
|
+
overwrite (bool, optional): Whether to overwrite the existing column with the same name. Defaults to False.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
pd.DataFrame: The DataFrame with the added acceleration dataframe.Column.
|
|
131
|
+
|
|
132
|
+
Raises:
|
|
133
|
+
ValueError: If the column with the specified name already exists in the DataFrame and overwrite is False.
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
# TODO: Needs to be tested.
|
|
137
|
+
columns_not_exists(df, [name], overwrite=overwrite)
|
|
138
|
+
|
|
139
|
+
df = df.copy()
|
|
140
|
+
|
|
141
|
+
speed_colname = get_unique_column_name(df)
|
|
142
|
+
df = add_speed(df, crs=crs, name=speed_colname)
|
|
143
|
+
|
|
144
|
+
timedelta_colname = get_unique_column_name(df)
|
|
145
|
+
df = add_timedelta(df, name=timedelta_colname)
|
|
146
|
+
|
|
147
|
+
df[name] = (df[speed_colname] - df[speed_colname].shift()) / df[
|
|
148
|
+
timedelta_colname
|
|
149
|
+
].dt.total_seconds()
|
|
150
|
+
|
|
151
|
+
return df.drop(columns=[speed_colname, timedelta_colname])
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def add_direction(
|
|
155
|
+
df: pd.DataFrame,
|
|
156
|
+
*,
|
|
157
|
+
name: str = Column.DIRECTION,
|
|
158
|
+
overwrite: bool = False,
|
|
159
|
+
) -> pd.DataFrame:
|
|
160
|
+
"""
|
|
161
|
+
Adds a column to the DataFrame with the direction between consecutive points.
|
|
162
|
+
Direction is calculated based on the change in longitude and latitude.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
df (pd.DataFrame): The input DataFrame.
|
|
166
|
+
name (str, optional): The name of the new dataframe.Column. Defaults to "direction".
|
|
167
|
+
overwrite (bool, optional): Whether to overwrite the existing column with the same name. Defaults to False.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
pd.DataFrame: The DataFrame with the added direction dataframe.Column.
|
|
171
|
+
|
|
172
|
+
Raises:
|
|
173
|
+
ValueError: If the column with the specified name already exists in the DataFrame and overwrite is False.
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
# TODO: Needs to be tested.
|
|
177
|
+
columns_exists(df, [Column.LATITUDE, Column.LONGITUDE])
|
|
178
|
+
columns_not_exists(df, [name], overwrite=overwrite)
|
|
179
|
+
|
|
180
|
+
df = df.copy()
|
|
181
|
+
|
|
182
|
+
# Convert to radians
|
|
183
|
+
lon1, lat1 = np.radians(df[Column.LONGITUDE]), np.radians(df[Column.LATITUDE])
|
|
184
|
+
lon2, lat2 = (
|
|
185
|
+
np.radians(df[Column.LONGITUDE].shift()),
|
|
186
|
+
np.radians(df[Column.LATITUDE].shift()),
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
dlon = lon2 - lon1
|
|
190
|
+
|
|
191
|
+
x = np.cos(lat2) * np.sin(dlon)
|
|
192
|
+
y = np.cos(lat1) * np.sin(lat2) - np.sin(lat1) * np.cos(lat2) * np.cos(dlon)
|
|
193
|
+
|
|
194
|
+
direction = np.arctan2(x, y)
|
|
195
|
+
direction = np.degrees(direction)
|
|
196
|
+
direction = (direction + 360) % 360
|
|
197
|
+
|
|
198
|
+
df[name] = direction
|
|
199
|
+
|
|
200
|
+
return df
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def add_elevation(
|
|
204
|
+
df: pd.DataFrame,
|
|
205
|
+
*,
|
|
206
|
+
name: str = Column.ELEVATION,
|
|
207
|
+
overwrite: bool = False,
|
|
208
|
+
) -> pd.DataFrame:
|
|
209
|
+
# TODO: Add elevation.
|
|
210
|
+
|
|
211
|
+
columns_not_exists(df, [name], overwrite=overwrite)
|
|
212
|
+
|
|
213
|
+
raise NotImplementedError("This method needs to be implemented.")
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# def add_weather(
|
|
217
|
+
# df: pd.DataFrame,
|
|
218
|
+
# *,
|
|
219
|
+
# name: str = "weather",
|
|
220
|
+
# overwrite: bool = False,
|
|
221
|
+
# ) -> pd.DataFrame:
|
|
222
|
+
# # TODO: https://open-meteo.com/en/docs/historical-weather-api/
|
|
223
|
+
# # TODO: Add weather.
|
|
224
|
+
|
|
225
|
+
# if not overwrite:
|
|
226
|
+
# check_columns_existence(df, [name], exists=False)
|
|
227
|
+
|
|
228
|
+
# raise NotImplementedError("This method needs to be implemented.")
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# def add_address(
|
|
232
|
+
# df: pd.DataFrame,
|
|
233
|
+
# *,
|
|
234
|
+
# name: str = "address",
|
|
235
|
+
# overwrite: bool = False,
|
|
236
|
+
# ) -> pd.DataFrame:
|
|
237
|
+
# # TODO: Add address.
|
|
238
|
+
|
|
239
|
+
# if not overwrite:
|
|
240
|
+
# check_columns_existence(df, [name], exists=False)
|
|
241
|
+
|
|
242
|
+
# raise NotImplementedError("This method needs to be implemented.")
|