def plot_wind_rose_with_gradient(freq_table, gradient_colors=['#f5faea', '#d6ebad', '#b8dc6f', '#9acd32', '#7ba428', '#5c7b1e'], percent_symbol=True): table = freq_table.copy() import matplotlib as mpl sectors = len(table.columns) table_binned = pd.DataFrame() if isinstance(table.index[0], pd.Interval): table.index = [i.mid for i in table.index] table_trans = table.T table_binned = pd.concat([table_binned, table_trans.loc[:, 0:3].sum(axis=1).rename(3)], axis=1, sort=True) table_binned = pd.concat([table_binned, table_trans.loc[:, 4:6].sum(axis=1).rename(6)], axis=1, sort=True) table_binned = pd.concat([table_binned, table_trans.loc[:, 7:9].sum(axis=1).rename(9)], axis=1, sort=True) table_binned = pd.concat([table_binned, table_trans.loc[:, 10:12].sum(axis=1).rename(12)], axis=1, sort=True) table_binned = pd.concat([table_binned, table_trans.loc[:, 13:15].sum(axis=1).rename(15)], axis=1, sort=True) table_binned = pd.concat([table_binned, table_trans.loc[:, 16:].sum(axis=1).rename(18)], axis=1, sort=True) table_binned = table_binned.T fig = plt.figure(figsize=(12, 12)) ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True) ax.set_theta_zero_location('N') ax.set_theta_direction(-1) ax.set_thetagrids(np.arange(0, 360, 360.0/sectors), zorder=2) if percent_symbol: symbol = '%' else: symbol = ' ' ax.set_rgrids(np.linspace(0.1, max(table.sum(axis=0))+2.0, 10), labels=['%.0f' % round(i)+symbol for i in np.linspace(0.1, max(table.sum(axis=0))+2.0, 10)], angle=0, zorder=2) direction_bins = utils.get_direction_bin_array(sectors)[1:-2] direction_bins = np.insert(direction_bins, 0, direction_bins[-2]) ax.set_ylim(0, max(table.sum(axis=0))+3.0) angular_width = 2*np.pi/sectors - (np.pi/180) # Leaving 1 degree gap ax.bar(0, 1, alpha=0) def _choose_color(speed_bin): colors = gradient_colors bins = [0, 3.5, 6.5, 9.5, 12.5, 15.5, 18.5, 41] return colors[np.digitize([speed_bin], bins)[0]-1] for column in table_binned: radial_pos = 0.0 angular_pos = (np.pi / 180.0) * float(column.split('-')[0]) for speed_bin, frequency in zip(table_binned.index, table_binned[column]): color = _choose_color(speed_bin) patch = mpl.patches.Rectangle((angular_pos, radial_pos), angular_width, frequency, facecolor=color, edgecolor='#5c7b1e', linewidth=0.3, zorder=3) ax.add_patch(patch) radial_pos += frequency legend_patches = [mpl.patches.Patch(color=gradient_colors[0], label='0-3 m/s'), mpl.patches.Patch(color=gradient_colors[1], label='4-6 m/s'), mpl.patches.Patch(color=gradient_colors[2], label='7-9 m/s'), mpl.patches.Patch(color=gradient_colors[3], label='10-12 m/s'), mpl.patches.Patch(color=gradient_colors[4], label='13-15 m/s'), mpl.patches.Patch(color=gradient_colors[5], label='15+ m/s')] ax.legend(handles=legend_patches) return ax.get_figure()
def distribution_by_dir_sector(var_series, direction_series, sectors=12, aggregation_method='%frequency', direction_bin_array=None, direction_bin_labels=None): """ Accepts a series of a variable and wind direction. Computes the distribution of first variable with respect to wind direction sectors :param var_series: Series of the variable whose distribution we need to find :param direction_series: Series of wind directions between [0-360] :param sectors: Number of sectors to bin direction to. The first sector is centered at 0 by default. To change that behaviour specify direction_bin_array :param aggregation_method: Statistical method used to find distribution it can be mean, max, min, std, count, describe, a custom function, etc. Computes frequency in percentages by default :param direction_bin_array: Optional, to change default behaviour of first sector centered at 0 assign an array of bins to this :param direction_bin_labels: Optional, you can specify an array of labels to be used for the bins. uses string labels of the format '30-90' by default :returns: A DataFrame/series with wind direction sector as row indexes and columns with statistics chosen by aggregation_method """ var_series = var_series.dropna() direction_series = direction_series.dropna() if direction_bin_array is None: direction_bin_array = utils.get_direction_bin_array(sectors) zero_centered = True else: sectors = len(direction_bin_array) - 1 zero_centered = False if direction_bin_labels is None: direction_bin_labels = _get_direction_bin_labels( sectors, direction_bin_array, zero_centered) direction_binned_series = _binned_direction_series(direction_series, sectors, direction_bin_array)\ .rename('direction_bin') data = pd.concat([var_series.rename('data'), direction_binned_series], join='inner', axis=1) if aggregation_method == '%frequency': result = data.groupby([ 'direction_bin' ])['data'].count().rename('%frequency') / len(data) * 100.0 else: result = data.groupby(['direction_bin' ])['data'].agg(aggregation_method) for i in range(1, sectors + 1): if not (i in result.index): result[i] = 0.0 result = result.sort_index() result.index = direction_bin_labels return result
def _binned_direction_series(direction_series, sectors, direction_bin_array=None): """ Accepts a series with wind directions and number of sectors you want to divide. :param direction_series: Series of directions to bin :param sectors: number of direction sectors :param direction_bin_array: An optional parameter, if you want custom direction bins pass an array of the bins. If nto specified direction_bins will be centered around 0 :returns: A series with direction-bins, bins centered around 0 degree by default if direction_bin_array is not specified """ if direction_bin_array is None: direction_bin_array = utils.get_direction_bin_array(sectors) return direction_series.dropna().apply(_map_direction_bin, bins=direction_bin_array, sectors=sectors)
def freq_table(var_series, direction_series, var_bin_array=np.arange(-0.5, 41, 1), sectors=12, var_bin_labels=None, direction_bin_array=None, direction_bin_labels=None, freq_as_percentage=True, return_data=False): """ Accepts a variable series and direction series and computes a frequency table of percentages. Both variable and direction are binned :param var_series: Series of variable to be binned :param direction_series: Series of wind directions between [0-360] :param var_bin_array: Array of numbers where adjacent elements of array form a bin :param sectors: Number of sectors to bin direction to. The first sector is centered at 0 by default. To change that behaviour specify direction_bin_array :param var_bin_labels: Optional, an array of labels to use for variable bins :param direction_bin_array: Optional, to change default behaviour of first sector centered at 0 assign an array of bins to this :param direction_bin_labels: Optional, you can specify an array of labels to be used for the bins. uses string labels of the format '30-90' by default :param freq_as_percentage: Optional, True by default. Returns the frequency as percentages. To return just the count, set to False :param return_data: Set to True if you want the data returned. :type return_data: bool :returns: A DataFrame with row indexes as variable bins and columns as wind direction bins. """ var_series = var_series.dropna() direction_series = direction_series.dropna() if direction_bin_array is None: direction_bin_array = utils.get_direction_bin_array(sectors) zero_centered = True else: sectors = len(direction_bin_array) - 1 zero_centered = False if direction_bin_labels is None: direction_bin_labels = _get_direction_bin_labels( sectors, direction_bin_array, zero_centered) var_binned_series = pd.cut(var_series, var_bin_array, right=False, labels=var_bin_labels).rename('variable_bin') direction_binned_series = _binned_direction_series( direction_series, sectors, direction_bin_array).rename('direction_bin') data = pd.concat([ var_series.rename('var_data'), var_binned_series, direction_binned_series ], axis=1).dropna() if freq_as_percentage: result = pd.crosstab(data.loc[:, 'variable_bin'], data.loc[:, 'direction_bin']) / len(data) * 100.0 else: result = pd.crosstab(data.loc[:, 'variable_bin'], data.loc[:, 'direction_bin']) for i in range(1, sectors + 1): if not (i in result.columns): result.insert(i - 1, i, 0.0) result.columns = direction_bin_labels result = result.sort_index() if return_data: return plt.plot_wind_rose_with_gradient( result, percent_symbol=freq_as_percentage), result else: return plt.plot_wind_rose_with_gradient( result, percent_symbol=freq_as_percentage)