Repository: geopandas/geopandas

Issue:
Plot points with markersize variable according to dataframe column
Hi,

Does the `GeoDataFrame.plot()` method support a `markersize` argument taking its value from a `column` in the dataframe. similarly to how the `column` argument works with `cmap` to affect the color?

It seems to me that it doesn't as I got an error when I've set `markersize='population'`, but is there a workaround?


Discussion:
@h4k1m0u `geopandas` does not currently offer such a feature, but that does seem like a good addition, so a pull request to implement that would be welcome. 
@h4k1m0u a workaround that I found was to iterate through the rows in the dataframe, and create a subplot that takes the markersize argument from each individual row. 

e.g. if you have column 'Marker Size' in GeoDataFrame, then on top of your base plot you can add code similar to:

for i in range(0,len(GeoDataFrame)):
        GeoDataFrame.geometry.iloc[i:i+1].plot(ax = ax, markersize = GeoDataFrame['Marker Size'].iloc[i])

Hope that helps?

Resolving patch:
diff --git a/geopandas/plotting.py b/geopandas/plotting.py
--- a/geopandas/plotting.py
+++ b/geopandas/plotting.py
@@ -1,5 +1,4 @@
 from __future__ import print_function
-
 from distutils.version import LooseVersion
 import warnings
 
@@ -168,16 +167,13 @@ def plot_point_collection(ax, geoms, values=None, color=None,
 
     Parameters
     ----------
-
     ax : matplotlib.axes.Axes
         where shapes will be plotted
-
     geoms : sequence of `N` Points
 
     values : a sequence of `N` values, optional
         Values mapped to colors using vmin, vmax, and cmap.
         Cannot be specified together with `color`.
-
     markersize : scalar or array-like, optional
         Size of the markers. Note that under the hood ``scatter`` is
         used, so the specified value will be proportional to the
@@ -212,30 +208,25 @@ def plot_series(s, cmap=None, color=None, ax=None, figsize=None, **style_kwds):
 
     Parameters
     ----------
-
     s : Series
-        The GeoSeries to be plotted.  Currently Polygon,
+        The GeoSeries to be plotted. Currently Polygon,
         MultiPolygon, LineString, MultiLineString and Point
         geometries can be plotted.
-
     cmap : str (default None)
-        The name of a colormap recognized by matplotlib.  Any
+        The name of a colormap recognized by matplotlib. Any
         colormap will work, but categorical colormaps are
-        generally recommended.  Examples of useful discrete
+        generally recommended. Examples of useful discrete
         colormaps include:
 
             tab10, tab20, Accent, Dark2, Paired, Pastel1, Set1, Set2
 
     color : str (default None)
         If specified, all objects will be colored uniformly.
-
     ax : matplotlib.pyplot.Artist (default None)
         axes on which to draw the plot
-
     figsize : pair of floats (default None)
         Size of the resulting matplotlib.figure.Figure. If the argument
         ax is given explicitly, figsize is ignored.
-
     **style_kwds : dict
         Color options to be passed on to the actual plot function, such
         as ``edgecolor``, ``facecolor``, ``linewidth``, ``markersize``,
@@ -243,8 +234,7 @@ def plot_series(s, cmap=None, color=None, ax=None, figsize=None, **style_kwds):
 
     Returns
     -------
-
-    matplotlib axes instance
+    ax : matplotlib axes instance
     """
     if 'colormap' in style_kwds:
         warnings.warn("'colormap' is deprecated, please use 'cmap' instead "
@@ -314,8 +304,8 @@ def plot_series(s, cmap=None, color=None, ax=None, figsize=None, **style_kwds):
 
 def plot_dataframe(df, column=None, cmap=None, color=None, ax=None,
                    categorical=False, legend=False, scheme=None, k=5,
-                   vmin=None, vmax=None, figsize=None, legend_kwds=None,
-                   **style_kwds):
+                   vmin=None, vmax=None, markersize=None, figsize=None,
+                   legend_kwds=None, **style_kwds):
     """
     Plot a GeoDataFrame.
 
@@ -325,55 +315,48 @@ def plot_dataframe(df, column=None, cmap=None, color=None, ax=None,
 
     Parameters
     ----------
-
     df : GeoDataFrame
         The GeoDataFrame to be plotted.  Currently Polygon,
         MultiPolygon, LineString, MultiLineString and Point
         geometries can be plotted.
-
     column : str (default None)
         The name of the column to be plotted. Ignored if `color` is also set.
-
     cmap : str (default None)
         The name of a colormap recognized by matplotlib.
-
+    color : str (default None)
+        If specified, all objects will be colored uniformly.
+    ax : matplotlib.pyplot.Artist (default None)
+        axes on which to draw the plot
     categorical : bool (default False)
         If False, cmap will reflect numerical values of the
         column being plotted.  For non-numerical columns, this
         will be set to True.
-
-    color : str (default None)
-        If specified, all objects will be colored uniformly.
-
     legend : bool (default False)
         Plot a legend. Ignored if no `column` is given, or if `color` is given.
-
-    legend_kwds : dict (default None)
-        Keyword arguments to pass to ax.legend()
-
-    ax : matplotlib.pyplot.Artist (default None)
-        axes on which to draw the plot
-
     scheme : str (default None)
         Name of a choropleth classification scheme (requires PySAL).
         A pysal.esda.mapclassify.Map_Classifier object will be used
         under the hood. Supported schemes: 'Equal_interval', 'Quantiles',
         'Fisher_Jenks'
-
-    k   : int (default 5)
+    k : int (default 5)
         Number of classes (ignored if scheme is None)
-
     vmin : None or float (default None)
         Minimum value of cmap. If None, the minimum data value
         in the column to be plotted is used.
-
     vmax : None or float (default None)
         Maximum value of cmap. If None, the maximum data value
         in the column to be plotted is used.
-
-    figsize
+    markersize : str or float or sequence (default None)
+        Only applies to point geometries within a frame.
+        If a str, will use the values in the column of the frame specified
+        by markersize to set the size of markers. Otherwise can be a value
+        to apply to all points, or a sequence of the same length as the
+        number of points.
+    figsize : tuple of integers (default None)
         Size of the resulting matplotlib.figure.Figure. If the argument
         axes is given explicitly, figsize is ignored.
+    legend_kwds : dict (default None)
+        Keyword arguments to pass to ax.legend()
 
     **style_kwds : dict
         Color options to be passed on to the actual plot function, such
@@ -382,7 +365,7 @@ def plot_dataframe(df, column=None, cmap=None, color=None, ax=None,
 
     Returns
     -------
-    matplotlib axes instance
+    ax : matplotlib axes instance
 
     """
     if 'colormap' in style_kwds:
@@ -410,9 +393,13 @@ def plot_dataframe(df, column=None, cmap=None, color=None, ax=None,
                       "empty. Nothing has been displayed.", UserWarning)
         return ax
 
+    if isinstance(markersize, str):
+        markersize = df[markersize].values
+
     if column is None:
         return plot_series(df.geometry, cmap=cmap, color=color, ax=ax,
-                           figsize=figsize, **style_kwds)
+                           figsize=figsize, markersize=markersize,
+                           **style_kwds)
 
     if df[column].dtype is np.dtype('O'):
         categorical = True
@@ -467,8 +454,11 @@ def plot_dataframe(df, column=None, cmap=None, color=None, ax=None,
     # plot all Points in the same collection
     points = df.geometry[point_idx]
     if not points.empty:
-        plot_point_collection(ax, points, values[point_idx],
-                              vmin=mn, vmax=mx, cmap=cmap, **style_kwds)
+        if isinstance(markersize, np.ndarray):
+            markersize = markersize[point_idx]
+        plot_point_collection(ax, points, values[point_idx], vmin=mn, vmax=mx,
+                              markersize=markersize, cmap=cmap,
+                              **style_kwds)
 
     if legend and not color:
         from matplotlib.lines import Line2D
@@ -503,20 +493,16 @@ def __pysal_choro(values, scheme, k=5):
 
     Parameters
     ----------
-
     values
         Series to be plotted
-
-    scheme
-        pysal.esda.mapclassify classificatin scheme
-        ['Equal_interval'|'Quantiles'|'Fisher_Jenks']
-
-    k
+    scheme : str
+        One of pysal.esda.mapclassify classification schemes
+        Options are 'Equal_interval', 'Quantiles', 'Fisher_Jenks'
+    k : int
         number of classes (2 <= k <=9)
 
     Returns
     -------
-
     binning
         Binning objects that holds the Series with values replaced with
         class identifier and the bins.


Test patch:
diff --git a/geopandas/tests/test_plotting.py b/geopandas/tests/test_plotting.py
--- a/geopandas/tests/test_plotting.py
+++ b/geopandas/tests/test_plotting.py
@@ -105,7 +105,7 @@ def test_single_color(self):
             ax = self.df.plot(column='values', color='green')
             _check_colors(self.N, ax.collections[0].get_facecolors(), ['green']*self.N)
 
-    def test_style_kwargs(self):
+    def test_markersize(self):
 
         ax = self.points.plot(markersize=10)
         assert ax.collections[0].get_sizes() == [10]
@@ -116,6 +116,17 @@ def test_style_kwargs(self):
         ax = self.df.plot(column='values', markersize=10)
         assert ax.collections[0].get_sizes() == [10]
 
+        ax = self.df.plot(markersize='values')
+        assert (ax.collections[0].get_sizes() == self.df['values']).all()
+
+        ax = self.df.plot(column='values', markersize='values')
+        assert (ax.collections[0].get_sizes() == self.df['values']).all()
+
+    def test_style_kwargs(self):
+
+        ax = self.points.plot(edgecolors='k')
+        assert (ax.collections[0].get_edgecolor() == [0, 0, 0, 1]).all()
+
     def test_legend(self):
         with warnings.catch_warnings(record=True) as _:  # don't print warning
             # legend ignored if color is given.