<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:

def plot_shapes_heat_map(df_shapes, shape_i_columns, values, axis=None,
                         vmin=None, vmax=None, value_formatter=None,
                         color_map=None):
    '''
    Plot polygon shapes, colored based on values mapped onto
    a colormap.

    Args
    ----

        df_shapes (pandas.DataFrame) : Polygon table containing
            the columns `'id', 'x', 'y'`.  Coordinates must be
            ordered to be grouped by `'id'`.
        values (pandas.Series) : Numeric values indexed by `'id'`.
            These values are used to look up color in the color map.
        axis : A matplotlib axis.  If `None`, an axis is created.
        vmin : Minimum value to clip values at.
        vmax : Maximum value to clip values at.
        color_map : A matplotlib color map (see `matplotlib.cm`).

    Returns
    -------

        (tuple) : First element is heat map axis, second element
            is colorbar axis.
    '''
    df_shapes = df_shapes.copy()

    # Matplotlib draws from bottom to top, so invert `y` coordinates.
    df_shapes.loc[:, 'y'] = df_shapes.y.max() - df_shapes.y.copy().values

    aspect_ratio = ((df_shapes.x.max() - df_shapes.x.min()) /
                    (df_shapes.y.max() - df_shapes.y.min()))

    if vmin is not None or vmax is not None:
        norm = mpl.colors.Normalize(vmin=vmin or min(values),
                                    vmax=vmax or max(values))
    else:
        norm = None

    if axis is None:
        fig, axis = plt.subplots(figsize=(10, 10 * aspect_ratio))
    else:
        fig = axis.get_figure()

    patches = OrderedDict([(id, Polygon(df_shape_i[['x', 'y']].values))
                           for id, df_shape_i in
                           df_shapes.groupby(shape_i_columns)])
    patches = pd.Series(patches)

    collection = PatchCollection(patches.values, cmap=color_map,
                                 norm=norm)
    collection.set_array(values.ix[patches.index])

    axis.add_collection(collection)

    axis_divider = make_axes_locatable(axis)

    # Append color axis to the right of `axis`, with 10% width of `axis`.
    color_axis = axis_divider.append_axes("right", size="10%", pad=0.05)

    colorbar = fig.colorbar(collection, format=value_formatter,
                            cax=color_axis)

    tick_labels = colorbar.ax.get_yticklabels()
    if vmin is not None:
        tick_labels[0] = '$\leq$%s' % tick_labels[0].get_text()
    if vmax is not None:
        tick_labels[-1] = '$\geq$%s' % tick_labels[-1].get_text()
    colorbar.ax.set_yticklabels(tick_labels)

    axis.set_xlim(df_shapes.x.min(), df_shapes.x.max())
    axis.set_ylim(df_shapes.y.min(), df_shapes.y.max())
    return axis, colorbar