#!/usr/bin/python3
# -*- coding: utf-8 -*-
########################################################################
#
# This file is part of python module <pyspc>.
# Copyright (C) 2013-2021 R. Marty
# (renaud.marty@developpement-durable.gouv.fr)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program (see COPYING.txt).
# If not, see <http://www.gnu.org/licenses/>.
#
########################################################################
"""
Objets natifs et convention de pyspc - Manipulation de dates
"""
from collections import OrderedDict
from datetime import datetime as dt, timedelta as td
import pyspc.core.exception as _exception
[docs]
def dtfmt(tdelta=None):
"""
Définir le format de la date selon le pas de temps
Parameters
----------
tdelta : timedelta
pas de temps
Returns
-------
dtfmt : str
Format de la date
"""
# Pas de temps inconnu
if tdelta is None:
return '%Y%m%d%H%M'
_exception.check_td(tdelta)
# Pas de temps : MOIS
if tdelta == td(days=31):
return '%Y%m'
# Pas de temps : multiple JOUR
if tdelta % td(days=1) == td(0):
return '%Y%m%d'
# Pas de temps : multiple HEURE
if tdelta % td(hours=1) == td(0):
return '%Y%m%d%H'
# Autres pas de temps
return '%Y%m%d%H%M'
[docs]
def lenstr2dtfmt(txt=None):
"""
Définir le format de la date selon la longueur de la chaine de caractères
Parameters
----------
txt : str
Chaine de caractères
Returns
-------
dtfmt : str
Format de la date
"""
_exception.check_str(txt)
dtstr = 'YYYYmmddHHMM'
return dtstr[:len(txt)].replace('YYYY', '%Y').replace('mm', '%m')\
.replace('dd', '%d').replace('HH', '%H').replace('MM', '%M')
[docs]
def str2dt(txt=None):
"""
Convertir une chaine de caractères en date
Parameters
----------
txt : str
Chaine de caractères : AAAAMMJJ[HH,[MM]]
Returns
-------
dt : datetime
Date
"""
if txt is None:
return None
_exception.check_str(txt)
return dt.strptime(txt, lenstr2dtfmt(txt))
# https://nedbatchelder.com/blog/201310/range_overlap_in_two_compares.html
[docs]
def overlap(start1, end1, start2, end2):
"""Does the range (start1, end1) overlap with (start2, end2)?"""
return not (end1 < start2 or end2 < start1)
# return end1 >= start2 and end2 >= start1
[docs]
def group_events(events=None, tol=None):
"""
Grouper des événements qui se chevauchent
Parameters
----------
events : list
Liste de événements (date début, date fin)
Other Parameters
----------------
tol : datetime.timedelta
Tolérance pour former un groupe. Par défaut: 0 seconde
Returns
-------
groups : list
Liste des événements après re-oganisation
Examples:
---------
>>>
>>> events = [
... (dt(2001, 1, 1), dt(2001, 1, 10)),
... (dt(2001, 2, 1), dt(2001, 2, 10)),
... (dt(2001, 1, 25), dt(2001, 2, 5)),
... (dt(2001, 2, 2), dt(2001, 2, 2)),
... (dt(2001, 2, 10), dt(2001, 2, 12)),
... (dt(2002, 1, 1), dt(2002, 1, 10)),
... ]
>>> groups_of_event = group_events(events=events)
>>> groups_of_event
[(datetime.datetime(2001, 1, 1), datetime.datetime(2001, 1, 10)),
(datetime.datetime(2001, 1, 25), datetime.datetime(2001, 2, 12)),
(datetime.datetime(2002, 1, 1), datetime.datetime(2002, 1, 10))]
"""
if tol is None:
tol = td(seconds=0) # 1 heure
_exception.check_td(tol)
elements = [list(e) for e in events]
for j in range(len(elements)-1):
for k in range(j+1, len(elements)):
if elements[j] is None:
continue
ej = list(elements[j])
ej[0] -= tol
ej[1] += tol
if overlap(*ej, *elements[k]):
elements[k][0] = min(elements[j][0], elements[k][0])
elements[k][1] = max(elements[j][1], elements[k][1])
elements[j] = None
return sorted([tuple(e) for e in elements if e is not None])
# https://python-forum.io/thread-22725.html
# https://stackoverflow.com/questions/20112143/clustering-grouping-a-list-based-on-time-python
[docs]
def group_dates(dates=None, tol=None):
"""
Grouper des dates selon une tolérance
Parameters
----------
dates : list
Liste de dates
Other Parameters
----------------
tol : datetime.timedelta
Tolérance pour former un groupe. Par défaut: 0 seconde
Returns
-------
groups : list
Groupes de dates triées chronologiquement
"""
if tol is None:
tol = td(seconds=0) # 1 heure
_exception.check_td(tol)
ds = sorted(list(set(dates)))
diff = [td(seconds=0)] + [ds[i+1] - ds[i] for i in range(len(ds)-1)]
i = 0
keys = []
for x in diff:
if x > tol:
i += 1
keys.append(i)
groups = OrderedDict()
for d, k in zip(ds, keys):
groups.setdefault(k, [])
groups[k].append(d)
return list(groups.values())