def __init__(self, station, data ): self.station = station self.data = data self.ymd = data[:,daydata.YYYYMMDD] self.date_s = utils.f_to_s(self.ymd[ 0]) # First day data self.date_e = utils.f_to_s(self.ymd[-1]) # Last day data self.period = f'{self.date_s}-{self.date_e}' self.tg_gem = stats.average(data,'TG') # Avergae TG self.tx_max_sort = stats.sort( data, 'TX') self.tx_max = self.tx_max_sort[0, daydata.ndx_ent('TX')] # Get max is 1st in list self.tg_max_sort = stats.sort( data, 'TG') self.tg_max = self.tg_max_sort[0, daydata.ndx_ent('TG')] self.tn_max_sort = stats.sort( data, 'TN') self.tn_max = self.tn_max_sort[0, daydata.ndx_ent('TN')] self.sq_tot = stats.sum(data,'SQ') # Total sunshine hours self.sq_sort = stats.sort(data, 'SQ') self.rh_tot = stats.sum(data,'RH') # Total rain self.rh_sort = stats.sort( data, 'RH') self.days_tx_gte_20 = stats.terms_days(data,'TX','≥',20) # Warm days self.days_tx_gte_25 = stats.terms_days(data,'TX','≥',25) # Summer days self.days_tx_gte_30 = stats.terms_days(data,'TX','≥',30) # Tropical days self.days_tx_gte_35 = stats.terms_days(data,'TX','≥',35) # Tropical days self.days_tx_gte_40 = stats.terms_days(data,'TX','≥',40) # Extreme Tropical days self.days_tg_gte_18 = stats.terms_days(data,'TG','≥',18) # Warmte getal dag self.days_tg_gte_20 = stats.terms_days(data,'TG','≥',20) # Warme gemiddelde self.days_tn_gte_20 = stats.terms_days(data,'TN','≥',20) # Tropical nights self.days_sq_gte_10 = stats.terms_days(data,'SQ','≥',10) # Sunny days > 10 hours self.days_rh_gte_10 = stats.terms_days(data,'RH','≥',10) # Rainy days > 10mm self.heat_ndx = stats.heat_ndx(data) self.days_heat_ndx = self.days_tg_gte_18
def table_days(days, entity, time_ent=''): html = '' total = np.size(days, axis=0) if total > 0: # Time th cell yess or no b_time_cell = True if time_ent != '' else False th_time_cell = '<th>time</th>' if b_time_cell else '' time_ndx = daydata.ndx_ent(time_ent) if b_time_cell else -1 val_ndx = daydata.ndx_ent(entity) # Index of value # HTML table header html += f''' <table class="popup"> <thead> <tr> <th>pos</th> <th>date</th> <th>val</th> {th_time_cell} </tr> </thead> <tbody> ''' pos, max = 1, cfg.html_popup_table_val_10 if max == -1: max = total for day in days: ymd = int(day[daydata.YYYYMMDD]) # Get data int format symd = utils.ymd_to_txt( ymd ) # Get date string format val = fix.ent( day[val_ndx], entity ) # Get value with right output # Get a time value or not time_val = f'<td>{fix.ent(day[time_ndx], time_ent)}</td>' if b_time_cell else '' html += f''' <tr> <td>{pos}</td> <td title="{symd}">{ymd}</td> <td>{val}</td> {time_val} </tr> ''' if pos == max: break else: pos += 1 html += ''' </tbody> </table> ''' return html
def table_days_count(days, entity, time_ent=''): html = '' total = np.size(days, axis=0) if total > 0: # Time th cell yess or no b_time_cell = True if time_ent != '' else False th_time_cell = '<th>time</th>' if b_time_cell else '' time_ndx = daydata.ndx_ent( time_ent ) if b_time_cell else -1 val_ndx = daydata.ndx_ent( entity ) # Index of value html += f''' <table class="popup"> <thead> <tr> <th>date</th> <th>val</th> {th_time_cell} <th>cnt</th> </tr> </thead> <tbody> ''' pos, max = 1, cfg.html_popup_table_cnt_rows if max == -1: max = total days = np.flip(days, axis=0) # Reverse the matrix. Last day first for day in days: ymd = int(day[daydata.YYYYMMDD]) symd = utils.ymd_to_txt( ymd ) val = fix.ent( day[val_ndx], entity ) tme = f'<td>{fix.ent(day[time_ndx],time_ent)}</td>' if b_time_cell else '' html += f''' <tr> <td title="{symd}">{ymd}</td> <td>{val}</td> {tme} <td>{total}</td> </tr> ''' if pos == max: break else: total, pos = total - 1, pos + 1 html += ''' </tbody> </table> ''' return html
def sort(data, entity, reverse=False): '''Function sorts data based on entity''' data = process_list(data, entity) # Remove nan values ndx = daydata.ndx_ent(entity) # Get index of entity in matrix data = data[ data[:, ndx].argsort()] # Sort the matrix based on ndx. Low to high if not reverse: data = np.flip(data, axis=0) # Reverse the matrix (if asked) return data
def table_hellmann(days, entity='TG'): html = '' total = np.size(days, axis=0) if total > 0: # HTML table header html += f''' <table class="popup"> <thead> <tr> <th>date</th> <th>tg</th> <th>val</th> <th>sum</th> <th>cnt</th> </tr> </thead> <tbody> ''' # Prepare values l, sum, cnt = list(), 0.0, 1 ndx_ent = daydata.ndx_ent(entity) # Index of value for day in days: ymd = int(day[daydata.YYYYMMDD]) # Get data int format symd = utils.ymd_to_txt( ymd ) # Get date string format raw = day[daydata.TG] hman = abs(raw) sum += hman l.append( f''' <tr> <td title="{symd}">{ymd}</td> <td>{fix.ent(raw, entity)}</td> <td>{fix.rounding(hman, entity)}</td> <td>{fix.rounding(sum, entity)}</td> <td>{cnt}</td> </tr> ''' ) cnt += 1 l.reverse() # Reverse list # Put to html until max cnt, max = 1, cfg.html_popup_table_val_10 if max == -1: max = total for el in l: html += el if cnt == max: break else: cnt += 1 html += ''' </tbody> </table> ''' return html
def __init__(self, station, data, year): self.station = station self.data = data self.ymd = data[:, daydata.YYYYMMDD] self.year = year self.p_start = utils.f_to_s(self.ymd[0]) # First day data self.p_end = utils.f_to_s(self.ymd[-1]) # Last day data self.period = f'{self.p_start}-{self.p_end}' self.tg_max_sort = stats.sort(data, 'TG') self.tg_max = cfg.no_data_given if self.tg_max_sort.size == 0 else self.tg_max_sort[ 0, daydata.ndx_ent('TG')] self.tg_min = cfg.no_data_given if self.tg_max_sort.size == 0 else self.tg_max_sort[ -1, daydata.ndx_ent('TG')] self.tx_max_sort = stats.sort(data, 'TX') self.tx_max = cfg.no_data_given if self.tx_max_sort.size == 0 else self.tx_max_sort[ 0, daydata.ndx_ent('TX')] self.tx_min = cfg.no_data_given if self.tx_max_sort.size == 0 else self.tx_max_sort[ -1, daydata.ndx_ent('TX')] self.tn_max_sort = stats.sort(data, 'TN') self.tn_max = cfg.no_data_given if self.tn_max_sort.size == 0 else self.tn_max_sort[ 0, daydata.ndx_ent('TN')] self.tn_min = cfg.no_data_given if self.tn_max_sort.size == 0 else self.tn_max_sort[ -1, daydata.ndx_ent('TN')] self.tn10_max_sort = stats.sort(data, 'T10N') self.tn10_max = cfg.no_data_given if self.tn10_max_sort.size == 0 else self.tn10_max_sort[ 0, daydata.ndx_ent('T10N')] self.tn10_min = cfg.no_data_given if self.tn10_max_sort.size == 0 else self.tn10_max_sort[ -1, daydata.ndx_ent('T10N')] self.sq_max_sort = stats.sort(data, 'SQ') self.sq_max = cfg.no_data_given if self.sq_max_sort.size == 0 else self.sq_max_sort[ 0, daydata.ndx_ent('SQ')] self.rh_max_sort = stats.sort(data, 'RH') self.rh_max = cfg.no_data_given if self.rh_max_sort.size == 0 else self.rh_max_sort[ 0, daydata.ndx_ent('RH')]
def frost_sum(data): '''Function calculates frost sum: Add min and max temp during a day only if TX < 0 or TN < 0 IE: TX=-1.0 and TN=-5.0 => 6.0 ''' TN = process_list(data, 'TN') # Remove nan values in TN TX = process_list(data, 'TX') # Remove nan values in TX tn_0 = terms_days(TN, 'TN', '<', 0.0) # All days TN < 0 tx_0 = terms_days(TX, 'TX', '<', 0.0) # All days TX < 0 tn_cnt = np.size(tn_0, axis=0) # Count tn days < 0 tx_cnt = np.size(tx_0, axis=0) # Count tx days < 0 frostie = 0.0 if tn_cnt > 0: ndx = daydata.ndx_ent('TN') # Get index for TN in matrix tn = tn_0[:, ndx] # Make TN list only frostie += abs(np.sum(tn)) if tx_cnt > 0: ndx = daydata.ndx_ent('TX') # Get index for TX in matrix tx = tx_0[:, ndx] # Make TX list only frostie += abs(np.sum(tx)) return frostie
def __init__(self, station, data): self.station = station self.data = data self.ymd = data[:, daydata.YYYYMMDD] self.date_s = utils.f_to_s(self.ymd[0]) # First day data self.date_e = utils.f_to_s(self.ymd[-1]) # Last day data self.period = f'{self.date_s}-{self.date_e}' # Average calculations self.tg_gem = stats.average(data, 'TG') # Min extremes self.tx_min_sort = stats.sort(data, 'TX', reverse=True) self.tx_min = self.tx_min_sort[0, daydata.ndx_ent('TX')] self.tg_min_sort = stats.sort(data, 'TG', reverse=True) self.tg_min = self.tg_min_sort[0, daydata.ndx_ent('TG')] self.tn_min_sort = stats.sort(data, 'TN', reverse=True) self.tn_min = self.tn_min_sort[0, daydata.ndx_ent('TN')] self.rh_sort = stats.sort(data, 'RH') self.rh_sum = stats.sum(data, 'RH') self.sq_sort = stats.sort(data, 'SQ') self.sq_sum = stats.sum(data, 'SQ') # Days lists self.days_tx_lt_0 = stats.terms_days(data, 'TX', '<', 0) self.days_tg_lt_0 = stats.terms_days(data, 'TG', '<', 0) self.days_tn_lt_0 = stats.terms_days(data, 'TN', '<', 0) self.days_tn_lt__5 = stats.terms_days(data, 'TN', '<', -5) self.days_tn_lt__10 = stats.terms_days(data, 'TN', '<', -10) self.days_tn_lt__15 = stats.terms_days(data, 'TN', '<', -15) self.days_tn_lt__20 = stats.terms_days(data, 'TN', '<', -20) self.days_hellmann = self.days_tg_lt_0 self.sum_hellmann = stats.hellmann(data) self.frost_sum = stats.frost_sum(data) self.frost_sum_data = self.days_tn_lt_0 # All days with a T < 0 self.ijnsen = stats.ijnsen(data)
def heat_ndx(data): '''Function calculates heat-ndx in given data''' ent = 'TG' # Entity for average temp ndx = daydata.ndx_ent(ent) # Get index for TG in matrix data = process_list(data, ent) # Remove nan values data = terms_days(data, ent, '≥', 18) # All days with TG >= 18 tg = data[:, ndx] # Make TG list only heat = 0.0 tg_cnt = np.size(tg, axis=0) # Count tg days >= 18 if tg_cnt > 0: heat = np.sum(tg - 180) # Sum heat above 18 degress return heat
def terms_days(data, entity, operator, value): '''Function select days based on terms like TX > 30 for example''' ndx = daydata.ndx_ent(entity) # Get index for entity in data matrix op = operator.lower() # Make operator lowercase f = df(value, entity) # Make input value equal to data in matrix if is_operator(op): # Check for allowed operator if op in ['gt', '>']: sel = np.where(data[:, ndx] > f) elif op in ['ge', '>=', '≥']: sel = np.where(data[:, ndx] >= f) elif op in ['eq', '==']: sel = np.where(data[:, ndx] == f) elif op in ['lt', '<']: sel = np.where(data[:, ndx] < f) elif op in ['le', '<=', '≤']: sel = np.where(data[:, ndx] <= f) elif op in ['ne', '!=', '<>']: sel = np.where(data[:, ndx] != f) else: console.log(f'Error in operator {op} in terms for day...') else: # Wrong input console.log(f'Error. Operator {op} is unknown...') return data[sel] # Return all days where the selected terms are true
def process_list_1d(data, entity): '''Function processes data values on false values''' ndx = daydata.ndx_ent(entity) # Get index of entity in matrix d1 = data[:, ndx] d2 = d1[~np.isnan(d1)] # Remove nan return d2
def process_list(data, entity): '''Function processes data values on false values''' ndx = daydata.ndx_ent(entity) # Get index of entity in matrix d1 = data[:, ndx] sel = np.where(d1 != np.isnan(d1)) # Remove false/nan values return data[sel]
def plot( stations, ent_type_graphs, period, title, ylabel, fname, options ): path = utils.mk_path( cfg.dir_period_img, fname + f'.{cfg.plot_image_type}' ) # Size values are inches. And figure always in front plt.figure( figsize=( convert.pixel_to_inch(options['plot_width']), convert.pixel_to_inch(options['plot_height']) ), dpi=options['plot_dpi'] ) # Color handling rnd_col = True if len(stations) > 1 else False if rnd_col: col_list = utils.shuffle_list(vcol.save_colors, level=2) col_ndx, col_cnt = 0, len(col_list) - 1 min, max = 999999.9, -9999999.9 period_extremes, clima_means = list(), list() min_max_ave_sum_txt = False min_max_ave_sum_mean_txt = False for station in stations: # console.log(f'Calculate weatherdata for {station.place}', True) ok, data = daydata.read(station) if ok: days = daydata.period(data, period) ymd = days[:, daydata.ndx_ent('YYYYMMDD')].astype( np.int, copy=False ).astype( np.str, copy=False ).tolist() # Convert to list s_ymd, e_ymd = ymd[0], ymd[-1] sy, sm, sd = s_ymd[0:4], s_ymd[4:6], s_ymd[6:8] ey, em, ed = e_ymd[0:4], e_ymd[4:6], e_ymd[6:8] clima_mean_sub_txt = '' for opts in ent_type_graphs: ent = opts[0].upper() graph_type = opts[1] line_width = opts[2] marker_size = opts[3] marker_text = opts[4] min_max_ave = opts[5] climate_ave = opts[6] climate_ave_marker_txt = opts[7] climate_periode = opts[8] cs_ymd, ce_ymd = climate_periode.split('-') scy, ecy = cs_ymd[0:4], ce_ymd[0:4] console.log(f'Process weatherdata {station.place} for {ent}', True) # Get the values needed for the graph f_val = days[:, daydata.ndx_ent(ent)] # console.log(f'{station.place} {ent} data values: {str(f_val)}') if utils.is_yes(min_max_ave): min_max_ave_sum_txt = True # Calculate extremes min_per = stats.min(days, ent) # slow max_per = stats.max(days, ent) ave_per = stats.average(days, ent) # Correct output s_max = f'max {fix.ent(max_per, ent)}' s_min = f'min {fix.ent(min_per, ent)}' s_ave = f'mean {fix.ent(ave_per, ent)}' s_ext = f'{ent} ({sy}{sm}{sd}-{ey}{em}{ed})' if ent in ['SQ', 'RH', 'EV24', 'Q']: sum_per = fix.ent(stats.sum(days, ent), ent) s_ext += f' {s_max} sum {sum_per} {s_ave}' else: s_ext += f' {s_max} {s_min} {s_ave}' period_extremes.append( s_ext ) console.log(s_ext) # Cumulative sum of values, if chosen if utils.is_yes(options['plot_cummul_val']): f_val = np.cumsum( f_val ) # Min/ max for ranges min_act = fix.rounding( np.min(f_val), ent ) max_act = fix.rounding( np.max(f_val), ent ) if min_act < min: min = min_act if max_act > max: max = max_act # Make correct output values l_val = [ fix.rounding(v, ent) for v in f_val.tolist() ] label = f'{station.place} {vt.ent_to_title(ent)}' color = col_list[col_ndx] if rnd_col else vcol.ent_to_color(ent) if utils.is_yes( climate_ave ): min_max_ave_sum_mean_txt = True label_clima = f'Day climate {station.place} {vt.ent_to_title(ent)}' clima_ymd = days[:, daydata.YYYYMMDD ].tolist() cli_txt = f'Calculate climate value {ent} for {station.place} (might take a while...)' console.log(cli_txt, True) l_clima = [] for d in clima_ymd: ds = utils.f_to_s(d) mmdd = ds[4:8] # What day it is ? sdat = datetime.strptime(ds, '%Y%m%d').strftime('%B %d').lower() val = stats.climate_average_for_day( station, mmdd, ent, climate_periode ) s_clima = fix.rounding(val, ent) console.log(f'Climate value {ent} for {sdat} is {s_clima}') # Append raw data without correct rounding l_clima.append(val) if utils.is_yes(min_max_ave): # Clima average round correctly based on entity if len(l_clima) > 0: # Calculate average sum = 0.0 for el in l_clima: sum += el ave = sum / len(l_clima) s_ave = f'Climate mean ({scy}-{ecy}) {ent} is {fix.ent(ave, ent)}' clima_mean_sub_txt = s_ave console.log( s_ave ) else: console.log('List with clima values is empthy.') # Round correctly al climate values based on ent for ndx, val in enumerate(l_clima): l_clima[ndx] = fix.rounding( val, ent ) console.log(' ') if graph_type == 'line': plt.plot(ymd, l_val, label = label, color = color, marker = cfg.plot_marker_type, linestyle = cfg.plot_line_style, linewidth = line_width, markersize = marker_size, alpha = 0.6 ) if utils.is_yes(climate_ave): plt.plot(ymd, l_clima, label = label_clima, color = color, marker = cfg.plot_clima_marker_type, linestyle = cfg.plot_clima_line_style, linewidth = line_width, markersize = marker_size, alpha = 0.6 ) elif graph_type == 'bar': plt.bar( ymd, l_val, label = label, color = color, alpha = 0.5 ) if utils.is_yes(climate_ave): plt.bar(ymd, l_clima, label=label_clima, color=color) # Marker texts diff = 0.2 if graph_type == 'line' else 0.1 if utils.is_yes(marker_text): # TODO No negative values for when graph is a bar text = l_val for d, v, t in zip( ymd, l_val, text ): plt.text( d, v+diff, t, color=cfg.plot_marker_color, **cfg.plot_marker_font, horizontalalignment=cfg.plot_marker_horizontalalignment, alpha=cfg.plot_marker_alpha ) if utils.is_yes(climate_ave_marker_txt): for d, v, t in zip( ymd, l_clima, l_clima ): plt.text( d, v+diff, t, color=cfg.plot_marker_color, **cfg.plot_marker_font, horizontalalignment=cfg.plot_marker_horizontalalignment, alpha=cfg.plot_marker_alpha ) if rnd_col: col_ndx = 0 if col_ndx == col_cnt else col_ndx + 1 clima_means.append(clima_mean_sub_txt) else: console.log('Read not oke in graphs.py -> plot') max = round(math.ceil(max), 1) min = round(math.floor(min), 1) # diff_val = max - min add_multi = (len(stations) * len(ent_type_graphs)) * 0.08 add_space = 1.0 + add_multi # Get legend some space above max_tick = math.ceil( max * add_space ) # upperrange extra min_tick = min # % underrange extra unused # Update steps diff_tick = max_tick - min_tick step_calc = math.floor( diff_tick / 10 ) step_end = 1 if step_calc < 1 else step_calc # min step = 1 pos_txt = max_tick - 0.3 # Under the edge yticks = np.arange( min_tick, max_tick + step_end, step_end ) # 1-10 % ranges xticks = ymd # print('max: ' + str(max)) # print('min: ' + str(min)) # print('diff_val: ' + str(diff_val)) # print('add_multi: ' + str(add_multi)) # print('!add_space: ' + str(add_space)) # print('max_tick: ' + str(max_tick)) # print('min_tick: ' + str(min_tick)) # print('diff_tick: ' + str(diff_tick)) # print('step_calc: ' + str(step_calc)) # print('step_end: ' + str(step_end)) # print('pos_txt: ' + str(pos_txt)) # print('xticks: ' + str(xticks)) # print('yticks: ' + str(yticks)) # input('') if min_max_ave_sum_txt: # Add min.max/sum t = '' for el in period_extremes: t += el + '\n' # Add clima calculations if min_max_ave_sum_mean_txt: for el in clima_means: t += el + '\n' # Add text to plot plt.text( ymd[0], pos_txt, # Most left and at the top t, **cfg.plot_add_txt_font, color='#555555', horizontalalignment='left', verticalalignment='top' ) plt.yticks( yticks, **cfg.plot_yas_font, color=cfg.plot_yas_color ) plt.xticks( xticks, **cfg.plot_xas_font, color=cfg.plot_xas_color, rotation=cfg.plot_xas_rotation ) plt.title( title, **cfg.plot_title_font, color=cfg.plot_title_color ) plt.xlabel( cfg.plot_xlabel_text, **cfg.plot_xlabel_font, color=cfg.plot_xlabel_color ) plt.ylabel( ylabel, **cfg.plot_ylabel_font, color=cfg.plot_ylabel_color ) plt.legend( loc=cfg.plot_legend_loc, prop=cfg.plot_legend_font, facecolor=cfg.plot_legend_facecolor, shadow=cfg.plot_legend_shadow, frameon=cfg.plot_legend_frameon, fancybox=cfg.plot_legend_fancybox ) if cfg.plot_grid_on: plt.grid( color=cfg.plot_grid_color, linestyle=cfg.plot_grid_linestyle, linewidth=cfg.plot_grid_linewidth ) if utils.is_yes(cfg.plot_tight_layout): plt.tight_layout() plt.savefig( path, dpi=options['plot_dpi'], format=options['plot_image_type'] ) if utils.is_yes(cfg.plot_show): plt.show() return path
def __init__(self, station, data): self.station = station self.data = data self.ymd = data[:, daydata.YYYYMMDD] self.p_start = utils.f_to_s(self.ymd[0]) # First day data self.p_end = utils.f_to_s(self.ymd[-1]) # Last day data self.period = f'{self.p_start}-{self.p_end}' self.tg_ave = stats.average(data, 'TG') self.tx_max_sort = stats.sort(data, 'TX') self.tx_max = self.tx_max_sort[0, daydata.ndx_ent('TX')] self.tg_max_sort = stats.sort(data, 'TG') self.tg_max = self.tg_max_sort[0, daydata.ndx_ent('TG')] self.tn_max_sort = stats.sort(data, 'TN') self.tn_max = self.tn_max_sort[0, daydata.ndx_ent('TN')] self.tx_min_sort = stats.sort(data, 'TX', reverse=True) self.tx_min = self.tx_min_sort[0, daydata.ndx_ent('TX')] self.tg_min_sort = stats.sort(data, 'TG', reverse=True) self.tg_min = self.tg_min_sort[0, daydata.ndx_ent('TG')] self.tn_min_sort = stats.sort(data, 'TN', reverse=True) self.tn_min = self.tn_min_sort[0, daydata.ndx_ent('TN')] self.sq_sum = stats.sum(data, 'SQ') # Total sunshine hours self.sq_sort = stats.sort(data, 'SQ') self.rh_sum = stats.sum(data, 'RH') # Total rain self.rh_sort = stats.sort(data, 'RH') # Days lists self.days_tx_lt_0 = stats.terms_days(data, 'TX', '<', 0) self.days_tg_lt_0 = stats.terms_days(data, 'TG', '<', 0) self.days_tn_lt_0 = stats.terms_days(data, 'TN', '<', 0) self.days_tn_lt__5 = stats.terms_days(data, 'TN', '<', -5) self.days_tn_lt__10 = stats.terms_days(data, 'TN', '<', -10) self.days_tn_lt__15 = stats.terms_days(data, 'TN', '<', -15) self.days_tn_lt__20 = stats.terms_days(data, 'TN', '<', -20) self.days_tx_gte_20 = stats.terms_days(data, 'TX', '≥', 20) # Warm days self.days_tx_gte_25 = stats.terms_days(data, 'TX', '≥', 25) # Summer days self.days_tx_gte_30 = stats.terms_days(data, 'TX', '≥', 30) # Tropical days self.days_tx_gte_35 = stats.terms_days(data, 'TX', '≥', 35) # Tropical days self.days_tx_gte_40 = stats.terms_days(data, 'TX', '≥', 40) # Tropical days self.days_tg_gte_18 = stats.terms_days(data, 'TG', '≥', 18) # Warmte getal dag self.days_tg_gte_20 = stats.terms_days(data, 'TG', '≥', 20) # Warme gemiddelde self.days_tn_gte_20 = stats.terms_days(data, 'TN', '≥', 20) # Tropical nights self.days_sq_gte_10 = stats.terms_days(data, 'SQ', '≥', 10) # Sunny days > 10 hours self.days_rh_gte_10 = stats.terms_days(data, 'RH', '≥', 10) # Rainy days > 10mm self.heat_ndx = stats.heat_ndx(data) self.days_heat_ndx = self.days_tg_gte_18 self.hellmann = stats.hellmann(data) self.days_hellmann = self.days_tg_lt_0 self.ijnsen = stats.ijnsen(data) self.frost_sum = stats.frost_sum(data) self.frost_sum_data = self.days_tn_lt_0 # All days with a T < 0