From bce1b507c47b58ddfb91dc0cd399a0efc4af8c64 Mon Sep 17 00:00:00 2001 From: akarich73 <61820266+akarich73@users.noreply.github.com> Date: Sat, 19 Oct 2024 13:15:00 +1100 Subject: [PATCH] Initial commit --- .idea/barra2-dl.iml | 3 +- .idea/misc.xml | 2 +- barra2_dl/__init__.py | 2 +- barra2_dl/{downloaders.py => downloader.py} | 12 +- barra2_dl/globals.py | 9 +- barra2_dl/helpers.py | 273 +++----------------- barra2_dl/helpers_geo.py | 108 ++++++++ barra2_dl/helpers_wind.py | 84 ++++++ barra2_dl/merger.py | 135 ++++++++++ poetry.lock | 202 ++++----------- pyproject.toml | 3 +- scripts/barra2_downloader_example.py | 4 +- tests/test_downloader.py | 2 +- tests/test_example/test_some_function.py | 1 - tests/test_helpers_wind.py | 16 ++ 15 files changed, 466 insertions(+), 390 deletions(-) rename barra2_dl/{downloaders.py => downloader.py} (90%) create mode 100644 barra2_dl/helpers_geo.py create mode 100644 barra2_dl/helpers_wind.py create mode 100644 barra2_dl/merger.py create mode 100644 tests/test_helpers_wind.py diff --git a/.idea/barra2-dl.iml b/.idea/barra2-dl.iml index be9e8b5..462e8c5 100644 --- a/.idea/barra2-dl.iml +++ b/.idea/barra2-dl.iml @@ -9,8 +9,9 @@ + - + diff --git a/.idea/misc.xml b/.idea/misc.xml index 3fc1e0f..434a144 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/barra2_dl/__init__.py b/barra2_dl/__init__.py index f1c931d..01f66fa 100644 --- a/barra2_dl/__init__.py +++ b/barra2_dl/__init__.py @@ -1 +1 @@ -from . import globals, helpers, downloaders +from . import globals, downloader, merger, helpers diff --git a/barra2_dl/downloaders.py b/barra2_dl/downloader.py similarity index 90% rename from barra2_dl/downloaders.py rename to barra2_dl/downloader.py index 6e348c7..491ddbd 100644 --- a/barra2_dl/downloaders.py +++ b/barra2_dl/downloader.py @@ -6,6 +6,7 @@ from pathlib import Path import calendar from .helpers import list_months +from .helpers_geo import format_lat_lon from .globals import LatLonPoint, LatLonBBox, barra2_aus11_index @@ -52,7 +53,7 @@ def barra2_point_downloader(base_url: str, lat_lon_point: LatLonPoint, start_datetime: str | datetime, end_datetime: str | datetime, - fileout_prefix: str, + fileout_prefix: str = None, fileout_folder: str = 'cache', fileout_type: str = 'csv_file') -> None: """Download barra2 data based on the url and variables list @@ -76,10 +77,11 @@ def barra2_point_downloader(base_url: str, Change from using os to pathlib """ + # set default fileout_prefix if not set by user # loop through each variable requested for download as each variable is saved in a separate url for var in barra2_var: - # loop through each month as each BARRA2 file is saved by month + # loop through each month as each BARRA2 file is saved by month todo check index enumerate addition works for date in list_months(start_datetime, end_datetime, freq="MS"): year = date.year month = date.month @@ -97,8 +99,12 @@ def barra2_point_downloader(base_url: str, folder_path = fileout_folder download_file(url, folder_path, fileout_name, create_folder=True) - return + # todo add option to name file_prefix using BARRA2 node; might need index 0 check + # if fileout_prefix is None: + # fileout_prefix = BARRA2_aus11_index[lat_lon_point['lat']][lat_lon_point['lon']] + + return diff --git a/barra2_dl/globals.py b/barra2_dl/globals.py index bdb7f69..fdad84e 100644 --- a/barra2_dl/globals.py +++ b/barra2_dl/globals.py @@ -40,8 +40,11 @@ class LatLonBBox(TypedDict): # VARIABLES # ----------------------------------------------------------------------------- -# barra2_aus11_extents -barra2_aus11_lat_lon_bbox = LatLonBBox(north=-23.0, west=133.0, east=134.0, south=-24) +# barra2_aus11_extents http://www.bom.gov.au/research/publications/researchreports/BRR-067.pdf +# todo consider updating Barra2 to Barra-r2 to match published data convention +barra_r2_aus11_lat_lon_bbox = LatLonBBox(north=12.95, south=-57.97, east=207.39, west=88.48) + +barra_r2_grid_spacing = 0.11 # base thredds url for BARRA2 11km 1hour reanalysis data barra2_aus11_csv_url = ("https://thredds.nci.org.au/thredds/ncss/grid/ob53/output/reanalysis/AUS-11/BOM/ERA5" @@ -49,7 +52,7 @@ class LatLonBBox(TypedDict): "{var}_AUS-11_ERA5_historical_hres_BOM_BARRA-R2_v1_1hr_{year}{month:02d}-{year}{month:02d}.nc") # index for barra2 used to join separate files -barra2_aus11_index = ['time', 'station', 'latitude[unit="degrees_north"]', 'longitude[unit="degrees_east"]'] +barra2_aus11_index = ['time','station', 'latitude[unit="degrees_north"]', 'longitude[unit="degrees_east"]'] # BARRA2 wind speed variable pairs barra2_aus11_wind_all = [('ua10m', 'va10m', '10m[unit="m s-1"]'), diff --git a/barra2_dl/helpers.py b/barra2_dl/helpers.py index c302909..56f9c25 100644 --- a/barra2_dl/helpers.py +++ b/barra2_dl/helpers.py @@ -1,11 +1,9 @@ """ -This module contains helper functions. +General helper functions. """ from pathlib import Path import pandas as pd -import numpy as np -import fnmatch from typing import List, Union @@ -25,118 +23,6 @@ def list_months(start_datetime: str, end_datetime: str, freq: str ='MS', **kwarg return df_to_list -def list_csv_files(folder_path): - """ - List all CSV files in the given folder. - - Args: - folder_path (str): The path to the folder containing the CSV files. - - Returns: - list: A list of CSV file names in the folder. - """ - folder = Path(folder_path) - csv_files = [file.name for file in folder.glob('*.csv')] - return csv_files - - -def filter_list_using_wildcard(input_list: list[str], pattern:str): - """ - Filter a list using a wildcard pattern. - - Args: - input_list (list[str]): The list of strings to be filtered. - pattern (str): The wildcard pattern to filter the list. - - Returns: - list: A list of strings that match the wildcard pattern. - """ - filtered_list = fnmatch.filter(input_list, pattern) - return filtered_list - - -def merge_csv_files_to_dataframe(filein_folder: str, - filename_pattern: str = '*.csv', - index_for_join: str = None) -> pd.DataFrame: - """ - Merge csv files from a folder based on optional filename wildcard using fnmatch. - If filename wildcard is omitted all csv files in the folder will be merged. - If fileout_folder is omitted the merged file will be saved in the filein_folder. - - Args: - filein_folder (str): Optional - filename_pattern (str): - index_for_join (str): - - Returns: - return_type: None. - - Todo: - Change from using os to pathlib - """ - - # todo add .csv check for filename_prefix - - - # list all csv files in folder - csv_files = list_csv_files(filein_folder) - - # filter csv files - csv_files_filtered = filter_list_using_wildcard(csv_files, filename_pattern) - - # initiate dataframe for combined csv results - df_combined = pd.DataFrame() - - for file in Path(filein_folder).glob(filename_pattern): - if df_combined.empty: - # read csv file without indexing to retain time as column for join - df_combined = pd.read_csv(file) - else: - # read next file into new df - df_add = pd.read_csv(file) - # combine on index join if not None, otherwise just concat together - if index_for_join is not None: - df_combined = df_combined.join(df_add.set_index(index_for_join),on=index_for_join) - else: - df_combined = pd.concat([df_combined, df_add], ignore_index = True) - - return df_combined - - -def export_dataframe_to_csv(dataframe: pd.DataFrame, - fileout_folder: str | Path, - fileout_name: str, - create_folder: bool = True) -> None: - """ - Export a DataFrame to a CSV file in the specified folder with the given file name. - - Args: - dataframe (pd.DataFrame): The Pandas DataFrame to export. - fileout_folder (str or Path): The path to the folder where the CSV file will be saved. - fileout_name (str): The name of the CSV file to save. - create_folder (bool): If True, creates the folder if it does not exist; otherwise, exits if the folder doesn't exist. - - Returns: - Path: The path of the saved CSV file. - """ - fileout_folder = Path(fileout_folder) - # Check if the folder exists - if not fileout_folder.exists(): - if create_folder: - fileout_folder.mkdir(parents=True) - print(f"The folder '{fileout_folder}' was created.") - else: - print(f"The folder '{fileout_folder}' does not exist. Exiting...") - return - - # Define the full path for the CSV file - fileout_path_name = fileout_folder / fileout_name - - # Export the DataFrame to CSV - dataframe.to_csv(fileout_path_name, index=False) - - return fileout_path_name - def get_timestamp_range_list(dataframe: pd.DataFrame, timestamp_column: str) -> List[pd.Timestamp]: """ @@ -193,121 +79,48 @@ def combine_csv_files(file_paths: list[str | Path], output_file: str | Path, ind return -# todo draft function to process csvs -def process_csvs: - # process barra2 variables to wind speed and direction todo split into modules - - # initiate DataFrame for adding new columns - df_processed = df_combined - - # loop through df_combined to df_processed - for tup in barra2_wind_speeds: - mask_wind_speed_h = tup[0][2:] - mask_ua = df_combined.columns.str.contains(tup[0]) # selects column header - mask_va = df_combined.columns.str.contains(tup[1]) # selects column header - - if np.any(mask_ua == True) and np.any(mask_va == True): - df_processed_ua = df_combined.loc[:, mask_ua] # selects mask - df_processed_va = df_combined.loc[:, mask_va] # selects mask - - print('Converted: ' + tup.__str__()) - - df_processed_v = pd.DataFrame(np.sqrt(df_processed_ua.iloc[:, 0] ** 2 + df_processed_va.iloc[:, 0] ** 2)) - df_processed_v.columns = ['v' + mask_wind_speed_h + '[unit="m s-1"]'] - - df_processed_phi_met = pd.DataFrame() - - for index, row in df_combined.iterrows(): - if (df_processed_ua.iloc[index, 0] == 0) and (df_processed_va.iloc[index, 0] == 0): - df_processed_phi_met.loc[index, 'v' + mask_wind_speed_h + '_' + 'phi_met[unit="degrees"]'] = 0.0 - else: - df_processed_phi_met.loc[index, 'v' + mask_wind_speed_h + '_' + 'phi_met[unit="degrees"]'] = ( - np.mod(180 + np.rad2deg( - np.arctan2(df_processed_ua.iloc[index, 0], df_processed_va.iloc[index, 0])), 360)) - - # Merge the current variable DataFrame with the combined DataFrame - df_processed = df_processed.join(df_processed_v) - df_processed = df_processed.join(df_processed_phi_met) - - # export combined to csv - df_processed.to_csv( - os.path.join(output_dir, - f"{output_filename_prefix}_processed_{start_date_time.strftime("%Y%m%d")}_{end_date_time.strftime("%Y%m%d")}.csv")) - - return - - -# todo add tests -def calculate_wind_speed(u: Union[float, int], v: Union[float, int]) -> float: - """ - Args: - u: The u component of the wind vector, which can be a float or an int. - v: The v component of the wind vector, which can be a float or an int. - Returns: - Wind speed. If both u and v are zero, it returns 0.0. - """ - if u == 0 and v == 0: - return 0.0 - return np.sqrt(u ** 2 + v ** 2) - -def wind_components_to_speed(ua: Union[float, int, List[float], List[int]], va: Union[float, int, List[float], List[int]]) -> Union[float, List[float]]: - """ - Convert wind components ua and va to wind speed v. - Args: - ua (Union[float, int, List[float], List[int]]): The u-component of the wind. - va (Union[float, int, List[float], List[int]]): The v-component of the wind. - Returns: - float or List[float]: The calculated wind speed. - Raises: - ValueError: If the input types do not match or if they are neither List[float] nor float. - """ - if isinstance(ua, (float, int)) and isinstance(va, (float, int)): - if ua == 0 and va == 0: - return 0.0 - return calculate_wind_speed(ua, va) - elif isinstance(ua, list) and isinstance(va, list): - if not all(isinstance(num, (float, int)) for num in ua + va): - raise ValueError("All elements in both lists must be either float or int.") - if len(ua) != len(va): - raise ValueError("Both lists must be of the same length.") - return [calculate_wind_speed(u, v) for u, v in zip(ua, va)] - else: - raise ValueError("Both arguments must be either both float/int or both lists of float/int.") - +# # todo draft function to process csvs +# def process_csvs: +# # process barra2 variables to wind speed and direction todo split into modules +# +# # initiate DataFrame for adding new columns +# df_processed = df_combined +# +# # loop through df_combined to df_processed +# for tup in barra2_wind_speeds: +# mask_wind_speed_h = tup[0][2:] +# mask_ua = df_combined.columns.str.contains(tup[0]) # selects column header +# mask_va = df_combined.columns.str.contains(tup[1]) # selects column header +# +# if np.any(mask_ua == True) and np.any(mask_va == True): +# df_processed_ua = df_combined.loc[:, mask_ua] # selects mask +# df_processed_va = df_combined.loc[:, mask_va] # selects mask +# +# print('Converted: ' + tup.__str__()) +# +# df_processed_v = pd.DataFrame(np.sqrt(df_processed_ua.iloc[:, 0] ** 2 + df_processed_va.iloc[:, 0] ** 2)) +# df_processed_v.columns = ['v' + mask_wind_speed_h + '[unit="m s-1"]'] +# +# df_processed_phi_met = pd.DataFrame() +# +# for index, row in df_combined.iterrows(): +# if (df_processed_ua.iloc[index, 0] == 0) and (df_processed_va.iloc[index, 0] == 0): +# df_processed_phi_met.loc[index, 'v' + mask_wind_speed_h + '_' + 'phi_met[unit="degrees"]'] = 0.0 +# else: +# df_processed_phi_met.loc[index, 'v' + mask_wind_speed_h + '_' + 'phi_met[unit="degrees"]'] = ( +# np.mod(180 + np.rad2deg( +# np.arctan2(df_processed_ua.iloc[index, 0], df_processed_va.iloc[index, 0])), 360)) +# +# # Merge the current variable DataFrame with the combined DataFrame +# df_processed = df_processed.join(df_processed_v) +# df_processed = df_processed.join(df_processed_phi_met) +# +# # export combined to csv +# df_processed.to_csv( +# os.path.join(output_dir, +# f"{output_filename_prefix}_processed_{start_date_time.strftime("%Y%m%d")}_{end_date_time.strftime("%Y%m%d")}.csv")) +# +# return -# todo add tests -def calculate_wind_direction(u: Union[float, int], v: Union[float, int]) -> float: - """ - Args: - u: The u component of the wind vector, which can be a float or an int. - v: The v component of the wind vector, which can be a float or an int. - Returns: - Wind direction in degrees. If both u and v are zero, it returns 0.0. - """ - if u == 0 and v == 0: - return 0.0 - return np.mod(180 + np.rad2deg(np.arctan2(u, v)), 360) - -def wind_components_to_direction(ua: Union[float, int, List[float], List[int]], va: Union[float, int, List[float], List[int]]) -> Union[float, List[float]]: - """ - Convert wind components ua and va to wind direction phi. - Args: - ua (Union[float, int, List[float], List[int]]): The u-component of the wind. - va (Union[float, int, List[float], List[int]]): The v-component of the wind. - Returns: - float or List[float]: The calculated wind speed. - Raises: - ValueError: If the input types and incorrect or do not match or if lists of different lengths are provided. - """ - if isinstance(ua, (float, int)) and isinstance(va, (float, int)): - return calculate_wind_direction(ua, va) - elif isinstance(ua, list) and isinstance(va, list): - if not all(isinstance(num, (float, int)) for num in ua + va): - raise ValueError("All elements in both lists must be either float or int.") - if len(ua) != len(va): - raise ValueError("Both lists must be of the same length.") - return [calculate_wind_direction(u, v) for u, v in zip(ua, va)] - else: - raise ValueError("Both arguments must be either both float/int or both lists of float/int.") diff --git a/barra2_dl/helpers_geo.py b/barra2_dl/helpers_geo.py new file mode 100644 index 0000000..7660090 --- /dev/null +++ b/barra2_dl/helpers_geo.py @@ -0,0 +1,108 @@ +""" +Helper funcitons for geo. +todo draft functions to set grid for mapping support. +todo implement for file naming +""" + +import numpy as np +import pandas as pd +from typing import Union + + +def generate_point_grid(lat_lon_bbox: Union[dict, tuple], lat_res: float, lon_res: float = None, offset:bool = None) -> ( + pd.DataFrame): + """ + Create a grid of longitude and latitude points between specified minimum and maximum values. + + Args: + lat_lon_bbox (Union[dict, tuple]): Dictionary or tuple containing geographic boundaries. + Dictionary should have keys 'north', 'south', 'east', 'west'. + Tuple should contain values in the order (north, south, east, west). + lon_res (float): Resolution of the longitude points. + lat_res (float): Resolution of the latitude points. Optional lon_res = lat_res if not specified. + offset: Offsets the first point by half the lat_res and lon_res to create points at centre. + + Returns: + pd.DataFrame: DataFrame containing the grid points with columns 'longitude' and 'latitude'. + + Raises: + ValueError: If bounds is neither a dictionary nor a tuple, or if the keys/values are missing or invalid. + """ + + if isinstance(lat_lon_bbox, dict): + required_keys = ['north', 'south', 'east', 'west'] + if not all(key in lat_lon_bbox for key in required_keys): + raise ValueError("Dictionary must contain 'north', 'south', 'east', and 'west' keys.") + elif isinstance(lat_lon_bbox, tuple): + if len(lat_lon_bbox) != 4: + raise ValueError("Tuple must contain exactly 4 values: (north, south, east, west).") + else: + raise ValueError("Bounds must be a dictionary or tuple.") + + if lon_res is None: + lon_res = lat_res + + if offset: + lat_lon_bbox = { + 'north': lat_lon_bbox['north'] - lat_res/2, + 'south': lat_lon_bbox['south'] + lat_res/2, + 'east': lat_lon_bbox['east'] - lon_res/2, + 'west': lat_lon_bbox['west'] + lon_res/2 + } + + longitudes = np.arange(lat_lon_bbox['west'], lat_lon_bbox['east'] + lon_res, lon_res) + latitudes = np.arange(lat_lon_bbox['south'], lat_lon_bbox['north'] + lat_res, lat_res) + long_grid, lat_grid = np.meshgrid(longitudes, latitudes) + + df_point_grid = pd.DataFrame({ + 'latitude': lat_grid.flatten(), + 'longitude': long_grid.flatten() + }) + + return df_point_grid + + +def find_nearest_point(df_point_grid: pd.DataFrame, target_lat: float, target_lon: float) -> pd.Series: + """ + Find the nearest point in a DataFrame of latitude, longitude points to a target point (target_lat, target_lon). + + Args: + df_point_grid (pd.DataFrame): DataFrame containing the points with 'Longitude' and 'Latitude' columns. + target_lon (float): The x-coordinate of the target point. + target_lat (float): The y-coordinate of the target point. + + Returns: + pd.Series: The row of the nearest point in the DataFrame. + + """ + # check if target falls within grid + min_lat, max_lat = df_point_grid['latitude'].min(), df_point_grid['latitude'].max() + min_lon, max_lon = df_point_grid['longitude'].min(), df_point_grid['longitude'].max() + + if not (min_lat <= target_lat <= max_lat) or not (min_lon <= target_lon <= max_lon): + raise ValueError("Target latitude and/or longitude are out of the range of the DataFrame's coordinates.") + + distances = np.sqrt((df_point_grid['latitude'] - target_lat) ** 2 + (df_point_grid['longitude'] - target_lon) ** 2) + nearest_index = distances.idxmin() + return df_point_grid.loc[nearest_index] + + +def format_lat_lon(coordinates: dict) -> list[str]: + """ + Format a dictionary containing 'lat' and 'lon' as floats to a string with 2 decimal precision, + converting any negative values to 'S'. + + Args: + coordinates (dict): Dictionary containing 'lat' and 'lon' as floats. + + Returns: + str: Formatted string of the latitude and longitude. + """ + lat = coordinates.get('lat', 0.0) + lon = coordinates.get('lon', 0.0) + + formatted_lat = ('S' if lat < 0 else '') + '{:.2f}'.format(abs(lat)) + formatted_lon = '{:.2f}'.format(abs(lon)) + + return [formatted_lat, formatted_lon] + diff --git a/barra2_dl/helpers_wind.py b/barra2_dl/helpers_wind.py new file mode 100644 index 0000000..efb5d10 --- /dev/null +++ b/barra2_dl/helpers_wind.py @@ -0,0 +1,84 @@ +""" +Helper functions for wind calculations. +# todo add tests +""" + +from typing import List +import numpy as np + + +def calculate_wind_speed(u: float | int, v: float | int) -> float: + """ + Calculate the wind speed from u and v components. + Args: + u (float | int): The u component of the wind vector. + v (float | int): The v component of the wind vector. + Returns: + Wind speed. If both u and v are zero, it returns 0.0. + """ + if u == 0 and v == 0: + return 0.0 + return np.sqrt(u ** 2 + v ** 2) + + +def wind_components_to_speed(ua: float | int | list[float | int], va: float | int | list[float | int]) -> float | list[float]: + """ + Convert wind components ua and va to wind speed v. + Args: + ua (float | int | list[float | int]): The u-component of the wind. + va (float | int | list[float | int]): The v-component of the wind. + Returns: + The calculated wind speed. + Raises: + ValueError: If the input types do not match or if they are neither list[float] nor list[int] nor float. + """ + if isinstance(ua, (float, int)) and isinstance(va, (float, int)): + if ua == 0 and va == 0: + return 0.0 + return calculate_wind_speed(ua, va) + elif isinstance(ua, list) and isinstance(va, list): + if not all(isinstance(num, (float, int)) for num in ua + va): + raise ValueError("All elements in both lists must be either float or int.") + if len(ua) != len(va): + raise ValueError("Both lists must be of the same length.") + return [calculate_wind_speed(u, v) for u, v in zip(ua, va)] + else: + raise ValueError("Both arguments must be either both float/int or both lists of float/int.") + + +# todo add tests +def calculate_wind_direction(u: float | int, v: float | int) -> float: + """ + Args: + u (float | int): The u component of the wind vector, which can be a float or an int. + v (float | int): The v component of the wind vector, which can be a float or an int. + Returns: + Calculated wind direction in degrees. If both u and v are zero, it returns 0.0. + """ + if u == 0 and v == 0: + return 0.0 + return np.mod(180 + np.rad2deg(np.arctan2(u, v)), 360) + + +def wind_components_to_direction(ua: float | int | List[float | int], va: float | int | List[float | int]) -> float | List[float]: + """ + Convert wind components ua and va to wind direction phi. + Args: + ua (float | int | List[float | int]): The u-component of the wind. + va (float | int | List[float | int]): The v-component of the wind. + Returns: + The calculated wind speed direction. + Raises: + ValueError: If the input types and incorrect or do not match or if lists of different lengths are provided. + """ + + if isinstance(ua, (float, int)) and isinstance(va, (float, int)): + return calculate_wind_direction(ua, va) + elif isinstance(ua, list) and isinstance(va, list): + if not all(isinstance(num, (float, int)) for num in ua + va): + raise ValueError("All elements in both lists must be either float or int.") + if len(ua) != len(va): + raise ValueError("Both lists must be of the same length.") + return [calculate_wind_direction(u, v) for u, v in zip(ua, va)] + else: + raise ValueError("Both arguments must be either both float/int or both lists of float/int.") diff --git a/barra2_dl/merger.py b/barra2_dl/merger.py new file mode 100644 index 0000000..38227f3 --- /dev/null +++ b/barra2_dl/merger.py @@ -0,0 +1,135 @@ +""" +Helper functions for csv files. +""" +from pathlib import Path +import pandas as pd +import fnmatch + + +def list_csv_files(folder_path): + """ + List all CSV files in the given folder. + + Args: + folder_path (str): The path to the folder containing the CSV files. + + Returns: + list: A list of CSV file names in the folder. + """ + folder = Path(folder_path) + csv_files = [file.name for file in folder.glob('*.csv')] + return csv_files + +def filter_list_using_wildcard(input_list: list[str], pattern:str): + """ + Filter a list using a wildcard pattern. + + Args: + input_list (list[str]): The list of strings to be filtered. + pattern (str): The wildcard pattern to filter the list. + + Returns: + list: A list of strings that match the wildcard pattern. + """ + filtered_list = fnmatch.filter(input_list, pattern) + return filtered_list + + +def export_df_to_csv(dataframe: pd.DataFrame, + fileout_folder: str | Path, + fileout_name: str, + create_folder: bool = True) -> None: + """ + Export a DataFrame to a CSV file in the specified folder with the given file name. + + Args: + dataframe (pd.DataFrame): The Pandas DataFrame to export. + fileout_folder (str or Path): The path to the folder where the CSV file will be saved. + fileout_name (str): The name of the CSV file to save. + create_folder (bool): If True, creates the folder if it does not exist; otherwise, exits if the folder doesn't exist. + + Returns: + Path: The path of the saved CSV file. + """ + fileout_folder = Path(fileout_folder) + # Check if the folder exists + if not fileout_folder.exists(): + if create_folder: + fileout_folder.mkdir(parents=True) + print(f"The folder '{fileout_folder}' was created.") + else: + print(f"The folder '{fileout_folder}' does not exist. Exiting...") + return + + # Define the full path for the CSV file + fileout_path_name = fileout_folder / fileout_name + + # Export the DataFrame to CSV + dataframe.to_csv(fileout_path_name, index=False) + + return fileout_path_name + + +def merge_csvs_to_df(filein_folder: str, + filename_pattern: str = '*.csv', + index_for_join: str = None) -> pd.DataFrame: + """ + Function to merge csv files iteratively from a folder based on filename wildcard. + If filename wildcard is omitted all csv files in the folder will be merged. + + Args: + filein_folder (str): Optional + filename_pattern (str): + index_for_join (str): + + Returns: + DataFrame: A DataFrame with the merged csvs. + + Todo: + add .csv check for filename_prefix + """ + + def merge_suffix_columns(df: pd.DataFrame, suffix_x: str = '_x', suffix_y: str = '_y') -> pd.DataFrame: + """ + Function to merge DataFrame columns with '_x' and '_y' suffixes. + + Args: + df: The DataFrame that contains columns with suffixes to be merged. + suffix_x: The suffix used in the first set of columns (default is '_x'). + suffix_y: The suffix used in the second set of columns (default is '_y'). + + Returns: + DataFrame: A DataFrame with the merged columns. + If no suffix_x returns the original DataFrame. + + Todo: + Add checks for multiple and mismatched suffixed columns + """ + for column in df.columns: + if column.endswith(suffix_x): + base_column = column[:-len(suffix_x)] + column_y = base_column + suffix_y + if column_y in df.columns: + # Create a new column without suffix and merge the values + df[base_column] = df[column].combine_first(df[column_y]) + # Drop the old suffix columns + df.drop([column, column_y], axis=1, inplace=True) + return df + + # initiate dataframe for combined csv results + df_combined = pd.DataFrame() + + for file in Path(filein_folder).glob(filename_pattern): + print(f"Merging file: {file}") + if df_combined.empty: + # read csv file without indexing to retain time as column for join + df_combined = pd.read_csv(file) + else: + # read next file into new df + df_add = pd.read_csv(file) + # combine on index join if not None, otherwise just concat together + # todo df_combined = df1.merge(df2,how='outer') seems to guess the matching columns + df_combined = pd.merge(df_combined, df_add, how='outer', on=index_for_join) + df_combined = merge_suffix_columns(df_combined) + + return df_combined diff --git a/poetry.lock b/poetry.lock index 95e559f..8e87aa1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -716,25 +716,6 @@ files = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] -[[package]] -name = "importlib-metadata" -version = "7.0.1" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, - {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, -] - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] - [[package]] name = "iniconfig" version = "2.0.0" @@ -1092,118 +1073,64 @@ test = ["freezegun", "pytest", "pytest-cov", "pytest-datadir", "pytest-socket", [[package]] name = "numpy" -version = "2.0.2" -description = "Fundamental package for array computing in Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece"}, - {file = "numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04"}, - {file = "numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66"}, - {file = "numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b"}, - {file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd"}, - {file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318"}, - {file = "numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8"}, - {file = "numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326"}, - {file = "numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97"}, - {file = "numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131"}, - {file = "numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448"}, - {file = "numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195"}, - {file = "numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57"}, - {file = "numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a"}, - {file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669"}, - {file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951"}, - {file = "numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9"}, - {file = "numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15"}, - {file = "numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4"}, - {file = "numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc"}, - {file = "numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b"}, - {file = "numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e"}, - {file = "numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c"}, - {file = "numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c"}, - {file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692"}, - {file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a"}, - {file = "numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c"}, - {file = "numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded"}, - {file = "numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5"}, - {file = "numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a"}, - {file = "numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c"}, - {file = "numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd"}, - {file = "numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b"}, - {file = "numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729"}, - {file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1"}, - {file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd"}, - {file = "numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d"}, - {file = "numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d"}, - {file = "numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa"}, - {file = "numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73"}, - {file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8"}, - {file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4"}, - {file = "numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c"}, - {file = "numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385"}, - {file = "numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78"}, -] - -[[package]] -name = "numpy" -version = "2.1.1" +version = "2.1.2" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" files = [ - {file = "numpy-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8a0e34993b510fc19b9a2ce7f31cb8e94ecf6e924a40c0c9dd4f62d0aac47d9"}, - {file = "numpy-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7dd86dfaf7c900c0bbdcb8b16e2f6ddf1eb1fe39c6c8cca6e94844ed3152a8fd"}, - {file = "numpy-2.1.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:5889dd24f03ca5a5b1e8a90a33b5a0846d8977565e4ae003a63d22ecddf6782f"}, - {file = "numpy-2.1.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:59ca673ad11d4b84ceb385290ed0ebe60266e356641428c845b39cd9df6713ab"}, - {file = "numpy-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13ce49a34c44b6de5241f0b38b07e44c1b2dcacd9e36c30f9c2fcb1bb5135db7"}, - {file = "numpy-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913cc1d311060b1d409e609947fa1b9753701dac96e6581b58afc36b7ee35af6"}, - {file = "numpy-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:caf5d284ddea7462c32b8d4a6b8af030b6c9fd5332afb70e7414d7fdded4bfd0"}, - {file = "numpy-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:57eb525e7c2a8fdee02d731f647146ff54ea8c973364f3b850069ffb42799647"}, - {file = "numpy-2.1.1-cp310-cp310-win32.whl", hash = "sha256:9a8e06c7a980869ea67bbf551283bbed2856915f0a792dc32dd0f9dd2fb56728"}, - {file = "numpy-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:d10c39947a2d351d6d466b4ae83dad4c37cd6c3cdd6d5d0fa797da56f710a6ae"}, - {file = "numpy-2.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0d07841fd284718feffe7dd17a63a2e6c78679b2d386d3e82f44f0108c905550"}, - {file = "numpy-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b5613cfeb1adfe791e8e681128f5f49f22f3fcaa942255a6124d58ca59d9528f"}, - {file = "numpy-2.1.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0b8cc2715a84b7c3b161f9ebbd942740aaed913584cae9cdc7f8ad5ad41943d0"}, - {file = "numpy-2.1.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:b49742cdb85f1f81e4dc1b39dcf328244f4d8d1ded95dea725b316bd2cf18c95"}, - {file = "numpy-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8d5f8a8e3bc87334f025194c6193e408903d21ebaeb10952264943a985066ca"}, - {file = "numpy-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d51fc141ddbe3f919e91a096ec739f49d686df8af254b2053ba21a910ae518bf"}, - {file = "numpy-2.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:98ce7fb5b8063cfdd86596b9c762bf2b5e35a2cdd7e967494ab78a1fa7f8b86e"}, - {file = "numpy-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:24c2ad697bd8593887b019817ddd9974a7f429c14a5469d7fad413f28340a6d2"}, - {file = "numpy-2.1.1-cp311-cp311-win32.whl", hash = "sha256:397bc5ce62d3fb73f304bec332171535c187e0643e176a6e9421a6e3eacef06d"}, - {file = "numpy-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:ae8ce252404cdd4de56dcfce8b11eac3c594a9c16c231d081fb705cf23bd4d9e"}, - {file = "numpy-2.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c803b7934a7f59563db459292e6aa078bb38b7ab1446ca38dd138646a38203e"}, - {file = "numpy-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6435c48250c12f001920f0751fe50c0348f5f240852cfddc5e2f97e007544cbe"}, - {file = "numpy-2.1.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3269c9eb8745e8d975980b3a7411a98976824e1fdef11f0aacf76147f662b15f"}, - {file = "numpy-2.1.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:fac6e277a41163d27dfab5f4ec1f7a83fac94e170665a4a50191b545721c6521"}, - {file = "numpy-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcd8f556cdc8cfe35e70efb92463082b7f43dd7e547eb071ffc36abc0ca4699b"}, - {file = "numpy-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b9cd92c8f8e7b313b80e93cedc12c0112088541dcedd9197b5dee3738c1201"}, - {file = "numpy-2.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:afd9c680df4de71cd58582b51e88a61feed4abcc7530bcd3d48483f20fc76f2a"}, - {file = "numpy-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8661c94e3aad18e1ea17a11f60f843a4933ccaf1a25a7c6a9182af70610b2313"}, - {file = "numpy-2.1.1-cp312-cp312-win32.whl", hash = "sha256:950802d17a33c07cba7fd7c3dcfa7d64705509206be1606f196d179e539111ed"}, - {file = "numpy-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:3fc5eabfc720db95d68e6646e88f8b399bfedd235994016351b1d9e062c4b270"}, - {file = "numpy-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:046356b19d7ad1890c751b99acad5e82dc4a02232013bd9a9a712fddf8eb60f5"}, - {file = "numpy-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6e5a9cb2be39350ae6c8f79410744e80154df658d5bea06e06e0ac5bb75480d5"}, - {file = "numpy-2.1.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:d4c57b68c8ef5e1ebf47238e99bf27657511ec3f071c465f6b1bccbef12d4136"}, - {file = "numpy-2.1.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:8ae0fd135e0b157365ac7cc31fff27f07a5572bdfc38f9c2d43b2aff416cc8b0"}, - {file = "numpy-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:981707f6b31b59c0c24bcda52e5605f9701cb46da4b86c2e8023656ad3e833cb"}, - {file = "numpy-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ca4b53e1e0b279142113b8c5eb7d7a877e967c306edc34f3b58e9be12fda8df"}, - {file = "numpy-2.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e097507396c0be4e547ff15b13dc3866f45f3680f789c1a1301b07dadd3fbc78"}, - {file = "numpy-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7506387e191fe8cdb267f912469a3cccc538ab108471291636a96a54e599556"}, - {file = "numpy-2.1.1-cp313-cp313-win32.whl", hash = "sha256:251105b7c42abe40e3a689881e1793370cc9724ad50d64b30b358bbb3a97553b"}, - {file = "numpy-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:f212d4f46b67ff604d11fff7cc62d36b3e8714edf68e44e9760e19be38c03eb0"}, - {file = "numpy-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:920b0911bb2e4414c50e55bd658baeb78281a47feeb064ab40c2b66ecba85553"}, - {file = "numpy-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bab7c09454460a487e631ffc0c42057e3d8f2a9ddccd1e60c7bb8ed774992480"}, - {file = "numpy-2.1.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:cea427d1350f3fd0d2818ce7350095c1a2ee33e30961d2f0fef48576ddbbe90f"}, - {file = "numpy-2.1.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:e30356d530528a42eeba51420ae8bf6c6c09559051887196599d96ee5f536468"}, - {file = "numpy-2.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8dfa9e94fc127c40979c3eacbae1e61fda4fe71d84869cc129e2721973231ef"}, - {file = "numpy-2.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910b47a6d0635ec1bd53b88f86120a52bf56dcc27b51f18c7b4a2e2224c29f0f"}, - {file = "numpy-2.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:13cc11c00000848702322af4de0147ced365c81d66053a67c2e962a485b3717c"}, - {file = "numpy-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53e27293b3a2b661c03f79aa51c3987492bd4641ef933e366e0f9f6c9bf257ec"}, - {file = "numpy-2.1.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7be6a07520b88214ea85d8ac8b7d6d8a1839b0b5cb87412ac9f49fa934eb15d5"}, - {file = "numpy-2.1.1-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:52ac2e48f5ad847cd43c4755520a2317f3380213493b9d8a4c5e37f3b87df504"}, - {file = "numpy-2.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50a95ca3560a6058d6ea91d4629a83a897ee27c00630aed9d933dff191f170cd"}, - {file = "numpy-2.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:99f4a9ee60eed1385a86e82288971a51e71df052ed0b2900ed30bc840c0f2e39"}, - {file = "numpy-2.1.1.tar.gz", hash = "sha256:d0cf7d55b1051387807405b3898efafa862997b4cba8aa5dbe657be794afeafd"}, + {file = "numpy-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:30d53720b726ec36a7f88dc873f0eec8447fbc93d93a8f079dfac2629598d6ee"}, + {file = "numpy-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8d3ca0a72dd8846eb6f7dfe8f19088060fcb76931ed592d29128e0219652884"}, + {file = "numpy-2.1.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:fc44e3c68ff00fd991b59092a54350e6e4911152682b4782f68070985aa9e648"}, + {file = "numpy-2.1.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:7c1c60328bd964b53f8b835df69ae8198659e2b9302ff9ebb7de4e5a5994db3d"}, + {file = "numpy-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cdb606a7478f9ad91c6283e238544451e3a95f30fb5467fbf715964341a8a86"}, + {file = "numpy-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d666cb72687559689e9906197e3bec7b736764df6a2e58ee265e360663e9baf7"}, + {file = "numpy-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c6eef7a2dbd0abfb0d9eaf78b73017dbfd0b54051102ff4e6a7b2980d5ac1a03"}, + {file = "numpy-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:12edb90831ff481f7ef5f6bc6431a9d74dc0e5ff401559a71e5e4611d4f2d466"}, + {file = "numpy-2.1.2-cp310-cp310-win32.whl", hash = "sha256:a65acfdb9c6ebb8368490dbafe83c03c7e277b37e6857f0caeadbbc56e12f4fb"}, + {file = "numpy-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:860ec6e63e2c5c2ee5e9121808145c7bf86c96cca9ad396c0bd3e0f2798ccbe2"}, + {file = "numpy-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b42a1a511c81cc78cbc4539675713bbcf9d9c3913386243ceff0e9429ca892fe"}, + {file = "numpy-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:faa88bc527d0f097abdc2c663cddf37c05a1c2f113716601555249805cf573f1"}, + {file = "numpy-2.1.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:c82af4b2ddd2ee72d1fc0c6695048d457e00b3582ccde72d8a1c991b808bb20f"}, + {file = "numpy-2.1.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:13602b3174432a35b16c4cfb5de9a12d229727c3dd47a6ce35111f2ebdf66ff4"}, + {file = "numpy-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ebec5fd716c5a5b3d8dfcc439be82a8407b7b24b230d0ad28a81b61c2f4659a"}, + {file = "numpy-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2b49c3c0804e8ecb05d59af8386ec2f74877f7ca8fd9c1e00be2672e4d399b1"}, + {file = "numpy-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2cbba4b30bf31ddbe97f1c7205ef976909a93a66bb1583e983adbd155ba72ac2"}, + {file = "numpy-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8e00ea6fc82e8a804433d3e9cedaa1051a1422cb6e443011590c14d2dea59146"}, + {file = "numpy-2.1.2-cp311-cp311-win32.whl", hash = "sha256:5006b13a06e0b38d561fab5ccc37581f23c9511879be7693bd33c7cd15ca227c"}, + {file = "numpy-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:f1eb068ead09f4994dec71c24b2844f1e4e4e013b9629f812f292f04bd1510d9"}, + {file = "numpy-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7bf0a4f9f15b32b5ba53147369e94296f5fffb783db5aacc1be15b4bf72f43b"}, + {file = "numpy-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b1d0fcae4f0949f215d4632be684a539859b295e2d0cb14f78ec231915d644db"}, + {file = "numpy-2.1.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f751ed0a2f250541e19dfca9f1eafa31a392c71c832b6bb9e113b10d050cb0f1"}, + {file = "numpy-2.1.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:bd33f82e95ba7ad632bc57837ee99dba3d7e006536200c4e9124089e1bf42426"}, + {file = "numpy-2.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b8cde4f11f0a975d1fd59373b32e2f5a562ade7cde4f85b7137f3de8fbb29a0"}, + {file = "numpy-2.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d95f286b8244b3649b477ac066c6906fbb2905f8ac19b170e2175d3d799f4df"}, + {file = "numpy-2.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ab4754d432e3ac42d33a269c8567413bdb541689b02d93788af4131018cbf366"}, + {file = "numpy-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e585c8ae871fd38ac50598f4763d73ec5497b0de9a0ab4ef5b69f01c6a046142"}, + {file = "numpy-2.1.2-cp312-cp312-win32.whl", hash = "sha256:9c6c754df29ce6a89ed23afb25550d1c2d5fdb9901d9c67a16e0b16eaf7e2550"}, + {file = "numpy-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:456e3b11cb79ac9946c822a56346ec80275eaf2950314b249b512896c0d2505e"}, + {file = "numpy-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a84498e0d0a1174f2b3ed769b67b656aa5460c92c9554039e11f20a05650f00d"}, + {file = "numpy-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4d6ec0d4222e8ffdab1744da2560f07856421b367928026fb540e1945f2eeeaf"}, + {file = "numpy-2.1.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:259ec80d54999cc34cd1eb8ded513cb053c3bf4829152a2e00de2371bd406f5e"}, + {file = "numpy-2.1.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:675c741d4739af2dc20cd6c6a5c4b7355c728167845e3c6b0e824e4e5d36a6c3"}, + {file = "numpy-2.1.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b2d4e667895cc55e3ff2b56077e4c8a5604361fc21a042845ea3ad67465aa8"}, + {file = "numpy-2.1.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43cca367bf94a14aca50b89e9bc2061683116cfe864e56740e083392f533ce7a"}, + {file = "numpy-2.1.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:76322dcdb16fccf2ac56f99048af32259dcc488d9b7e25b51e5eca5147a3fb98"}, + {file = "numpy-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:32e16a03138cabe0cb28e1007ee82264296ac0983714094380b408097a418cfe"}, + {file = "numpy-2.1.2-cp313-cp313-win32.whl", hash = "sha256:242b39d00e4944431a3cd2db2f5377e15b5785920421993770cddb89992c3f3a"}, + {file = "numpy-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f2ded8d9b6f68cc26f8425eda5d3877b47343e68ca23d0d0846f4d312ecaa445"}, + {file = "numpy-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ffef621c14ebb0188a8633348504a35c13680d6da93ab5cb86f4e54b7e922b5"}, + {file = "numpy-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ad369ed238b1959dfbade9018a740fb9392c5ac4f9b5173f420bd4f37ba1f7a0"}, + {file = "numpy-2.1.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d82075752f40c0ddf57e6e02673a17f6cb0f8eb3f587f63ca1eaab5594da5b17"}, + {file = "numpy-2.1.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:1600068c262af1ca9580a527d43dc9d959b0b1d8e56f8a05d830eea39b7c8af6"}, + {file = "numpy-2.1.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a26ae94658d3ba3781d5e103ac07a876b3e9b29db53f68ed7df432fd033358a8"}, + {file = "numpy-2.1.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13311c2db4c5f7609b462bc0f43d3c465424d25c626d95040f073e30f7570e35"}, + {file = "numpy-2.1.2-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:2abbf905a0b568706391ec6fa15161fad0fb5d8b68d73c461b3c1bab6064dd62"}, + {file = "numpy-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ef444c57d664d35cac4e18c298c47d7b504c66b17c2ea91312e979fcfbdfb08a"}, + {file = "numpy-2.1.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:bdd407c40483463898b84490770199d5714dcc9dd9b792f6c6caccc523c00952"}, + {file = "numpy-2.1.2-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:da65fb46d4cbb75cb417cddf6ba5e7582eb7bb0b47db4b99c9fe5787ce5d91f5"}, + {file = "numpy-2.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c193d0b0238638e6fc5f10f1b074a6993cb13b0b431f64079a509d63d3aa8b7"}, + {file = "numpy-2.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a7d80b2e904faa63068ead63107189164ca443b42dd1930299e0d1cb041cec2e"}, + {file = "numpy-2.1.2.tar.gz", hash = "sha256:13532a088217fa624c99b843eeb54640de23b3414b14aa66d023805eb731066c"}, ] [[package]] @@ -1489,7 +1416,6 @@ files = [ ] [package.dependencies] -importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} pytest = "*" [[package]] @@ -1819,7 +1745,6 @@ babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.18.1,<0.21" imagesize = ">=1.3" -importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" Pygments = ">=2.14" @@ -2124,22 +2049,7 @@ files = [ [package.extras] dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] -[[package]] -name = "zipp" -version = "3.17.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] - [metadata] lock-version = "2.0" -python-versions = "^3.9" -content-hash = "6924569113877b7f5065dd555a3f81fcecb7b4e438bdb0d60e30d1b16cb711cc" +python-versions = "^3.10" +content-hash = "494a653a2e7235307dfd8ab2b09daa5520b9b2f27367f0e756dd4b7571229b16" diff --git a/pyproject.toml b/pyproject.toml index dc5618d..564e800 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,10 +25,11 @@ classifiers = [ ] [tool.poetry.dependencies] -python = "^3.9" +python = "^3.10" pandas = "^2.2.3" pathlib = "^1.0.1" requests = "^2.32.3" +numpy = "^2.1.2" [tool.poetry.group.dev.dependencies] mypy = "^1.8" diff --git a/scripts/barra2_downloader_example.py b/scripts/barra2_downloader_example.py index 89e37ef..af57eef 100644 --- a/scripts/barra2_downloader_example.py +++ b/scripts/barra2_downloader_example.py @@ -21,8 +21,8 @@ start_datetime = datetime.strptime("2023-01-01T00:00:00Z", "%Y-%m-%dT%H:%M:%SZ") end_datetime = datetime.strptime("2023-03-31T23:00:00Z", "%Y-%m-%dT%H:%M:%SZ") -# set output file custom name prefix to indicate a device or project location for the downloaded data -output_filename_prefix = "test" +# Option to set output file custom name prefix to indicate a device or project location for the downloaded data +output_filename_prefix = "demo" # ----------------------------------------------------------------------------- diff --git a/tests/test_downloader.py b/tests/test_downloader.py index cd22760..1c0f62e 100644 --- a/tests/test_downloader.py +++ b/tests/test_downloader.py @@ -1,6 +1,6 @@ import pytest -from barra2_dl import downloaders +from barra2_dl import downloader from barra2_dl.globals import * @pytest.mark.parametrize(('first', 'second', 'expected'), [ diff --git a/tests/test_example/test_some_function.py b/tests/test_example/test_some_function.py index 1cc48f0..b129265 100644 --- a/tests/test_example/test_some_function.py +++ b/tests/test_example/test_some_function.py @@ -1,5 +1,4 @@ import pytest - from barra2_dl.example import some_function diff --git a/tests/test_helpers_wind.py b/tests/test_helpers_wind.py new file mode 100644 index 0000000..3200211 --- /dev/null +++ b/tests/test_helpers_wind.py @@ -0,0 +1,16 @@ +import pytest + +from barra2_dl import helpers_wind + + +@pytest.mark.parametrize(('u', 'v', 'expected'), [ + (1, 1, 1.4142135623730951), + (-1, -1, 1.4142135623730951), + (1., 1., 1.4142135623730951), + (-1., -1., 1.4142135623730951), + (0, 0, 0), + (0., 0., 0.), +]) +def test_calculate_wind_speed(u: float, v: float, expected: float) -> None: + """Example test with parametrization.""" + assert helpers_wind.calculate_wind_speed(u, v) == expected