Code source de pyspc.data.meteofrance.open_data

#!/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/>.
#
########################################################################
"""
Données d'observation et de prévision - Météo-France - OPEN DATA
"""
import os.path
import numpy as np
import pandas as pnd

from pyspc.convention.meteofrance import MDG_PREFIX
import pyspc.core.exception as _exception


[docs] class MF_OpenData(): """ Lecteur des données de METEO.DATA.GOUV.FR. Attributes ---------- filename : str Fichier de données prefix : str Préfixe du fichier de données timestep : datetime.timedelta Pas de temps des données dept : str Département des stations de mesure period : str Période des données varname : str, None Grandeurs (seulement si prefix=='Q') compressed : bool Format compressé """
[docs] def __init__(self, filename=None): """ Instanciation du lecteur du fichier METEO.DATA.GOUV.FR. Parameters ---------- filename : str Fichier de données Examples -------- >>> from pyspc.data.meteofrance import MF_OpenData >>> f = 'Q_07_latest-2023-2024_RR-T-Vent.csv.gz' >>> data = MF_OpenData(filename=f) >>> data ******************************************* *********** MF - OPEN DATA **************** ******************************************* * NOM FICHIER = Q_07_latest-2023-2024_RR-T-Vent.csv.gz * PREFIXE = Q * PAS DE TEMPS = 1 day, 0:00:00 * DEPARTEMENT = 07 * PERIOD = latest-2023-2024 * GRANDEURS = RR-T-Vent * FORMAT COMPRESSE = True ************************************* """ self.filename = filename info = self.split_basename(self.filename) self.prefix = info[0] self.dept = info[1] self.period = info[2] self.timestep = info[3] self.varname = info[4] self.compressed = info[5]
def __str__(self): """Afficher les méta-données de l'instance.""" text = """ ******************************************* *********** MF - OPEN DATA **************** ******************************************* * NOM FICHIER = {filename} * PREFIXE = {prefix} * PAS DE TEMPS = {timestep} * DEPARTEMENT = {dept} * PERIOD = {period} * GRANDEURS = {varname} * FORMAT COMPRESSE = {compressed} ************************************* """ return text.format(**vars(self))
[docs] def read(self, codes=None): """ Lecture du fichier de données METEO.DATA.GOUV.FR. Parameters ---------- codes : list Identifiants des postes météo. Information uniquement utilisée lors de la lecture en mode dégradée, mode imposé par une mémoire insuffisante. Returns ------- pandas.DataFrame Tableau des données Examples -------- >>> from pyspc.data.meteofrance import MF_OpenData >>> reader = MF_OpenData(filename='data/data/mf/open_data/' ... 'MN_43_previous-2020-2022.csv.gz') >>> content = reader.read() >>> content NUM_POSTE NOM_USUEL [...] ALTI AAAAMMJJHHMN RR QRR 0 43091005 LES ESTABLES_SAPC [...] 1350 202105101100 0.800 9 1 43091005 LES ESTABLES_SAPC [...] 1350 202105101106 0.400 9 2 43091005 LES ESTABLES_SAPC [...] 1350 202105101112 0.600 9 3 43091005 LES ESTABLES_SAPC [...] 1350 202105101118 1.400 9 4 43091005 LES ESTABLES_SAPC [...] 1350 202105101124 1.400 9 5 43091005 LES ESTABLES_SAPC [...] 1350 202105101130 1.200 9 6 43091005 LES ESTABLES_SAPC [...] 1350 202105101136 0.600 9 7 43091005 LES ESTABLES_SAPC [...] 1350 202105101142 0.700 9 8 43091005 LES ESTABLES_SAPC [...] 1350 202105101148 1.000 9 9 43091005 LES ESTABLES_SAPC [...] 1350 202105101154 1.200 9 10 43091005 LES ESTABLES_SAPC [...] 1350 202105101200 2.200 9 11 43091005 LES ESTABLES_SAPC [...] 1350 202105101206 1.400 9 12 43091005 LES ESTABLES_SAPC [...] 1350 202105101212 0.800 9 13 43091005 LES ESTABLES_SAPC [...] 1350 202105101218 0.800 9 14 43091005 LES ESTABLES_SAPC [...] 1350 202105101224 0.800 9 15 43091005 LES ESTABLES_SAPC [...] 1350 202105101230 1.100 9 16 43091005 LES ESTABLES_SAPC [...] 1350 202105101236 1.000 9 17 43091005 LES ESTABLES_SAPC [...] 1350 202105101242 0.800 9 18 43091005 LES ESTABLES_SAPC [...] 1350 202105101248 1.000 9 19 43091005 LES ESTABLES_SAPC [...] 1350 202105101254 1.200 9 20 43130002 MAZET-VOLAMONT [...] 1130 202105101100 0.800 9 21 43130002 MAZET-VOLAMONT [...] 1130 202105101106 0.600 9 22 43130002 MAZET-VOLAMONT [...] 1130 202105101112 0.600 9 23 43130002 MAZET-VOLAMONT [...] 1130 202105101118 1.000 9 24 43130002 MAZET-VOLAMONT [...] 1130 202105101124 0.800 9 25 43130002 MAZET-VOLAMONT [...] 1130 202105101130 1.000 9 26 43130002 MAZET-VOLAMONT [...] 1130 202105101136 1.400 9 27 43130002 MAZET-VOLAMONT [...] 1130 202105101142 1.000 9 28 43130002 MAZET-VOLAMONT [...] 1130 202105101148 1.200 9 29 43130002 MAZET-VOLAMONT [...] 1130 202105101154 1.000 9 30 43130002 MAZET-VOLAMONT [...] 1130 202105101200 1.000 9 31 43130002 MAZET-VOLAMONT [...] 1130 202105101206 0.800 9 32 43130002 MAZET-VOLAMONT [...] 1130 202105101212 1.700 9 33 43130002 MAZET-VOLAMONT [...] 1130 202105101218 1.200 9 34 43130002 MAZET-VOLAMONT [...] 1130 202105101224 1.400 9 35 43130002 MAZET-VOLAMONT [...] 1130 202105101230 1.000 9 36 43130002 MAZET-VOLAMONT [...] 1130 202105101236 0.800 9 37 43130002 MAZET-VOLAMONT [...] 1130 202105101242 1.400 9 38 43130002 MAZET-VOLAMONT [...] 1130 202105101248 1.200 9 39 43130002 MAZET-VOLAMONT [...] 1130 202105101254 1.400 9 """ converters = {'NUM_POSTE': lambda x: str(x).strip()} usecols = [] if self.prefix == 'MN': converters['AAAAMMJJHHMN'] = str usecols = ['RR'] elif self.prefix == 'H': converters['AAAAMMJJHH'] = str # usecols = ['RR1', ' T'] usecols = ['RR1'] elif self.prefix == 'Q': converters['AAAAMMJJ'] = str usecols = ['RR', 'TNTXM'] usecols.extend(converters.keys()) # MODES DE LECTURE DANS ORDRE DE PREFERENCE funcs = { 1: pnd.read_csv, 2: pnd.read_csv, 3: self._read_serial, 4: self._read_serial, } kwargs = { 1: {'sep': ';', 'header': 0, 'converters': converters}, 2: {'sep': ';', 'header': 0, 'converters': converters, 'usecols': usecols}, 3: {'sep': ';', 'header': 0, 'converters': converters, 'usecols': usecols, 'codes': None}, 4: {'sep': ';', 'header': 0, 'converters': converters, 'usecols': usecols, 'codes': codes}, } for mode in funcs.keys(): func = funcs[mode] kw = kwargs[mode] try: df = func(self.filename, **kw) except (pnd.errors.ParserError, MemoryError) as me: if mode == 1: _exception.Warning( None, "pandas ne peut lire le fichier en 1 seule passe. " "Seules les principales colonnes sont conservées: " f"{usecols}") continue if mode == 2: _exception.Warning( None, "pandas ne peut lire le fichier malgré la réduction " "des colonnes lues. La lecture en série est réalisée.") continue if mode == 3 and not isinstance(codes, list): raise MemoryError( "La lecture en série ne peut lire le fichier en " "entier. Veuillez renseigner une liste de codes." ) from me if mode == 3: _exception.Warning( None, "La lecture en série ne peut lire le fichier en " "entier. La liste de codes est appliquée.") continue if mode == 4: raise MemoryError( "Aucun mode de lecture, parmi les 4 codés, ne permet " "de lire ce fichier trop volumineux pour pyspc" ) from me else: break return df
def _read_serial(self, filename, sep=None, header=None, converters=None, usecols=None, codes=None): """Lecteur en série des fichiers MF.""" _exception.raise_valueerror( not os.path.exists(filename), f"Fichier manquant '{filename}'") if filename.endswith('.gz'): import gzip my_open = gzip.open else: my_open = open k = -1 records = [] hindex = [] with my_open(filename, 'rt') as txt: for line in txt: info = line.strip().split(sep) k += 1 if k == header: hinfo = info hindex = [hinfo.index(h) for h in usecols] continue ridx = hindex.index(hinfo.index('NUM_POSTE')) r = self._info2record(info, hinfo, hindex, converters) if (not isinstance(codes, list)) or ( isinstance(codes, list) and r[ridx] in codes): records.append(r) df = pnd.DataFrame.from_records(records, columns=usecols) df = df[[hinfo[h] for h in sorted(hindex)]] return df def _info2record(self, info, hinfo, hindex, converters): """Convertir.""" record = [] for idx in hindex: try: val = converters.get(hinfo[idx], float)(info[idx]) except ValueError: val = np.nan record.append(val) return tuple(record)
[docs] @staticmethod def split_basename(filename=None): """ Extraire les informations depuis le nom du fichier. Parameters ---------- filename : str Fichier de données METEO.DATA.GOUV.FR. Returns ------- prefix : str Préfixe du fichier de données dept : str Département des stations de mesure period : str Période des données timestep : datetime.timedelta Pas de temps des données varname : str, None Grandeurs (seulement si prefix=='Q') compressed : bool Format compressé Examples -------- >>> from pyspc.data.meteofrance import MF_OpenData >>> f = 'Q_07_latest-2023-2024_RR-T-Vent.csv.gz' >>> ['prefix', 'timestep', 'dept', 'period', 'varname', ... 'compressed'] = MF_OpenData.split_basename(filename=f) >>> prefix Q >>> timestep 1 day, 0:00:00 >>> dept 07 >>> period latest-2023-2024 >>> varname RR-T-Vent >>> compressed True """ if filename is None: return None, None, None, None, None, None basename = os.path.splitext(os.path.basename(filename))[0] compressed = False if filename.endswith('.gz'): basename = os.path.splitext(basename)[0] compressed = True try: info = basename.split('_') prefix = info.pop(0) dept = info.pop(0) period = info.pop(0) timestep = MDG_PREFIX[prefix] except (IndexError, ValueError) as ive: raise ValueError("Le nom de fichier ne respecte pas le " "nommage de METEO.DATA.GOUV.FR.") from ive except KeyError as ke: raise ValueError(f"Le préfixe '{prefix}' est inconnu") from ke try: varname = info.pop(0) except IndexError: varname = None return prefix, dept, period, timestep, varname, compressed