for i in range(emiss77.count): polygon = Polygon(emiss77.grid_geometry.cell_data[i], True) patches.append(polygon) p = PatchCollection(patches) p.set_array(emiss77.emissivities * np.pi * 4 / 1E6) fig, ax = plt.subplots() ax.add_collection(p) plt.xlim(1, 2.5) plt.ylim(-1.5, 1.5) title = emiss77.case_id + " - Emissivity" plt.title(title) plt.axis('equal') fig.colorbar(p, ax=ax) colour_limits = p.get_clim() print('Phantom total power - {:.4G}MW'.format(emiss77.total_radiated_power() / 1E6)) def plot_inversion(inversion, colour_scale): patches = [] for i in range(inversion.count): polygon = Polygon(inversion.grid_geometry.cell_data[i], True) patches.append(polygon) p = PatchCollection(patches) p.set_array(inversion.emissivities * np.pi * 4 / 1E6) fig, ax = plt.subplots()
def make_shot_chart(df, kind='normal', show_misses=True, title=None, title_size=22, context=None, context_size=14, show_pct=True, make_marker='o', miss_marker='x', make_marker_size=90, miss_marker_size=86, make_marker_color='#007A33', miss_marker_color='#C80A18', make_width=1, miss_width=3, hex_grid=50, scale_factor=5, min_factor=0, scale='P_PPS'): ''' Returns a matplotlib fig of the player's shot chart given certain parameters. Will create the shot chart given a df created from the get_shot_chart method Parameters: kind (string, default: 'normal') 'normal' or 'hex' Kind of shot chart 'normal' - shows makes as dots Best for single game 'hex' - shows frequency of shots in area as size of hex and color of zone compared to league average in zone Best for multiple games or players show_misses (boolean, default: True) title (string, default: None) The title on the top of the figure title_size (integer, default: 22) The title on the top of the figure context (string, default: None) Text on the bottom of the plot. Used to add context about a plot. context_size (integer, default: 14) context fontsize show_pct (boolean, default: True) Adds text in bottom right detailing 3pt% and 2pt% 'normal' parameters: make_marker (string, default: 'o') Marker for the made shots miss_marker (string, default: 'x') Marker for missed shots make_marker_size (integer, default: 18) Marker size for made shots miss_marker_size (integer, default: 20) Marker size for missed shots make_marker_color (string, default: '#007A33' - green) Marker color for made shots miss_marker_color (string, default: '#C80A18' - red) Marker color for missed shots make_width (integer, default: 1) Width of marker for made shots miss_width (integer, default: 3) Width of marker for missed shots 'hex' parameters: hex_grid (integer, default: 50) Number of hexes in the axis of each grid Larger number = smaller hexes scale_factor (integer, default: 5) Number of points in a hex to register as max size Usually between 4-6 works but it's a preference thing. min_factor (integer, default: 0) Number of points in a hex to register as min size Usually low, like 0-2 scale (string, default: P_PPS) Must be one of 'PCT_DIFF', 'P_PPS', 'D_PPS' The value that the zones will be colored by Returns: fig fig of shot data ''' # * add parameter to toggle scale factor # ? see if I can dynamically pull team logos to add to charts, maybe store them in a folder in this workspace fig, ax = make_shot_fig(title, title_size, context, context_size) df_t = df.copy() if scale == 'P_PPS': # - error if highest val is 1 df_t['P_PPS'] = df_t['P_PPS'] / 3 if show_pct: att_2 = len(df[(df['PTS'] == 2)]) att_3 = len(df[(df['PTS'] == 3)]) if att_2 != 0: made_2 = len(df[(df['PTS'] == 2) & (df['SHOT_MADE'] == 1)]) if made_2 != 0: _2pt = round(round(made_2 / att_2, 4) * 100, 2) else: _2pt = 0 _2_str = '2pt%: {0}/{1} for {2}%'.format(made_2, att_2, _2pt) if att_3 != 0: made_3 = len(df[(df['PTS'] == 3) & (df['SHOT_MADE'] == 1)]) if made_3 != 0: _3pt = round(round(made_3 / att_3, 4) * 100, 2) else: _3pt = 0 _3_str = '3pt%: {0}/{1} for {2}%'.format(made_3, att_3, _3pt) if kind == 'hex': txt_x = 245 txt_b = 382.5 txt_t = 370 f_size = 12 else: txt_x = 245 txt_b = 417.5 txt_t = 400 f_size = 15 if (att_2 == 0) and (att_3 == 0): pass elif (att_2 != 0) and (att_3 == 0): # - just 2pt% plt.text(txt_x, txt_b, _2_str, horizontalalignment='right', verticalalignment='bottom', fontsize=f_size) elif (att_3 != 0) and (att_2 == 0): # - just 3pt% plt.text(txt_x, txt_b, _3_str, horizontalalignment='right', verticalalignment='bottom', fontsize=f_size) else: # - both 2 and 3pt% plt.text(txt_x, txt_t, _2_str, horizontalalignment='right', verticalalignment='bottom', fontsize=f_size) plt.text(txt_x, txt_b, _3_str, horizontalalignment='right', verticalalignment='bottom', fontsize=f_size) if kind == 'normal': df_1 = df_t[df_t['SHOT_MADE'] == 1].copy() plt.scatter(df_1['X'], df_1['Y'], s=make_marker_size, marker=make_marker, c=make_marker_color, linewidth=make_width) if show_misses: df_2 = df[df['SHOT_MADE'] == 0].copy() # - linewidths increase plt.scatter(df_2['X'], df_2['Y'], s=miss_marker_size, marker=miss_marker, c=miss_marker_color, linewidth=miss_width) else: plt.text(196, 414, 'The larger hexagons\nrepresent a higher\ndensity of shots', horizontalalignment='center', bbox=dict(facecolor='#d9d9d9', boxstyle='round')) if not show_misses: df_t = df_t[df_t['SHOT_MADE'] == 1].copy() hexbin = ax.hexbin(df_t['X'], df_t['Y'], C=df_t[scale].values, gridsize=hex_grid, edgecolors='black', cmap=cm.get_cmap('RdYlBu_r'), extent=[-275, 275, -50, 425], reduce_C_function=np.sum) # - color hexbin2 = ax.hexbin(df_t['X'], df_t['Y'], C=df_t[scale].values, gridsize=hex_grid, edgecolors='black', cmap=cm.get_cmap('RdYlBu_r'), extent=[-275, 275, -50, 425], reduce_C_function=np.mean) axins1 = inset_axes(ax, width="16%", height="2%", loc='lower left') cbar = fig.colorbar(hexbin, cax=axins1, orientation="horizontal", ticks=[-1, 1]) interval = hexbin.get_clim()[1] - hexbin.get_clim()[0] ltick = hexbin.get_clim()[0] + (interval * .2) rtick = hexbin.get_clim()[1] - (interval * .2) cbar.set_ticks([ltick, rtick]) axins1.xaxis.set_ticks_position('top') if scale == 'PCT_DIFF': legend_text = '% Compared to \nLeague Average' tick_labels = ['Below', 'Above'] elif scale == 'P_PPS': legend_text = 'Efficiency by Zone' tick_labels = ['Lower', 'Higher'] else: legend_text = 'Efficiency compared to \nLeague Average' tick_labels = ['Lower', 'Higher'] cbar.ax.set_title(legend_text, fontsize=10) cbar.set_ticklabels(tick_labels) offsets = hexbin.get_offsets() orgpath = hexbin.get_paths()[0] verts = orgpath.vertices values1 = hexbin.get_array() # - scale factor - usually 4 or 5 works values1 = np.array([ scale_factor if i > scale_factor else 0 if i < min_factor else i for i in values1 ]) values1 = ((values1 - 1.0) / (scale_factor - 1.0)) * (1.0 - .4) + .4 values2 = hexbin2.get_array() patches = [] for offset, val in zip(offsets, values1): v1 = verts * val + offset path = Path(v1, orgpath.codes) patch = PathPatch(path) patches.append(patch) pc = PatchCollection(patches, cmap=cm.get_cmap('RdYlBu_r'), edgecolors='black') if scale == 'PCT_DIFF': if pc.get_clim()[0] is None: bottom = abs(df_t[scale].min()) top = abs(df_t[scale].max()) else: top = abs(pc.get_clim()[1]) bottom = abs(pc.get_clim()[0]) m = min(top, bottom) # - Need one extreme of the comparison to be at least 1.5 percent off from average if m < .025: m = .025 pc.set_clim([-1 * m, m]) # - pps is .4 to 1.something elif scale in ['P_PPS', 'L_PPS']: # - for 2: 20% to 60% # - for 3: 13% to 40% pc.set_clim([0.13333, .4]) else: pc.set_clim([-.05, .05]) pc.set_array(values2) ax.add_collection(pc) hexbin.remove() hexbin2.remove() return fig
def update_plot(self): # print("plot") start = timeit.default_timer() # calculate lod auto switch if self.lod_auto_switch: if self.scale < self.lod_auto_switch_threshold[ self.lod_auto_switch_order.index(self.lod) - 1]: self.lod = self.lod_auto_switch_order[ self.lod_auto_switch_order.index(self.lod) - 1] elif self.scale > self.lod_auto_switch_threshold[ self.lod_auto_switch_order.index(self.lod)]: self.lod = self.lod_auto_switch_order[ self.lod_auto_switch_order.index(self.lod) + 1] print(self.lod) print(self.scale) width_deg = self.fig.get_figwidth() * self.scale height_deg = self.fig.get_figheight() * self.scale bbox = ((self.center[0] - width_deg / 2, self.center[0] + width_deg / 2), (self.center[1] - height_deg / 2, self.center[1] + height_deg / 2)) # print(bbox) if self.lod == self.DetailLevel.ZIPCODE: zipcodes_visible = self.repo.get_zipcodes_within_bbox( bbox[0], bbox[1]) geo_data_visible = self.zipcode_data[self.zipcode_data.id.isin( zipcodes_visible)].copy() geo_data_visible["value"] = geo_data_visible["id"].apply( lambda zipcode: self.repo.get_house_value_by_date_and_zipcode( zipcode, self.cur_date)) elif self.lod == self.DetailLevel.COUNTY: counties_visible = self.repo.get_counties_within_bbox( bbox[0], bbox[1]) geo_data_visible = self.county_data[self.county_data.id.isin( counties_visible)].copy() geo_data_visible["value"] = geo_data_visible["id"].apply( lambda county: self.repo.get_house_value_by_date_and_county( county, self.cur_date)) else: states_visible = self.repo.get_states_within_bbox(bbox[0], bbox[1]) geo_data_visible = self.state_data[self.state_data.id.isin( states_visible)].copy() geo_data_visible["value"] = geo_data_visible["id"].apply( lambda state: self.repo.get_house_value_by_date_and_state( state, self.cur_date)) if self.color_bar_auto_range: self.graph_min = geo_data_visible["value"].quantile(0.25) self.graph_max = geo_data_visible["value"].quantile(0.75) t1 = timeit.default_timer() self.axes.clear() self.fig.clear() self.axes = self.fig.gca() t2 = timeit.default_timer() self.axes.clear() self.axes.set(xlim=bbox[0], ylim=bbox[1]) patches = [] values = [] for index, row in geo_data_visible.iterrows(): poly = row["geometry"] value = row["value"] if value is None: value = np.nan if isinstance(poly, shapely.geometry.MultiPolygon): for sub_poly in poly: a = np.asarray(sub_poly.exterior) patches.append(Polygon(a)) values.append(value) else: a = np.asarray(poly.exterior) patches.append(Polygon(a)) values.append(value) patches = PatchCollection(patches, edgecolors="white") values = np.asarray(values) if values is not None: patches.set_array(values) if self.color_bar_auto_range: patches.set_clim(vmin=self.graph_min, vmax=self.graph_max) else: patches.set_clim(vmin=self.color_axis[0], vmax=self.color_axis[1]) self.color_axis = patches.get_clim() self.clim_changed.emit(self.color_axis) patches.set_cmap(self.cmap) self.axes.add_collection(patches, autolim=True) if self.show_zip_codes: graph_mid = (self.graph_max + self.graph_min) / 2 if self.lod == self.DetailLevel.ZIPCODE: for idx, row in geo_data_visible.iterrows(): color = "white" if row["value"] < graph_mid else "black" self.axes.annotate( text=f"{row['id']:0>5}", xy=row["geometry"].centroid.coords[:][0], horizontalalignment='center', color=color, fontsize=10, ) else: for idx, row in geo_data_visible.iterrows(): color = "white" if row["value"] is not None and row[ "value"] < graph_mid else "black" self.axes.annotate( text=row["NAME"], xy=row["geometry"].centroid.coords[:][0], horizontalalignment='center', color=color, fontsize=10, ) t3 = timeit.default_timer() comma_fmt = StrMethodFormatter("${x:,.0f}") self.fig.colorbar(cm.ScalarMappable(norm=patches.norm, cmap=self.cmap), ax=self.axes, format=comma_fmt, pad=0.05) # self.fig.tight_layout() self.fig.canvas.draw() end = timeit.default_timer() print(f"{end - start}, {t1 - start}, {end - t1}") print(f"clear: {t2-t1}, plot: {t3-t2}, render: {end - t3}")
def hex_animate(i): if i != seasons[0]: ax.clear() frame = frames[i] df = frame['data'] df_t = df.copy() scale = frame['params']['scale'] show_misses = frame['params']['show_misses'] hex_grid = frame['params']['hex_grid'] scale_factor = frame['params']['scale_factor'] min_factor = frame['params']['min_factor'] if scale == 'P_PPS': # - error if highest val is 1 df_t['P_PPS'] = df_t['P_PPS']/3 if not show_misses: df_t = df_t[df_t['SHOT_MADE'] == 1].copy() hexbin = ax.hexbin(df_t['X'], df_t['Y'], C=df_t[scale].values , gridsize=hex_grid, edgecolors='black',cmap=cm.get_cmap('RdYlBu_r'), extent=[-275, 275, -50, 425] , reduce_C_function=np.sum) # - color hexbin2 = ax.hexbin(df_t['X'], df_t['Y'], C=df_t[scale].values, gridsize=hex_grid, edgecolors='black', cmap=cm.get_cmap('RdYlBu_r'), extent=[-275, 275, -50, 425], reduce_C_function=np.mean) if chart_params['title'] is not None: ax.set_title(chart_params['title'], pad=10, fontdict={'fontsize': chart_params['title_size'], 'fontweight':'semibold'}) court_elements = draw_court() for element in court_elements: ax.add_patch(element) img = plt.imread("basketball-floor-texture.png") ax.imshow(img,zorder=0, extent=[-275, 275, -50, 425]) ax.set_xlim(-250,250) ax.set_ylim(422.5, -47.5) ax.axis(False) if chart_params['context'] is not None: # - If multiple lines then add context size to second variable for each additional line ax.text(0, 435 + (chart_params['context_size'] * chart_params['context'].count('\n')), s=chart_params['context'], fontsize=chart_params['context_size'], ha='center') # - gets the color for the legend on the bottom left using the first season of data offsets = hexbin.get_offsets() orgpath = hexbin.get_paths()[0] verts = orgpath.vertices values1 = hexbin.get_array() values1 = np.array([scale_factor if i > scale_factor else 0 if i < min_factor else i for i in values1]) values1 = ((values1 - 1.0)/(scale_factor-1.0))*(1.0-.4) + .4 values2 = hexbin2.get_array() patches = [] for offset, val in zip(offsets,values1): v1 = verts*val + offset path = mpatches.Path(v1, orgpath.codes) patch = mpatches.PathPatch(path) patches.append(patch) pc = PatchCollection(patches, cmap=cm.get_cmap('RdYlBu_r'), edgecolors='black') if scale == 'PCT_DIFF': if pc.get_clim()[0] is None: bottom = abs(df_t[scale].min()) top = abs(df_t[scale].max()) else: top = abs(pc.get_clim()[1]) bottom = abs(pc.get_clim()[0]) m = min(top, bottom) # - Need one extreme of the comparison to be at least 1.5 percent off from average if m < .025: m = .025 pc.set_clim([-1 * m, m]) # - pps is .4 to 1.something elif scale in ['P_PPS', 'L_PPS']: # - for 2: 20% to 60% # - for 3: 13% to 40% pc.set_clim([0.13333, .4]) else: pc.set_clim([-.05,.05]) pc.set_array(values2) ax.add_collection(pc) hexbin.remove() hexbin2.remove() ax.text(200, 375, str(frame['season']) + "-" + str(frame['season'] + 1)[2:] + ' season', horizontalalignment='center', fontsize=12, bbox=dict(facecolor=background_color, boxstyle='round')) return hexbin,