diff --git a/glucopy/classes/Gframe.py b/glucopy/classes/Gframe.py index c737542..e98d989 100644 --- a/glucopy/classes/Gframe.py +++ b/glucopy/classes/Gframe.py @@ -8,7 +8,8 @@ # Local from glucopy.utils import (disjoin_days_and_hours, - mgdl_to_mmoll) + mgdl_to_mmoll, + mmoll_to_mgdl) import glucopy.metrics as metrics class Gframe: @@ -165,7 +166,7 @@ def convert_unit(self, raise ValueError('The data is already in mmol/L') else: if self.unit == 'mmol/L': - self.data['CGM'] = mgdl_to_mmoll(self.data['CGM'], reverse=True) + self.data['CGM'] = mmoll_to_mgdl(self.data['CGM']) self.unit = new_unit else: raise ValueError('The data is already in mg/dL') @@ -501,7 +502,7 @@ def modd(self, # Time in Range def tir(self, per_day: bool = False, - target_range:list= [0,70,180], + interval:list= [0,70,180], percentage: bool = True, decimals: int = 2): ''' @@ -518,7 +519,7 @@ def tir(self, ---------- per_day : bool, default False If True, returns a pandas Series with the TIR for each day. If False, returns the TIR for all days combined. - target_range : list of int|float, default [0,70,180] + interval : list of int|float, default [0,70,180] Interval of glucose concentration to calculate :math:`\\tau`. Can be a list of 1 number, in that case the time will be calculated below and above that number. It will always try to calculate the time below the first number and above the last number. @@ -547,7 +548,7 @@ def tir(self, .. ipython:: python - gf.tir(target_range=[0,70,150,180,230]) + gf.tir(interval=[0,70,150,180,230]) Calculating the TIR for each day and the default range (0,70,180): @@ -565,10 +566,10 @@ def tir(self, # Calculate TIR for each day for day, day_data in day_groups: - tir[str(day)] = metrics.tir(df=day_data, target_range=target_range, percentage=percentage, decimals=decimals).tolist() + tir[str(day)] = metrics.tir(df=day_data, interval=interval, percentage=percentage, decimals=decimals).tolist() else: # Calculate TIR for all data - tir = metrics.tir(df=self.data, target_range=target_range, percentage=percentage, decimals=decimals) + tir = metrics.tir(df=self.data, interval=interval, percentage=percentage, decimals=decimals) return tir @@ -577,7 +578,7 @@ def tir(self, # Frecuency distribution : counts the amount of observations given certain intervals of CGM def fd(self, per_day: bool = False, - target_range: list = [0,70,180], + interval: list = [0,70,180], decimals: int = 2, count: bool = False): ''' @@ -594,7 +595,7 @@ def fd(self, ---------- per_day : bool, default False If True, returns a pandas Series with the FD for each day. If False, returns the FD for all days combined. - target_range : list of int|float, default [0,70,180] + interval : list of int|float, default [0,70,180] Interval of glucose concentration to calculate `FD`. Can be a list of 1 number, in that case the time will be calculated below and above that number. It will always try to calculate the time below the first number and above the last number. @@ -622,7 +623,7 @@ def fd(self, .. ipython:: python - gf.fd(target_range=[0,70,150,180,230]) + gf.fd(interval=[0,70,150,180,230]) Calculating the FD for each day and the default range (0,70,180): @@ -638,10 +639,10 @@ def fd(self, fd.index.name = 'Day' for day, day_data in day_groups: - fd[str(day)] = metrics.fd(df=day_data, target_range=target_range, decimals=decimals, count=count).values + fd[str(day)] = metrics.fd(df=day_data, interval=interval, decimals=decimals, count=count).values else: - fd = metrics.fd(df=self.data, target_range=target_range, decimals=decimals, count=count) + fd = metrics.fd(df=self.data, interval=interval, decimals=decimals, count=count) return fd @@ -1060,11 +1061,11 @@ def qscore(self, ''' # Time in range [70.2,160.2] mg/dL = [3.9,8.9] mmol/L if self.unit == 'mmol/L': - target_range = [3.9,8.9] + interval = [3.9,8.9] elif self.unit == 'mg/dL': - target_range = [70.2,160.2] + interval = [70.2,160.2] - tir_per_day = self.tir(per_day=True, target_range=target_range, percentage=False) + tir_per_day = self.tir(per_day=True, interval=interval, percentage=False) # List with the Timedelta corresponding to the time spent under 3.9 mmol/L for each day tir_per_day_minus_3_9 = [time[0].total_seconds() for time in tir_per_day] @@ -1604,8 +1605,13 @@ def summary(self, gf = gp.data('prueba_1') gf.summary() ''' + # Time in range [70,180] mg/dL + tir_interval = np.array([0, 70, 180]) + if self.unit == 'mmol/L': + tir_interval = mgdl_to_mmoll(tir_interval) + # Metrics that return Series - tir = self.tir() + tir = self.tir(interval=tir_interval) grade = self.grade() # Summary diff --git a/glucopy/metrics/fd.py b/glucopy/metrics/fd.py index 0170344..4523015 100644 --- a/glucopy/metrics/fd.py +++ b/glucopy/metrics/fd.py @@ -3,7 +3,7 @@ import numpy as np def fd(df: pd.DataFrame, - target_range: list = [0,70,180], + interval: list = [0,70,180], decimals: int = 2, count: bool = False ): @@ -22,7 +22,7 @@ def fd(df: pd.DataFrame, df : pandas.DataFrame DataFrame containing the CGM values. The dataframe must contain 'CGM' column present in :attr:`glucopy.Gframe.data`. - target_range : list of int|float, default [0,70,180] + interval : list of int|float, default [0,70,180] Target range in CGM unit. It must have at least 2 values, for the "normal" range, low and high values will be values outside that range. decimals : int, default 2 @@ -39,22 +39,26 @@ def fd(df: pd.DataFrame, ----- This function is meant to be used by :meth:`glucopy.Gframe.fd` ''' - # Check input, Ensure target_range is a list with 0 and the max value of the data - if not isinstance(target_range, list) or not all(isinstance(i, (int, float)) for i in target_range): - raise ValueError("target_range must be a list of numbers") + # Check input, Ensure interval is a list or numpy array of numbers + if not isinstance(interval, (list, np.ndarray)): + raise ValueError("interval must be a list or numpy array of numbers") + + # Convert interval to a list if it's a numpy array + if isinstance(interval, np.ndarray): + interval = interval.tolist() # Add 0 to the target range if it is not present to count the time below the target range - if 0 not in target_range: - target_range = [0] + target_range + if 0 not in interval: + interval = [0] + interval # Add the max value of the data to the target range if it is not present to count the time above the target range max_value = max(df['CGM']) - if max_value <= target_range[-1]: - max_value = target_range[-1] + 1 - if max_value > target_range[-1]: - target_range = target_range + [max_value] + if max_value <= interval[-1]: + max_value = interval[-1] + 1 + if max_value > interval[-1]: + interval = interval + [max_value] - result = pd.cut(df['CGM'], bins=target_range).groupby(pd.cut(df['CGM'], bins=target_range), observed=False).count() + result = pd.cut(df['CGM'], bins=interval).groupby(pd.cut(df['CGM'], bins=interval), observed=False).count() if not count: result = result / result.sum() diff --git a/glucopy/metrics/tir.py b/glucopy/metrics/tir.py index 7c0ae17..c309dab 100644 --- a/glucopy/metrics/tir.py +++ b/glucopy/metrics/tir.py @@ -3,7 +3,7 @@ import numpy as np def tir(df: pd.DataFrame, - target_range:list= [0,70,180], + interval:list= [0,70,180], percentage: bool = True, decimals: int = 2 ): @@ -21,7 +21,7 @@ def tir(df: pd.DataFrame, ---------- per_day : bool, default False If True, returns a pandas Series with the TIR for each day. If False, returns the TIR for all days combined. - target_range : list of int|float, default [0,70,180] + interval : list of int|float, default [0,70,180] Interval of glucose concentration to calculate :math:`\\tau`. Can be a list of 1 number, in that case the time will be calculated below and above that number. It will always try to calculate the time below the first number and above the last number. @@ -39,26 +39,30 @@ def tir(df: pd.DataFrame, ----- This function is meant to be used by :meth:`glucopy.Gframe.tir` ''' - # Check input, Ensure target_range is a list with 0 and the max value of the data - if not isinstance(target_range, list) or not all(isinstance(i, (int, float)) for i in target_range): - raise ValueError("target_range must be a list of numbers") + # Check input, Ensure interval is a list or numpy array of numbers + if not isinstance(interval, (list, np.ndarray)): + raise ValueError("interval must be a list or numpy array of numbers") + + # Convert interval to a list if it's a numpy array + if isinstance(interval, np.ndarray): + interval = interval.tolist() # Add 0 to the target range if it is not present to count the time below the target range - if 0 not in target_range: - target_range = [0] + target_range + if 0 not in interval: + interval = [0] + interval # Add the max value of the data to the target range if it is not present to count the time above the target range max_value = max(df['CGM']) - if max_value <= target_range[-1]: - max_value = target_range[-1] + 1 - if max_value > target_range[-1]: - target_range = target_range + [max_value] + if max_value <= interval[-1]: + max_value = interval[-1] + 1 + if max_value > interval[-1]: + interval = interval + [max_value] data_copy = df.copy() # Calculate time difference between consecutive timestamps data_copy['Time_Diff'] = data_copy['Timestamp'].diff().dt.total_seconds() # Create a column with the range that each CGM value belongs to - data_copy['ranges'] = pd.cut(data_copy['CGM'], bins=target_range) + data_copy['ranges'] = pd.cut(data_copy['CGM'], bins=interval) # Group data by range and sum the time difference time_count = data_copy.groupby('ranges', observed=False)['Time_Diff'].sum() diff --git a/glucopy/plot/hist/freq.py b/glucopy/plot/hist/freq.py index ba19a1f..9264bb0 100644 --- a/glucopy/plot/hist/freq.py +++ b/glucopy/plot/hist/freq.py @@ -1,12 +1,13 @@ # 3rd party import plotly.graph_objects as go +import numpy as np # Local from ...classes import Gframe def freq(gf: Gframe, per_day: bool = True, - target_range: list = [0,70,180], + interval: list = [0,70,180], count: bool = False, height: float = None, width: float = None): @@ -19,7 +20,7 @@ def freq(gf: Gframe, Gframe object to plot per_day : bool, default True If True, the plot will be separated by days - target_range : list, default [0,70,180] + interval : list, default [0,70,180] Target range for the glucose count : bool, default False If True, the y axis will be the count of the glucose in the target range. If False, the y axis will be the @@ -56,7 +57,7 @@ def freq(gf: Gframe, .. ipython:: python - fig = gp.plot.freq(gf, per_day=False, target_range=[0,100,200,300]) + fig = gp.plot.freq(gf, per_day=False, interval=[0,100,200,300]) .. image:: /../img/freq_plot_2.png :alt: Frequency plot @@ -66,17 +67,28 @@ def freq(gf: Gframe, if not isinstance(gf, Gframe): raise TypeError('gf must be a Gframe object') - # Check input, Ensure target_range is a list with 0 and the max value of the data - if not isinstance(target_range, list) or not all(isinstance(i, (int, float)) for i in target_range): - raise ValueError("target_range must be a list of numbers") - if 0 not in target_range: - target_range = [0] + target_range - if max(gf.data['CGM']) > target_range[-1]: - target_range = target_range + [max(gf.data['CGM'])] + # Check input, Ensure interval is a list or numpy array of numbers + if not isinstance(interval, (list, np.ndarray)): + raise ValueError("interval must be a list or numpy array of numbers") + + # Convert interval to a list if it's a numpy array + if isinstance(interval, np.ndarray): + interval = interval.tolist() + + # Add 0 to the target range if it is not present to count the time below the target range + if 0 not in interval: + interval = [0] + interval + + # Add the max value of the data to the target range if it is not present to count the time above the target range + max_value = max(gf.data['CGM']) + if max_value <= interval[-1]: + max_value = interval[-1] + 1 + if max_value > interval[-1]: + interval = interval + [max_value] # Get frequencies - frequencies = gf.fd(per_day = per_day, target_range = target_range, count = count) - range_labels = [f'{target_range[i]}-{target_range[i+1]}' for i in range(len(target_range)-1)] + frequencies = gf.fd(per_day = per_day, interval = interval, count = count) + range_labels = [f'{interval[i]}-{interval[i+1]}' for i in range(len(interval)-1)] fig = go.Figure() first_day = ''