예제 #1
0
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()
예제 #2
0
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
예제 #3
0
    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}")
예제 #4
0
        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,