diff --git a/devicely/_compat.py b/devicely/_compat.py new file mode 100644 index 0000000..6c28288 --- /dev/null +++ b/devicely/_compat.py @@ -0,0 +1,13 @@ +"""Pandas 1/2 compatibility.""" + +try: + import importlib.metadata as importlib_metadata +except ModuleNotFoundError: + import importlib_metadata + +_pd_version = importlib_metadata.version('pandas') +_pd_major = int(_pd_version.split('.')[0]) +_have_pd_2 = _pd_major >= 2 +# Pandas 1.5 renamed the `line_terminator` parameter of the `to_csv` method to +# `lineterminator` for consistency, and then Pandas 2 removed the old name. +_to_csv_line_terminator = 'lineterminator' if _have_pd_2 else 'line_terminator' diff --git a/devicely/empatica.py b/devicely/empatica.py index f70ba1f..0985077 100644 --- a/devicely/empatica.py +++ b/devicely/empatica.py @@ -10,6 +10,8 @@ import numpy as np import pandas as pd +from ._compat import _to_csv_line_terminator + class EmpaticaReader: """ @@ -155,7 +157,8 @@ def _write_signal(self, path, dataframe, signal_name): [self.sample_freqs[signal_name]] * n_cols]) with open(path, 'w') as file: np.savetxt(file, meta, fmt='%s', delimiter=', ', newline='\n') - dataframe.to_csv(file, index=None, header=None, line_terminator='\n') + dataframe.to_csv(file, index=None, header=None, + **{_to_csv_line_terminator: '\n'}) def _read_ibi(self, path): try: @@ -179,7 +182,8 @@ def _write_ibi(self, path): file.write(f"{self.start_times['IBI'].value // 1e9}, IBI\n") write_df = self.IBI.copy() write_df.index = (write_df.index - self.start_times['IBI']).values.astype(int) / 1e9 - write_df.to_csv(file, header=None, line_terminator='\n') + write_df.to_csv(file, header=None, + **{_to_csv_line_terminator: '\n'}) def _read_tags(self, path): try: @@ -200,7 +204,8 @@ def _read_tags(self, path): def _write_tags(self, path): if self.tags is not None: tags_write_series = self.tags.map(lambda x: x.value / 1e9) - tags_write_series.to_csv(path, header=None, index=None, line_terminator='\n') + tags_write_series.to_csv(path, header=None, index=None, + **{_to_csv_line_terminator: '\n'}) def timeshift(self, shift='random'): """ diff --git a/devicely/everion.py b/devicely/everion.py index 38312e5..17b2874 100644 --- a/devicely/everion.py +++ b/devicely/everion.py @@ -12,6 +12,9 @@ import numpy as np import pandas as pd +from ._compat import _to_csv_line_terminator + + class EverionReader: """ Read, timeshift and write data generated by Biovotion Everion. @@ -240,7 +243,7 @@ def _convert_single_dataframe(self, dataframe, selected_tags=None): if selected_tags is not None: dataframe = dataframe[dataframe['tag'].isin(selected_tags)] - dataframe['time'] = dataframe['time'].map(lambda x: x.value) / 10**9 + dataframe['time'] = dataframe['time'].astype(np.int64) / 10**9 timestamps_min_and_count = dataframe.groupby('time').agg( count_min=pd.NamedAgg(column='count', aggfunc='min'), count_range=pd.NamedAgg( @@ -318,7 +321,8 @@ def _write_single_dataframe(self, dataframe, filepath): writing_dataframe.loc[quality_col.index, 'values'] += ';' + quality_col writing_dataframe.drop(columns=['quality'], inplace=True) - writing_dataframe.to_csv(filepath, index=None, line_terminator='\n') + writing_dataframe.to_csv(filepath, index=None, + **{_to_csv_line_terminator: '\n'}) def timeshift(self, shift='random'): """ diff --git a/devicely/faros.py b/devicely/faros.py index 50b8458..a1670cb 100644 --- a/devicely/faros.py +++ b/devicely/faros.py @@ -11,6 +11,8 @@ import pandas as pd import pyedflib as edf +from ._compat import _to_csv_line_terminator + class FarosReader: """ @@ -220,10 +222,14 @@ def _write_to_directory(self, path): with open(os.path.join(path, 'meta.json'), 'w') as meta_file: json.dump(meta, meta_file) - self.ECG.to_csv(os.path.join(path, 'ECG.csv'), index=None, line_terminator='\n') - self.ACC.to_csv(os.path.join(path, 'ACC.csv'), index=None, line_terminator='\n') - self.Marker.to_csv(os.path.join(path, 'Marker.csv'), index=None, line_terminator='\n') - self.HRV.to_csv(os.path.join(path, 'HRV.csv'), index=None, line_terminator='\n') + self.ECG.to_csv(os.path.join(path, 'ECG.csv'), index=None, + **{_to_csv_line_terminator: '\n'}) + self.ACC.to_csv(os.path.join(path, 'ACC.csv'), index=None, + **{_to_csv_line_terminator: '\n'}) + self.Marker.to_csv(os.path.join(path, 'Marker.csv'), index=None, + **{_to_csv_line_terminator: '\n'}) + self.HRV.to_csv(os.path.join(path, 'HRV.csv'), index=None, + **{_to_csv_line_terminator: '\n'}) def timeshift(self, shift='random'): """ diff --git a/devicely/shimmer_plus.py b/devicely/shimmer_plus.py index 03f85a7..082677f 100644 --- a/devicely/shimmer_plus.py +++ b/devicely/shimmer_plus.py @@ -11,6 +11,8 @@ import numpy as np import pandas as pd +from ._compat import _to_csv_line_terminator + class ShimmerPlusReader: """ @@ -69,7 +71,8 @@ def write(self, path): with open(path, 'w') as f: f.write(f'"sep={self.delimiter}"\n') - write_df.to_csv(f, index=False, sep=self.delimiter, line_terminator=f"{self.delimiter}\n") + write_df.to_csv(f, index=False, sep=self.delimiter, + **{_to_csv_line_terminator: f"{self.delimiter}\n"}) def timeshift(self, shift='random'): """ diff --git a/devicely/spacelabs.py b/devicely/spacelabs.py index c19e6f9..1a96daa 100644 --- a/devicely/spacelabs.py +++ b/devicely/spacelabs.py @@ -10,6 +10,8 @@ import pandas as pd +from ._compat import _to_csv_line_terminator + class SpacelabsReader: """ @@ -147,7 +149,8 @@ def write(self, path): printing_df.replace('-9999', '""', inplace=True) printing_df.replace('-9998', '"EB"', inplace=True) printing_df.replace('-9997', '"AB"', inplace=True) - printing_df.to_csv(file, header=None, index=None, quoting=csv.QUOTE_NONE, line_terminator='\n') + printing_df.to_csv(file, header=None, index=None, quoting=csv.QUOTE_NONE, + **{_to_csv_line_terminator: '\n'}) xml_node = ET.Element('XML') xml_node.extend(self._dict_to_etree(self.metadata)) diff --git a/devicely/time_stamp.py b/devicely/time_stamp.py index aac94ce..9d6d93b 100644 --- a/devicely/time_stamp.py +++ b/devicely/time_stamp.py @@ -8,6 +8,9 @@ import pandas as pd +from ._compat import _to_csv_line_terminator + + class TimeStampReader: """ Read, timeshift and write data generated by the Android app TimeStamp @@ -45,7 +48,8 @@ def write(self, path): df_to_write = self.data.reset_index()[['tag_number', 'time', 'tag']] df_to_write.time = df_to_write.time.dt.strftime("%Y/%-m/%-d(%a)\u3000%H:%M:%S").str.lower() - df_to_write.to_csv(path, header=None, index=None, line_terminator='\n') + df_to_write.to_csv(path, header=None, index=None, + **{_to_csv_line_terminator: '\n'}) def timeshift(self, shift='random'): """