Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove all region-plotting related features, docs, etc. #275

Merged
merged 5 commits into from
Oct 22, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 0 additions & 26 deletions doc/source/_examples/plot_regions.py

This file was deleted.

15 changes: 0 additions & 15 deletions doc/source/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,11 @@ Via Pip

pip install pyam-iamc

By default, this will not install the optional extras (see `Depedencies`_).
To install the optional extras, execute the following command.

.. code-block:: bash

pip install pyam-iamc[geoplots]

As a word of warning, if you want to make geospatial plots this may not be the simplest route.
The reason is that many geospatial plotting libraries, including :code:`cartopy`, may not install properly with pip because pip cannot handle the installation of the complicated c-level libraries required.

From Source
~~~~~~~~~~~

:code:`pyam` can also be installed from source.
As with installation via pip, if you want to make geospatial plots this may not be the simplest route.
You will have to handle the installation of any c-level libraries which are required for geospatial plotting yourself.

.. code-block:: bash

Expand All @@ -54,10 +43,6 @@ The required depedencies for :code:`pyam` are:

.. program-output:: python -c 'import sys; sys.path.append("../.."); import setup; print("\n".join([r for r in setup.REQUIREMENTS]))'

The optional depedencies for :code:`pyam` are:

.. program-output:: python -c 'import sys; sys.path.append("../.."); import setup; print("\n".join([r for r in setup.EXTRA_REQUIREMENTS["geoplots"]]))'

The depedencies for building this documentation are:

.. include:: ../requirements.txt
Expand Down
98 changes: 0 additions & 98 deletions pyam/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1293,104 +1293,6 @@ def scatter(self, x, y, **kwargs):
ax = plotting.scatter(df.dropna(), x, y, **kwargs)
return ax

def map_regions(self, map_col, agg=None, copy_col=None, fname=None,
region_col=None, remove_duplicates=False, inplace=False):
"""Plot regional data for a single model, scenario, variable, and year

see pyam.plotting.region_plot() for all available options

Parameters
----------
map_col: string
The column used to map new regions to. Common examples include
iso and 5_region.
agg: string, optional
Perform a data aggregation. Options include: sum.
copy_col: string, optional
Copy the existing region data into a new column for later use.
fname: string, optional
Use a non-default region mapping file
region_col: string, optional
Use a non-default column name for regions to map from.
remove_duplicates: bool, optional, default: False
If there are duplicates in the mapping from one regional level to
another, then remove these duplicates by counting the most common
mapped value.
This option is most useful when mapping from high resolution
(e.g., model regions) to low resolution (e.g., 5_region).
inplace : bool, default False
if True, do operation inplace and return None
"""
models = self.meta.index.get_level_values('model').unique()
fname = fname or run_control()['region_mapping']['default']
mapping = read_pandas(fname).rename(str.lower, axis='columns')
map_col = map_col.lower()

ret = copy.deepcopy(self) if not inplace else self
_df = ret.data
columns_orderd = _df.columns

# merge data
dfs = []
for model in models:
df = _df[_df['model'] == model]
_col = region_col or '{}.REGION'.format(model)
_map = mapping.rename(columns={_col.lower(): 'region'})
_map = _map[['region', map_col]].dropna().drop_duplicates()
_map = _map[_map['region'].isin(_df['region'])]
if remove_duplicates and _map['region'].duplicated().any():
# find duplicates
where_dup = _map['region'].duplicated(keep=False)
dups = _map[where_dup]
logger().warning("""
Duplicate entries found for the following regions.
Mapping will occur only for the most common instance.
{}""".format(dups['region'].unique()))
# get non duplicates
_map = _map[~where_dup]
# order duplicates by the count frequency
dups = (dups
.groupby(['region', map_col])
.size()
.reset_index(name='count')
.sort_values(by='count', ascending=False)
.drop('count', axis=1))
# take top occurance
dups = dups[~dups['region'].duplicated(keep='first')]
# combine them back
_map = pd.concat([_map, dups])
if copy_col is not None:
df[copy_col] = df['region']

df = (df
.merge(_map, on='region')
.drop('region', axis=1)
.rename(columns={map_col: 'region'})
)
dfs.append(df)
df = pd.concat(dfs)

# perform aggregations
if agg == 'sum':
df = df.groupby(self._LONG_IDX).sum().reset_index()

ret.data = (df
.reindex(columns=columns_orderd)
.sort_values(SORT_IDX)
.reset_index(drop=True)
)
if not inplace:
return ret

def region_plot(self, **kwargs):
"""Plot regional data for a single model, scenario, variable, and year

see pyam.plotting.region_plot() for all available options
"""
df = self.as_pandas(with_metadata=True)
ax = plotting.region_plot(df, **kwargs)
return ax


def _meta_idx(data):
return data[META_IDX].drop_duplicates().set_index(META_IDX).index
Expand Down
147 changes: 0 additions & 147 deletions pyam/plotting.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,13 @@
import itertools
import warnings

try:
import cartopy
cartopy_message = 'all good!'
except ImportError as e:
cartopy = None
cartopy_message = str(e)

import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.cm as cmx
import matplotlib.patches as mpatches
import numpy as np
import pandas as pd

try:
import geopandas as gpd
gpd_message = 'all good!'
except ImportError as e:
gpd = None
gpd_message = str(e)

from collections import defaultdict, Iterable
from contextlib import contextmanager

Expand Down Expand Up @@ -185,139 +171,6 @@ def reshape_bar_plot(df, x, y, bars):
return df


@requires_package(gpd, 'Requires geopandas: ' + gpd_message)
@lru_cache()
def read_shapefile(fname, region_col=None, **kwargs):
"""Read a shapefile for use in regional plots. Shapefiles must have a
column denoted as "region".

Parameters
----------
fname : string
path to shapefile to be read by geopandas
region_col : string, default None
if provided, rename a column in the shapefile to "region"
"""
gdf = gpd.read_file(fname, **kwargs)
if region_col is not None:
gdf = gdf.rename(columns={region_col: 'region'})
if 'region' not in gdf.columns:
raise IOError('Must provide a region column')
gdf['region'] = gdf['region'].str.upper()
return gdf


@requires_package(gpd, 'Requires geopandas: ' + gpd_message)
@requires_package(cartopy, 'Requires cartopy: ' + cartopy_message)
def region_plot(df, column='value', ax=None, crs=None, gdf=None,
add_features=True, vmin=None, vmax=None, cmap=None,
cbar=True, legend=False, title=True):
"""Plot data on a map.

Parameters
----------
df : pd.DataFrame
Data to plot as a long-form data frame
column : string, optional, default: 'value'
The column to use for plotting values
ax : matplotlib.Axes, optional
crs : cartopy.crs, optional
The crs to plot, PlateCarree is used by default.
gdf : geopandas.GeoDataFrame, optional
The geometries to plot. The gdf must have a "region" column.
add_features : bool, optional, default: True
If true, add land, ocean, coastline, and border features.
vmin : numeric, optional
The minimum value to plot.
vmax : numeric, optional
The maximum value to plot.
cmap : string, optional
The colormap to use.
cbar : bool or dictionary, optional, default: True
Add a colorbar. If a dictionary is provided, it will be used as keyword
arguments in creating the colorbar.
legend : bool or dictionary, optional, default: False
Add a legend. If a dictionary is provided, it will be used as keyword
arguments in creating the legend.
title : bool or string, optional
Display a default or custom title.
"""
for col in ['model', 'scenario', 'year', 'variable']:
if len(df[col].unique()) > 1:
msg = 'Can not plot multiple {}s in region_plot'
raise ValueError(msg.format(col))

crs = crs or cartopy.crs.PlateCarree()
if ax is None:
fig, ax = plt.subplots(subplot_kw=dict(projection=crs))
elif not isinstance(ax, cartopy.mpl.geoaxes.GeoAxesSubplot):
msg = 'Must provide a cartopy axes object, not: {}'
raise ValueError(msg.format(type(ax)))

gdf = gdf or read_shapefile(gpd.datasets.get_path('naturalearth_lowres'),
region_col='iso_a3')
data = gdf.merge(df, on='region', how='inner').to_crs(crs.proj4_init)
if data.empty: # help users with iso codes
df['region'] = df['region'].str.upper()
data = gdf.merge(df, on='region', how='inner').to_crs(crs.proj4_init)
if data.empty:
raise ValueError('No data to plot')

if add_features:
ax.add_feature(cartopy.feature.LAND)
ax.add_feature(cartopy.feature.OCEAN)
ax.add_feature(cartopy.feature.COASTLINE)
ax.add_feature(cartopy.feature.BORDERS)

vmin = vmin if vmin is not None else data['value'].min()
vmax = vmax if vmax is not None else data['value'].max()
norm = colors.Normalize(vmin=vmin, vmax=vmax)
cmap = plt.get_cmap(cmap)
scalar_map = cmx.ScalarMappable(norm=norm, cmap=cmap)
labels = []
handles = []
for _, row in data.iterrows():
label = row['label'] if 'label' in row else row['region']
color = scalar_map.to_rgba(row['value'])
ax.add_geometries(
[row['geometry']],
crs,
facecolor=color,
label=label,
)
if label not in labels:
labels.append(label)
handle = mpatches.Rectangle((0, 0), 5, 5, facecolor=color)
handles.append(handle)

if cbar:
scalar_map._A = [] # for some reason you have to clear this
if cbar is True: # use some defaults
cbar = dict(
fraction=0.022, # these are magic numbers
pad=0.02, # that just seem to "work"
)
plt.colorbar(scalar_map, ax=ax, **cbar)

if legend is not False:
if legend is True: # use some defaults
legend = dict(
bbox_to_anchor=(1.32, 0.5) if cbar else (1.2, 0.5),
loc='right',
)
_add_legend(ax, handles, labels, legend)

if title:
var = df['variable'].unique()[0]
unit = df['unit'].unique()[0]
year = df['year'].unique()[0]
default_title = '{} ({}) in {}'.format(var, unit, year)
title = default_title if title is True else title
ax.set_title(title)

return ax


def pie_plot(df, value='value', category='variable',
ax=None, legend=False, title=True, cmap=None,
**kwargs):
Expand Down
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@

EXTRA_REQUIREMENTS = {
'tests': ['coverage', 'coveralls', 'pytest', 'pytest-cov', 'pytest-mpl'],
'geoplots': ['geopandas<0.5.0', 'cartopy', 'gdal', 'fiona'],
'deploy': ['twine', 'setuptools', 'wheel'],
}

Expand Down
Binary file removed tests/expected_figs/test_region.png
Binary file not shown.
Binary file removed tests/expected_figs/test_region_cbar.png
Binary file not shown.
Binary file removed tests/expected_figs/test_region_cbar_args.png
Binary file not shown.
Binary file removed tests/expected_figs/test_region_cmap.png
Binary file not shown.
Binary file removed tests/expected_figs/test_region_crs.png
Binary file not shown.
Binary file removed tests/expected_figs/test_region_map_regions.png
Binary file not shown.
Binary file not shown.
Binary file removed tests/expected_figs/test_region_vmin_vmax.png
Binary file not shown.
Loading