From 29d084753f8324163ff741763a33f162f8c30d37 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 13 May 2015 19:00:18 -0700 Subject: [PATCH 0001/1738] Fix bug in boxplot where some level had only missing data --- seaborn/categorical.py | 18 ++++++++++++++---- seaborn/tests/test_categorical.py | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 580412c6ea..863f4cfd69 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -435,13 +435,18 @@ def draw_boxplot(self, ax, kws): if self.plot_hues is None: - # Handle case where there is no data to plot - if not group_data.size: + # Handle case where there is data at this level + if group_data.size == 0: continue # Draw a single box or a set of boxes # with a single level of grouping box_data = remove_na(group_data) + + # Handle case where there is no non-null data + if box_data.size == 0: + continue + artist_dict = ax.boxplot(box_data, vert=vert, patch_artist=True, @@ -460,11 +465,16 @@ def draw_boxplot(self, ax, kws): if not i: self.add_legend_data(ax, self.colors[j], hue_level) - # Handle case where there is no data to plot - if not group_data.size or not hue_mask.any(): + # Handle case where there is data at this level + if group_data.size == 0: continue box_data = remove_na(group_data[hue_mask]) + + # Handle case where there is no non-null data + if box_data.size == 0: + continue + center = i + offsets[j] artist_dict = ax.boxplot(box_data, vert=vert, diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index f4e736ef78..66172dde6e 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -727,6 +727,24 @@ def test_draw_missing_boxes(self): nt.assert_equal(len(ax.artists), 3) plt.close("all") + def test_missing_data(self): + + x = ["a", "a", "b", "b", "c", "c", "d", "d"] + h = ["x", "y", "x", "y", "x", "y", "x", "y"] + y = self.rs.randn(8) + y[-2:] = np.nan + + ax = cat.boxplot(x, y) + nt.assert_equal(len(ax.artists), 3) + + plt.close("all") + + y[-1] = 0 + ax = cat.boxplot(x, y, h) + nt.assert_equal(len(ax.artists), 7) + + plt.close("all") + def test_boxplots(self): # Smoke test the high level boxplot options From 19bb727db708c3f285b773ef2c787a21baf5d69f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 13 May 2015 20:25:15 -0700 Subject: [PATCH 0002/1738] Fix despine(trim=True) when axes are inverted --- seaborn/tests/test_utils.py | 14 ++++++++++++++ seaborn/utils.py | 8 ++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index 528c4e9b7c..094a5354c5 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -195,6 +195,20 @@ def test_despine_trim_spines(self): plt.close("all") + def test_despine_trim_inverted(self): + + f, ax = plt.subplots() + ax.plot([1, 2, 3], [1, 2, 3]) + ax.set_ylim(.85, 3.15) + ax.invert_yaxis() + + utils.despine(trim=True) + for side in self.inner_sides: + bounds = ax.spines[side].get_bounds() + nt.assert_equal(bounds, (1, 3)) + + plt.close("all") + def test_offset_spines_warns(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always", category=UserWarning) diff --git a/seaborn/utils.py b/seaborn/utils.py index 06a6467c84..d88aca1e31 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -208,8 +208,8 @@ def despine(fig=None, ax=None, top=True, right=True, left=False, if trim: # clip off the parts of the spines that extend past major ticks xticks = ax_i.get_xticks() - firsttick = np.compress(xticks >= ax_i.get_xlim()[0], xticks)[0] - lasttick = np.compress(xticks <= ax_i.get_xlim()[-1], xticks)[-1] + firsttick = np.compress(xticks >= min(ax_i.get_xlim()), xticks)[0] + lasttick = np.compress(xticks <= max(ax_i.get_xlim()), xticks)[-1] ax_i.spines['bottom'].set_bounds(firsttick, lasttick) ax_i.spines['top'].set_bounds(firsttick, lasttick) newticks = xticks.compress(xticks <= lasttick) @@ -217,8 +217,8 @@ def despine(fig=None, ax=None, top=True, right=True, left=False, ax_i.set_xticks(newticks) yticks = ax_i.get_yticks() - firsttick = np.compress(yticks >= ax_i.get_ylim()[0], yticks)[0] - lasttick = np.compress(yticks <= ax_i.get_ylim()[-1], yticks)[-1] + firsttick = np.compress(yticks >= min(ax_i.get_ylim()), yticks)[0] + lasttick = np.compress(yticks <= max(ax_i.get_ylim()), yticks)[-1] ax_i.spines['left'].set_bounds(firsttick, lasttick) ax_i.spines['right'].set_bounds(firsttick, lasttick) newticks = yticks.compress(yticks <= lasttick) From e62461d00e30e0085f08ff076e5885a621c92d9a Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 13 May 2015 20:55:47 -0700 Subject: [PATCH 0003/1738] Avoid error when trying to trim spine with no ticks --- seaborn/tests/test_utils.py | 8 ++++++++ seaborn/utils.py | 34 ++++++++++++++++++++-------------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index 094a5354c5..a2a3dd301c 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -209,6 +209,14 @@ def test_despine_trim_inverted(self): plt.close("all") + def test_despine_trim_noticks(self): + + f, ax = plt.subplots() + ax.plot([1, 2, 3], [1, 2, 3]) + ax.set_yticks([]) + utils.despine(trim=True) + nt.assert_equal(ax.get_yticks().size, 0) + def test_offset_spines_warns(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always", category=UserWarning) diff --git a/seaborn/utils.py b/seaborn/utils.py index d88aca1e31..fe4c5589ac 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -208,22 +208,28 @@ def despine(fig=None, ax=None, top=True, right=True, left=False, if trim: # clip off the parts of the spines that extend past major ticks xticks = ax_i.get_xticks() - firsttick = np.compress(xticks >= min(ax_i.get_xlim()), xticks)[0] - lasttick = np.compress(xticks <= max(ax_i.get_xlim()), xticks)[-1] - ax_i.spines['bottom'].set_bounds(firsttick, lasttick) - ax_i.spines['top'].set_bounds(firsttick, lasttick) - newticks = xticks.compress(xticks <= lasttick) - newticks = newticks.compress(newticks >= firsttick) - ax_i.set_xticks(newticks) + if xticks.size: + firsttick = np.compress(xticks >= min(ax_i.get_xlim()), + xticks)[0] + lasttick = np.compress(xticks <= max(ax_i.get_xlim()), + xticks)[-1] + ax_i.spines['bottom'].set_bounds(firsttick, lasttick) + ax_i.spines['top'].set_bounds(firsttick, lasttick) + newticks = xticks.compress(xticks <= lasttick) + newticks = newticks.compress(newticks >= firsttick) + ax_i.set_xticks(newticks) yticks = ax_i.get_yticks() - firsttick = np.compress(yticks >= min(ax_i.get_ylim()), yticks)[0] - lasttick = np.compress(yticks <= max(ax_i.get_ylim()), yticks)[-1] - ax_i.spines['left'].set_bounds(firsttick, lasttick) - ax_i.spines['right'].set_bounds(firsttick, lasttick) - newticks = yticks.compress(yticks <= lasttick) - newticks = newticks.compress(newticks >= firsttick) - ax_i.set_yticks(newticks) + if yticks.size: + firsttick = np.compress(yticks >= min(ax_i.get_ylim()), + yticks)[0] + lasttick = np.compress(yticks <= max(ax_i.get_ylim()), + yticks)[-1] + ax_i.spines['left'].set_bounds(firsttick, lasttick) + ax_i.spines['right'].set_bounds(firsttick, lasttick) + newticks = yticks.compress(yticks <= lasttick) + newticks = newticks.compress(newticks >= firsttick) + ax_i.set_yticks(newticks) def offset_spines(offset=10, fig=None, ax=None): From d3a884a9347fb20aae5ea946a79792c7e5c53ce7 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 20 May 2015 16:16:28 -0700 Subject: [PATCH 0004/1738] Fix bug in PairGrid missing data handling --- seaborn/axisgrid.py | 11 +++++---- seaborn/tests/test_axisgrid.py | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 8241c2274a..b76c089dcd 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -937,7 +937,8 @@ def map(self, func, **kwargs): try: data_k = hue_grouped.get_group(label_k) except KeyError: - data_k = np.array([]) + data_k = pd.DataFrame(columns=self.data.columns, + dtype=np.float) ax = self.axes[i, j] plt.sca(ax) @@ -996,7 +997,7 @@ def map_diag(self, func, **kwargs): for label in self.hue_names: # Attempt to get data for this level, allowing for empty try: - vals.append(hue_grouped.get_group(label)) + vals.append(np.asarray(hue_grouped.get_group(label))) except KeyError: vals.append(np.array([])) func(vals, color=self.palette, histtype="barstacked", @@ -1035,7 +1036,8 @@ def map_lower(self, func, **kwargs): try: data_k = hue_grouped.get_group(label_k) except KeyError: - data_k = np.array([]) + data_k = pd.DataFrame(columns=self.data.columns, + dtype=np.float) ax = self.axes[i, j] plt.sca(ax) @@ -1079,7 +1081,8 @@ def map_upper(self, func, **kwargs): try: data_k = hue_grouped.get_group(label_k) except KeyError: - data_k = np.array([]) + data_k = pd.DataFrame(columns=self.data.columns, + dtype=np.float) ax = self.axes[i, j] plt.sca(ax) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 1dfcd26782..86d411fa96 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -944,6 +944,50 @@ def test_hue_order(self): plt.close("all") + @skipif(old_matplotlib) + def test_hue_order_missing_level(self): + + order = list("dcaeb") + g = ag.PairGrid(self.df, hue="a", hue_order=order) + g.map(plt.plot) + + for line, level in zip(g.axes[1, 0].lines, order): + x, y = line.get_xydata().T + npt.assert_array_equal(x, self.df.loc[self.df.a == level, "x"]) + npt.assert_array_equal(y, self.df.loc[self.df.a == level, "y"]) + + plt.close("all") + + g = ag.PairGrid(self.df, hue="a", hue_order=order) + g.map_diag(plt.plot) + + for line, level in zip(g.axes[0, 0].lines, order): + x, y = line.get_xydata().T + npt.assert_array_equal(x, self.df.loc[self.df.a == level, "x"]) + npt.assert_array_equal(y, self.df.loc[self.df.a == level, "x"]) + + plt.close("all") + + g = ag.PairGrid(self.df, hue="a", hue_order=order) + g.map_lower(plt.plot) + + for line, level in zip(g.axes[1, 0].lines, order): + x, y = line.get_xydata().T + npt.assert_array_equal(x, self.df.loc[self.df.a == level, "x"]) + npt.assert_array_equal(y, self.df.loc[self.df.a == level, "y"]) + + plt.close("all") + + g = ag.PairGrid(self.df, hue="a", hue_order=order) + g.map_upper(plt.plot) + + for line, level in zip(g.axes[0, 1].lines, order): + x, y = line.get_xydata().T + npt.assert_array_equal(x, self.df.loc[self.df.a == level, "y"]) + npt.assert_array_equal(y, self.df.loc[self.df.a == level, "x"]) + + plt.close("all") + def test_nondefault_index(self): df = self.df.copy().set_index("b") From 848ceaf4c7975739582afcb36123657ba5a17ef2 Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Fri, 12 Jun 2015 06:32:48 -0700 Subject: [PATCH 0005/1738] ENH: allow local cacheing of datasets --- seaborn/utils.py | 53 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/seaborn/utils.py b/seaborn/utils.py index fe4c5589ac..00dfa71e20 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -1,5 +1,5 @@ """Small plotting-related utility functions.""" -from __future__ import division +from __future__ import print_function, division import colorsys import warnings @@ -9,10 +9,12 @@ import matplotlib.colors as mplcol import matplotlib.pyplot as plt +import os + from distutils.version import LooseVersion pandas_has_categoricals = LooseVersion(pd.__version__) >= "0.15" -from .external.six.moves.urllib.request import urlopen +from .external.six.moves.urllib.request import urlopen, urlretrieve def ci_to_errsize(cis, heights): @@ -371,7 +373,27 @@ def get_dataset_names(): if l.text.endswith('.csv')] -def load_dataset(name, **kws): +def get_data_home(data_home=None): + """Return the path of the seaborn data directory. + + This is used by the ``load_dataset`` function. + + If the ``data_home`` argument is not specified, the default location + is ``~/seaborn-data``. + + Alternatively, a different default location can be specified using the + environment variable ``SEABORN_DATA``. + """ + if data_home is None: + data_home = os.environ.get('SEABORN_DATA', + os.path.join('~', 'seaborn_data')) + data_home = os.path.expanduser(data_home) + if not os.path.exists(data_home): + os.makedirs(data_home) + return data_home + + +def load_dataset(name, cache=False, data_home=None, quiet=True, **kws): """Load a dataset from the online repository (requires internet). Parameters @@ -380,13 +402,36 @@ def load_dataset(name, **kws): Name of the dataset (`name`.csv on https://github.com/mwaskom/seaborn-data). You can obtain list of available datasets using :func:`get_dataset_names` + cache : boolean, optional + If True, then cache data locally and use the cache on subsequent calls + data_home : string, optional + The directory in which to cache data. By default, uses ~/seaborn_data/ + quiet : boolean, default + If False, then print info about file download kws : dict, optional Passed to pandas.read_csv """ path = "https://github.com/mwaskom/seaborn-data/raw/master/{0}.csv" full_path = path.format(name) - df = pd.read_csv(full_path, **kws) + + def quietprint(*args, **kwargs): + if not quiet: + print(*args, **kwargs) + + if not cache: + quietprint("reading file from {0}".format(full_path)) + df = pd.read_csv(full_path, **kws) + else: + cache_path = os.path.join(get_data_home(data_home), + os.path.basename(full_path)) + if not os.path.exists(cache_path): + quietprint("downloading file from {0}".format(full_path)) + urlretrieve(full_path, cache_path) + + quietprint("reading file from {0}".format(cache_path)) + df = pd.read_csv(cache_path, **kws) + if df.iloc[-1].isnull().all(): df = df.iloc[:-1] From 81e1d6439f0553ff94e10e60bc15f2b2052829ef Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Fri, 12 Jun 2015 11:00:57 -0700 Subject: [PATCH 0006/1738] fix typos; simplify load_data enhancement --- seaborn/utils.py | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/seaborn/utils.py b/seaborn/utils.py index 00dfa71e20..aa7a62378b 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -386,14 +386,14 @@ def get_data_home(data_home=None): """ if data_home is None: data_home = os.environ.get('SEABORN_DATA', - os.path.join('~', 'seaborn_data')) + os.path.join('~', 'seaborn-data')) data_home = os.path.expanduser(data_home) if not os.path.exists(data_home): os.makedirs(data_home) return data_home -def load_dataset(name, cache=False, data_home=None, quiet=True, **kws): +def load_dataset(name, cache=False, data_home=None, **kws): """Load a dataset from the online repository (requires internet). Parameters @@ -406,8 +406,6 @@ def load_dataset(name, cache=False, data_home=None, quiet=True, **kws): If True, then cache data locally and use the cache on subsequent calls data_home : string, optional The directory in which to cache data. By default, uses ~/seaborn_data/ - quiet : boolean, default - If False, then print info about file download kws : dict, optional Passed to pandas.read_csv @@ -415,23 +413,14 @@ def load_dataset(name, cache=False, data_home=None, quiet=True, **kws): path = "https://github.com/mwaskom/seaborn-data/raw/master/{0}.csv" full_path = path.format(name) - def quietprint(*args, **kwargs): - if not quiet: - print(*args, **kwargs) - - if not cache: - quietprint("reading file from {0}".format(full_path)) - df = pd.read_csv(full_path, **kws) - else: + if cache: cache_path = os.path.join(get_data_home(data_home), os.path.basename(full_path)) if not os.path.exists(cache_path): - quietprint("downloading file from {0}".format(full_path)) urlretrieve(full_path, cache_path) + full_path = cache_path - quietprint("reading file from {0}".format(cache_path)) - df = pd.read_csv(cache_path, **kws) - + df = pd.read_csv(full_path, **kws) if df.iloc[-1].isnull().all(): df = df.iloc[:-1] From 6426f8975fc2bf0393d3222707a1112888e3dd9d Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Fri, 12 Jun 2015 11:20:26 -0700 Subject: [PATCH 0007/1738] import os in the standard place --- seaborn/utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/seaborn/utils.py b/seaborn/utils.py index aa7a62378b..3a2841f06c 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -2,6 +2,7 @@ from __future__ import print_function, division import colorsys import warnings +import os import numpy as np from scipy import stats @@ -9,8 +10,6 @@ import matplotlib.colors as mplcol import matplotlib.pyplot as plt -import os - from distutils.version import LooseVersion pandas_has_categoricals = LooseVersion(pd.__version__) >= "0.15" @@ -393,7 +392,7 @@ def get_data_home(data_home=None): return data_home -def load_dataset(name, cache=False, data_home=None, **kws): +def load_dataset(name, cache=True, data_home=None, **kws): """Load a dataset from the online repository (requires internet). Parameters From ab840244808dd93a1cd56c1643bb96ba27ce7909 Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Fri, 12 Jun 2015 11:21:10 -0700 Subject: [PATCH 0008/1738] Mention dataset cacheing in v0.6.0 release notes --- doc/releases/v0.6.0.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.6.0.txt b/doc/releases/v0.6.0.txt index be790125e4..100b745c54 100644 --- a/doc/releases/v0.6.0.txt +++ b/doc/releases/v0.6.0.txt @@ -73,6 +73,8 @@ Other additions - The :func:`color_palette` function no longer trims palettes that are longer than 6 colors when passed into it. +- The :func:`load_dataset` function now caches datasets locally after downloading them, and uses the local copy on subsequent calls. + Bug fixes ~~~~~~~~~ From 03f2d2cfdeca394056b7238ec64f47dc4a0e0a66 Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Fri, 12 Jun 2015 11:45:32 -0700 Subject: [PATCH 0009/1738] TST: add testing of dataset cacheing --- seaborn/tests/test_utils.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index a2a3dd301c..52fc647645 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -1,5 +1,7 @@ """Tests for plotting utilities.""" import warnings +import tempfile +import shutil import numpy as np import pandas as pd @@ -320,9 +322,25 @@ def test_categorical_order(): if LooseVersion(pd.__version__) >= "0.15": def check_load_dataset(name): - ds = load_dataset(name) + ds = load_dataset(name, cache=False) assert(isinstance(ds, pd.DataFrame)) + def check_load_cached_dataset(name): + # Test the cacheing using a temporary file. + # With Python 3.2+, we could use the tempfile.TemporaryDirectory() + # context manager instead of this try...finally statement + tmpdir = tempfile.mkdtemp() + try: + # download and cache + ds = load_dataset(name, cache=True, data_home=tmpdir) + + # use cached version + ds2 = load_dataset(name, cache=True, data_home=tmpdir) + assert_array_equal(ds, ds2) + + finally: + shutil.rmtree(tmpdir) + @network(url="https://github.com/mwaskom/seaborn-data") def test_get_dataset_names(): if not BeautifulSoup: @@ -342,3 +360,15 @@ def test_load_datasets(): # does not get in effect, so we need to call explicitly # yield check_load_dataset, name check_load_dataset(name) + + @network(url="https://github.com/mwaskom/seaborn-data") + def test_load_cached_datasets(): + if not BeautifulSoup: + raise nose.SkipTest("No BeautifulSoup available for parsing html") + + # Heavy test to verify that we can load all available datasets + for name in get_dataset_names(): + # unfortunately @network somehow obscures this generator so it + # does not get in effect, so we need to call explicitly + # yield check_load_dataset, name + check_load_cached_dataset(name) From 8507b5c939b411f90041833cab4c3c6e93ff9458 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 13 May 2015 16:24:49 -0700 Subject: [PATCH 0010/1738] Add comments to example scripts and fix broken examples --- examples/anscombes_quartet.py | 5 +++- examples/color_palettes.py | 13 +++++++--- examples/cubehelix_palette.py | 12 ++++++--- examples/distplot_options.py | 23 +++++++++++------ examples/elaborate_violinplot.py | 16 +++++++++--- examples/facets_with_custom_projection.py | 12 +++++++-- examples/factorplot_bars.py | 9 ++++--- examples/grouped_boxplot.py | 5 +++- examples/heatmap_annotation.py | 5 ++-- examples/interactplot.py | 6 ++--- examples/joint_kde.py | 2 ++ examples/logistic_regression.py | 6 ++++- examples/many_facets.py | 12 +++++++-- examples/many_pairwise_correlations.py | 30 +++++++++++++++++------ examples/marginal_ticks.py | 9 ++++--- examples/multiple_regression.py | 11 ++++++--- examples/network_correlations.py | 12 ++++++--- examples/paired_pointplots.py | 6 ++++- examples/pointplot_anova.py | 8 +++--- examples/regression_marginals.py | 5 ++-- examples/residplot.py | 4 ++- examples/simple_violinplots.py | 14 +++++++---- examples/structured_heatmap.py | 9 ++++++- examples/timeseries_bootstrapped.py | 5 ++-- examples/timeseries_from_dataframe.py | 6 ++++- examples/timeseries_of_barplots.py | 9 +++++-- 26 files changed, 181 insertions(+), 73 deletions(-) diff --git a/examples/anscombes_quartet.py b/examples/anscombes_quartet.py index 9ef90325db..36d0d9212e 100644 --- a/examples/anscombes_quartet.py +++ b/examples/anscombes_quartet.py @@ -7,7 +7,10 @@ import seaborn as sns sns.set(style="ticks") +# Load the example dataset for Anscombe's quartet df = sns.load_dataset("anscombe") -sns.lmplot("x", "y", col="dataset", hue="dataset", data=df, + +# Show the results of a linear regression within each dataset +sns.lmplot(x="x", y="y", col="dataset", hue="dataset", data=df, col_wrap=2, ci=None, palette="muted", size=4, scatter_kws={"s": 50, "alpha": 1}) diff --git a/examples/color_palettes.py b/examples/color_palettes.py index 49d7b18fa5..21d8edbee9 100644 --- a/examples/color_palettes.py +++ b/examples/color_palettes.py @@ -9,22 +9,27 @@ sns.set(style="white", context="talk") rs = np.random.RandomState(7) -x = np.array(list("ABCDEFGHI")) +# Set up the matplotlib figure f, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(8, 6), sharex=True) +# Generate some sequential data +x = np.array(list("ABCDEFGHI")) y1 = np.arange(1, 10) -sns.barplot(x, y1, ci=None, palette="BuGn_d", hline=.1, ax=ax1) +sns.barplot(x, y1, palette="BuGn_d", ax=ax1) ax1.set_ylabel("Sequential") +# Center the data to make it diverging y2 = y1 - 5 -sns.barplot(x, y2, ci=None, palette="coolwarm", hline=0, ax=ax2) +sns.barplot(x, y2, palette="RdBu_r", ax=ax2) ax2.set_ylabel("Diverging") +# Randomly reorder the data to make it qualitative y3 = rs.choice(y1, 9, replace=False) -sns.barplot(x, y3, ci=None, palette="Paired", hline=.1, ax=ax3) +sns.barplot(x, y3, palette="Set3", ax=ax3) ax3.set_ylabel("Qualitative") +# Finalize the plot sns.despine(bottom=True) plt.setp(f.axes, yticks=[]) plt.tight_layout(h_pad=3) diff --git a/examples/cubehelix_palette.py b/examples/cubehelix_palette.py index c02f41bbc7..753f20d47f 100644 --- a/examples/cubehelix_palette.py +++ b/examples/cubehelix_palette.py @@ -9,14 +9,20 @@ import matplotlib.pyplot as plt sns.set(style="dark") +rs = np.random.RandomState(50) +# Set up the matplotlib figure f, axes = plt.subplots(3, 3, figsize=(9, 9), sharex=True, sharey=True) -rs = np.random.RandomState(50) - +# Rotate the starting point around the cubehelix hue circle for ax, s in zip(axes.flat, np.linspace(0, 3, 10)): - x, y = rs.randn(2, 50) + + # Create a cubehelix colormap to use with kdeplot cmap = sns.cubehelix_palette(start=s, light=1, as_cmap=True) + + # Generate and plot a random bivariate dataset + x, y = rs.randn(2, 50) sns.kdeplot(x, y, cmap=cmap, shade=True, cut=5, ax=ax) ax.set(xlim=(-3, 3), ylim=(-3, 3)) + f.tight_layout() diff --git a/examples/distplot_options.py b/examples/distplot_options.py index 73ba92bc47..f5adac4b4d 100644 --- a/examples/distplot_options.py +++ b/examples/distplot_options.py @@ -7,20 +7,27 @@ import seaborn as sns import matplotlib.pyplot as plt -sns.set(style="white", palette="muted") +sns.set(style="white", palette="muted", color_codes=True) +rs = np.random.RandomState(10) + +# Set up the matplotlib figure f, axes = plt.subplots(2, 2, figsize=(7, 7), sharex=True) sns.despine(left=True) -rs = np.random.RandomState(10) +# Generate a random univariate dataset +d = rs.normal(size=100) -b, g, r, p = sns.color_palette("muted", 4) +# Plot a simple histogram with binsize determined automatically +sns.distplot(d, kde=False, color="b", ax=axes[0, 0]) -d = rs.normal(size=100) +# Plot a kernel density estimate and rug plot +sns.distplot(d, hist=False, rug=True, color="r", ax=axes[0, 1]) + +# Plot a filled kernel density estimate +sns.distplot(d, hist=False, color="g", kde_kws={"shade": True}, ax=axes[1, 0]) -sns.distplot(d, kde=False, color=b, ax=axes[0, 0]) -sns.distplot(d, hist=False, rug=True, color=r, ax=axes[0, 1]) -sns.distplot(d, hist=False, color=g, kde_kws={"shade": True}, ax=axes[1, 0]) -sns.distplot(d, color=p, ax=axes[1, 1]) +# Plot a historgram and kernel density estimate +sns.distplot(d, color="m", ax=axes[1, 1]) plt.setp(axes, yticks=[]) plt.tight_layout() diff --git a/examples/elaborate_violinplot.py b/examples/elaborate_violinplot.py index ddec91f8e0..3ec9705ff8 100644 --- a/examples/elaborate_violinplot.py +++ b/examples/elaborate_violinplot.py @@ -1,6 +1,6 @@ """ -Violinplots showing observations -================================ +Violinplot from a wide-form dataset +=================================== _thumb: .6, .45 """ @@ -8,19 +8,27 @@ import matplotlib.pyplot as plt sns.set(style="whitegrid") +# Load the example dataset of brain network correlations df = sns.load_dataset("brain_networks", header=[0, 1, 2], index_col=0) + +# Pull out a specific subset of networks used_networks = [1, 3, 4, 5, 6, 7, 8, 11, 12, 13, 16, 17] used_columns = (df.columns.get_level_values("network") .astype(int) .isin(used_networks)) df = df.loc[:, used_columns] +# Compute the correlation matrix and average over networks corr_df = df.corr().groupby(level="network").mean() corr_df.index = corr_df.index.astype(int) corr_df = corr_df.sort_index().T +# Set up the matplotlib figure f, ax = plt.subplots(figsize=(11, 6)) -sns.violinplot(data=corr_df, palette="Set3", bw=.2, cut=1, - linewidth=.5, inner="points") + +# Draw a violinplot with a narrower bandwidth than the default +sns.violinplot(data=corr_df, palette="Set3", bw=.2, cut=1, linewidth=1) + +# Finalize the figure ax.set(ylim=(-.7, 1.05)) sns.despine(left=True, bottom=True) diff --git a/examples/facets_with_custom_projection.py b/examples/facets_with_custom_projection.py index b15d1f3c59..c2b9ea1065 100644 --- a/examples/facets_with_custom_projection.py +++ b/examples/facets_with_custom_projection.py @@ -2,7 +2,7 @@ FacetGrid with custom projection ================================ -_thumb: .38, .5 +_thumb: .33, .5 """ import matplotlib.pyplot as plt @@ -10,11 +10,19 @@ import pandas as pd import seaborn as sns +sns.set() + +# Generate an example radial datast r = np.linspace(0, 10, num=100) df = pd.DataFrame({'r': r, 'slow': r, 'medium': 2 * r, 'fast': 4 * r}) + +# Convert the dataframe to long-form or "tidy" format df = pd.melt(df, id_vars=['r'], var_name='speed', value_name='theta') +# Set up a grid of axes with a polar projection g = sns.FacetGrid(df, col="speed", hue="speed", - subplot_kws=dict(projection='polar'), size=4, + subplot_kws=dict(projection='polar'), size=4.5, sharex=False, sharey=False, despine=False) + +# Draw a scatterplot onto each axes in the grid g.map(plt.scatter, "theta", "r") diff --git a/examples/factorplot_bars.py b/examples/factorplot_bars.py index 40b4419f91..0ecc7dd964 100644 --- a/examples/factorplot_bars.py +++ b/examples/factorplot_bars.py @@ -2,15 +2,16 @@ Grouped barplots ================ -_thumb: .5, .4 +_thumb: .45, .5 """ import seaborn as sns sns.set(style="whitegrid") +# Load the example Titanic dataset titanic = sns.load_dataset("titanic") -g = sns.factorplot("class", "survived", "sex", - data=titanic, kind="bar", - size=6, palette="muted") +# Draw a nested barplot to show survival for class and sex +g = sns.factorplot(x="class", y="survived", hue="sex", data=titanic, + size=6, kind="bar", palette="muted") g.despine(left=True) g.set_ylabels("survival probability") diff --git a/examples/grouped_boxplot.py b/examples/grouped_boxplot.py index 238c4fbb86..4a7e9a481b 100644 --- a/examples/grouped_boxplot.py +++ b/examples/grouped_boxplot.py @@ -6,6 +6,9 @@ import seaborn as sns sns.set(style="ticks") +# Load the example tips dataset tips = sns.load_dataset("tips") -sns.boxplot("day", "total_bill", "sex", tips, palette="PRGn") + +# Draw a nested boxplot to show bills by day and sex +sns.boxplot(x="day", y="total_bill", hue="sex", data=tips, palette="PRGn") sns.despine(offset=10, trim=True) diff --git a/examples/heatmap_annotation.py b/examples/heatmap_annotation.py index 001e826923..33de576008 100644 --- a/examples/heatmap_annotation.py +++ b/examples/heatmap_annotation.py @@ -6,8 +6,9 @@ import seaborn as sns sns.set() +# Load the example flights dataset and conver to long-form flights_long = sns.load_dataset("flights") flights = flights_long.pivot("month", "year", "passengers") -flights = flights.reindex(flights_long.iloc[:12].month) -sns.heatmap(flights, annot=True, fmt="d") +# Draw a heatmap with the numeric values in each cell +sns.heatmap(flights, annot=True, fmt="d", linewidths=.5) diff --git a/examples/interactplot.py b/examples/interactplot.py index ead6ed3b84..28ae928fd6 100644 --- a/examples/interactplot.py +++ b/examples/interactplot.py @@ -8,14 +8,14 @@ import seaborn as sns sns.set(style="darkgrid") -rs = np.random.RandomState(11) - +# Generate a random dataset with strong simple effects and an interaction n = 80 +rs = np.random.RandomState(11) x1 = rs.randn(n) x2 = x1 / 5 + rs.randn(n) b0, b1, b2, b3 = .5, .25, -1, 2 y = b0 + b1 * x1 + b2 * x2 + b3 * x1 * x2 + rs.randn(n) - df = pd.DataFrame(np.c_[x1, x2, y], columns=["x1", "x2", "y"]) +# Show a scatterplot of the predictors with the estimated model surface sns.interactplot("x1", "x2", "y", df) diff --git a/examples/joint_kde.py b/examples/joint_kde.py index 51300e1efd..2e458eeaae 100644 --- a/examples/joint_kde.py +++ b/examples/joint_kde.py @@ -9,6 +9,7 @@ import seaborn as sns sns.set(style="white") +# Generate a random correlated bivariate dataset rs = np.random.RandomState(5) mean = [0, 0] cov = [(1, .5), (.5, 1)] @@ -16,4 +17,5 @@ x1 = pd.Series(x1, name="$X_1$") x2 = pd.Series(x2, name="$X_2$") +# Show the joint distribution using kernel density estimation g = sns.jointplot(x1, x2, kind="kde", size=7, space=0) diff --git a/examples/logistic_regression.py b/examples/logistic_regression.py index 501a30f285..1fc98d0d13 100644 --- a/examples/logistic_regression.py +++ b/examples/logistic_regression.py @@ -7,9 +7,13 @@ import seaborn as sns sns.set(style="darkgrid") +# Load the example titanic dataset df = sns.load_dataset("titanic") +# Make a custom palette with gendered colors pal = dict(male="#6495ED", female="#F08080") -g = sns.lmplot("age", "survived", col="sex", hue="sex", data=df, + +# Show the survival proability as a function of age and sex +g = sns.lmplot(x="age", y="survived", col="sex", hue="sex", data=df, palette=pal, y_jitter=.02, logistic=True) g.set(xlim=(0, 80), ylim=(-.05, 1.05)) diff --git a/examples/many_facets.py b/examples/many_facets.py index 7f33f5702c..05cca4d2a7 100644 --- a/examples/many_facets.py +++ b/examples/many_facets.py @@ -9,19 +9,27 @@ import matplotlib.pyplot as plt sns.set(style="ticks") +# Create a dataset with many short random walks rs = np.random.RandomState(4) - pos = rs.randint(-1, 2, (20, 5)).cumsum(axis=1) pos -= pos[:, 0, np.newaxis] step = np.tile(range(5), 20) walk = np.repeat(range(20), 5) - df = pd.DataFrame(np.c_[pos.flat, step, walk], columns=["position", "step", "walk"]) +# Initialize a grid of plots with an Axes for each walk grid = sns.FacetGrid(df, col="walk", hue="walk", col_wrap=5, size=1.5) + +# Draw a horizontal line to show the starting point grid.map(plt.axhline, y=0, ls=":", c=".5") + +# Draw a line plot to show the trajectory of each random walk grid.map(plt.plot, "step", "position", marker="o", ms=4) + +# Adjust the tick positions and labels grid.set(xticks=np.arange(5), yticks=[-3, 3], xlim=(-.5, 4.5), ylim=(-3.5, 3.5)) + +# Adjust the arrangement of the plots grid.fig.tight_layout(w_pad=1) diff --git a/examples/many_pairwise_correlations.py b/examples/many_pairwise_correlations.py index 9ed5e2f617..6517ca32e7 100644 --- a/examples/many_pairwise_correlations.py +++ b/examples/many_pairwise_correlations.py @@ -1,19 +1,33 @@ """ -Plotting a large correlation matrix -=================================== +Plotting a diagonal correlation matrix +====================================== _thumb: .3, .6 """ import numpy as np import seaborn as sns import matplotlib.pyplot as plt -sns.set(style="darkgrid") +sns.set(style="white") + +# Generate a large random dataset rs = np.random.RandomState(33) -d = rs.normal(size=(100, 30)) +d = rs.normal(size=(30, 100)) + +# Compute the correlation matrix +corr = np.corrcoef(d) + +# Generate a mask for the upper triangle +mask = np.zeros_like(corr, dtype=np.bool) +mask[np.triu_indices_from(mask)] = True -f, ax = plt.subplots(figsize=(9, 9)) +# Set up the matplotlib figure +f, ax = plt.subplots(figsize=(11, 9)) + +# Generate a custom diverging colormap cmap = sns.diverging_palette(220, 10, as_cmap=True) -sns.corrplot(d, annot=False, sig_stars=False, - diag_names=False, cmap=cmap, ax=ax) -f.tight_layout() + +# Draw the heatmap with the mask and correct aspect ratio +sns.heatmap(corr, mask=mask, cmap=cmap, vmax=.3, + square=True, xticklabels=5, yticklabels=5, + linewidths=.5, cbar_kws={"shrink": .5}, ax=ax) diff --git a/examples/marginal_ticks.py b/examples/marginal_ticks.py index f50619c3b5..5f14bf9aed 100644 --- a/examples/marginal_ticks.py +++ b/examples/marginal_ticks.py @@ -7,14 +7,15 @@ import numpy as np import seaborn as sns import matplotlib.pyplot as plt -sns.set(style="white") +sns.set(style="white", color_codes=True) +# Generate a random bivariate dataset rs = np.random.RandomState(9) mean = [0, 0] cov = [(1, 0), (0, 2)] x, y = rs.multivariate_normal(mean, cov, 100).T -color = sns.color_palette()[1] +# Use JointGrid directly to draw a custom plot grid = sns.JointGrid(x, y, space=0, size=6, ratio=50) -grid.plot_joint(plt.scatter, color=color, alpha=.8) -grid.plot_marginals(sns.rugplot, color=color) +grid.plot_joint(plt.scatter, color="g") +grid.plot_marginals(sns.rugplot, color="g") diff --git a/examples/multiple_regression.py b/examples/multiple_regression.py index 12b3f610a4..e5e65ab832 100644 --- a/examples/multiple_regression.py +++ b/examples/multiple_regression.py @@ -6,10 +6,15 @@ import seaborn as sns sns.set(style="ticks", context="talk") +# Load the example tips dataset tips = sns.load_dataset("tips") -days = ["Thur", "Fri", "Sat", "Sun"] +# Make a custom sequential palette using the cubehelix system pal = sns.cubehelix_palette(4, 1.5, .75, light=.6, dark=.2) -g = sns.lmplot("total_bill", "tip", hue="day", data=tips, - hue_order=days, palette=pal, size=6) + +# Plot tip as a function of toal bill across days +g = sns.lmplot(x="total_bill", y="tip", hue="day", data=tips, + palette=pal, size=7) + +# Use more informative axis labels than are provided by default g.set_axis_labels("Total bill ($)", "Tip ($)") diff --git a/examples/network_correlations.py b/examples/network_correlations.py index c084c8d349..c2f6dafb2c 100644 --- a/examples/network_correlations.py +++ b/examples/network_correlations.py @@ -1,22 +1,26 @@ """ -Cortical networks correlation matrix -==================================== +Correlation matrix heatmap +========================== """ import seaborn as sns import matplotlib.pyplot as plt sns.set(context="paper", font="monospace") +# Load the datset of correlations between cortical brain networks df = sns.load_dataset("brain_networks", header=[0, 1, 2], index_col=0) corrmat = df.corr() +# Set up the matplotlib figure f, ax = plt.subplots(figsize=(12, 9)) -sns.heatmap(corrmat, vmax=.8, linewidths=0, square=True) +# Draw the heatmap using seaborn +sns.heatmap(corrmat, vmax=.8, square=True) + +# Use matplotlib directly to emphasize known networks networks = corrmat.columns.get_level_values("network") for i, network in enumerate(networks): if i and network != networks[i - 1]: ax.axhline(len(networks) - i, c="w") ax.axvline(i, c="w") - f.tight_layout() diff --git a/examples/paired_pointplots.py b/examples/paired_pointplots.py index f1ff9a000d..9b558fbeb0 100644 --- a/examples/paired_pointplots.py +++ b/examples/paired_pointplots.py @@ -6,11 +6,15 @@ import seaborn as sns sns.set(style="whitegrid") +# Load the example Titanic dataset titanic = sns.load_dataset("titanic") +# Set up a grid to plot survival probability against several variables g = sns.PairGrid(titanic, y_vars="survived", x_vars=["class", "sex", "who", "alone"], size=5, aspect=.5) + +# Draw a seaborn pointplot onto each Axes g.map(sns.pointplot, color=sns.xkcd_rgb["plum"]) g.set(ylim=(0, 1)) -sns.despine(left=True) +sns.despine(fig=g.fig, left=True) diff --git a/examples/pointplot_anova.py b/examples/pointplot_anova.py index cf4af82d3e..0b210848e2 100644 --- a/examples/pointplot_anova.py +++ b/examples/pointplot_anova.py @@ -7,8 +7,10 @@ import seaborn as sns sns.set(style="whitegrid") +# Load the example exercise dataset df = sns.load_dataset("exercise") -sns.factorplot("time", "pulse", hue="kind", col="diet", data=df, - hue_order=["rest", "walking", "running"], - palette="YlGnBu_d", aspect=.75).despine(left=True) +# Draw a pointplot to show pulse as a function of three categorical factors +g = sns.factorplot(x="time", y="pulse", hue="kind", col="diet", data=df, + palette="YlGnBu_d", size=6, aspect=.75) +g.despine(left=True) diff --git a/examples/regression_marginals.py b/examples/regression_marginals.py index 97167350db..e4e2d1c7cb 100644 --- a/examples/regression_marginals.py +++ b/examples/regression_marginals.py @@ -5,9 +5,8 @@ _thumb: .5, .6 """ import seaborn as sns -sns.set(style="darkgrid") +sns.set(style="darkgrid", color_codes=True) tips = sns.load_dataset("tips") -color = sns.color_palette()[2] g = sns.jointplot("total_bill", "tip", data=tips, kind="reg", - xlim=(0, 60), ylim=(0, 12), color=color, size=7) + xlim=(0, 60), ylim=(0, 12), color="r", size=7) diff --git a/examples/residplot.py b/examples/residplot.py index ac1f70ce35..1a557947ac 100644 --- a/examples/residplot.py +++ b/examples/residplot.py @@ -7,8 +7,10 @@ import seaborn as sns sns.set(style="whitegrid") +# Make an example dataset with y ~ x rs = np.random.RandomState(7) x = rs.normal(2, 1, 75) y = 2 + 1.5 * x + rs.normal(0, 2, 75) -sns.residplot(x, y, lowess=True, color="navy") +# Plot the residuals after fitting a linear model +sns.residplot(x, y, lowess=True, color="g") diff --git a/examples/simple_violinplots.py b/examples/simple_violinplots.py index 3d993df822..631632cdb5 100644 --- a/examples/simple_violinplots.py +++ b/examples/simple_violinplots.py @@ -1,6 +1,6 @@ """ -Simple Violinplots -================== +Violinplots with observations +============================= """ import numpy as np @@ -8,11 +8,15 @@ sns.set() +# Create a random dataset across several variables rs = np.random.RandomState(0) - n, p = 40, 8 -d = rs.normal(0, 1, (n, p)) +d = rs.normal(0, 2, (n, p)) d += np.log(np.arange(1, p + 1)) * -5 + 10 +# Use cubehelix to get a custom sequential palette pal = sns.cubehelix_palette(p, rot=-.5, dark=.3) -sns.violinplot(data=d, palette=pal) + +# Show each distribution with both violins and points +sns.violinplot(data=d, palette=pal, inner=None) +sns.stripplot(data=d, palette=pal, jitter=True) diff --git a/examples/structured_heatmap.py b/examples/structured_heatmap.py index 27a1bc3e80..1fa909749f 100644 --- a/examples/structured_heatmap.py +++ b/examples/structured_heatmap.py @@ -8,22 +8,29 @@ import seaborn as sns sns.set(font="monospace") +# Load the brain networks example dataset df = sns.load_dataset("brain_networks", header=[0, 1, 2], index_col=0) + +# Select a subset of the networks used_networks = [1, 5, 6, 7, 8, 11, 12, 13, 16, 17] used_columns = (df.columns.get_level_values("network") .astype(int) .isin(used_networks)) df = df.loc[:, used_columns] +# Create a custom palette to identify the networks network_pal = sns.cubehelix_palette(len(used_networks), light=.9, dark=.1, reverse=True, start=1, rot=-2) network_lut = dict(zip(map(str, used_networks), network_pal)) +# Convert the palette to vectors that will be drawn on the side of the matrix networks = df.columns.get_level_values("network") network_colors = pd.Series(networks).map(network_lut) +# Create a custom colormap for the heatmap values cmap = sns.diverging_palette(h_neg=210, h_pos=350, s=90, l=30, as_cmap=True) -sns.clustermap(df.corr(), row_colors=network_colors, method="average", +# Draw the full plot +sns.clustermap(df.corr(), row_colors=network_colors, linewidths=.5, col_colors=network_colors, figsize=(13, 13), cmap=cmap) diff --git a/examples/timeseries_bootstrapped.py b/examples/timeseries_bootstrapped.py index 4961cdecfe..1b18804782 100644 --- a/examples/timeseries_bootstrapped.py +++ b/examples/timeseries_bootstrapped.py @@ -7,12 +7,13 @@ import seaborn as sns sns.set(style="darkgrid", palette="Set2") -rs = np.random.RandomState(8) - +# Create a noisy periodic dataset sines = [] +rs = np.random.RandomState(8) for _ in range(15): x = np.linspace(0, 30 / 2, 30) y = np.sin(x) + rs.normal(0, 1.5) + rs.normal(0, .3, 30) sines.append(y) +# Plot the average over replicates with bootstrap resamples sns.tsplot(sines, err_style="boot_traces", n_boot=500) diff --git a/examples/timeseries_from_dataframe.py b/examples/timeseries_from_dataframe.py index c9c4069353..554ce98b24 100644 --- a/examples/timeseries_from_dataframe.py +++ b/examples/timeseries_from_dataframe.py @@ -7,5 +7,9 @@ import seaborn as sns sns.set(style="darkgrid") +# Load the long-form example gammas dataset gammas = sns.load_dataset("gammas") -sns.tsplot(gammas, "timepoint", "subject", "ROI", "BOLD signal") + +# Plot the response with standard error +sns.tsplot(data=gammas, time="timepoint", unit="subject", + condition="ROI", value="BOLD signal") diff --git a/examples/timeseries_of_barplots.py b/examples/timeseries_of_barplots.py index 5b5321c12c..722cf00937 100644 --- a/examples/timeseries_of_barplots.py +++ b/examples/timeseries_of_barplots.py @@ -6,10 +6,15 @@ """ import numpy as np import seaborn as sns - sns.set(style="white") + +# Load the example planets dataset planets = sns.load_dataset("planets") + +# Make a range of years to show categories with no observations years = np.arange(2000, 2015) -g = sns.factorplot("year", data=planets, palette="BuPu", + +# Draw a count plot to show the number of planets discovered each year +g = sns.factorplot(x="year", data=planets, palette="BuPu", kind="count", size=6, aspect=1.5, x_order=years) g.set_xticklabels(step=2) From 9594eb03e527743e40e09d6ce9bc51f9f5340771 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 13 May 2015 18:42:09 -0700 Subject: [PATCH 0011/1738] Add new examples --- examples/horizontal_boxplot.py | 20 ++++++++++++++++++++ examples/scatterplot_categorical.py | 17 +++++++++++++++++ examples/simple_violinplots.py | 3 +-- 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 examples/horizontal_boxplot.py create mode 100644 examples/scatterplot_categorical.py diff --git a/examples/horizontal_boxplot.py b/examples/horizontal_boxplot.py new file mode 100644 index 0000000000..9f13159658 --- /dev/null +++ b/examples/horizontal_boxplot.py @@ -0,0 +1,20 @@ +""" +Horizontal boxplot with log axis +================================ + +_thumb: .6, .5 +""" +import numpy as np +import seaborn as sns +sns.set(style="ticks", palette="muted", color_codes=True) + +# Load the example planets dataset +planets = sns.load_dataset("planets") + +# Plot the orbital period with horizontal boxes +ax = sns.boxplot(x="orbital_period", y="method", data=planets, + whis=np.inf, color="c") + +# Make the quantitative axis logarithmic +ax.set_xscale("log") +sns.despine(left=True) diff --git a/examples/scatterplot_categorical.py b/examples/scatterplot_categorical.py new file mode 100644 index 0000000000..6ddbd043a6 --- /dev/null +++ b/examples/scatterplot_categorical.py @@ -0,0 +1,17 @@ +""" +Scatterplot with categorical variables + +""" +import pandas as pd +import seaborn as sns +sns.set(style="whitegrid", palette="pastel") + +# Load the example iris dataset +iris = sns.load_dataset("iris") + +# "Melt" the dataset to "long-form" or "tidy" representation +iris = pd.melt(iris, "species", var_name="measurement") + +# Draw a categorical scatterplot to show each observation +sns.stripplot(x="measurement", y="value", hue="species", data=iris, + jitter=True, edgecolor="gray") diff --git a/examples/simple_violinplots.py b/examples/simple_violinplots.py index 631632cdb5..d04a9d5225 100644 --- a/examples/simple_violinplots.py +++ b/examples/simple_violinplots.py @@ -18,5 +18,4 @@ pal = sns.cubehelix_palette(p, rot=-.5, dark=.3) # Show each distribution with both violins and points -sns.violinplot(data=d, palette=pal, inner=None) -sns.stripplot(data=d, palette=pal, jitter=True) +sns.violinplot(data=d, palette=pal, inner="points") From b56e0d86cd092aa7622d29ba0dae309e05bfc027 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 13 May 2015 22:02:44 -0700 Subject: [PATCH 0012/1738] Add horizontal barplot example --- examples/horizontal_barplot.py | 30 +++++++++++++++++++++++++++++ examples/scatterplot_categorical.py | 1 + 2 files changed, 31 insertions(+) create mode 100644 examples/horizontal_barplot.py diff --git a/examples/horizontal_barplot.py b/examples/horizontal_barplot.py new file mode 100644 index 0000000000..0b50d53265 --- /dev/null +++ b/examples/horizontal_barplot.py @@ -0,0 +1,30 @@ +""" +Horizontal bar plots +==================== + +""" +import seaborn as sns +import matplotlib.pyplot as plt +sns.set(style="whitegrid") + +# Initialize the matplotlib figure +f, ax = plt.subplots(figsize=(6, 15)) + +# Load the example car crash dataset +crashes = sns.load_dataset("car_crashes").sort("total", ascending=False) + +# Plot the total crashes +sns.set_color_codes("pastel") +sns.barplot(x="total", y="abbrev", data=crashes, + label="Total", color="b") + +# Plot the crashes where alcohol was involved +sns.set_color_codes("muted") +sns.barplot(x="alcohol", y="abbrev", data=crashes, + label="Alcohol-involved", color="b") + +# Add a legend and informative axis label +ax.legend(ncol=2, loc="lower right", frameon=True) +ax.set(xlim=(0, 24), ylabel="", + xlabel="Automobile collisions per billion miles") +sns.despine(left=True, bottom=True) diff --git a/examples/scatterplot_categorical.py b/examples/scatterplot_categorical.py index 6ddbd043a6..5f4813d44f 100644 --- a/examples/scatterplot_categorical.py +++ b/examples/scatterplot_categorical.py @@ -1,5 +1,6 @@ """ Scatterplot with categorical variables +====================================== """ import pandas as pd From 790163173efb34e2b9b0ad0d8927057836f0ad51 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 14 May 2015 10:44:04 -0700 Subject: [PATCH 0013/1738] Add a split violin example script --- examples/grouped_violinplots.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 examples/grouped_violinplots.py diff --git a/examples/grouped_violinplots.py b/examples/grouped_violinplots.py new file mode 100644 index 0000000000..c71f38ca27 --- /dev/null +++ b/examples/grouped_violinplots.py @@ -0,0 +1,15 @@ +""" +Grouped violinplots with split violins +====================================== + +""" +import seaborn as sns +sns.set(style="darkgrid", palette="pastel", color_codes=True) + +# Load the example tips dataset +tips = sns.load_dataset("tips") + +# Draw a nested violinplot and split the violins for easier comparison +sns.violinplot(x="day", y="total_bill", hue="sex", data=tips, + inner="quart", split=True, palette={"Male": "r", "Female": "y"}) +sns.despine(offset=10, trim=True) From 432c8fd2bd392db0b54cd88843db93d6665b54a5 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 14 May 2015 10:44:16 -0700 Subject: [PATCH 0014/1738] Updates to the general introductory documentation --- doc/index.rst | 25 ++++++++----- doc/installing.rst | 89 ++++++++++++++++++++++++++++++++++---------- doc/introduction.rst | 47 +++++++++++++++++++---- 3 files changed, 124 insertions(+), 37 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index 22ec8d0603..135683b85b 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -63,15 +63,22 @@ Seaborn: statistical data visualization
-Seaborn is a Python visualization library based on matplotlib. It provides a high-level interface for drawing attractive statistical graphics. - -For a brief introduction to the ideas behind the package, you can read the :ref:`introductory notes `. - -Much more detail can be found in the seaborn :ref:`tutorial `. You can also browse the :ref:`example gallery ` or :ref:`API reference ` to see the kind of tools that are available. - -To check out the code, report a bug, or contribute a new feature, please visit -the `github repository `_. You can also get -in touch on `twitter `_. +Seaborn is a Python visualization library based on matplotlib. It provides a +high-level interface for drawing attractive statistical graphics. + +For a brief introduction to the ideas behind the package, you can read the +:ref:`introductory notes `. More practical information is on the +:ref:`installation page `. You may also want to browse the +:ref:`example gallery ` to get a sense for what kind of tools +that are available. + +Much more detail can be found in the :ref:`API reference `, which +includes documentation for all the functions along with short examples of how +to use them. For a higher-level perspective, you can read through the seaborn +:ref:`tutorial `. + +To check out the code or report a bug please visit the `github repository +`_. General support issues are most at home on `StackOverflow `_, where there is a seaborn tag. .. raw:: html diff --git a/doc/installing.rst b/doc/installing.rst index 49606d9e5c..4484ec4876 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -3,18 +3,27 @@ Installing and getting started ------------------------------ -To install the released version of seaborn, you can use ``pip`` (i.e. ``pip install seaborn``). -Alternatively, you can use ``pip`` to install the development version, with the command ``pip install -git+git://github.com/mwaskom/seaborn.git#egg=seaborn``. Another option would be -to to clone the `github repository `_ and -install with ``pip install .`` from the source directory. Seaborn itself is pure -Python, so installation should be reasonably straightforward. +To install the released version of seaborn, you can use ``pip`` (i.e. ``pip +install seaborn``). It's also possible to install the released version using +``conda`` (i.e. ``conda install seaborn``), although this may lag behind the +version availible from PyPI. + +Alternatively, you can use ``pip`` to install the development version, with the +command ``pip install git+git://github.com/mwaskom/seaborn.git#egg=seaborn``. +Another option would be to to clone the `github repository +`_ and install with ``pip install .`` from +the source directory. Seaborn itself is pure Python, so installation should be +reasonably straightforward. + +When using the development version, you may want to refer to the `development +docs `_. Note that these +are not built automatically and may at times fall out of sync with the actual +master branch on github. + Dependencies ~~~~~~~~~~~~ -We recommend using seaborn with the `Anaconda distribution `_. - - Python 2.7 or 3.3+ Mandatory dependencies @@ -33,19 +42,40 @@ Recommended dependencies - `statsmodels `__ -- `patsy `__ +The `pip` installation script will attempt to download the mandatory +dependencies if they do not exist at install-time. + +I recommend using seaborn with the `Anaconda distribution +`_, as this makes it easy to manage +the main dependencies, which otherwise can be difficult to install. + +I attempt to keep seaborn working on the versions available through the stable +Debian channels. There may be cases where some more advanced features only +work with newer versions of these dependencies, although these should be +relatively rare. + +There are also some known bugs on older versions of matplotlib, so you should +in general try to use a modern version. For many use cases, though, older +matplotlibs will work fine. -Version-wise, we make an attempt to keep seaborn working on the stable Debian -channels. There may be cases where some more advanced features only work with -newer versions of these dependencies, although these should be rare. There are -also some known bugs on older versions of matplotlib, so you should in general -try to use a modern version, but for many cases older matplotlibs will work -fine. Seaborn is tested on the most recent versions offered through ``conda``. +Seaborn is tested on the most recent versions offered through ``conda``. -Import conventions -~~~~~~~~~~~~~~~~~~ -By convention, ``seaborn`` is abbreviated to ``sns`` on imports. +Importing seaborn +~~~~~~~~~~~~~~~~~ + +Seaborn will apply its default style parameters to the global matplotlib style +dictionary when you import it. This will change the look of all plots, +including those created by using matplotlib functions directly. To avoid this +behavior and use the default matplotlib aesthetics (along with any +customization in your `matplotlibrc`), you can import the `seaborn.apionly` +namespace. + +Seaborn has several other pre-packaged styles along with high-level :ref:`tools +` for managing them, so you should not limit yourself to the +default aesthetics. + +By convention, ``seaborn`` is abbreviated to ``sns`` on import. Testing ~~~~~~~ @@ -55,11 +85,30 @@ distribution. This runs the unit test suite (which can also be exercised separately by running ``nosetests``). It also runs the code in the example notebooks to smoke-test a broader and more realistic range of example usage. +The full set of tests requires an internet connection to download the example +datasets, but the unit tests should be able to run offline. + Bugs ~~~~ Please report any bugs you encounter through the github `issue tracker `_. It will be most helpful to -upload an IPython notebook that can reproduce the error in a `gist -`_ and link to that gist in the bug report. +include a reproducible example on one of the example datasets (accessed through +:func:`load_dataset`). It is difficult debug any issues without knowing the +versions of seaborn and matplotlib you are using, as well as what matplotlib +backend you are using to draw the plots, so please include those in your bug +report. + +Known issues +~~~~~~~~~~~~ + +There is a `bug `_ in the +matplotlib OSX backend that causes unavoidable problems with some of the +seaborn functions. If you encounter this, you will want to try a `different +backend `_. +An unfortunate consequence of how the matplotlib marker styles work is that +line-art markers (e.g. ``"+"``) or markers with ``facecolor`` set to ``"none"`` +will be invisible when the default seaborn style is in effect. This can be +changed by using a different ``markeredgewidth`` (aliased to ``mew``) either in +the function call or globally in the `rcParams`. diff --git a/doc/introduction.rst b/doc/introduction.rst index 58878f8796..d6680919c1 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -3,7 +3,13 @@ An introduction to seaborn ========================== -Seaborn is a library for making attractive and informative statistical graphics in Python. It is built on top of `matplotlib `_ and tightly integrated with the `PyData `_ stack, including support for `numpy `_ and `pandas `_ data structures and statistical routines from `scipy `_ and `statsmodels `_. +Seaborn is a library for making attractive and informative statistical graphics +in Python. It is built on top of `matplotlib `_ and +tightly integrated with the `PyData `_ stack, including +support for `numpy `_ and `pandas +`_ data structures and statistical routines from +`scipy `_ and `statsmodels +`_. Some of the features that seaborn offers are @@ -15,11 +21,36 @@ Some of the features that seaborn offers are - A function to plot :ref:`statistical timeseries ` data with flexible estimation and :ref:`representation ` of uncertainty around the estimate - High-level abstractions for structuring :ref:`grids of plots ` that let you easily build :ref:`complex ` visualizations -Seaborn aims to make visualization a central part of exploring and understanding data. The plotting functions operate on dataframes and arrays containing a whole dataset and internally perform the necessary aggregation and statistical model-fitting to produce informative plots. Seaborn's goals are similar to those of R's `ggplot `_, but it takes a different approach with an imperative and object-oriented style that tries to make it straightforward to construct sophisticated plots. If matplotlib "tries to make easy things easy and hard things possible", seaborn aims to make a well-defined set of hard things easy too. - -The plotting functions try to do something useful when called with a minimal set of arguments, and they expose a number of customizable options through additional parameters. Some of the functions plot directly into a matplotlib axes object, while others operate on an entire figure and produce plots with several panels. In the latter case, the plot is drawn using a Grid object that links the structure of the figure to the structure of the dataset in an abstract way. - -Because seaborn uses matplotlib, the graphics can be further tweaked using matplotlib tools and rendered with any of the matplotlib backends to generate publication-quality figures. Seaborn can also be used to target web-based graphics through the `mpld3 `_ and `Bokeh `_ libraries. - -For more detailed information and copious examples of the syntax and resulting plots, you can check out the :ref:`example gallery `, :ref:`tutorial ` or :ref:`API reference `. +Seaborn aims to make visualization a central part of exploring and +understanding data. The plotting functions operate on dataframes and arrays +containing a whole dataset and internally perform the necessary aggregation and +statistical model-fitting to produce informative plots. If matplotlib "tries to +make easy things easy and hard things possible", seaborn aims to make a +well-defined set of hard things easy too. + +The plotting functions try to do something useful when called with a minimal +set of arguments, and they expose a number of customizable options through +additional parameters. Some of the functions plot directly into a matplotlib +axes object, while others operate on an entire figure and produce plots with +several panels. In the latter case, the plot is drawn using a Grid object that +links the structure of the figure to the structure of the dataset in an +abstract way. + +Because seaborn uses matplotlib, the graphics can be further tweaked using +matplotlib tools and rendered with any of the matplotlib backends to generate +publication-quality figures. Seaborn can also be used to target web-based +graphics through the `mpld3 `_ and `Bokeh +`_ libraries. + +Seaborn should be thought of as a complement to matplotlib, not a replacement +for it. When using seaborn, it is likely that you will often invoke matplotlib +functions directly to draw simpler plots already available through the +``pyplot`` namespace. Further, while the seaborn functions aim to make plots +that are reasonably "production ready" (including extracting semantic +information from Pandas objects to add informative labels), full customization +of the figures will require a sophisticated understanding of matplotlib objects. + +For more detailed information and copious examples of the syntax and resulting +plots, you can check out the :ref:`example gallery `, +:ref:`tutorial ` or :ref:`API reference `. From 29e054dcf966826af443d1c6a78118e84689034c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 14 May 2015 22:19:30 -0700 Subject: [PATCH 0015/1738] Add API docs for distplot --- seaborn/distributions.py | 83 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index e2370474c6..89a13e8a91 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -34,13 +34,19 @@ def distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, hist_kws=None, kde_kws=None, rug_kws=None, fit_kws=None, color=None, vertical=False, norm_hist=False, axlabel=None, label=None, ax=None): - """Flexibly plot a distribution of observations. + """Flexibly plot a univariate distribution of observations. + + This function combines the matplotlib ``hist`` function (with automatic + calculation of a good default bin size) with the seaborn :func:`kdeplot` + and :func:`rugplot` functions. It can also fit ``scipy.stats`` + distributions and plot the estimated PDF over the data. Parameters ---------- - a : (squeezable to) 1d array - Observed data. + a : Series, 1d-array, or list. + Observed data. If this is a Series object with a ``name`` attribute, + the name will be used to label the data axis. bins : argument for matplotlib hist(), or None, optional Specification of hist bins, or None to use Freedman-Diaconis rule. hist : bool, optional @@ -74,6 +80,77 @@ def distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, ------- ax : matplotlib axis + See Also + -------- + kdeplot : Show a univariate or bivariate distribution with a kernel + density estimate. + rugplot : Draw small vertical lines to show each observation in a + distribution. + + Examples + -------- + + Show a default plot with a kernel density estimate and histogram with bin + size determined automatically with a reference rule: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns, numpy as np + >>> sns.set(rc={"figure.figsize": (8, 4)}); np.random.seed(0) + >>> x = np.random.randn(100) + >>> ax = sns.distplot(x) + + Use Pandas objects to get an informative axis label: + + .. plot:: + :context: close-figs + + >>> import pandas as pd + >>> x = pd.Series(x, name="x variable") + >>> ax = sns.distplot(x) + + Plot the distribution with a kenel density estimate and rug plot: + + .. plot:: + :context: close-figs + + >>> ax = sns.distplot(x, rug=True, hist=False) + + Plot the distribution with a histogram and maximum likelihood gaussian + distribution fit: + + .. plot:: + :context: close-figs + + >>> from scipy.stats import norm + >>> ax = sns.distplot(x, fit=norm, kde=False) + + Plot the distribution on the vertical axis: + + .. plot:: + :context: close-figs + + >>> ax = sns.distplot(x, vertical=True) + + Change the color of all the plot elements: + + .. plot:: + :context: close-figs + + >>> sns.set_color_codes() + >>> ax = sns.distplot(x, color="y") + + Pass specific parameters to the underlying plot functions: + + .. plot:: + :context: close-figs + + >>> ax = sns.distplot(x, rug=True, rug_kws={"color": "g"}, + ... kde_kws={"color": "k", "lw": 3, "label": "KDE"}, + ... hist_kws={"histtype": "step", "linewidth": 3, + ... "alpha": 1, "color": "g"}) + """ if ax is None: ax = plt.gca() From 96dbce1d92a95902623aa67c59fade84d78fef36 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 14 May 2015 22:20:04 -0700 Subject: [PATCH 0016/1738] Reorganize thumbnail plots on doc index page --- doc/index.rst | 33 +++++++++++++++++---------------- examples/grouped_violinplots.py | 9 +++++---- examples/horizontal_boxplot.py | 15 ++++++++++----- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index 135683b85b..d5af5de444 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -33,9 +33,9 @@ Seaborn: statistical data visualization - +
- +
@@ -43,19 +43,19 @@ Seaborn: statistical data visualization - +
- +
- +
- +
- +
- +
@@ -69,16 +69,17 @@ high-level interface for drawing attractive statistical graphics. For a brief introduction to the ideas behind the package, you can read the :ref:`introductory notes `. More practical information is on the :ref:`installation page `. You may also want to browse the -:ref:`example gallery ` to get a sense for what kind of tools -that are available. +:ref:`example gallery ` to get a sense for what kind of plots +you can make with seaborn. -Much more detail can be found in the :ref:`API reference `, which -includes documentation for all the functions along with short examples of how -to use them. For a higher-level perspective, you can read through the seaborn -:ref:`tutorial `. +For a higher-level perspective on the various tools that are included in the +library, you can read through the :ref:`tutorial `. Much more detail +about individual tools can be found in the :ref:`API reference `, +which includes documentation for all the functions along with short examples of +how to use them. -To check out the code or report a bug please visit the `github repository -`_. General support issues are most at home on `StackOverflow `_, where there is a seaborn tag. +To check out the code or report a bug, please visit the `github repository +`_. General support issues are most at home on `stackoverflow `_, where there is a seaborn tag. .. raw:: html diff --git a/examples/grouped_violinplots.py b/examples/grouped_violinplots.py index c71f38ca27..bb022df246 100644 --- a/examples/grouped_violinplots.py +++ b/examples/grouped_violinplots.py @@ -2,14 +2,15 @@ Grouped violinplots with split violins ====================================== +_thumb: .5, .47 """ import seaborn as sns -sns.set(style="darkgrid", palette="pastel", color_codes=True) +sns.set(style="whitegrid", palette="pastel", color_codes=True) # Load the example tips dataset tips = sns.load_dataset("tips") # Draw a nested violinplot and split the violins for easier comparison -sns.violinplot(x="day", y="total_bill", hue="sex", data=tips, - inner="quart", split=True, palette={"Male": "r", "Female": "y"}) -sns.despine(offset=10, trim=True) +sns.violinplot(x="day", y="total_bill", hue="sex", data=tips, split=True, + inner="quart", palette={"Male": "b", "Female": "y"}) +sns.despine(left=True) diff --git a/examples/horizontal_boxplot.py b/examples/horizontal_boxplot.py index 9f13159658..44995ddbf9 100644 --- a/examples/horizontal_boxplot.py +++ b/examples/horizontal_boxplot.py @@ -1,8 +1,8 @@ """ -Horizontal boxplot with log axis -================================ +Horizontal boxplot with observations +==================================== -_thumb: .6, .5 +_thumb: .7, .45 """ import numpy as np import seaborn as sns @@ -12,9 +12,14 @@ planets = sns.load_dataset("planets") # Plot the orbital period with horizontal boxes -ax = sns.boxplot(x="orbital_period", y="method", data=planets, +ax = sns.boxplot(x="distance", y="method", data=planets, whis=np.inf, color="c") +# Add in points to show each observation +sns.stripplot(x="distance", y="method", data=planets, + jitter=True, size=3, color=".3", linewidth=0) + + # Make the quantitative axis logarithmic ax.set_xscale("log") -sns.despine(left=True) +sns.despine(trim=True) From 2bdcc9f8a8e14c6240b7d4cf76da59f54822c252 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 16 May 2015 13:38:56 -0700 Subject: [PATCH 0017/1738] Add note on matplotlib inline backend weirdness --- doc/installing.rst | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/doc/installing.rst b/doc/installing.rst index 4484ec4876..cd2eaecd59 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -49,10 +49,10 @@ I recommend using seaborn with the `Anaconda distribution `_, as this makes it easy to manage the main dependencies, which otherwise can be difficult to install. -I attempt to keep seaborn working on the versions available through the stable -Debian channels. There may be cases where some more advanced features only -work with newer versions of these dependencies, although these should be -relatively rare. +I attempt to keep seaborn importable and generally functional on the versions +available through the stable Debian channels. There may be cases where some +more advanced features only work with newer versions of these dependencies, +although these should be relatively rare. There are also some known bugs on older versions of matplotlib, so you should in general try to use a modern version. For many use cases, though, older @@ -77,6 +77,7 @@ default aesthetics. By convention, ``seaborn`` is abbreviated to ``sns`` on import. + Testing ~~~~~~~ @@ -88,6 +89,7 @@ notebooks to smoke-test a broader and more realistic range of example usage. The full set of tests requires an internet connection to download the example datasets, but the unit tests should be able to run offline. + Bugs ~~~~ @@ -99,16 +101,25 @@ versions of seaborn and matplotlib you are using, as well as what matplotlib backend you are using to draw the plots, so please include those in your bug report. + Known issues ~~~~~~~~~~~~ There is a `bug `_ in the matplotlib OSX backend that causes unavoidable problems with some of the -seaborn functions. If you encounter this, you will want to try a `different -backend `_. +seaborn functions (particularly those that draw multi-panel figures). If you +encounter this, you will want to try a `different backend +`_. An unfortunate consequence of how the matplotlib marker styles work is that line-art markers (e.g. ``"+"``) or markers with ``facecolor`` set to ``"none"`` will be invisible when the default seaborn style is in effect. This can be changed by using a different ``markeredgewidth`` (aliased to ``mew``) either in the function call or globally in the `rcParams`. + +Some changes to the inline plotting backend in IPython 3.0 interfere with the +seaborn style (you'll see plots that appear to have a white axes background and +invisible spines). The solution to this is to call ``%matplotlib inline`` in a +cell *before* importing seaborn, and not to call ``%matplotlib inline`` after +seaborn is imported. I believe these changes have been reverted, so this should +stop being a problem with the release of IPython 3.2. From cebe1c3b72eb9e0fd4114d5664e269a73bdc06a1 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 16 May 2015 13:39:24 -0700 Subject: [PATCH 0018/1738] Use dataframe to show how semantic information is used --- examples/many_pairwise_correlations.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/many_pairwise_correlations.py b/examples/many_pairwise_correlations.py index 6517ca32e7..f43fc089be 100644 --- a/examples/many_pairwise_correlations.py +++ b/examples/many_pairwise_correlations.py @@ -4,7 +4,9 @@ _thumb: .3, .6 """ +from string import letters import numpy as np +import pandas as pd import seaborn as sns import matplotlib.pyplot as plt @@ -12,10 +14,11 @@ # Generate a large random dataset rs = np.random.RandomState(33) -d = rs.normal(size=(30, 100)) +d = pd.DataFrame(data=rs.normal(size=(100, 26)), + columns=list(letters[:26])) # Compute the correlation matrix -corr = np.corrcoef(d) +corr = d.corr() # Generate a mask for the upper triangle mask = np.zeros_like(corr, dtype=np.bool) From 1106bdefa4b971c57f8479c6fd12efe33045353a Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 16 May 2015 13:40:00 -0700 Subject: [PATCH 0019/1738] Reorder release notes to emphasize larger changes --- doc/releases/v0.6.0.txt | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/doc/releases/v0.6.0.txt b/doc/releases/v0.6.0.txt index 100b745c54..e7dee08a64 100644 --- a/doc/releases/v0.6.0.txt +++ b/doc/releases/v0.6.0.txt @@ -9,9 +9,9 @@ Additionally, the structure of the docs is changing in version 0.6. The API docs Changes and updates to categorical plots ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In version 0.6, the "categorical" plots have been unified with a common API. This new category of functions groups together plots that show the relationship between one numeric variable and one or two categorical variables. This includes plots that show distribution of the numeric variable in each bin (:func:`barplot`, :func:`pointplot`, and :func:`stripplot`) and plots that apply a statistical estimation within each bin (:func:`pointplot` and :func:`barplot`). +In version 0.6, the "categorical" plots have been unified with a common API. This new category of functions groups together plots that show the relationship between one numeric variable and one or two categorical variables. This includes plots that show distribution of the numeric variable in each bin (:func:`barplot`, :func:`pointplot`, and :func:`stripplot`) and plots that apply a statistical estimation within each bin (:func:`pointplot`, :func:`barplot`, and :func:`countplot`). -These functions now each accept the same formats of input data and can be invoked in the same way. The can plot using long- or wide-form data, and can be drawn vertically or horizontally. When long-form data is used, the orientation of the plots is inferred from the types of the input data. Additionally, all functions natively take a ``hue`` variable to add a second layer of categorization. +These functions now each accept the same formats of input data and can be invoked in the same way. They can plot using long- or wide-form data, and can be drawn vertically or horizontally. When long-form data is used, the orientation of the plots is inferred from the types of the input data. Additionally, all functions natively take a ``hue`` variable to add a second layer of categorization. With the (in some cases new) API, these functions can all be drawn correctly by :class:`FacetGrid`. However, :func:`factorplot` can also now create faceted verisons of any of these kinds of plots, so in most cases it will be unnecessary to use :class:`FacetGrid` directly.. By default, :func:`factorplot` draws a point plot, but this is controlled by the ``kind`` parameter. @@ -27,7 +27,7 @@ Here are details on what has changed in the process of unifying these APIs: Along with these API changes, the following changes/enhancements were made to the plotting functions: -- The default rules for ordering the categories has changed. Instead of automatically sorting the category levels, the plots now show the levels in the order the appear in the input data (i.e., the order given by ``Series.unique()``). Order can be specified when plotting with the ``order`` and ``hue_order`` parameters. Additionally, when variables are pandas objects with a "categorical" dtype, the category order is inferred from the data object. This change also affects :class:`FacetGrid` and :class:`PairGrid`. +- The default rules for ordering the categories has changed. Instead of automatically sorting the category levels, the plots now show the levels in the order they appear in the input data (i.e., the order given by ``Series.unique()``). Order can be specified when plotting with the ``order`` and ``hue_order`` parameters. Additionally, when variables are pandas objects with a "categorical" dtype, the category order is inferred from the data object. This change also affects :class:`FacetGrid` and :class:`PairGrid`. - Added the ``scale`` and ``scale_hue`` parameters to :func:`violinplot`. These control how the width of the violins are scaled. The default is ``area``, which is different from how the violins used to be drawn. Use ``scale='width'`` to get the old behavior. @@ -40,8 +40,19 @@ New plotting functions - Added the :func:`countplot` function, which uses a bar plot representation to show counts of variables in one or more categorical bins. This replaces the old approach of calling :func:`barplot` without a numeric variable. -Other additions -~~~~~~~~~~~~~~~ +Other additions and changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +- Added the :func:`set_color_codes` function and the ``color_codes`` argument to :func:`set` and :func:`set_palette`. This changes the interpretation of shorthand color codes (i.e. "b", "g", k", etc.) within matplotlib to use the values from one of the named seaborn palettes (i.e. "deep", "muted", etc.). That makes it easier to have a more uniform look when using matplotlib functions directly with seaborn imported. This could be disruptive to existing plots, so it does not happen by default. It is possible this could change in the future. + +- The :func:`color_palette` function no longer trims palettes that are longer than 6 colors when passed into it. + +- Added the ``as_hex`` method to color palette objects, to return a list of hex codes rather than rgb tuples. + +- Changed the default ``linewidths`` in :func:`heatmap` and :`clustermap` to 0 so that larger matrices plot correctly. This parameter still exists and can be used to get the old effect of lines demarcating each cell in the heatmap (the old default ``linewidths`` was 0.5). + +- :func:`heatmap` and :func:`clustermap` now automatically use a mask for missing values, which previously were shown with the "under" value of the colormap per default `plt.pcolormesh` behavior. - Added the ``seaborn.crayons`` dictionary and the :func:`crayon_palette` function to define colors from the 120 box (!) of `Crayola crayons `_. @@ -55,24 +66,14 @@ Other additions - Added a catch in :func:`distplot` when calculating a default number of bins. For highly skewed data it will now use 10 bins, where previously the reference rule would return "infinite" bins and cause an exception in matplotlib. -- :func:`heatmap` and :func:`clustermap` now automatically use a mask for missing values, which previously were shown with the "under" value of the colormap per default `plt.pcolormesh` behavior. +- Added a ceiling (50) to the default number of bins used for :func:`distplot` histograms. This will help avoid confusing errors with certain kinds of datasets that heavily violate the assumptions of the reference rule used to get a default number of bins. The ceiling is not applied when passing a specific number of bins. - The various property dictionaries that can be passed to ``plt.boxplot`` are now applied after the seaborn restyling to allow for full customizability. -- Added a ceiling (50) to the default number of bins used for :func:`distplot` histograms. This will help avoid confusing errors with certain kinds of datasets that heavily violate the assumptions of the reference rule used to get a default number of bins. The ceiling is not applied when passing a specific number of bins. - - Added a ``savefig`` method to :class:`JointGrid` that defaults to a tight bounding box to make it easier to save figures using this class. -- Changed the default ``linewidths`` in :func:`heatmap` and :`clustermap` to 0 so that larger matrices plot correctly. This parameter still exists and can be used to get the old effect of lines demarcating each cell in the heatmap (the old default ``linewidths`` was 0.5). - - You can now pass an integer to the ``xticklabels`` and ``yticklabels`` parameter of :func:`heatmap` (and, by extension, :func:`clustermap`). This will make the plot use the ticklabels inferred from the data, but only plot every ``n`` label, where ``n`` is the number you pass. This can help when visualizing larger matrices with some sensible ordering to the rows or columns of the dataframe. -- Added the :func:`set_color_codes` function and the ``color_codes`` argument to :func:`set` and :func:`set_palette`. This changes the interpretation of shorthand color codes (i.e. "b", "g", k", etc.) within matplotlib to use the values from one of the named seaborn palettes (i.e. "deep", "muted", etc.). That makes it easier to have a more uniform look when using matplotlib functions directly with seaborn imported. This could be disruptive to existing plots, so it does not happen by default. It is possible this could change in the future. - -- Added the ``as_hex`` method to color palette objects, to return a list of hex codes rather than rgb tuples. - -- The :func:`color_palette` function no longer trims palettes that are longer than 6 colors when passed into it. - - The :func:`load_dataset` function now caches datasets locally after downloading them, and uses the local copy on subsequent calls. Bug fixes @@ -85,3 +86,5 @@ Bug fixes - Fixed a bug in :meth:`FacetGrid.set_xticklabels` or :meth:`FacetGrid.set_yticklabels` when ``col_wrap`` is being used. - Fixed a bug in :class:`PairGrid` where the ``hue_order`` parameter was ignored. + +- Fixed two bugs in :func:`despine` that caused errors when trying to trim the spines on plots that had inverted axes or no ticks. From 091399d697d7401b15dd600160c4947ea164c324 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 16 May 2015 14:06:13 -0700 Subject: [PATCH 0020/1738] Add index page for tutorial subdocs --- doc/tutorial/index.rst | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 doc/tutorial/index.rst diff --git a/doc/tutorial/index.rst b/doc/tutorial/index.rst new file mode 100644 index 0000000000..ced7776efe --- /dev/null +++ b/doc/tutorial/index.rst @@ -0,0 +1,34 @@ +.. _tutorial: + +Seaborn tutorial +================ + +Style management +---------------- + +.. toctree:: + :maxdepth: 2 + + aesthetics + color_palettes + +Plotting functions +------------------ + +.. toctree:: + :maxdepth: 2 + + plotting_distributions + quantitative_linear_models + categorical_linear_models + dataset_exploration + timeseries_plots + +Structured grids +---------------- + +.. toctree:: + :maxdepth: 2 + + axis_grids + From 82642c9d15b79176f2df4dc83d7a3b52d738ca65 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 16 May 2015 14:06:24 -0700 Subject: [PATCH 0021/1738] Expand palette function docstrings --- seaborn/palettes.py | 369 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 343 insertions(+), 26 deletions(-) diff --git a/seaborn/palettes.py b/seaborn/palettes.py index 2176619c88..c2ad4881e6 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -212,8 +212,45 @@ def hls_palette(n_colors=6, h=.01, l=.6, s=.65): Returns ------- - palette : list of tuples - color palette + palette : seaborn color palette + List-like object of colors as RGB tuples. + + See Also + -------- + husl_palette : Make a palette using evently spaced circular hues in the + HUSL system. + + Examples + -------- + + Create a palette of 10 colors with the default parameters: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set() + >>> sns.palplot(sns.hls_palette(10)) + + Create a palette of 10 colors that begins at a different hue value: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.hls_palette(10, h=.5)) + + Create a palette of 10 colors that are darker than the default: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.hls_palette(10, l=.4)) + + Create a palette of 10 colors that are less saturated than the default: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.hls_palette(10, s=.4)) """ hues = np.linspace(0, 1, n_colors + 1)[:-1] @@ -243,8 +280,45 @@ def husl_palette(n_colors=6, h=.01, s=.9, l=.65): Returns ------- - palette : list of tuples - color palette + palette : seaborn color palette + List-like object of colors as RGB tuples. + + See Also + -------- + hls_palette : Make a palette using evently spaced circular hues in the + HSL system. + + Examples + -------- + + Create a palette of 10 colors with the default parameters: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set() + >>> sns.palplot(sns.husl_palette(10)) + + Create a palette of 10 colors that begins at a different hue value: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.husl_palette(10, h=.5)) + + Create a palette of 10 colors that are darker than the default: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.husl_palette(10, l=.4)) + + Create a palette of 10 colors that are less saturated than the default: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.husl_palette(10, s=.4)) """ hues = np.linspace(0, 1, n_colors + 1)[:-1] @@ -262,8 +336,10 @@ def mpl_palette(name, n_colors=6): Note that this handles the qualitative colorbrewer palettes properly, although if you ask for more colors than a particular - qualitative palette can provide you will fewer than you are - expecting. + qualitative palette can provide you will get fewer than you are + expecting. In contrast, asking for qualitative color brewer palettes + using :func:`color_palette` will return the expected number of colors, + but they will cycle. If you are using the IPython notebook, you can also use the function :func:`choose_colorbrewer_palette` to interactively select palettes. @@ -271,14 +347,48 @@ def mpl_palette(name, n_colors=6): Parameters ---------- name : string - name of the palette + Name of the palette. This should be a named matplotlib colormap. n_colors : int - number of colors in the palette + Number of discrete colors in the palette. Returns ------- - palette : list of tuples - palette colors in r, g, b format + palette or cmap : seaborn color palette or matplotlib colormap + List-like object of colors as RGB tuples, or colormap object that + can map continuous values to colors, depending on the value of the + ``as_cmap`` parameter. + + Examples + -------- + + Create a qualitative colorbrewer palette with 8 colors: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set() + >>> sns.palplot(sns.mpl_palette("Set2", 8)) + + Create a sequential colorbrewer palette: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.mpl_palette("Blues")) + + Create a diverging palette: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.mpl_palette("seismic", 8)) + + Create a "dark" sequential palette: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.mpl_palette("GnBu_d")) """ brewer_qual_pals = {"Accent": 8, "Dark2": 8, "Paired": 12, @@ -341,11 +451,50 @@ def dark_palette(color, n_colors=6, reverse=False, as_cmap=False, input="rgb"): Returns ------- - palette : list or colormap + palette or cmap : seaborn color palette or matplotlib colormap + List-like object of colors as RGB tuples, or colormap object that + can map continuous values to colors, depending on the value of the + ``as_cmap`` parameter. See Also -------- light_palette : Create a sequential palette with bright low values. + diverging_palette : Create a diverging palette with two colors. + + Examples + -------- + + Generate a palette from an HTML color: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set() + >>> sns.palplot(sns.dark_palette("purple")) + + Generate a palette that decreases in lightness: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.dark_palette("seagreen", reverse=True)) + + Generate a palette from an HUSL-space seed: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.dark_palette((260, 75, 60), input="husl")) + + Generate a colormap object: + + .. plot:: + :context: close-figs + + >>> from numpy import arange + >>> x = arange(25).reshape(5, 5) + >>> cmap = sns.dark_palette("#2ecc71", as_cmap=True) + >>> ax = sns.heatmap(x, cmap=cmap) """ color = _color_to_rgb(color, input) @@ -385,11 +534,50 @@ def light_palette(color, n_colors=6, reverse=False, as_cmap=False, Returns ------- - palette : list or colormap + palette or cmap : seaborn color palette or matplotlib colormap + List-like object of colors as RGB tuples, or colormap object that + can map continuous values to colors, depending on the value of the + ``as_cmap`` parameter. See Also -------- dark_palette : Create a sequential palette with dark low values. + diverging_palette : Create a diverging palette with two colors. + + Examples + -------- + + Generate a palette from an HTML color: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set() + >>> sns.palplot(sns.light_palette("purple")) + + Generate a palette that increases in lightness: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.light_palette("seagreen", reverse=True)) + + Generate a palette from an HUSL-space seed: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.light_palette((260, 75, 60), input="husl")) + + Generate a colormap object: + + .. plot:: + :context: close-figs + + >>> from numpy import arange + >>> x = arange(25).reshape(5, 5) + >>> cmap = sns.light_palette("#2ecc71", as_cmap=True) + >>> ax = sns.heatmap(x, cmap=cmap) """ color = _color_to_rgb(color, input) @@ -450,10 +638,53 @@ def diverging_palette(h_neg, h_pos, s=75, l=50, sep=10, n=6, center="light", Returns ------- - palette : array of r, g, b colors or colormap + palette or cmap : seaborn color palette or matplotlib colormap + List-like object of colors as RGB tuples, or colormap object that + can map continuous values to colors, depending on the value of the + ``as_cmap`` parameter. - """ + See Also + -------- + dark_palette : Create a sequential palette with dark values. + light_palette : Create a sequential palette with light values. + Examples + -------- + + Generate a blue-white-red palette: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set() + >>> sns.palplot(sns.diverging_palette(240, 10, n=9)) + + Generate a brighter green-white-purple palette: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.diverging_palette(150, 275, s=80, l=55, n=9)) + + Generate a blue-black-red palette: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.diverging_palette(250, 15, s=75, l=40, + ... n=9, center="dark")) + + Generate a colormap object: + + .. plot:: + :context: close-figs + + >>> from numpy import arange + >>> x = arange(25).reshape(5, 5) + >>> cmap = sns.diverging_palette(220, 20, sep=20, as_cmap=True) + >>> ax = sns.heatmap(x, cmap=cmap) + + """ palfunc = dark_palette if center == "dark" else light_palette neg = palfunc((h_neg, s, l), 128 - (sep / 2), reverse=True, input="husl") pos = palfunc((h_pos, s, l), 128 - (sep / 2), input="husl") @@ -469,16 +700,19 @@ def blend_palette(colors, n_colors=6, as_cmap=False, input="rgb"): Parameters ---------- - colors : sequence of matplotlib colors - hex, rgb-tuple, or html color name + colors : sequence of colors in various formats interpreted by ``input`` + hex code, html color name, or tuple in ``input`` space. n_colors : int, optional - number of colors in the palette + Number of colors in the palette. as_cmap : bool, optional - if True, return as a matplotlib colormap instead of list + If True, return as a matplotlib colormap instead of list. Returns ------- - palette : list or colormap + palette or cmap : seaborn color palette or matplotlib colormap + List-like object of colors as RGB tuples, or colormap object that + can map continuous values to colors, depending on the value of the + ``as_cmap`` parameter. """ colors = [_color_to_rgb(color, input) for color in colors] @@ -492,10 +726,25 @@ def blend_palette(colors, n_colors=6, as_cmap=False, input="rgb"): def xkcd_palette(colors): """Make a palette with color names from the xkcd color survey. - This is just a simple wrapper around the seaborn.xkcd_rbg dictionary. - See xkcd for the full list of colors: http://xkcd.com/color/rgb/ + This is just a simple wrapper around the ``seaborn.xkcd_rgb`` dictionary. + + Parameters + ---------- + colors : list of strings + List of keys in the ``seaborn.xkcd_rgb`` dictionary. + + Returns + ------- + palette : seaborn color palette + Returns the list of colors as RGB tuples in an object that behaves like + other seaborn color palettes. + + See Also + -------- + crayon_palette : Make a palette with Crayola crayon colors. + """ palette = [xkcd_rgb[name] for name in colors] return color_palette(palette, len(palette)) @@ -507,7 +756,22 @@ def crayon_palette(colors): Colors are taken from here: http://en.wikipedia.org/wiki/List_of_Crayola_crayon_colors - This is just a simple wrapper around the seaborn.crayons dictionary. + This is just a simple wrapper around the ``seaborn.crayons`` dictionary. + + Parameters + ---------- + colors : list of strings + List of keys in the ``seaborn.crayons`` dictionary. + + Returns + ------- + palette : seaborn color palette + Returns the list of colors as rgb tuples in an object that behaves like + other seaborn color palettes. + + See Also + -------- + xkcd_palette : Make a palette with named colors from the XKCD color survey. """ palette = [crayons[name] for name in colors] @@ -549,7 +813,10 @@ def cubehelix_palette(n_colors=6, start=0, rot=.4, gamma=1.0, hue=0.8, Returns ------- - palette : list or colormap + palette or cmap : seaborn color palette or matplotlib colormap + List-like object of colors as RGB tuples, or colormap object that + can map continuous values to colors, depending on the value of the + ``as_cmap`` parameter. See Also -------- @@ -564,6 +831,56 @@ def cubehelix_palette(n_colors=6, start=0, rot=.4, gamma=1.0, hue=0.8, intensity images". Bulletin of the Astromical Society of India, Vol. 39, p. 289-295. + Examples + -------- + + Generate the default palette: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set() + >>> sns.palplot(sns.cubehelix_palette()) + + Rotate backwards from the same starting location: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.cubehelix_palette(rot=-.4)) + + Use a different starting point and shorter rotation: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.cubehelix_palette(start=2.8, rot=.1)) + + Reverse the direction of the lightness ramp: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.cubehelix_palette(reverse=True)) + + Generate a colormap object: + + .. plot:: + :context: close-figs + + >>> from numpy import arange + >>> x = arange(25).reshape(5, 5) + >>> cmap = sns.cubehelix_palette(as_cmap=True) + >>> ax = sns.heatmap(x, cmap=cmap) + + Use the full lightness range: + + .. plot:: + :context: close-figs + + >>> cmap = sns.cubehelix_palette(dark=0, light=1, as_cmap=True) + >>> ax = sns.heatmap(x, cmap=cmap) + """ cdict = mpl._cm.cubehelix(gamma, start, rot, hue) cmap = mpl.colors.LinearSegmentedColormap("cubehelix", cdict) @@ -613,7 +930,7 @@ def set_color_codes(palette="deep"): >>> import matplotlib.pyplot as plt >>> import seaborn as sns; sns.set() >>> sns.set_color_codes() - >>> _ = plt.plot([0, 1, 2], [0, 2, 1], color="r") + >>> _ = plt.plot([0, 1], color="r") Use a different seaborn palette. @@ -621,8 +938,8 @@ def set_color_codes(palette="deep"): :context: close-figs >>> sns.set_color_codes("dark") - >>> _ = plt.plot([0, 1, 2], [0, 2, 1], color="k") - >>> _ = plt.plot([0, 1, 2], [1, 0, 2], color="m") + >>> _ = plt.plot([0, 1], color="g") + >>> _ = plt.plot([0, 2], color="m") """ if palette == "reset": From effdbfdf54adb483e51cd6f8cce904028a43e9df Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 17 May 2015 10:25:21 -0700 Subject: [PATCH 0022/1738] Expand distribution docstrings --- seaborn/categorical.py | 2 +- seaborn/distributions.py | 195 +++++++++++++++++++++++++++++++++++---- 2 files changed, 177 insertions(+), 20 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 863f4cfd69..d0b9c1ac64 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2789,7 +2789,7 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, Returns ------- - g : FacetGrid + g : :class:`FacetGrid` Returns the :class:`FacetGrid` object with the plot on it for further tweaking. diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 89a13e8a91..7308e8cc96 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -78,7 +78,8 @@ def distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, Returns ------- - ax : matplotlib axis + ax : matplotlib Axes + Returns the Axes object with the plot for further tweaking. See Also -------- @@ -429,18 +430,16 @@ def _scipy_bivariate_kde(x, y, bw, gridsize, cut, clip): def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", - bw="scott", gridsize=100, cut=3, clip=None, legend=True, ax=None, - cumulative=False, **kwargs): - """Fit and plot a univariate or bivarate kernel density estimate. + bw="scott", gridsize=100, cut=3, clip=None, legend=True, + cumulative=False, ax=None, **kwargs): + """Fit and plot a univariate or bivariate kernel density estimate. Parameters ---------- - data : 1d or 2d array-like - Input data. If two-dimensional, assumed to be shaped (n_unit x n_var), - and a bivariate contour plot will be drawn. + data : 1d array-like + Input data. data2: 1d array-like - Second input data. If provided `data` must be one-dimensional, and - a bivariate plot is produced. + Second input data. If present, a bivariate KDE will be estimated. shade : bool, optional If true, shade in the area under the KDE curve (or draw with filled contours when data is bivariate). @@ -461,16 +460,87 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", a pair of (low, high) bounds for bivariate plots. legend : bool, optoinal If True, add a legend or label the axes when possible. - ax : matplotlib axis, optional - Axis to plot on, otherwise uses current axis. cumulative : bool If draw, draw the cumulative distribution estimated by the kde. - kwargs : other keyword arguments for plot() + ax : matplotlib axis, optional + Axis to plot on, otherwise uses current axis. + kwargs : key, value pairings + Other keyword arguments are passed to ``plt.plot()`` or + ``plt.contour{f}`` depending on whether a univariate or bivariate + plot is being drawn. Returns ------- - ax : matplotlib axis - Axis with plot. + ax : matplotlib axes + Axes with plot. + + See Also + -------- + distplot: Flexibly plot a univariate distribution of observations. + jointplot: Plot a joint dataset with bivariate and marginal distributions. + + Examples + -------- + + Plot a basic univariate density: + + .. plot:: + :context: close-figs + + >>> import numpy as np; np.random.seed(10) + >>> import seaborn as sns; sns.set(color_codes=True) + >>> mean, cov = [0, 2], [(1, .5), (.5, 1)] + >>> x, y = np.random.multivariate_normal(mean, cov, size=50).T + >>> ax = sns.kdeplot(x) + + Shade under the density curve and use a different color: + + .. plot:: + :context: close-figs + + >>> ax = sns.kdeplot(x, shade=True, color="r") + + Plot a bivariate density: + + .. plot:: + :context: close-figs + + >>> ax = sns.kdeplot(x, y) + + Use filled contours with a specified colormap: + + .. plot:: + :context: close-figs + + >>> ax = sns.kdeplot(x, y, shade=True, cmap="PuBuGn") + + Use more contour levels and a different color palette: + + .. plot:: + :context: close-figs + + >>> ax = sns.kdeplot(x, y, n_levels=30, cmap="Purples_d") + + Use a narrower bandwith: + + .. plot:: + :context: close-figs + + >>> ax = sns.kdeplot(x, bw=.15) + + Plot the density on the vertical axis: + + .. plot:: + :context: close-figs + + >>> ax = sns.kdeplot(y, vertical=True) + + Limit the density curve within the range of the data: + + .. plot:: + :context: close-figs + + >>> ax = sns.kdeplot(x, cut=0) """ if ax is None: @@ -552,12 +622,17 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, joint_kws=None, marginal_kws=None, annot_kws=None): """Draw a plot of two variables with bivariate and univariate graphs. + This function provides a convenient interface to the :class:`JointGrid` + class, with several canned plot kinds. This is intended to be a fairly + lightweight wrapper; if you need more flexibility, you should use + :class:`JointGrid` directly. + Parameters ---------- x, y : strings or vectors - Data or names of variables in `data`. + Data or names of variables in ``data``. data : DataFrame, optional - DataFrame when `x` and `y` are variable names. + DataFrame when ``x`` and ``y`` are variable names. kind : { "scatter" | "reg" | "resid" | "kde" | "hex" }, optional Kind of plot to draw. stat_func : callable or None @@ -574,7 +649,7 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, space : numeric, optional Space between the joint and marginal axes dropna : bool, optional - If True, remove observations that are missing from `x` and `y`. + If True, remove observations that are missing from ``x`` and ``y``. {x, y}lim : two-tuples, optional Axis limits to set before plotting. {joint, marginal, annot}_kws : dicts @@ -582,14 +657,96 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, Returns ------- - grid : JointGrid - JointGrid object with the plot on it. + grid : :class:`JointGrid` + :class:`JointGrid` object with the plot on it. See Also -------- JointGrid : The Grid class used for drawing this plot. Use it directly if you need more flexibility. + Examples + -------- + + Draw a scatterplot with marginal histograms: + + .. plot:: + :context: close-figs + + >>> import numpy as np, pandas as pd; np.random.seed(0) + >>> import seaborn as sns; sns.set(style="white", color_codes=True) + >>> tips = sns.load_dataset("tips") + >>> g = sns.jointplot(x="total_bill", y="tip", data=tips) + + Add regression and kernel density fits: + + .. plot:: + :context: close-figs + + >>> g = sns.jointplot("total_bill", "tip", data=tips, kind="reg") + + Replace the scatterplot with a joint histogram using hexagonal bins: + + .. plot:: + :context: close-figs + + >>> g = sns.jointplot("total_bill", "tip", data=tips, kind="hex") + + Replace the scatterplots and histograms with density estimates and align + the marginal Axes tightly with the joint Axes: + + .. plot:: + :context: close-figs + + >>> iris = sns.load_dataset("iris") + >>> g = sns.jointplot("sepal_width", "petal_length", data=iris, + ... kind="kde", space=0, color="g") + + Use a different statistic for the annotation: + + .. plot:: + :context: close-figs + + >>> from scipy.stats import spearmanr + >>> g = sns.jointplot("size", "total_bill", data=tips, + ... stat_func=spearmanr, color="m") + + Draw a scatterplot, then add a joint density estimate: + + .. plot:: + :context: close-figs + + >>> g = (sns.jointplot("sepal_length", "sepal_width", + ... data=iris, color="k") + ... .plot_joint(sns.kdeplot, zorder=0, n_levels=6)) + + Pass vectors in directly without using Pandas, then name the axes: + + .. plot:: + :context: close-figs + + >>> x, y = np.random.randn(2, 300) + >>> g = (sns.jointplot(x, y, kind="hex", stat_func=None) + ... .set_axis_labels("x", "y")) + + Draw a smaller figure with more space devoted to the marginal plots: + + .. plot:: + :context: close-figs + + >>> g = sns.jointplot("total_bill", "tip", data=tips, + ... size=5, ratio=3, color="g") + + Pass keyword arguments down to the underlying plots: + + .. plot:: + :context: close-figs + + >>> g = sns.jointplot("petal_length", "sepal_length", data=iris, + ... joint_kws=dict(s=40, edgecolor="w", linewidth=1), + ... marginal_kws=dict(bins=15), + ... annot_kws=dict(stat="r")) + """ # Set up empty default kwarg dicts if joint_kws is None: From c2b778a91a17693a20fc6f062e09e78fbdaceea4 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 17 May 2015 10:57:17 -0700 Subject: [PATCH 0023/1738] Explicitly get cmap object to work on old matplotlibs --- seaborn/distributions.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 7308e8cc96..c3166cc5d9 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -7,6 +7,8 @@ import matplotlib.pyplot as plt import warnings +from six import string_types + try: import statsmodels.nonparametric.api as smnp _has_statsmodels = True @@ -338,7 +340,7 @@ def _scipy_univariate_kde(data, bw, gridsize, cut, clip): msg = ("Ignoring bandwidth choice, " "please upgrade scipy to use a different bandwidth.") warnings.warn(msg, UserWarning) - if isinstance(bw, str): + if isinstance(bw, string_types): bw = "scotts" if bw == "scott" else bw bw = getattr(kde, "%s_factor" % bw)() grid = _kde_support(data, bw, gridsize, cut, clip) @@ -349,7 +351,6 @@ def _scipy_univariate_kde(data, bw, gridsize, cut, clip): def _bivariate_kdeplot(x, y, filled, kernel, bw, gridsize, cut, clip, axlabel, ax, **kwargs): """Plot a joint KDE estimate as a bivariate contour plot.""" - # Determine the clipping if clip is None: clip = [(-np.inf, np.inf), (-np.inf, np.inf)] @@ -365,11 +366,14 @@ def _bivariate_kdeplot(x, y, filled, kernel, bw, gridsize, cut, clip, axlabel, # Plot the contours n_levels = kwargs.pop("n_levels", 10) cmap = kwargs.get("cmap", "BuGn" if filled else "BuGn_d") - if isinstance(cmap, str): + if isinstance(cmap, string_types): if cmap.endswith("_d"): pal = ["#333333"] pal.extend(color_palette(cmap.replace("_d", "_r"), 2)) cmap = blend_palette(pal, as_cmap=True) + else: + cmap = mpl.cm.get_cmap(cmap) + kwargs["cmap"] = cmap contour_func = ax.contourf if filled else ax.contour contour_func(xx, yy, z, n_levels, **kwargs) @@ -386,7 +390,7 @@ def _bivariate_kdeplot(x, y, filled, kernel, bw, gridsize, cut, clip, axlabel, def _statsmodels_bivariate_kde(x, y, bw, gridsize, cut, clip): """Compute a bivariate kde using statsmodels.""" - if isinstance(bw, str): + if isinstance(bw, string_types): bw_func = getattr(smnp.bandwidths, "bw_" + bw) x_bw = bw_func(x) y_bw = bw_func(y) @@ -412,7 +416,7 @@ def _scipy_bivariate_kde(x, y, bw, gridsize, cut, clip): data = np.c_[x, y] kde = stats.gaussian_kde(data.T) data_std = data.std(axis=0, ddof=1) - if isinstance(bw, str): + if isinstance(bw, string_types): bw = "scotts" if bw == "scott" else bw bw_x = getattr(kde, "%s_factor" % bw)() * data_std[0] bw_y = getattr(kde, "%s_factor" % bw)() * data_std[1] @@ -507,12 +511,12 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", >>> ax = sns.kdeplot(x, y) - Use filled contours with a specified colormap: + Use filled contours: .. plot:: :context: close-figs - >>> ax = sns.kdeplot(x, y, shade=True, cmap="PuBuGn") + >>> ax = sns.kdeplot(x, y, shade=True) Use more contour levels and a different color palette: From 38798a3a688caf837d8a76e00c48299ab02155da Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 17 May 2015 11:24:31 -0700 Subject: [PATCH 0024/1738] Use sqrt(n) rather than fixed bins when ref rule is invalid --- doc/releases/v0.6.0.txt | 2 +- seaborn/distributions.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/releases/v0.6.0.txt b/doc/releases/v0.6.0.txt index e7dee08a64..588a898f48 100644 --- a/doc/releases/v0.6.0.txt +++ b/doc/releases/v0.6.0.txt @@ -64,7 +64,7 @@ Other additions and changes - The interactive palette widgets now show a continuous colorbar, rather than a discrete palette, when `as_cmap` is True. -- Added a catch in :func:`distplot` when calculating a default number of bins. For highly skewed data it will now use 10 bins, where previously the reference rule would return "infinite" bins and cause an exception in matplotlib. +- Added a catch in :func:`distplot` when calculating a default number of bins. For highly skewed data it will now use sqrt(n) bins, where previously the reference rule would return "infinite" bins and cause an exception in matplotlib. - Added a ceiling (50) to the default number of bins used for :func:`distplot` histograms. This will help avoid confusing errors with certain kinds of datasets that heavily violate the assumptions of the reference rule used to get a default number of bins. The ceiling is not applied when passing a specific number of bins. diff --git a/seaborn/distributions.py b/seaborn/distributions.py index c3166cc5d9..0be210f84c 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -25,9 +25,9 @@ def _freedman_diaconis_bins(a): # From http://stats.stackexchange.com/questions/798/ a = np.asarray(a) h = 2 * iqr(a) / (len(a) ** (1 / 3)) - # fall back to 10 bins if iqr is 0 + # fall back to sqrt(a) bins if iqr is 0 if h == 0: - return 10. + return np.sqrt(a.size) else: return np.ceil((a.max() - a.min()) / h) From d44e0f78ba958e766ef4d3d1d8c116e1365d533f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 17 May 2015 14:28:49 -0700 Subject: [PATCH 0025/1738] Fix FacetGrid order with missing category levels --- seaborn/axisgrid.py | 72 +++++++++++++++++----------------- seaborn/tests/test_axisgrid.py | 13 ++++++ 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index b76c089dcd..28cfa22e75 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -236,9 +236,43 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, MPL_GRIDSPEC_VERSION = LooseVersion('1.4') OLD_MPL = LooseVersion(mpl.__version__) < MPL_GRIDSPEC_VERSION + # Determine the hue facet layer information + hue_var = hue + if hue is None: + hue_names = None + else: + hue_names = utils.categorical_order(data[hue], hue_order) + + colors = self._get_palette(data, hue, hue_order, palette) + + # Set up the lists of names for the row and column facet variables + if row is None: + row_names = [] + else: + row_names = utils.categorical_order(data[row], row_order) + + if col is None: + col_names = [] + else: + col_names = utils.categorical_order(data[col], col_order) + + # Additional dict of kwarg -> list of values for mapping the hue var + hue_kws = hue_kws if hue_kws is not None else {} + + # Make a boolean mask that is True anywhere there is an NA + # value in one of the faceting variables, but only if dropna is True + none_na = np.zeros(len(data), np.bool) + if dropna: + row_na = none_na if row is None else data[row].isnull() + col_na = none_na if col is None else data[col].isnull() + hue_na = none_na if hue is None else data[hue].isnull() + not_na = ~(row_na | col_na | hue_na) + else: + not_na = ~none_na + # Compute the grid shape - ncol = 1 if col is None else len(data[col].unique()) - nrow = 1 if row is None else len(data[row].unique()) + ncol = 1 if col is None else len(col_names) + nrow = 1 if row is None else len(row_names) self._n_facets = ncol * nrow self._col_wrap = col_wrap @@ -312,40 +346,6 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, label.set_visible(False) ax.yaxis.offsetText.set_visible(False) - # Determine the hue facet layer information - hue_var = hue - if hue is None: - hue_names = None - else: - hue_names = utils.categorical_order(data[hue], hue_order) - - colors = self._get_palette(data, hue, hue_order, palette) - - # Additional dict of kwarg -> list of values for mapping the hue var - hue_kws = hue_kws if hue_kws is not None else {} - - # Make a boolean mask that is True anywhere there is an NA - # value in one of the faceting variables, but only if dropna is True - none_na = np.zeros(len(data), np.bool) - if dropna: - row_na = none_na if row is None else data[row].isnull() - col_na = none_na if col is None else data[col].isnull() - hue_na = none_na if hue is None else data[hue].isnull() - not_na = ~(row_na | col_na | hue_na) - else: - not_na = ~none_na - - # Set up the lists of names for the row and column facet variables - if row is None: - row_names = [] - else: - row_names = utils.categorical_order(data[row], row_order) - - if col is None: - col_names = [] - else: - col_names = utils.categorical_order(data[col], col_order) - # Set up the class attributes # --------------------------- diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 86d411fa96..a69877f8f8 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -533,6 +533,7 @@ def test_data_orders(self): nt.assert_equal(g.row_names, list("abc")) nt.assert_equal(g.col_names, list("mn")) nt.assert_equal(g.hue_names, list("tuv")) + nt.assert_equal(g.axes.shape, (3, 2)) g = ag.FacetGrid(self.df, row="a", col="b", hue="c", row_order=list("bca"), @@ -542,6 +543,18 @@ def test_data_orders(self): nt.assert_equal(g.row_names, list("bca")) nt.assert_equal(g.col_names, list("nm")) nt.assert_equal(g.hue_names, list("vtu")) + nt.assert_equal(g.axes.shape, (3, 2)) + + g = ag.FacetGrid(self.df, row="a", col="b", hue="c", + row_order=list("bcda"), + col_order=list("nom"), + hue_order=list("qvtu")) + + nt.assert_equal(g.row_names, list("bcda")) + nt.assert_equal(g.col_names, list("nom")) + nt.assert_equal(g.hue_names, list("qvtu")) + nt.assert_equal(g.axes.shape, (4, 3)) + plt.close("all") def test_palette(self): From 3bb4fced369aacee4112140698219ee25d8f53c3 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 19 May 2015 16:31:59 -0700 Subject: [PATCH 0026/1738] Add new docstring and examples for regplot --- doc/_static/style.css | 4 + doc/api.rst | 1 - seaborn/axisgrid.py | 12 ++ seaborn/distributions.py | 4 +- seaborn/linearmodels.py | 423 ++++++++++++++++++++++++++++++--------- 5 files changed, 343 insertions(+), 101 deletions(-) diff --git a/doc/_static/style.css b/doc/_static/style.css index a94c8096ec..bc579f4e06 100644 --- a/doc/_static/style.css +++ b/doc/_static/style.css @@ -3,6 +3,10 @@ blockquote p { font-size: 14px !important; } +blockquote { + margin: 0 0 4px !important; +} + code { color: #49759c !important; background-color: #f3f5f9 !important; diff --git a/doc/api.rst b/doc/api.rst index bf3abacee8..54f972b81c 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -17,7 +17,6 @@ Regression plots interactplot residplot coefplot - corrplot Categorical plots ----------------- diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 28cfa22e75..2206933576 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -2,6 +2,7 @@ from itertools import product from distutils.version import LooseVersion import warnings +from textwrap import dedent import numpy as np import pandas as pd @@ -163,6 +164,17 @@ def _get_palette(self, data, hue, hue_order, palette): return palette +_facet_docs = dict( + + data=dedent("""\ + data : DataFrame + Tidy ("long-form") dataframe where each column is a variable and each + row is an observation.\ + """), + + ) + + class FacetGrid(Grid): """Subplot grid for plotting conditional relationships in a dataset.""" diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 0be210f84c..7297eb5d16 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -475,7 +475,7 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", Returns ------- - ax : matplotlib axes + ax : matplotlib Axes Axes with plot. See Also @@ -748,7 +748,7 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, >>> g = sns.jointplot("petal_length", "sepal_length", data=iris, ... joint_kws=dict(s=40, edgecolor="w", linewidth=1), - ... marginal_kws=dict(bins=15), + ... marginal_kws=dict(bins=15, rug=True), ... annot_kws=dict(stat="r")) """ diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index d4f29fafa2..aad6f3edd6 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -2,12 +2,15 @@ from __future__ import division import copy import itertools +from textwrap import dedent import numpy as np import pandas as pd from scipy.spatial import distance import matplotlib as mpl import matplotlib.pyplot as plt +import warnings + try: import statsmodels assert statsmodels @@ -21,7 +24,7 @@ from . import utils from . import algorithms as algo from .palettes import color_palette -from .axisgrid import FacetGrid, PairGrid +from .axisgrid import FacetGrid, PairGrid, _facet_docs from .distributions import kdeplot @@ -382,6 +385,139 @@ def lineplot(self, ax, kws): ax.set_xlim(*xlim) +_regression_docs = dict( + + model_api=dedent("""\ + There are a number of mutually exclusive options for estimating the + regression model: ``order``, ``logistic``, ``lowess``, ``robust``, and + ``logx``. See the parameter docs for more information on these options.\ + """), + + regplot_vs_lmplot=dedent("""\ + Understanding the difference between :func:`regplot` and :func:`lmplot` can + be a bit tricky. In fact, they are closely related, as :func:`lmplot` uses + :func:`regplot` internally and takes most of its parameters. However, + :func:`regplot` is an axes-level function, so it draws directly onto an + axes (either the currently active axes or the one provided by the ``ax`` + parameter), while :func:`lmplot` is a figure-level function and creates its + own figure, which is managed through a :class:`FacetGrid`. This has a few + consequences, namely that :func:`regplot` can happily coexist in a figure + with other kinds of plots and will follow the global matplotlib color + cycle. In contrast, :func:`lmplot` needs to occupy an entire figure, and + the size and color cycle are controlled through function parameters, + ignoring the global defaults.\ + """), + + x_estimator=dedent("""\ + x_estimator : callable that maps vector -> scalar, optional + Apply this function to each unique value of ``x`` and plot the + resulting estimate. This is useful when ``x`` is a discrete variable. + If ``x_ci`` is not ``None``, this estimate will be bootstrapped and a + confidence interval will be drawn.\ + """), + x_bins=dedent("""\ + x_bins : int or vector, optional + Bin the ``x`` variable into discrete bins and then estimate the central + tendency and a confidence interval. This binning only influences how + the scatterplot is drawn; the regression is still fit to the original + data. This parameter is interpreted either as the number of + evenly-sized (not necessary spaced) bins or the positions of the bin + centers. When this parameter is used, it implies that the default of + ``x_estimator`` is ``numpy.mean``.\ + """), + x_ci=dedent("""\ + x_ci : "ci", int in [0, 100] or None, optional + Size of the confidence interval used when plotting a central tendency + for discrete values of ``x``. If "ci", defer to the value of the``ci`` + parameter.\ + """), + scatter=dedent("""\ + scatter : bool, optional + If ``True``, draw a scatterplot with the underlying observations (or + the ``x_estimator`` values).\ + """), + fit_reg=dedent("""\ + fit_reg : bool, optional + If ``True``, estimate and plot a regression model relating the ``x`` + and ``y`` variables.\ + """), + ci=dedent("""\ + ci : int in [0, 100] or None, optional + Size of the confidence interval for the regression estimate. This will + be drawn using translucent bands around the regression line. The + confidence interval is estimated using a bootstrap; for large + datasets, it may be advisable to avoid that computation by setting + this parameter to None.\ + """), + n_boot=dedent("""\ + n_boot : int, optional + Number of bootstrap resamples used to estimate the ``ci``. The default + value attempts to balance time and stability; you may want to increase + this value for "final" versions of plots.\ + """), + units=dedent("""\ + units : variable name in ``data``, optional + If the ``x`` and ``y`` observations are nested within sampling units, + those can be specified here. This will be taken into account when + computing the confidence intervals by performing a multilevel bootstrap + that resamples both units and observations (within unit). This does not + otherwise influence how the regression is estimated or drawn.\ + """), + order=dedent("""\ + order : int, optional + If ``order`` is greater than 1, use ``numpy.polyfit`` to estimate a + polynomial regression.\ + """), + logistic=dedent("""\ + logistic : bool, optional + If ``True``, assume that ``y`` is a binary variable and use + ``statsmodels`` to estimate a logistic regression model. Note that this + is substantially more computationally intensive than linear regression, + so you may wish to decrease the number of bootstrap resamples + (``n_boot``) or set ``ci`` to None.\ + """), + lowess=dedent("""\ + lowess : bool, optional + If ``True``, use ``statsmodels`` to estimate a nonparametric lowess + model (locally weighted linear regression). Note that confidence + intervals cannot currently be drawn for this kind of model.\ + """), + robust=dedent("""\ + robust : bool, optional + If ``True``, use ``statsmodels`` to estimate a robust regression. This + will de-weight outliers. Note that this is substantially more + computationally intensive than standard linear regression, so you may + wish to decrease the number of bootstrap resamples (``n_boot``) or set + ``ci`` to None.\ + """), + logx=dedent("""\ + logx : bool, optional + If ``True``, estimate a linear regression of the form y ~ log(x), but + plot the scatterplot and regression model in the input space. Note that + ``x`` must be positive for this to work.\ + """), + xy_partial=dedent("""\ + {x,y}_partial : strings in ``data`` or matrices + Confounding variables to regress out of the ``x`` or ``y`` variables + before plotting.\ + """), + truncate=dedent("""\ + truncate : bool, optional + By default, the regression line is drawn to fill the x axis limits + after the scatterplot is drawn. If ``truncate`` is ``True``, it will + instead by bounded by the data limits.\ + """), + xy_jitter=dedent("""\ + {x,y}_jitter : floats, optional + Add uniform random noise of this size to either the ``x`` or ``y`` + variables. The noise is added to a copy of the data after fitting the + regression, and only influences the look of the scatterplot. This can + be helpful when plotting variables that take discrete values.\ + """), + ) +_regression_docs.update(_facet_docs) + + def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, col_wrap=None, size=5, aspect=1, markers="o", sharex=True, sharey=True, hue_order=None, col_order=None, row_order=None, @@ -486,107 +622,14 @@ def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, return facets -def regplot(x, y, data=None, x_estimator=None, x_bins=None, x_ci=95, +def regplot(x, y, data=None, x_estimator=None, x_bins=None, x_ci="ci", scatter=True, fit_reg=True, ci=95, n_boot=1000, units=None, order=1, logistic=False, lowess=False, robust=False, logx=False, x_partial=None, y_partial=None, truncate=False, dropna=True, x_jitter=None, y_jitter=None, - xlabel=None, ylabel=None, label=None, - color=None, marker="o", scatter_kws=None, line_kws=None, - ax=None): - """Draw a scatter plot between x and y with a regression line. - - Parameters - ---------- - x : vector or string - Data or column name in `data` for the predictor variable. - y : vector or string - Data or column name in `data` for the response variable. - data : DataFrame, optional - DataFrame to use if `x` and `y` are column names. - x_estimator : function that aggregates a vector into one value, optional - When `x` is a discrete variable, apply this estimator to the data - at each value and plot the data as a series of point estimates and - confidence intervals rather than a scatter plot. - x_bins : int or vector, optional - When `x` is a continuous variable, use the values in this vector (or - a vector of evenly spaced values with this length) to discretize the - data by assigning each point to the closest bin value. This applies - only to the plot; the regression is fit to the original data. This - implies that `x_estimator` is numpy.mean if not otherwise provided. - x_ci: int between 0 and 100, optional - Confidence interval to compute and draw around the point estimates - when `x` is treated as a discrete variable. - scatter : boolean, optional - Draw the scatter plot or point estimates with CIs representing the - observed data. - fit_reg : boolean, optional - If False, don't fit a regression; just draw the scatterplot. - ci : int between 0 and 100 or None, optional - Confidence interval to compute for regression estimate, which is drawn - as translucent bands around the regression line. - n_boot : int, optional - Number of bootstrap resamples used to compute the confidence intervals. - units : vector or string - Data or column name in `data` with ids for sampling units, so that the - bootstrap is performed by resampling units and then observations within - units for more accurate confidence intervals when data have repeated - measures. - order : int, optional - Order of the polynomial to fit. Use order > 1 to explore higher-order - trends in the relationship. - logistic : boolean, optional - Fit a logistic regression model. This requires `y` to be dichotomous - with values of either 0 or 1. - lowess : boolean, optional - Plot a lowess model (locally weighted nonparametric regression). - robust : boolean, optional - Fit a robust linear regression, which may be useful when the data - appear to have outliers. - logx : boolean, optional - Fit the regression in log(x) space. - {x, y}_partial : matrix or string(s) , optional - Matrix with same first dimension as `x`, or column name(s) in `data`. - These variables are treated as confounding and are removed from - the `x` or `y` variables before plotting. - truncate : boolean, optional - If True, truncate the regression estimate at the minimum and maximum - values of the `x` variable. - dropna : boolean, optional - Remove observations that are NA in at least one of the variables. - {x, y}_jitter : floats, optional - Add uniform random noise from within this range (in data coordinates) - to each datapoint in the x and/or y direction. This can be helpful when - plotting discrete values. - label : string, optional - Label to use for the regression line, or for the scatterplot if not - fitting a regression. - color : matplotlib color, optional - Color to use for all elements of the plot. Can set the scatter and - regression colors separately using the `kws` dictionaries. If not - provided, the current color in the axis cycle is used. - marker : matplotlib marker code, optional - Marker to use for the scatterplot points. - {scatter, line}_kws : dictionaries, optional - Additional keyword arguments passed to scatter() and plot() for drawing - the components of the plot. - ax : matplotlib axis, optional - Plot into this axis, otherwise grab the current axis or make a new - one if not existing. - - Returns - ------- - ax: matplotlib axes - Axes with the regression plot. + label=None, color=None, marker="o", + scatter_kws=None, line_kws=None, ax=None): - See Also - -------- - lmplot : Combine regplot and a FacetGrid. - residplot : Calculate and plot the residuals of a linear model. - jointplot (with kind="reg"): Draw a regplot with univariate marginal - distrbutions. - - """ plotter = _RegressionPlotter(x, y, data, x_estimator, x_bins, x_ci, scatter, fit_reg, ci, n_boot, units, order, logistic, lowess, robust, logx, @@ -602,6 +645,174 @@ def regplot(x, y, data=None, x_estimator=None, x_bins=None, x_ci=95, plotter.plot(ax, scatter_kws, line_kws) return ax +regplot.__doc__ = dedent("""\ + Plot data and a linear regression model fit. + + {model_api} + + Parameters + ---------- + x, y: string, series, or vector array + Input variables. If strings, these should correspond with column names + in ``data``. When pandas objects are used, axes will be labeled with + the series name. + {data} + {x_estimator} + {x_bins} + {x_ci} + {scatter} + {fit_reg} + {ci} + {n_boot} + {units} + {order} + {logistic} + {lowess} + {robust} + {logx} + {xy_partial} + {truncate} + {xy_jitter} + label : string + Label to apply to ether the scatterplot or regression line (if + ``scatter`` is ``False``) for use in a legend. + color : matplotlib color + Color to apply to all plot elements; will be superseded by colors + passed in ``scatter_kws`` or ``line_kws``. + marker : matplotlib marker code + Marker to use for the scatterplot glyphs. + {{scatter,line}}_kws : dictionaries + Additional keyword arguments to pass to ``plt.scatter`` and + ``plt.plot``. + ax : matplotlib Axes + The Axes object containing the plot. + + Returns + ------- + + See Also + -------- + lmplot : Combine :func:`regplot` and :class:`FacetGrid` to plot multiple + linear relationships in a dataset. + jointplot : Combine :func:`regplot` and :class:`JointGrid` (when used with + ``kind="reg"``). + pairplot : Combine :func:`pairplot` and :class:`PairGrid` (when used with + ``kind="reg"``). + residplot : Plot the residuals of a linear regression model. + interactplot : Plot a two-way interaction between continuous variables + + Notes + ----- + + {regplot_vs_lmplot} + + + It's also easy to combine combine :func:`regplot` and :class:`JointGrid` or + :class:`PairGrid` through the :func:`jointplot` and :func:`pairplot` + functions, although these do not directly accept all of :func:`regplot`'s + parameters. + + Examples + -------- + + Plot the relationship between two variables in a DataFrame: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set(color_codes=True) + >>> tips = sns.load_dataset("tips") + >>> ax = sns.regplot(x="total_bill", y="tip", data=tips) + + Plot with two variables defined as numpy arrays; use a different color: + + .. plot:: + :context: close-figs + + >>> import numpy as np; np.random.seed(8) + >>> mean, cov = [4, 6], [(1.5, .7), (.7, 1)] + >>> x, y = np.random.multivariate_normal(mean, cov, 80).T + >>> ax = sns.regplot(x=x, y=y, color="g") + + Plot with two variables defined as pandas Series; use a different marker: + + .. plot:: + :context: close-figs + + >>> import pandas as pd + >>> x, y = pd.Series(x, name="x_var"), pd.Series(y, name="y_var") + >>> ax = sns.regplot(x=x, y=y, marker="+") + + Use a 68% confidence interval, which corresponds with the standard error + of the estimate: + + .. plot:: + :context: close-figs + + >>> ax = sns.regplot(x=x, y=y, ci=68) + + Plot with a discrete ``x`` variable and add some jitter: + + .. plot:: + :context: close-figs + + >>> ax = sns.regplot(x="size", y="total_bill", data=tips, x_jitter=.1) + + Plot with a discrete ``x`` variable showing means and confidence intervals + for unique values: + + .. plot:: + :context: close-figs + + >>> ax = sns.regplot(x="size", y="total_bill", data=tips, + ... x_estimator=np.mean) + + Plot with a continuous variable divided into discrete bins: + + .. plot:: + :context: close-figs + + >>> ax = sns.regplot(x=x, y=y, x_bins=4) + + Fit a higher-order polynomial regression and truncate the model prediction: + + .. plot:: + :context: close-figs + + >>> ans = sns.load_dataset("anscombe") + >>> ax = sns.regplot(x="x", y="y", data=ans.loc[ans.dataset == "II"], + ... scatter_kws={{"s": 80}}, + ... order=2, ci=None, truncate=True) + + Fit a robust regression and don't plot a confidence interval: + + .. plot:: + :context: close-figs + + >>> ax = sns.regplot(x="x", y="y", data=ans.loc[ans.dataset == "III"], + ... scatter_kws={{"s": 80}}, + ... robust=True, ci=None) + + Fit a logistic regression; jitter the y variable and use fewer bootstrap + iterations: + + .. plot:: + :context: close-figs + + >>> tips["big_tip"] = (tips.tip / tips.total_bill) > .175 + >>> ax = sns.regplot(x="total_bill", y="big_tip", data=tips, + ... logistic=True, n_boot=500, y_jitter=.03) + + Fit the regression model using log(x) and truncate the model prediction: + + .. plot:: + :context: close-figs + + >>> ax = sns.regplot(x="size", y="total_bill", data=tips, + ... x_estimator=np.mean, logx=True, truncate=True) + + """).format(**_regression_docs) + def residplot(x, y, data=None, lowess=False, x_partial=None, y_partial=None, order=1, robust=False, dropna=True, label=None, color=None, @@ -903,6 +1114,9 @@ def corrplot(data, names=None, annot=True, sig_stars=True, sig_tail="both", diag_names=True, method=None, ax=None, **kwargs): """Plot a correlation matrix with colormap and r values. + NOTE: This function is deprecated in favor of :func:`heatmap` and will + be removed in a forthcoming release. + Parameters ---------- data : Dataframe or nobs x nvars array @@ -939,6 +1153,10 @@ def corrplot(data, names=None, annot=True, sig_stars=True, sig_tail="both", Axis object with plot. """ + warnings.warn(("The `corrplot` function has been deprecated in favor " + "of `heatmap` and will be removed in a forthcoming " + "release. Please update your code.")) + if not isinstance(data, pd.DataFrame): if names is None: names = ["var_%d" % i for i in range(data.shape[1])] @@ -995,7 +1213,16 @@ def corrplot(data, names=None, annot=True, sig_stars=True, sig_tail="both", def symmatplot(mat, p_mat=None, names=None, cmap="Greys", cmap_range=None, cbar=True, annot=True, diag_names=True, ax=None, **kwargs): - """Plot a symmetric matrix with colormap and statistic values.""" + """Plot a symmetric matrix with colormap and statistic values. + + NOTE: This function is deprecated in favor of :func:`heatmap` and will + be removed in a forthcoming release. + + """ + warnings.warn(("The `symmatplot` function has been deprecated in favor " + "of `heatmap` and will be removed in a forthcoming " + "release. Please update your code.")) + if ax is None: ax = plt.gca() From abef7e7a6cc5b812ae165566939c47879a1b337f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 19 May 2015 16:32:47 -0700 Subject: [PATCH 0027/1738] Note deprecation of corrplot --- doc/releases/v0.6.0.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/releases/v0.6.0.txt b/doc/releases/v0.6.0.txt index 588a898f48..b492c98c1f 100644 --- a/doc/releases/v0.6.0.txt +++ b/doc/releases/v0.6.0.txt @@ -43,6 +43,7 @@ New plotting functions Other additions and changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- The :func:`corrplot` and underlying :func:`symmatplot` have been deprecated in favor of :func:`heatmap`, which is much more flexible and robust. These two functions are still available in version 0.6, but they will be removed in a future version. - Added the :func:`set_color_codes` function and the ``color_codes`` argument to :func:`set` and :func:`set_palette`. This changes the interpretation of shorthand color codes (i.e. "b", "g", k", etc.) within matplotlib to use the values from one of the named seaborn palettes (i.e. "deep", "muted", etc.). That makes it easier to have a more uniform look when using matplotlib functions directly with seaborn imported. This could be disruptive to existing plots, so it does not happen by default. It is possible this could change in the future. From 269cb12bc897cfe3053b16a2b66ca1e906b8c79e Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 19 May 2015 16:32:52 -0700 Subject: [PATCH 0028/1738] Run tutorial notebooks --- doc/tutorial/aesthetics.ipynb | 2 +- doc/tutorial/color_palettes.ipynb | 2 +- doc/tutorial/index.rst | 34 ------------------------------- 3 files changed, 2 insertions(+), 36 deletions(-) delete mode 100644 doc/tutorial/index.rst diff --git a/doc/tutorial/aesthetics.ipynb b/doc/tutorial/aesthetics.ipynb index 789afdd5f0..a5454846df 100644 --- a/doc/tutorial/aesthetics.ipynb +++ b/doc/tutorial/aesthetics.ipynb @@ -465,4 +465,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index 28f55eef61..4b347e08b9 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -778,4 +778,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file diff --git a/doc/tutorial/index.rst b/doc/tutorial/index.rst deleted file mode 100644 index ced7776efe..0000000000 --- a/doc/tutorial/index.rst +++ /dev/null @@ -1,34 +0,0 @@ -.. _tutorial: - -Seaborn tutorial -================ - -Style management ----------------- - -.. toctree:: - :maxdepth: 2 - - aesthetics - color_palettes - -Plotting functions ------------------- - -.. toctree:: - :maxdepth: 2 - - plotting_distributions - quantitative_linear_models - categorical_linear_models - dataset_exploration - timeseries_plots - -Structured grids ----------------- - -.. toctree:: - :maxdepth: 2 - - axis_grids - From 45a14d97c3070915252a5e9771149face13bca5f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 21 May 2015 19:17:05 -0700 Subject: [PATCH 0029/1738] Fix typos Closes #573 --- seaborn/distributions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 7297eb5d16..f5a0eed015 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -445,7 +445,7 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", data2: 1d array-like Second input data. If present, a bivariate KDE will be estimated. shade : bool, optional - If true, shade in the area under the KDE curve (or draw with filled + If True, shade in the area under the KDE curve (or draw with filled contours when data is bivariate). vertical : bool If True, density is on x-axis. @@ -462,10 +462,10 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", clip : pair of scalars, or pair of pair of scalars, optional Lower and upper bounds for datapoints used to fit KDE. Can provide a pair of (low, high) bounds for bivariate plots. - legend : bool, optoinal + legend : bool, optinal If True, add a legend or label the axes when possible. cumulative : bool - If draw, draw the cumulative distribution estimated by the kde. + If True, draw the cumulative distribution estimated by the kde. ax : matplotlib axis, optional Axis to plot on, otherwise uses current axis. kwargs : key, value pairings From 57c4c5446354e9f134f384b551add9490d92f2ef Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 23 May 2015 18:10:37 -0700 Subject: [PATCH 0030/1738] Enhance lmplot function signature and API docs --- seaborn/axisgrid.py | 39 +++++- seaborn/categorical.py | 28 ++--- seaborn/linearmodels.py | 273 +++++++++++++++++++++++++++++----------- 3 files changed, 245 insertions(+), 95 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 2206933576..b0671624e5 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -171,12 +171,47 @@ def _get_palette(self, data, hue, hue_order, palette): Tidy ("long-form") dataframe where each column is a variable and each row is an observation.\ """), - + col_wrap=dedent("""\ + col_wrap : int, optional + "Wrap" the column variable at this width, so that the column facets + span multiple rows. Incompatible with a ``row`` facet.\ + """), + share_xy=dedent("""\ + share_{x,y} : bool, optional + If true, the facets will share y axes across columns and/or x axes + across rows.\ + """), + size=dedent("""\ + size : scalar, optional + Height (in inches) of each facet. See also: ``aspect``.\ + """), + aspect=dedent("""\ + aspect : scalar, optional + Aspect ratio of each facet, so that ``aspect * size`` gives the width + of each facet in inches.\ + """), + palette=dedent("""\ + palette : seaborn color palette or dict, optional + Colors to use for the different levels of the ``hue`` variable. Should + be something that can be interpreted by :func:`color_palette`, or a + dictionary mapping hue levels to matplotlib colors.\ + """), + legend_out=dedent("""\ + legend_out : bool, optional + If ``True``, the figure size will be extended, and the legend will be + drawn outside the plot on the center right.\ + """), + margin_titles=dedent("""\ + margin_titles : bool, optional + If ``True``, the titles for the row variable are drawn to the right of + the last column. This option is experimental and may not work in all + cases.\ + """), ) class FacetGrid(Grid): - """Subplot grid for plotting conditional relationships in a dataset.""" + """Subplot grid for plotting conditional relationships.""" def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, sharex=True, sharey=True, size=3, aspect=1, palette=None, diff --git a/seaborn/categorical.py b/seaborn/categorical.py index d0b9c1ac64..812ac3ff05 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -16,7 +16,7 @@ from .utils import desaturate, iqr, categorical_order from .algorithms import bootstrap from .palettes import color_palette, husl_palette, light_palette -from .axisgrid import FacetGrid +from .axisgrid import FacetGrid, _facet_docs class _CategoricalPlotter(object): @@ -1555,6 +1555,8 @@ def plot(self, ax): """), ) +_categorical_docs.update(_facet_docs) + def boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, orient=None, color=None, palette=None, saturation=.75, @@ -2751,10 +2753,7 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, {long_form_data} row, col : names of variables in ``data``, optional Categorical variables that will determine the faceting of the grid. - col_wrap : int, optional - "Wrap" the column facets at this number so that they occupy multiple - rows. Can be useful when using a variable with a large number of - levels. Cannot be used with a ``row`` variable. + {col_wrap} {stat_api_params} {order_vars} row_order, col_order : lists of strings, optional @@ -2762,25 +2761,16 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, orders are inferred from the data objects. kind : {{``point``, ``bar``, ``count``, ``box``, ``violin``, ``strip``}} The kind of plot to draw. - size : float, optional - The size (height) of each facet, in inches. - aspect : float, optional - The aspect ratio of the plot, ``size * aspect`` gives the width of each - facet, in inches. + {size} + {aspect} {orient} {color} {palette} legend : bool, optional If ``True`` and there is a ``hue`` variable, draw a legend on the plot. - legend_out : bool, optional - If ``True``, draw the plot outside of the plot axes. - sharex, sharey : bool, optional - If ``True``, the axeas are shared across the rows and columns of the - grid. - margin_titles : bool, optional - If ``True``, the titles for the row variable are drawn to the right of - the last column. This option is experimental and may not work in all - cases. + {legend_out} + {share_xy} + {margin_titles} facet_kws : dict, optional Dictionary of other keyword arguments to pass to :class:`FacetGrid`. kwargs : key, value pairings diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index aad6f3edd6..f74f98b8ab 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -69,12 +69,7 @@ class _RegressionPlotter(_LinearPlotter): """Plotter for numeric independent variables with regression model. This does the computations and drawing for the `regplot` function, and - is thus also used indirectly by `lmplot`. It is generally similar to - the `_DiscretePlotter`, but it's intended for use when the independent - variable is numeric (continuous or discrete), and its primary advantage - is that a regression model can be fit to the data and visualized, allowing - extrapolations beyond the observed datapoints. - + is thus also used indirectly by `lmplot`. """ def __init__(self, x, y, data=None, x_estimator=None, x_bins=None, x_ci="ci", scatter=True, fit_reg=True, ci=95, n_boot=1000, @@ -514,6 +509,11 @@ def lineplot(self, ax, kws): regression, and only influences the look of the scatterplot. This can be helpful when plotting variables that take discrete values.\ """), + scatter_line_kws=dedent("""\ + {scatter,line}_kws : dictionaries + Additional keyword arguments to pass to ``plt.scatter`` and + ``plt.plot``.\ + """), ) _regression_docs.update(_facet_docs) @@ -521,66 +521,13 @@ def lineplot(self, ax, kws): def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, col_wrap=None, size=5, aspect=1, markers="o", sharex=True, sharey=True, hue_order=None, col_order=None, row_order=None, - dropna=True, legend=True, legend_out=True, **kwargs): - """Plot a data and a regression model fit onto a FacetGrid. - - Parameters - ---------- - x, y : strings - Column names in ``data``. - data : DataFrame - Long-form (tidy) dataframe with variables in columns and observations - in rows. - hue, col, row : strings, optional - Variable names to facet on the hue, col, or row dimensions (see - :class:`FacetGrid` docs for more information). - palette : seaborn palette or dict, optional - Color palette if using a `hue` facet. Should be something that - seaborn.color_palette can read, or a dictionary mapping values of the - hue variable to matplotlib colors. - col_wrap : int, optional - Wrap the column variable at this width. Incompatible with `row`. - size : scalar, optional - Height (in inches) of each facet. - aspect : scalar, optional - Aspect * size gives the width (in inches) of each facet. - markers : single matplotlib marker code or list, optional - Either the marker to use for all datapoints or a list of markers with - a length the same as the number of levels in the hue variable so that - differently colored points will also have different scatterplot - markers. - share{x, y}: booleans, optional - Lock the limits of the vertical and horizontal axes across the - facets. - {hue, col, row}_order: sequence of strings, optional - Order to plot the values in the faceting variables in, otherwise - sorts the unique values. - dropna : boolean, optional - Drop missing values from the data before plotting. - legend : boolean, optional - Draw a legend for the data when using a `hue` variable. - legend_out: boolean, optional - Draw the legend outside the grid of plots. - kwargs : key, value pairs - Other keyword arguments are pasted to :func:`regplot` - - Returns - ------- - facets : FacetGrid - Returns the :class:`FacetGrid` instance with the plot on it - for further tweaking. - - See Also - -------- - regplot : Axes-level function for plotting linear regressions. + legend=True, legend_out=True, x_estimator=None, x_bins=None, + x_ci="ci", scatter=True, fit_reg=True, ci=95, n_boot=1000, + units=None, order=1, logistic=False, lowess=False, robust=False, + logx=False, x_partial=None, y_partial=None, truncate=False, + x_jitter=None, y_jitter=None, scatter_kws=None, line_kws=None): - """ # Reduce the dataframe to only needed columns - # Otherwise when dropna is True we could lose data because it is missing - # in a column that isn't relevant to this plot - units = kwargs.get("units", None) - x_partial = kwargs.get("x_partial", None) - y_partial = kwargs.get("y_partial", None) need_cols = [x, y, hue, col, row, units, x_partial, y_partial] cols = np.unique([a for a in need_cols if a is not None]).tolist() data = data[cols] @@ -588,9 +535,9 @@ def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, # Initialize the grid facets = FacetGrid(data, row, col, hue, palette=palette, row_order=row_order, col_order=col_order, - hue_order=hue_order, dropna=dropna, - size=size, aspect=aspect, col_wrap=col_wrap, - sharex=sharex, sharey=sharey, legend_out=legend_out) + hue_order=hue_order, size=size, aspect=aspect, + col_wrap=col_wrap, sharex=sharex, sharey=sharey, + legend_out=legend_out) # Add the markers here as FacetGrid has figured out how many levels of the # hue variable are needed and we don't want to duplicate that process @@ -614,7 +561,15 @@ def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, scatter.remove() # Draw the regression plot on each facet - facets.map_dataframe(regplot, x, y, **kwargs) + regplot_kws = dict( + x_estimator=x_estimator, x_bins=x_bins, x_ci=x_ci, + scatter=scatter, fit_reg=fit_reg, ci=ci, n_boot=n_boot, units=units, + order=order, logistic=logistic, lowess=lowess, robust=robust, + logx=logx, x_partial=x_partial, y_partial=y_partial, truncate=truncate, + x_jitter=x_jitter, y_jitter=y_jitter, + scatter_kws=scatter_kws, line_kws=line_kws, + ) + facets.map_dataframe(regplot, x, y, **regplot_kws) # Add a legend if legend and (hue is not None) and (hue not in [col, row]): @@ -622,6 +577,176 @@ def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, return facets +lmplot.__doc__ = dedent("""\ + Plot data and regression model fits across a FacetGrid. + + This function combines :func:`regplot` and :class:`FacetGrid`. It is + intended as a convenient interface to fit regression models across + conditional subsets of a dataset. + + When thinking about how to assign variables to different facets, a general + rule is that it makes sense to use ``hue`` for the most important + comparison, followed by ``col`` and ``row``. However, always think about + your particular dataset and the goals of the visualization you are + creating. + + {model_api} + + The parameters to this function span most of the options in + :class:`FacetGrid`, although there may be occasional cases where you will + want to use that class and :func:`regplot` directly. + + Parameters + ---------- + x, y : strings, optional + Input variables; these should be column names in ``data``. + {data} + hue, col, row : strings + Variables that define subsets of the data, which will be drawn on + separate facets in the grid. See the ``*_order`` parameters to control + the order of levels of this variable. + {palette} + {col_wrap} + {size} + {aspect} + markers : matplotlib marker code or list of marker codes, optional + Markers for the scatterplot. If a list, each marker in the list will be + used for each level of the ``hue`` variable. + {share_xy} + {{hue,col,row}}_order : lists, optional + Order for the levels of the faceting variables. By default, this will + be the order that the levels appear in ``data`` or, if the variables + are pandas categoricals, the category order. + legend : bool, optional + If ``True`` and there is a ``hue`` variable, add a legend. + {legend_out} + {x_estimator} + {x_bins} + {x_ci} + {scatter} + {fit_reg} + {ci} + {n_boot} + {units} + {order} + {logistic} + {lowess} + {robust} + {logx} + {xy_partial} + {truncate} + {xy_jitter} + {scatter_line_kws} + + See Also + -------- + regplot : Plot data and a conditional model fit. + FacetGrid : Subplot grid for plotting conditional relationships. + pairplot : Combine :func:`regplot` and :class:`PairGrid` (when used with + ``kind="reg"``). + + Notes + ----- + + {regplot_vs_lmplot} + + Examples + -------- + + These examples focus on basic regression model plots to exhibit the + various faceting options; see the :func:`regplot` docs for demonstrations + of the other options for fitting the models. + + Plot a simple linear relationship between two variables: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set(color_codes=True) + >>> tips = sns.load_dataset("tips") + >>> g = sns.lmplot(x="total_bill", y="tip", data=tips) + + Condition on a third variable and plot the levels in different colors: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips) + + Use different markers as well as colors so the plot will reproduce to + black-and-white more easily: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips, + ... markers=["o", "x"]) + + Use a different color palette: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips, + ... palette="Set1") + + Map ``hue`` levels to colors with a dictionary: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips, + ... palette=dict(Yes="g", No="m")) + + Plot the levels of the third variable across different columns: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="total_bill", y="tip", col="smoker", data=tips) + + Change the size and aspect ratio of the facets: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="size", y="total_bill", hue="day", col="day", + ... data=tips, aspect=.4, x_jitter=.1) + + Wrap the levels of the column variable into multiple rows: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="total_bill", y="tip", col="day", hue="day", + ... data=tips, col_wrap=2, size=3) + + Condition on two variables to make a full grid: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="total_bill", y="tip", row="sex", col="time", + ... data=tips, size=3) + + Use methods on the returned :class:`FacetGrid` instance to further tweak + the plot: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="total_bill", y="tip", row="sex", col="time", + ... data=tips, size=3) + >>> g = (g.set_axis_labels("Total bill (US Dollars)", "Tip") + ... .set(xlim=(0, 60), ylim=(0, 12), + ... xticks=[10, 30, 50], yticks=[2, 6, 10]) + ... .fig.subplots_adjust(wspace=.02)) + + + + """).format(**_regression_docs) + + def regplot(x, y, data=None, x_estimator=None, x_bins=None, x_ci="ci", scatter=True, fit_reg=True, ci=95, n_boot=1000, units=None, order=1, logistic=False, lowess=False, robust=False, @@ -681,14 +806,14 @@ def regplot(x, y, data=None, x_estimator=None, x_bins=None, x_ci="ci", passed in ``scatter_kws`` or ``line_kws``. marker : matplotlib marker code Marker to use for the scatterplot glyphs. - {{scatter,line}}_kws : dictionaries - Additional keyword arguments to pass to ``plt.scatter`` and - ``plt.plot``. - ax : matplotlib Axes - The Axes object containing the plot. + {scatter_line_kws} + ax : matplotlib Axes, optional + Axes object to draw the plot onto, otherwise uses the current Axes. Returns ------- + ax : matplotlib Axes + The Axes object containing the plot. See Also -------- @@ -696,7 +821,7 @@ def regplot(x, y, data=None, x_estimator=None, x_bins=None, x_ci="ci", linear relationships in a dataset. jointplot : Combine :func:`regplot` and :class:`JointGrid` (when used with ``kind="reg"``). - pairplot : Combine :func:`pairplot` and :class:`PairGrid` (when used with + pairplot : Combine :func:`regplot` and :class:`PairGrid` (when used with ``kind="reg"``). residplot : Plot the residuals of a linear regression model. interactplot : Plot a two-way interaction between continuous variables From ec4860ab209be8b449381791787f040b619b85a3 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 25 May 2015 21:08:55 -0700 Subject: [PATCH 0031/1738] Update FacetGrid docstring with examples and more prose --- seaborn/axisgrid.py | 318 ++++++++++++++++++++++++++++++++-------- seaborn/linearmodels.py | 4 +- 2 files changed, 259 insertions(+), 63 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index b0671624e5..57c7564dee 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -219,66 +219,6 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, dropna=True, legend_out=True, despine=True, margin_titles=False, xlim=None, ylim=None, subplot_kws=None, gridspec_kws=None): - """Initialize the plot figure and FacetGrid object. - - Parameters - ---------- - data : DataFrame - Tidy (long-form) dataframe where each column is a variable and - each row is an observation. - row, col, hue : strings, optional - Variable (column) names to subset the data for the facets. - col_wrap : int, optional - Wrap the column variable at this width. Incompatible with ``row``. - share{x, y}: booleans, optional - Lock the limits of the vertical andn horizontal axes across the - facets. - size : scalar, optional - Height (in inches) of each facet. - aspect : scalar, optional - Aspect * size gives the width (in inches) of each facet. - palette : dict or seaborn color palette - Set of colors for mapping the `hue` variable. If a dict, keys - should be values in the `hue` variable. - {row, col, hue}_order: sequence of strings - Order to plot the values in the faceting variables in, otherwise - infer from the input data using pandas Category order or the - order of appearance. - hue_kws : dictionary of param -> list of values mapping - Other keyword arguments to insert into the plotting call to let - other plot attributes vary across levels of the hue variable (e.g. - the markers in a scatterplot). - dropna : boolean, optional - Drop missing values from the data before plotting. - legend_out: boolean, optional - Draw the legend outside the grid of plots. - despine : boolean, optional - Remove the top and right spines from the plots. - margin_titles : boolean, optional - Write the column and row variable labels on the margins of the - grid rather than above each plot. - {x, y}lim: tuples, optional - Limits for each of the axes on each facet when share{x, y} is True. - subplot_kws : dict, optional - Dictionary of keyword arguments passed to matplotlib subplot(s) - methods. - gridspec_kws : dict, optional - Dictionary of keyword arguments passed to matplotlib's ``gridspec`` - module (via ``plt.subplots``). Requires matplotlib >= 1.4 and is - ignored if ``col_wrap`` is not ``None``. - - Returns - ------- - self : FacetGrid - Returns self for plotting onto the grid. - - See Also - -------- - PairGrid : Subplot grid for plotting pairwise relationships. - lmplot : Combines a regression plot and a FacetGrid. - factorplot : Combines a categorical plot and a FacetGrid. - - """ MPL_GRIDSPEC_VERSION = LooseVersion('1.4') OLD_MPL = LooseVersion(mpl.__version__) < MPL_GRIDSPEC_VERSION @@ -429,6 +369,259 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, if despine: self.despine() + __init__.__doc__ = dedent("""\ + Initialize the matplotlib figure and FacetGrid object. + + The :class:`FacetGrid` is an object that links a Pandas DataFrame to + a matplotlib figure with a particular structure. + + In particular, :class:`FacetGrid` is used to draw plots with multiple + Axes where each Axes shows the same relationship conditioned on + different levels of some variable. It's possible to condition on up to + three variables by assigning variables to the rows and columns of the + grid and using different colors for the plot elements. + + The general approach to plotting here is called "small multiples", + where the same kind of plot is repeated multiple times, and the + specific use of small multiples to display the same relationship + conditioned on one ore more other variables is often called a "trellis + plot". + + The basic workflow is to initialize the :class:`FacetGrid` object with + the dataset and the variables that are used to structure the grid. Then + one or more plotting functions can be applied to each subset by calling + :meth:`FacetGrid.map` or :meth:`FacetGrid.map_dataframe`. Finally, the + plot can be tweaked with other methods to do things like change the + axis labels, use different ticks, or add a legend. See the detailed + code examples below for more information. + + Parameters + ---------- + {data} + row, col, hue : strings + Variables that define subsets of the data, which will be drawn on + separate facets in the grid. See the ``*_order`` parameters to + control the order of levels of this variable. + {col_wrap} + {share_xy} + {size} + {aspect} + {palette} + {{row,col,hue}}_order : lists, optional + Order for the levels of the faceting variables. By default, this + will be the order that the levels appear in ``data`` or, if the + variables are pandas categoricals, the category order. + hue_kws : dictionary of param -> list of values mapping + Other keyword arguments to insert into the plotting call to let + other plot attributes vary across levels of the hue variable (e.g. + the markers in a scatterplot). + {legend_out} + despine : boolean, optional + Remove the top and right spines from the plots. + {margin_titles} + {{x, y}}lim: tuples, optional + Limits for each of the axes on each facet (only relevant when + share{{x, y}} is True. + subplot_kws : dict, optional + Dictionary of keyword arguments passed to matplotlib subplot(s) + methods. + gridspec_kws : dict, optional + Dictionary of keyword arguments passed to matplotlib's ``gridspec`` + module (via ``plt.subplots``). Requires matplotlib >= 1.4 and is + ignored if ``col_wrap`` is not ``None``. + + See Also + -------- + PairGrid : Subplot grid for plotting pairwise relationships. + lmplot : Combine a regression plot and a :class:`FacetGrid`. + factorplot : Combine a categorical plot and a :class:`FacetGrid`. + + Examples + -------- + + Initialize a 2x2 grid of facets using the tips dataset: + + .. plot:: + :context: close-figs + + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> import seaborn as sns; sns.set(style="ticks", color_codes=True) + >>> tips = sns.load_dataset("tips") + >>> g = sns.FacetGrid(tips, col="time", row="smoker") + + Draw a univariate plot on each facet: + + .. plot:: + :context: close-figs + + >>> g = sns.FacetGrid(tips, col="time", row="smoker") + >>> g = g.map(plt.hist, "total_bill") + + (Note that it's not necessary to re-catch the returned variable; it's + the same object, but doing so in the examples makes dealing with the + doctests somewhat less annoying). + + Pass additional keyword arguments to the mapped function: + + .. plot:: + :context: close-figs + + >>> g = sns.FacetGrid(tips, col="time", row="smoker") + >>> bins = np.arange(0, 65, 5) + >>> g = g.map(plt.hist, "total_bill", bins=bins, color="r") + + Plot a bivariate function on each facet: + + .. plot:: + :context: close-figs + + >>> g = sns.FacetGrid(tips, col="time", row="smoker") + >>> g = g.map(plt.scatter, "total_bill", "tip", edgecolor="w") + + Assign one of the variables to the color of the plot elements: + + .. plot:: + :context: close-figs + + >>> g = sns.FacetGrid(tips, col="time", hue="smoker") + >>> g = (g.map(plt.scatter, "total_bill", "tip", edgecolor="w") + ... .add_legend()) + + Change the size and aspect ratio of each facet: + + .. plot:: + :context: close-figs + + >>> g = sns.FacetGrid(tips, col="day", size=4, aspect=.5) + >>> g = g.map(sns.boxplot, "time", "total_bill") + + Specify the order for plot elements: + + .. plot:: + :context: close-figs + + >>> g = sns.FacetGrid(tips, col="smoker", col_order=["Yes", "No"]) + >>> g = g.map(plt.hist, "total_bill", bins=bins, color="m") + + Use a different color palette: + + .. plot:: + :context: close-figs + + >>> kws = dict(s=50, linewidth=.5, edgecolor="w") + >>> g = sns.FacetGrid(tips, col="sex", hue="time", palette="Set1", + ... hue_order=["Dinner", "Lunch"]) + >>> g = (g.map(plt.scatter, "total_bill", "tip", **kws) + ... .add_legend()) + + Use a dictionary mapping hue levels to colors: + + .. plot:: + :context: close-figs + + >>> pal = dict(Lunch="seagreen", Dinner="gray") + >>> g = sns.FacetGrid(tips, col="sex", hue="time", palette=pal, + ... hue_order=["Dinner", "Lunch"]) + >>> g = (g.map(plt.scatter, "total_bill", "tip", **kws) + ... .add_legend()) + + Additionally use a different marker for the hue levels: + + .. plot:: + :context: close-figs + + >>> g = sns.FacetGrid(tips, col="sex", hue="time", palette=pal, + ... hue_order=["Dinner", "Lunch"], + ... hue_kws=dict(marker=["^", "v"])) + >>> g = (g.map(plt.scatter, "total_bill", "tip", **kws) + ... .add_legend()) + + "Wrap" a column variable with many levels into the rows: + + .. plot:: + :context: close-figs + + >>> attend = sns.load_dataset("attention") + >>> g = sns.FacetGrid(attend, col="subject", col_wrap=5, + ... size=1.5, ylim=(0, 10)) + >>> g = g.map(sns.pointplot, "solutions", "score", scale=.7) + + Define a custom bivariate function to map onto the grid: + + .. plot:: + :context: close-figs + + >>> from scipy import stats + >>> def qqplot(x, y, **kwargs): + ... _, xr = stats.probplot(x, fit=False) + ... _, yr = stats.probplot(y, fit=False) + ... plt.scatter(xr, yr, **kwargs) + >>> g = sns.FacetGrid(tips, col="smoker", hue="sex") + >>> g = (g.map(qqplot, "total_bill", "tip", **kws) + ... .add_legend()) + + Define a custom function that uses a ``DataFrame`` object and accepts + column names as positional variables: + + .. plot:: + :context: close-figs + + >>> import pandas as pd + >>> df = pd.DataFrame( + ... data=np.random.randn(90, 4), + ... columns=pd.Series(list("ABCD"), name="walk"), + ... index=pd.date_range("Jan 1", "March 31", name="date")) + >>> df = df.cumsum(axis=0).stack().reset_index(name="val") + >>> def dateplot(x, y, **kwargs): + ... ax = plt.gca() + ... data = kwargs.pop("data") + ... data.plot(x=x, y=y, ax=ax) + >>> g = sns.FacetGrid(df, col="walk", col_wrap=2, size=3.5) + >>> g.map_dataframe(dateplot, "date", "val") + + Use different axes labels after plotting: + + .. plot:: + :context: close-figs + + >>> g = sns.FacetGrid(tips, col="smoker", row="sex") + >>> g = (g.map(plt.scatter, "total_bill", "tip", color="g", **kws) + ... .set_axis_labels("Total bill (US Dollars)", "Tip")) + + Set other attributes that are shared across the facetes: + + .. plot:: + :context: close-figs + + >>> g = sns.FacetGrid(tips, col="smoker", row="sex") + >>> g = (g.map(plt.scatter, "total_bill", "tip", color="r", **kws) + ... .set(xlim=(0, 60), ylim=(0, 12), + ... xticks=[10, 30, 50], yticks=[2, 6, 10])) + + Use a different template for the facet titles: + + .. plot:: + :context: close-figs + + >>> g = sns.FacetGrid(tips.sort("size"), col="size", col_wrap=3) + >>> g = (g.map(plt.hist, "tip", bins=np.arange(0, 13), color="c") + ... .set_titles("{{col_name}} diners")) + + Tighten the facets: + + .. plot:: + :context: close-figs + + >>> g = sns.FacetGrid(tips, col="smoker", row="sex", + ... margin_titles=True) + >>> g = (g.map(plt.scatter, "total_bill", "tip", color="m", **kws) + ... .set(xlim=(0, 60), ylim=(0, 12), + ... xticks=[10, 30, 50], yticks=[2, 6, 10]) + ... .fig.subplots_adjust(wspace=.05, hspace=.05)) + + """).format(**_facet_docs) + def facet_data(self): """Generator for name indices and data subsets for each facet. @@ -523,8 +716,9 @@ def map(self, func, *args, **kwargs): plot_args = [v for k, v in plot_data.iteritems()] # Some matplotlib functions don't handle pandas objects correctly - if func.__module__.startswith("matplotlib"): - plot_args = [v.values for v in plot_args] + if func.__module__ is not None: + if func.__module__.startswith("matplotlib"): + plot_args = [v.values for v in plot_args] # Draw the plot self._facet_plot(func, ax, plot_args, kwargs) diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index f74f98b8ab..65b289208a 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -655,7 +655,9 @@ def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, These examples focus on basic regression model plots to exhibit the various faceting options; see the :func:`regplot` docs for demonstrations - of the other options for fitting the models. + of the other options for plotting the data and models. There are also + other examples for how to manipulate plot using the returned object on + the :class:`FacetGrid` docs. Plot a simple linear relationship between two variables: From 104597498e9ce315eb9d534bbab4c3107aea243c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 29 May 2015 14:12:11 -0700 Subject: [PATCH 0032/1738] Add examples for JointGrid --- seaborn/axisgrid.py | 123 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 111 insertions(+), 12 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 57c7564dee..4dbc4abc1b 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -444,8 +444,6 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, .. plot:: :context: close-figs - >>> import numpy as np - >>> import matplotlib.pyplot as plt >>> import seaborn as sns; sns.set(style="ticks", color_codes=True) >>> tips = sns.load_dataset("tips") >>> g = sns.FacetGrid(tips, col="time", row="smoker") @@ -455,6 +453,7 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, .. plot:: :context: close-figs + >>> import matplotlib.pyplot as plt >>> g = sns.FacetGrid(tips, col="time", row="smoker") >>> g = g.map(plt.hist, "total_bill") @@ -467,8 +466,9 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, .. plot:: :context: close-figs - >>> g = sns.FacetGrid(tips, col="time", row="smoker") + >>> import numpy as np >>> bins = np.arange(0, 65, 5) + >>> g = sns.FacetGrid(tips, col="time", row="smoker") >>> g = g.map(plt.hist, "total_bill", bins=bins, color="r") Plot a bivariate function on each facet: @@ -569,14 +569,14 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, >>> import pandas as pd >>> df = pd.DataFrame( - ... data=np.random.randn(90, 4), - ... columns=pd.Series(list("ABCD"), name="walk"), - ... index=pd.date_range("Jan 1", "March 31", name="date")) + ... data=np.random.randn(90, 4), + ... columns=pd.Series(list("ABCD"), name="walk"), + ... index=pd.date_range("Jan 1", "March 31", name="date")) >>> df = df.cumsum(axis=0).stack().reset_index(name="val") >>> def dateplot(x, y, **kwargs): ... ax = plt.gca() ... data = kwargs.pop("data") - ... data.plot(x=x, y=y, ax=ax) + ... data.plot(x=x, y=y, ax=ax, grid=False, **kwargs) >>> g = sns.FacetGrid(df, col="walk", col_wrap=2, size=3.5) >>> g.map_dataframe(dateplot, "date", "val") @@ -1382,6 +1382,7 @@ def _find_numeric_cols(self, data): class JointGrid(object): """Grid for drawing a bivariate plot with marginal univariate plots.""" + def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, dropna=True, xlim=None, ylim=None): """Set up the grid of subplots. @@ -1389,11 +1390,11 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, Parameters ---------- x, y : strings or vectors - Data or names of variables in `data`. + Data or names of variables in ``data``. data : DataFrame, optional - DataFrame when `x` and `y` are variable names. + DataFrame when ``x`` and ``y`` are variable names. size : numeric - Size of the figure (it will be square). + Size of each side of the figure in inches (it will be square). ratio : numeric Ratio of joint axes size to marginal axes height. space : numeric, optional @@ -1405,8 +1406,106 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, See Also -------- - jointplot : Inteface for drawing bivariate plots with several different - default plot kinds. + jointplot : High-level interface for drawing bivariate plots with + several different default plot kinds. + + Examples + -------- + + Initialize the figure but don't draw any plots onto it: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set(style="ticks", color_codes=True) + >>> tips = sns.load_dataset("tips") + >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips) + + Add plots using default parameters: + + .. plot:: + :context: close-figs + + >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips) + >>> g.plot(sns.regplot, sns.distplot) + + Draw the join and marginal plots separately, which allows finer-level + control other parameters: + + .. plot:: + :context: close-figs + + >>> import matplotlib.pyplot as plt + >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips) + >>> g.plot_joint(plt.scatter, color=".5", edgecolor="white") + >>> g.plot_marginals(sns.distplot, kde=False, color=".5") + + Draw the two marginal plots separately: + + .. plot:: + :context: close-figs + + >>> import numpy as np + >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips) + >>> g.plot_joint(plt.scatter, color="m", edgecolor="white") + >>> g.ax_marg_x.hist(tips["total_bill"], color="b", alpha=.6, + ... bins=np.arange(0, 60, 5)) + >>> g.ax_marg_y.hist(tips["tip"], color="r", alpha=.6, + ... orientation="horizontal", + ... bins=np.arange(0, 12, 1)) + + Add an annotation with a statistic summarizing the bivariate + relationship: + + .. plot:: + :context: close-figs + + >>> from scipy import stats + >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips) + >>> g.plot_joint(plt.scatter, color="g", s=40, edgecolor="white") + >>> g.plot_marginals(sns.distplot, kde=False, color="g") + >>> g.annotate(stats.pearsonr) + + Use a custom function and formatting for the annotation + + .. plot:: + :context: close-figs + + >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips) + >>> g.plot_joint(plt.scatter, color="g", s=40, edgecolor="white") + >>> g.plot_marginals(sns.distplot, kde=False, color="g") + >>> rsquare = lambda a, b: stats.pearsonr(a, b)[0] ** 2 + >>> g.annotate(rsquare, template="{stat}: {val:.2f}", + ... stat="$R^2$", loc="upper left", fontsize=12) + + Remove the space between the joint and marginal axes: + + .. plot:: + :context: close-figs + + >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips, space=0) + >>> g.plot_joint(sns.kdeplot, cmap="Blues_d") + >>> g.plot_marginals(sns.kdeplot, shade=True) + + Draw a smaller plot with relatively larger marginal axes: + + .. plot:: + :context: close-figs + + >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips, + ... size=5, ratio=2) + >>> g.plot_joint(sns.kdeplot, cmap="Reds_d") + >>> g.plot_marginals(sns.kdeplot, color="r", shade=True) + + Set limits on the axes: + + .. plot:: + :context: close-figs + + >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips, + ... xlim=(0, 50), ylim=(0, 8)) + >>> g.plot_joint(sns.kdeplot, cmap="Purples_d") + >>> g.plot_marginals(sns.kdeplot, color="m", shade=True) """ # Set up the subplot grid From 0cf9f297e483041538951302bcfc1978f3d7cfef Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 2 Jun 2015 22:37:09 -0700 Subject: [PATCH 0033/1738] Pass jointplot kwargs to joint function --- doc/releases/v0.6.0.txt | 2 ++ seaborn/distributions.py | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/releases/v0.6.0.txt b/doc/releases/v0.6.0.txt index b492c98c1f..8f8c4a3f5b 100644 --- a/doc/releases/v0.6.0.txt +++ b/doc/releases/v0.6.0.txt @@ -51,6 +51,8 @@ Other additions and changes - Added the ``as_hex`` method to color palette objects, to return a list of hex codes rather than rgb tuples. +- :func:`jointplot` now passes additional keyword arguments to the function used to draw the plot on the joint axes. + - Changed the default ``linewidths`` in :func:`heatmap` and :`clustermap` to 0 so that larger matrices plot correctly. This parameter still exists and can be used to get the old effect of lines demarcating each cell in the heatmap (the old default ``linewidths`` was 0.5). - :func:`heatmap` and :func:`clustermap` now automatically use a mask for missing values, which previously were shown with the "under" value of the colormap per default `plt.pcolormesh` behavior. diff --git a/seaborn/distributions.py b/seaborn/distributions.py index f5a0eed015..81e7f8ead3 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -623,7 +623,7 @@ def rugplot(a, height=None, axis="x", ax=None, **kwargs): def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, color=None, size=6, ratio=5, space=.2, dropna=True, xlim=None, ylim=None, - joint_kws=None, marginal_kws=None, annot_kws=None): + joint_kws=None, marginal_kws=None, annot_kws=None, **kwargs): """Draw a plot of two variables with bivariate and univariate graphs. This function provides a convenient interface to the :class:`JointGrid` @@ -658,6 +658,10 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, Axis limits to set before plotting. {joint, marginal, annot}_kws : dicts Additional keyword arguments for the plot components. + kwargs : key, value pairs + Additional keyword arguments are passed to the function used to + draw the plot on the joint Axes, superseding items in the + ``joint_kws`` dictionary. Returns ------- @@ -747,14 +751,15 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, :context: close-figs >>> g = sns.jointplot("petal_length", "sepal_length", data=iris, - ... joint_kws=dict(s=40, edgecolor="w", linewidth=1), ... marginal_kws=dict(bins=15, rug=True), - ... annot_kws=dict(stat="r")) + ... annot_kws=dict(stat="r"), + ... s=40, edgecolor="w", linewidth=1) """ # Set up empty default kwarg dicts if joint_kws is None: joint_kws = {} + joint_kws.update(kwargs) if marginal_kws is None: marginal_kws = {} if annot_kws is None: From 8281faed4ecd5abd64445113665734cd2780c597 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 2 Jun 2015 22:37:15 -0700 Subject: [PATCH 0034/1738] PairGrid examples --- seaborn/axisgrid.py | 92 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 7 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 4dbc4abc1b..edb380f725 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1046,7 +1046,7 @@ class PairGrid(Grid): def __init__(self, data, hue=None, hue_order=None, palette=None, hue_kws=None, vars=None, x_vars=None, y_vars=None, - diag_sharey=True, size=3, aspect=1, + diag_sharey=True, size=2.5, aspect=1, despine=True, dropna=True): """Initialize the plot figure and PairGrid object. @@ -1081,15 +1081,84 @@ def __init__(self, data, hue=None, hue_order=None, palette=None, dropna : boolean, optional Drop missing values from the data before plotting. - Returns - ------- - self : PairGrid - Returns self for plotting onto the grid. - See Also -------- + pairplot : Easily drawing common uses of :class:`PairGrid`. FacetGrid : Subplot grid for plotting conditional relationships. - pairplot : Function for easily drawing common uses of PairGrid. + + Examples + -------- + + Draw a scatterplot for each pairwise relationship: + + .. plot:: + :context: close-figs + + >>> import matplotlib.pyplot as plt + >>> import seaborn as sns; sns.set(color_codes=True) + >>> iris = sns.load_dataset("iris") + >>> g = sns.PairGrid(iris) + >>> g = g.map(plt.scatter) + + Show a univariate distribution on the diagonal: + + .. plot:: + :context: close-figs + + >>> g = sns.PairGrid(iris) + >>> g = g.map_diag(plt.hist) + >>> g = g.map_offdiag(plt.scatter) + + (It's not actually necessary to catch the return value every time, + as it is the same object, but it makes it easier to deal with the + doc tests). + + Color the points using a categorical variable: + + .. plot:: + :context: close-figs + + >>> g = sns.PairGrid(iris, hue="species") + >>> g = g.map(plt.scatter) + >>> g = g.add_legend() + + Plot a subset of variables + + .. plot:: + :context: close-figs + + >>> g = sns.PairGrid(iris, vars=["sepal_length", "sepal_width"]) + >>> g = g.map(plt.scatter) + + Use different variables for the rows and columns: + + .. plot:: + :context: close-figs + + >>> g = sns.PairGrid(iris, + ... x_vars=["sepal_length", "sepal_width"], + ... y_vars=["petal_length", "petal_width"]) + >>> g = g.map(plt.scatter) + + Use different functions on the upper and lower triangles: + + .. plot:: + :context: close-figs + + >>> g = sns.PairGrid(iris) + >>> g.map_upper(plt.scatter) + >>> g.map_lower(sns.kdeplot, cmap="Blues_d") + >>> g.map_diag(sns.kdeplot, lw=3, legend=False) + + Use different colors and markers for each categorical level: + + .. plot:: + :context: close-figs + + >>> g = sns.PairGrid(iris, hue="species", palette="Set2", + ... hue_kws={"marker": ["o", "s", "D"]}) + >>> g = g.map(plt.scatter, linewidths=1, edgecolor="w", s=40) + >>> g = g.add_legend() """ @@ -1199,6 +1268,8 @@ def map(self, func, **kwargs): kwargs["color"] = kw_color self._add_axis_labels() + return self + def map_diag(self, func, **kwargs): """Plot with a univariate function on each diagonal subplot. @@ -1258,6 +1329,8 @@ def map_diag(self, func, **kwargs): self._add_axis_labels() + return self + def map_lower(self, func, **kwargs): """Plot with a bivariate function on the lower diagonal subplots. @@ -1301,6 +1374,8 @@ def map_lower(self, func, **kwargs): kwargs["color"] = kw_color self._add_axis_labels() + return self + def map_upper(self, func, **kwargs): """Plot with a bivariate function on the upper diagonal subplots. @@ -1345,6 +1420,8 @@ def map_upper(self, func, **kwargs): if kw_color is not None: kwargs["color"] = kw_color + return self + def map_offdiag(self, func, **kwargs): """Plot with a bivariate function on the off-diagonal subplots. @@ -1358,6 +1435,7 @@ def map_offdiag(self, func, **kwargs): self.map_lower(func, **kwargs) self.map_upper(func, **kwargs) + return self def _add_axis_labels(self): """Add labels to the left and bottom Axes.""" From 24a8366bdde181f1bceb7da5c394fc9330c45d08 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 3 Jun 2015 07:01:43 -0700 Subject: [PATCH 0035/1738] Document tsplot legend (closes #577) --- seaborn/timeseries.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/seaborn/timeseries.py b/seaborn/timeseries.py index 52e5aa246f..fac220330b 100644 --- a/seaborn/timeseries.py +++ b/seaborn/timeseries.py @@ -79,6 +79,9 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, err_kws : dict, optional Keyword argument dictionary passed through to matplotlib function generating the error plot, + legend : bool, optional + If ``True`` and there is a ``condition`` variable, add a legend to + the plot. ax : axis object, optional Plot in given axis; if None creates a new figure kwargs : From f3aa95f1ec6e0e429c94371429bd650b353bd243 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 3 Jun 2015 15:10:22 -0700 Subject: [PATCH 0036/1738] Add examples for pairplot --- doc/releases/v0.6.0.txt | 2 + seaborn/axisgrid.py | 13 +++++- seaborn/linearmodels.py | 97 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 109 insertions(+), 3 deletions(-) diff --git a/doc/releases/v0.6.0.txt b/doc/releases/v0.6.0.txt index 8f8c4a3f5b..2c452d203b 100644 --- a/doc/releases/v0.6.0.txt +++ b/doc/releases/v0.6.0.txt @@ -67,6 +67,8 @@ Other additions and changes - The interactive palette widgets now show a continuous colorbar, rather than a discrete palette, when `as_cmap` is True. +- The default Axes size for :func:`pairplot` and :class:`PairGrid` is now slightly smaller. + - Added a catch in :func:`distplot` when calculating a default number of bins. For highly skewed data it will now use sqrt(n) bins, where previously the reference rule would return "infinite" bins and cause an exception in matplotlib. - Added a ceiling (50) to the default number of bins used for :func:`distplot` histograms. This will help avoid confusing errors with certain kinds of datasets that heavily violate the assumptions of the reference rule used to get a default number of bins. The ceiling is not applied when passing a specific number of bins. diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index edb380f725..95a9fea3b3 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1095,7 +1095,7 @@ def __init__(self, data, hue=None, hue_order=None, palette=None, :context: close-figs >>> import matplotlib.pyplot as plt - >>> import seaborn as sns; sns.set(color_codes=True) + >>> import seaborn as sns; sns.set() >>> iris = sns.load_dataset("iris") >>> g = sns.PairGrid(iris) >>> g = g.map(plt.scatter) @@ -1111,7 +1111,7 @@ def __init__(self, data, hue=None, hue_order=None, palette=None, (It's not actually necessary to catch the return value every time, as it is the same object, but it makes it easier to deal with the - doc tests). + doctests). Color the points using a categorical variable: @@ -1130,6 +1130,15 @@ def __init__(self, data, hue=None, hue_order=None, palette=None, >>> g = sns.PairGrid(iris, vars=["sepal_length", "sepal_width"]) >>> g = g.map(plt.scatter) + Pass additional keyword arguments to the functions + + .. plot:: + :context: close-figs + + >>> g = sns.PairGrid(iris) + >>> g = g.map_diag(plt.hist, edgecolor="w") + >>> g = g.map_offdiag(plt.scatter, edgecolor="w", s=40) + Use different variables for the rows and columns: .. plot:: diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index 65b289208a..d9529fcd51 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -1421,10 +1421,23 @@ def symmatplot(mat, p_mat=None, names=None, cmap="Greys", cmap_range=None, def pairplot(data, hue=None, hue_order=None, palette=None, vars=None, x_vars=None, y_vars=None, kind="scatter", diag_kind="hist", markers=None, - size=3, aspect=1, dropna=True, + size=2.5, aspect=1, dropna=True, plot_kws=None, diag_kws=None, grid_kws=None): """Plot pairwise relationships in a dataset. + By default, this function will create a grid of Axes such that each + variable in ``data`` will by shared in the y-axis across a single row and + in the x-axis across a single column. The diagonal Axes are treated + differently, drawing a plot to show the univariate distribution of the data + for the variable in that column. + + It is also possible to show a subset of variables or plot different + variables on the rows and columns. + + This is a high-level interface for :class:`PairGrid` that is intended to + make it easy to draw a few common styles. You should use :class`PairGrid` + directly if you need more flexibility. + Parameters ---------- data : DataFrame @@ -1471,6 +1484,88 @@ def pairplot(data, hue=None, hue_order=None, palette=None, PairGrid : Subplot grid for more flexible plotting of pairwise relationships. + Examples + -------- + + Draw scatterplots for joint relationships and histograms for univariate + distributions: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set(style="ticks", color_codes=True) + >>> iris = sns.load_dataset("iris") + >>> sns.pairplot(iris) + + Show different levels of a categorical variable by the color of plot + elements: + + .. plot:: + :context: close-figs + + >>> sns.pairplot(iris, hue="species") + + Use a different color palette: + + .. plot:: + :context: close-figs + + >>> sns.pairplot(iris, hue="species", palette="husl") + + Use different markers for each level of the hue variable: + + .. plot:: + :context: close-figs + + >>> sns.pairplot(iris, hue="species", markers=["o", "s", "D"]) + + Plot a subset of variables: + + .. plot:: + :context: close-figs + + >>> sns.pairplot(iris, vars=["sepal_width", "sepal_length"]) + + Draw larger plots: + + .. plot:: + :context: close-figs + + >>> sns.pairplot(iris, size=3, vars=["sepal_width", "sepal_length"]) + + Plot different variables in the rows and columns: + + .. plot:: + :context: close-figs + + >>> sns.pairplot(iris, + ... x_vars=["sepal_width", "sepal_length"], + ... y_vars=["petal_width", "petal_length"]) + + Use kernel density estimates for univariate plots: + + .. plot:: + :context: close-figs + + >>> sns.pairplot(iris, diag_kind="kde") + + Fit linear regression models to the scatter plots: + + .. plot:: + :context: close-figs + + >>> sns.pairplot(iris, kind="reg") + + Pass keyword arguments down to the underlying functions (it may be easier + to use :class:`PairGrid` directly): + + .. plot:: + :context: close-figs + + >>> sns.pairplot(iris, diag_kind="kde", markers="+", + ... plot_kws=dict(s=50, edgecolor="b", linewidth=1), + ... diag_kws=dict(shade=True)) + """ if plot_kws is None: plot_kws = {} From d68662c3c67bffe8bed11496d65548071fddeb22 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 7 Jun 2015 08:56:39 -0700 Subject: [PATCH 0037/1738] Add note on margin_titles annoyance (fixes #509) --- seaborn/axisgrid.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 95a9fea3b3..f4d8956d8d 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -205,7 +205,9 @@ def _get_palette(self, data, hue, hue_order, palette): margin_titles : bool, optional If ``True``, the titles for the row variable are drawn to the right of the last column. This option is experimental and may not work in all - cases.\ + cases. If you call ``map`` multiple times when using this option, the + titles will stack; to avoid this, remove figure texts before the final + call to ``map``. See ``set_titles`` for more information.\ """), ) @@ -903,6 +905,15 @@ def set_titles(self, template=None, row_template=None, col_template=None, self: object Returns self. + Note + ---- + + When using margin titles for the row facets, calling this directly + will add titles on top of the existing titles (because the margin + titles aren't really "titles", just figure texts). To avoid that, + you should remove the existing titles first by doing, e.g., + ``plt.setp(fig.texts, text="")``. + """ args = dict(row_var=self._row_var, col_var=self._col_var) kwargs["size"] = kwargs.pop("size", mpl.rcParams["axes.labelsize"]) From dd54bd9135735acc838e62e1495514a7383836ed Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 12 Jun 2015 08:47:08 -0700 Subject: [PATCH 0038/1738] Make note about sequential index requirement in tsplot (closes #585) --- seaborn/timeseries.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seaborn/timeseries.py b/seaborn/timeseries.py index fac220330b..4bbc371861 100644 --- a/seaborn/timeseries.py +++ b/seaborn/timeseries.py @@ -31,7 +31,8 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, Data for the plot. Should either be a "long form" dataframe or an array with dimensions (unit, time, condition). In both cases, the condition field/dimension is optional. The type of this argument - determines the interpretation of the next few parameters. + determines the interpretation of the next few parameters. When + using a DataFrame, the index has to be sequential. time : string or series-like Either the name of the field corresponding to time in the data DataFrame or x values for a plot when data is an array. If a Series, From 74ed5ea8ceec4aaf499566e550f711f56bf898e7 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 14 Jun 2015 20:01:05 -0700 Subject: [PATCH 0039/1738] Draw rugplot using axh/vlines for constant size --- doc/releases/v0.6.0.txt | 6 ++++-- seaborn/distributions.py | 35 +++++++++++++++-------------------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/doc/releases/v0.6.0.txt b/doc/releases/v0.6.0.txt index 2c452d203b..90a8e5aa51 100644 --- a/doc/releases/v0.6.0.txt +++ b/doc/releases/v0.6.0.txt @@ -4,7 +4,7 @@ v0.6.0 (Unreleased) This is a major release from 0.5. The main objective of this release is to unify the API for categorical plots, which means that there are some relatively large API changes in some of the older functions. See below for details of those changes, which may break code written for older versions of seaborn. There are also some new functions (:func:`stripplot`, and :func:`countplot`), enhancements to existing functions, and bug fixes. -Additionally, the structure of the docs is changing in version 0.6. The API docs page for each function will have numerous examples with embedded plots showing how to use the various options. These pages should be considered the most comprehensive resource for examples, as the tutorial pages are going to be streamlined to provide a higher-level introduction. Currently there are examples for all the categorical plot functions, it's unclear whether they will be completed for the rest of the package by release. +Additionally, the structure of the docs is changing in version 0.6. The API docs page for each function will have numerous examples with embedded plots showing how to use the various options. These pages should be considered the most comprehensive resource for examples, as the tutorial pages are going to be streamlined to provide a higher-level introduction. Changes and updates to categorical plots ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -13,7 +13,7 @@ In version 0.6, the "categorical" plots have been unified with a common API. Thi These functions now each accept the same formats of input data and can be invoked in the same way. They can plot using long- or wide-form data, and can be drawn vertically or horizontally. When long-form data is used, the orientation of the plots is inferred from the types of the input data. Additionally, all functions natively take a ``hue`` variable to add a second layer of categorization. -With the (in some cases new) API, these functions can all be drawn correctly by :class:`FacetGrid`. However, :func:`factorplot` can also now create faceted verisons of any of these kinds of plots, so in most cases it will be unnecessary to use :class:`FacetGrid` directly.. By default, :func:`factorplot` draws a point plot, but this is controlled by the ``kind`` parameter. +With the (in some cases new) API, these functions can all be drawn correctly by :class:`FacetGrid`. However, :func:`factorplot` can also now create faceted verisons of any of these kinds of plots, so in most cases it will be unnecessary to use :class:`FacetGrid` directly. By default, :func:`factorplot` draws a point plot, but this is controlled by the ``kind`` parameter. Here are details on what has changed in the process of unifying these APIs: @@ -69,6 +69,8 @@ Other additions and changes - The default Axes size for :func:`pairplot` and :class:`PairGrid` is now slightly smaller. +- The ``height`` parameter of :func:`rugplot` is now interpreted as a function of the axis size and is invariant to changes in the data scale on that axis. The rug lines are also slightly narrower by default. + - Added a catch in :func:`distplot` when calculating a default number of bins. For highly skewed data it will now use sqrt(n) bins, where previously the reference rule would return "infinite" bins and cause an exception in matplotlib. - Added a ceiling (50) to the default number of bins used for :func:`distplot` histograms. This will help avoid confusing errors with certain kinds of datasets that heavily violate the assumptions of the reference rule used to get a default number of bins. The ceiling is not applied when passing a specific number of bins. diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 81e7f8ead3..85eb2a7f74 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -581,42 +581,37 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", return ax -def rugplot(a, height=None, axis="x", ax=None, **kwargs): +def rugplot(a, height=.05, axis="x", ax=None, **kwargs): """Plot datapoints in an array as sticks on an axis. Parameters ---------- a : vector - 1D array of datapoints. + 1D array of observations. height : scalar, optional - Height of ticks, if None draw at 5% of axis range. + Height of ticks as proportion of the axis. axis : {'x' | 'y'}, optional Axis to draw rugplot on. - ax : matplotlib axis - Axis to draw plot into; otherwise grabs current axis. - kwargs : other keyword arguments for plt.plot() + ax : matplotlib axes + Axes to draw plot into; otherwise grabs current axes. + kwargs : key, value mappings + Other keyword arguments are passed to ``axvline`` or ``axhline``. Returns ------- - ax : matplotlib axis - Axis with rugplot. + ax : matplotlib axes + The Axes object with the plot on it. """ if ax is None: ax = plt.gca() a = np.asarray(a) - vertical = kwargs.pop("vertical", None) - if vertical is not None: - axis = "y" if vertical else "x" - other_axis = dict(x="y", y="x")[axis] - min, max = getattr(ax, "get_%slim" % other_axis)() - if height is None: - range = max - min - height = range * .05 - if axis == "x": - ax.plot([a, a], [min, min + height], **kwargs) - else: - ax.plot([min, min + height], [a, a], **kwargs) + vertical = kwargs.pop("vertical", axis == "y") + func = ax.axhline if vertical else ax.axvline + kwargs.setdefault("linewidth", 1) + for pt in a: + func(pt, 0, height, **kwargs) + return ax From 00bdcd977b77afba7c26217fe17632e53e73be0a Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 18 Jun 2015 21:58:47 -0700 Subject: [PATCH 0040/1738] Update distributions tutorial --- doc/.gitignore | 1 + doc/tutorial.rst | 3 +- doc/tutorial/Makefile | 4 +- doc/tutorial/distributions.ipynb | 510 ++++++++++++++++++ doc/tutorial/quantitative_linear_models.ipynb | 22 +- seaborn/distributions.py | 34 +- 6 files changed, 561 insertions(+), 13 deletions(-) create mode 100644 doc/tutorial/distributions.ipynb diff --git a/doc/.gitignore b/doc/.gitignore index ae76f09488..5117317b8a 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -5,6 +5,7 @@ examples/ example_thumbs/ aesthetics.rst color_palettes.rst +distributions.rst quantitative_linear_models.rst categorical_linear_models.rst plotting_distributions.rst diff --git a/doc/tutorial.rst b/doc/tutorial.rst index 512ae70bdc..440b7c2644 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -18,11 +18,10 @@ Plotting functions .. toctree:: :maxdepth: 2 - tutorial/plotting_distributions + tutorial/distributions tutorial/quantitative_linear_models tutorial/categorical_linear_models tutorial/dataset_exploration - tutorial/timeseries_plots Structured grids ---------------- diff --git a/doc/tutorial/Makefile b/doc/tutorial/Makefile index 113e0e2e81..90d9dc314a 100644 --- a/doc/tutorial/Makefile +++ b/doc/tutorial/Makefile @@ -1,10 +1,8 @@ notebooks: - ipython nbconvert --to rst ../../examples/plotting_distributions.ipynb - ipython nbconvert --to rst ../../examples/timeseries_plots.ipynb - tools/nb_to_doc.py aesthetics tools/nb_to_doc.py color_palettes + tools/nb_to_doc.py distributions tools/nb_to_doc.py quantitative_linear_models tools/nb_to_doc.py categorical_linear_models tools/nb_to_doc.py dataset_exploration diff --git a/doc/tutorial/distributions.ipynb b/doc/tutorial/distributions.ipynb new file mode 100644 index 0000000000..53c8561fb5 --- /dev/null +++ b/doc/tutorial/distributions.ipynb @@ -0,0 +1,510 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": {}, + "source": [ + ".. _distributions_tutorial::\n", + "\n", + ".. currentmodule:: seaborn" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Visualizing the distribution of a dataset" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "When dealing with a set of data, often the first thing you'll want to do is get a sense for how the variables are distributed. This chapter of the tutorial will give a brief introduction to some of the tools in seborn for examining univariate and bivariate distributions. You may also want to look at the :ref:`categorical plots ` chapter for examples of functions that make it easy to compare the distribution of a variable across levels of other variables." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "from scipy import stats, integrate\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import seaborn as sns\n", + "sns.set(color_codes=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "np.random.seed(sum(map(ord, \"distributions\")))" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Plotting univariate distributions\n", + "---------------------------------\n", + "\n", + "The most convenient way to take a quick look at a univariate distribution in seaborn is the :func:`distplot` function. By default, this will draw a `histogram `_ and fit a `kernel density estimate `_ (KDE). " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "x = np.random.normal(size=100)\n", + "sns.distplot(x);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Histograms\n", + "^^^^^^^^^^\n", + "\n", + "Histograms are likely familiar, and a ``hist`` function already exists in matplotlib. A histogram represents the distribution of data by forming bins along the range of the data and then drawing bars to show the number of observations that fall in each bin.\n", + "\n", + "To illustrate this, let's remove the density curve and add a rug plot, which draws a small vertical tick at each observation. You can make the rug plot itself with the :func:`rugplot` function, but it is also available in :func:`distplot`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.distplot(x, kde=False, rug=True);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "When drawing histograms, the main choice you have is the number of bins to use and where to place them. :func:`distplot` uses a simple rule to make a good guess for what the right number is by default, but trying more or fewer bins might reveal other features in the data:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.distplot(x, bins=20, kde=False, rug=True);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Kernel density estimaton\n", + "^^^^^^^^^^^^^^^^^^^^^^^^\n", + "\n", + "The kernel density estimate may be less familiar, but it can be a useful tool for plotting the shape of a distribution. Like the histogram, the KDE plots encodes the density of observations on one axis with height along the other axis:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.distplot(x, hist=False, rug=True);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Drawing a KDE is more computationally involved than drawing a histogram. What happens is that each observation is first replaced with a normal (Gaussian) curve centered at that value:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "x = np.random.normal(0, 1, size=30)\n", + "bandwidth = 1.06 * x.std() * x.size ** (-1 / 5.)\n", + "support = np.linspace(-4, 4, 200)\n", + "\n", + "kernels = []\n", + "for x_i in x:\n", + "\n", + " kernel = stats.norm(x_i, bandwidth).pdf(support)\n", + " kernels.append(kernel)\n", + " plt.plot(support, kernel, color=\"r\")\n", + "\n", + "sns.rugplot(x, color=\".2\", linewidth=3);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Next, these curves are summed to compute the value of the density at each point in the support grid. The resulting curve is then normalized so that the area under it is equal to 1:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "density = np.sum(kernels, axis=0)\n", + "density /= integrate.trapz(density, support)\n", + "plt.plot(support, density);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "We can see that if we use the :func:`kdeplot` function in seaborn, we get the same curve. This function is used by :func:`distplot`, but it provides a more direct interface with easier access to other options when you just want the density estimate:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.kdeplot(x, shade=True);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "The bandwidth (``bw``) parameter of the KDE controls how tightly the estimation is fit to the data, much like the bin size in a histogram. It corresponds to the width of the kernels we plotted above. The default value tries to guess a good value using a common reference rule, but it may be helpful to try larger or smaller values:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.kdeplot(x)\n", + "sns.kdeplot(x, bw=.2, label=\"bw: 0.2\")\n", + "sns.kdeplot(x, bw=2, label=\"bw: 2\")\n", + "plt.legend();" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "As you can see above, the nature of the Gaussian KDE process means that estimation extends past the largest and smallest values in the dataset. It's possible to control how far past the extreme values the curve is drawn with the ``cut`` parameter; however, this only influences how the curve is drawn and not how it is fit:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.kdeplot(x, shade=True, cut=0)\n", + "sns.rugplot(x);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Fitting parametric distributions\n", + "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + "\n", + "You can also use :func:`distplot` to fit a parametric distribution to a dataset and visually evaluate how closely it corresponds to the observed data:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "x = np.random.gamma(6, size=200)\n", + "sns.distplot(x, kde=False, fit=stats.gamma);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Plotting bivariate distributions\n", + "--------------------------------\n", + "\n", + "It can also be useful to visualize a bivariate distribution of two variables. The easiest way to do this in seaborn is to just the :func:`jointplot` function, which creates a multi-panel figure that shows both the bivariate (or joint) relationship between two variables along with the univariate (or marginal) distribution of each on separate axes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "mean, cov = [0, 1], [(1, .5), (.5, 1)]\n", + "data = np.random.multivariate_normal(mean, cov, 200)\n", + "df = pd.DataFrame(data, columns=[\"x\", \"y\"])" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Scatterplots\n", + "^^^^^^^^^^^^\n", + "\n", + "The most familiar way to visualize a bivariate distribution is a scatterplot, where each observation is shown with point at the *x* and *y* values. This is analgous to a rug plot on two dimensions. You can draw a scatterplot with the matplotlib ``plt.scatter`` function, and it is also the default kind of plot shown by the :func:`jointplot` function:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.jointplot(x=\"x\", y=\"y\", data=df);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Hexbin plots\n", + "^^^^^^^^^^^^\n", + "\n", + "The bivariate analogue of a histogram is known as a \"hexbin\" plot, because it shows the counts of observations that fall within hexagonal bins. This plot works best with relatively large datasets. It's availible through the matplotlib ``plt.hexbin`` function and as a style in :func:`jointplot`. It looks best with a white background:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "x, y = np.random.multivariate_normal(mean, cov, 1000).T\n", + "with sns.axes_style(\"white\"):\n", + " sns.jointplot(x=x, y=y, kind=\"hex\", color=\"k\");" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Kernel density estimation\n", + "^^^^^^^^^^^^^^^^^^^^^^^^^\n", + "\n", + "It is also posible to use the kernel density estimation procedure described above to visualize a bivariate distribution. In seaborn, this kind of plot is shown with a contour plot and is available as a style in :func:`jointplot`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.jointplot(x=\"x\", y=\"y\", data=df, kind=\"kde\");" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "You can also draw a two-dimensional kernel density plot with the :func:`kdeplot` function. This allows you to draw this kind of plot onto a specific (and possibly already existing) matplotlib axes, whereas the :func:`jointplot` function manages its own figure:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "f, ax = plt.subplots(figsize=(6, 6))\n", + "sns.kdeplot(df.x, df.y, ax=ax)\n", + "sns.rugplot(df.x, color=\"g\", ax=ax)\n", + "sns.rugplot(df.y, vertical=True, ax=ax);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "If you wish to show the bivariate density more continuously, you can simple increase the number of contour levels:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "f, ax = plt.subplots(figsize=(6, 6))\n", + "cmap = sns.cubehelix_palette(as_cmap=True, dark=0, light=1, reverse=True)\n", + "sns.kdeplot(df.x, df.y, cmap=cmap, n_levels=60, shade=True);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "The :func:`jointplot` function uses a :class:`JointGrid` to manage the figure. For more flexibility, you may want to draw your figure by using :class:`JointGrid` directly. :func:`jointplot` returns the :class:`JointGrid` object after plotting, which you can use to add more layers or to tweak other aspects of the visualization:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "g = sns.jointplot(x=\"x\", y=\"y\", data=df, kind=\"kde\", color=\"m\")\n", + "g.plot_joint(plt.scatter, c=\"w\", s=30, linewidth=1, marker=\"+\")\n", + "g.ax_joint.collections[0].set_alpha(0)\n", + "g.set_axis_labels(\"$X$\", \"$Y$\");" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Visualizing pairwise relationships in a dataset\n", + "-----------------------------------------------\n", + "\n", + "To plot multiple pairwise bivariate distributions in a dataset, you can use the :func:`pairplot` function. This creates a matrix of axes and shows the relationship for each pair of columns in a DataFrame. by default, it also draws the univariate distribution of each variable on the diagonal Axes:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "iris = sns.load_dataset(\"iris\")\n", + "sns.pairplot(iris);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Much like the relationship between :func:`jointplot` and :class:`JointGrid`, the :func:`pairplot` function is built on top of a :class:`PairGrid` object, which can be used directly for more flexibility:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "g = sns.PairGrid(iris)\n", + "g.map_diag(sns.kdeplot)\n", + "g.map_offdiag(sns.kdeplot, cmap=\"Blues_d\", n_levels=6);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.10" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/doc/tutorial/quantitative_linear_models.ipynb b/doc/tutorial/quantitative_linear_models.ipynb index 7693790fef..c288c91051 100644 --- a/doc/tutorial/quantitative_linear_models.ipynb +++ b/doc/tutorial/quantitative_linear_models.ipynb @@ -1061,7 +1061,25 @@ ] } ], - "metadata": {}, + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.9" + } + }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 85eb2a7f74..b5da64c662 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -348,8 +348,9 @@ def _scipy_univariate_kde(data, bw, gridsize, cut, clip): return grid, y -def _bivariate_kdeplot(x, y, filled, kernel, bw, gridsize, cut, clip, axlabel, - ax, **kwargs): +def _bivariate_kdeplot(x, y, filled, fill_lowest, + kernel, bw, gridsize, cut, clip, + axlabel, ax, **kwargs): """Plot a joint KDE estimate as a bivariate contour plot.""" # Determine the clipping if clip is None: @@ -376,7 +377,9 @@ def _bivariate_kdeplot(x, y, filled, kernel, bw, gridsize, cut, clip, axlabel, kwargs["cmap"] = cmap contour_func = ax.contourf if filled else ax.contour - contour_func(xx, yy, z, n_levels, **kwargs) + cset = contour_func(xx, yy, z, n_levels, **kwargs) + if filled and not fill_lowest: + cset.collections[0].set_alpha(0) kwargs["n_levels"] = n_levels # Label the axes @@ -435,7 +438,7 @@ def _scipy_bivariate_kde(x, y, bw, gridsize, cut, clip): def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", bw="scott", gridsize=100, cut=3, clip=None, legend=True, - cumulative=False, ax=None, **kwargs): + cumulative=False, shade_lowest=True, ax=None, **kwargs): """Fit and plot a univariate or bivariate kernel density estimate. Parameters @@ -466,6 +469,11 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", If True, add a legend or label the axes when possible. cumulative : bool If True, draw the cumulative distribution estimated by the kde. + shade_lowest : bool + If True, shade the lowest contour of a bivariate KDE plot. Not + relevant when drawing a univariate plot or when ``shade=False``. + Setting this to ``False`` can be useful when you want multiple + densities on the same Axes. ax : matplotlib axis, optional Axis to plot on, otherwise uses current axis. kwargs : key, value pairings @@ -525,6 +533,19 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", >>> ax = sns.kdeplot(x, y, n_levels=30, cmap="Purples_d") + Plot two shaded bivariate densities: + + .. plot:: + :context: close-figs + + >>> iris = sns.load_dataset("iris") + >>> setosa = iris.loc[iris.species == "setosa"] + >>> virginica = iris.loc[iris.species == "virginica"] + >>> ax = sns.kdeplot(setosa.sepal_width, setosa.sepal_length, + ... cmap="Reds", shade=True, shade_lowest=False) + >>> ax = sns.kdeplot(virginica.sepal_width, virginica.sepal_length, + ... cmap="Blues", shade=True, shade_lowest=False) + Use a narrower bandwith: .. plot:: @@ -571,8 +592,9 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", raise TypeError("Cumulative distribution plots are not" "supported for bivariate distributions.") if bivariate: - ax = _bivariate_kdeplot(x, y, shade, kernel, bw, gridsize, - cut, clip, legend, ax, **kwargs) + ax = _bivariate_kdeplot(x, y, shade, shade_lowest, + kernel, bw, gridsize, cut, clip, legend, + ax, **kwargs) else: ax = _univariate_kdeplot(data, shade, vertical, kernel, bw, gridsize, cut, clip, legend, ax, From 9fa5d5b8d244fda2209cce042390da445a373a0e Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 19 Jun 2015 07:19:37 -0700 Subject: [PATCH 0041/1738] Add note on shade_lowest to release notes --- doc/releases/v0.6.0.txt | 2 ++ seaborn/distributions.py | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/doc/releases/v0.6.0.txt b/doc/releases/v0.6.0.txt index 90a8e5aa51..c4de096492 100644 --- a/doc/releases/v0.6.0.txt +++ b/doc/releases/v0.6.0.txt @@ -69,6 +69,8 @@ Other additions and changes - The default Axes size for :func:`pairplot` and :class:`PairGrid` is now slightly smaller. +- Added the ``shade_lowest`` parameter to :func:`kdeplot` which will set the alpha for the lowest contour level to 0, making it easier to plot multiple bivariate distributions on the same axes. + - The ``height`` parameter of :func:`rugplot` is now interpreted as a function of the axis size and is invariant to changes in the data scale on that axis. The rug lines are also slightly narrower by default. - Added a catch in :func:`distplot` when calculating a default number of bins. For highly skewed data it will now use sqrt(n) bins, where previously the reference rule would return "infinite" bins and cause an exception in matplotlib. diff --git a/seaborn/distributions.py b/seaborn/distributions.py index b5da64c662..687e6d83a0 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -533,19 +533,6 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", >>> ax = sns.kdeplot(x, y, n_levels=30, cmap="Purples_d") - Plot two shaded bivariate densities: - - .. plot:: - :context: close-figs - - >>> iris = sns.load_dataset("iris") - >>> setosa = iris.loc[iris.species == "setosa"] - >>> virginica = iris.loc[iris.species == "virginica"] - >>> ax = sns.kdeplot(setosa.sepal_width, setosa.sepal_length, - ... cmap="Reds", shade=True, shade_lowest=False) - >>> ax = sns.kdeplot(virginica.sepal_width, virginica.sepal_length, - ... cmap="Blues", shade=True, shade_lowest=False) - Use a narrower bandwith: .. plot:: @@ -567,6 +554,19 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", >>> ax = sns.kdeplot(x, cut=0) + Plot two shaded bivariate densities: + + .. plot:: + :context: close-figs + + >>> iris = sns.load_dataset("iris") + >>> setosa = iris.loc[iris.species == "setosa"] + >>> virginica = iris.loc[iris.species == "virginica"] + >>> ax = sns.kdeplot(setosa.sepal_width, setosa.sepal_length, + ... cmap="Reds", shade=True, shade_lowest=False) + >>> ax = sns.kdeplot(virginica.sepal_width, virginica.sepal_length, + ... cmap="Blues", shade=True, shade_lowest=False) + """ if ax is None: ax = plt.gca() From d3da2c4457be0a3e441a0c466e8533d8033d2b61 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 22 Jun 2015 08:54:54 -0700 Subject: [PATCH 0042/1738] Add new categorical plots tutorial --- doc/.gitignore | 4 +- doc/releases/v0.6.0.txt | 2 +- doc/tutorial.rst | 5 +- doc/tutorial/Makefile | 4 +- doc/tutorial/categorical.ipynb | 573 +++++++++ doc/tutorial/categorical_linear_models.ipynb | 470 ------- doc/tutorial/quantitative_linear_models.ipynb | 1085 ----------------- doc/tutorial/regression.ipynb | 593 +++++++++ 8 files changed, 1173 insertions(+), 1563 deletions(-) create mode 100644 doc/tutorial/categorical.ipynb delete mode 100644 doc/tutorial/categorical_linear_models.ipynb delete mode 100644 doc/tutorial/quantitative_linear_models.ipynb create mode 100644 doc/tutorial/regression.ipynb diff --git a/doc/.gitignore b/doc/.gitignore index 5117317b8a..30dbc876a8 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -6,8 +6,8 @@ example_thumbs/ aesthetics.rst color_palettes.rst distributions.rst -quantitative_linear_models.rst -categorical_linear_models.rst +regression.rst +categorical.rst plotting_distributions.rst dataset_exploration.rst timeseries_plots.rst diff --git a/doc/releases/v0.6.0.txt b/doc/releases/v0.6.0.txt index c4de096492..b4ebbe379b 100644 --- a/doc/releases/v0.6.0.txt +++ b/doc/releases/v0.6.0.txt @@ -9,7 +9,7 @@ Additionally, the structure of the docs is changing in version 0.6. The API docs Changes and updates to categorical plots ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In version 0.6, the "categorical" plots have been unified with a common API. This new category of functions groups together plots that show the relationship between one numeric variable and one or two categorical variables. This includes plots that show distribution of the numeric variable in each bin (:func:`barplot`, :func:`pointplot`, and :func:`stripplot`) and plots that apply a statistical estimation within each bin (:func:`pointplot`, :func:`barplot`, and :func:`countplot`). +In version 0.6, the "categorical" plots have been unified with a common API. This new category of functions groups together plots that show the relationship between one numeric variable and one or two categorical variables. This includes plots that show distribution of the numeric variable in each bin (:func:`boxplot`, :func:`violinplot`, and :func:`stripplot`) and plots that apply a statistical estimation within each bin (:func:`pointplot`, :func:`barplot`, and :func:`countplot`). These functions now each accept the same formats of input data and can be invoked in the same way. They can plot using long- or wide-form data, and can be drawn vertically or horizontally. When long-form data is used, the orientation of the plots is inferred from the types of the input data. Additionally, all functions natively take a ``hue`` variable to add a second layer of categorization. diff --git a/doc/tutorial.rst b/doc/tutorial.rst index 440b7c2644..1967b874c7 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -19,9 +19,8 @@ Plotting functions :maxdepth: 2 tutorial/distributions - tutorial/quantitative_linear_models - tutorial/categorical_linear_models - tutorial/dataset_exploration + tutorial/regression + tutorial/categorical Structured grids ---------------- diff --git a/doc/tutorial/Makefile b/doc/tutorial/Makefile index 90d9dc314a..1de4491689 100644 --- a/doc/tutorial/Makefile +++ b/doc/tutorial/Makefile @@ -3,7 +3,7 @@ notebooks: tools/nb_to_doc.py aesthetics tools/nb_to_doc.py color_palettes tools/nb_to_doc.py distributions - tools/nb_to_doc.py quantitative_linear_models - tools/nb_to_doc.py categorical_linear_models + tools/nb_to_doc.py regression + tools/nb_to_doc.py categorical tools/nb_to_doc.py dataset_exploration tools/nb_to_doc.py axis_grids diff --git a/doc/tutorial/categorical.ipynb b/doc/tutorial/categorical.ipynb new file mode 100644 index 0000000000..2a44b27326 --- /dev/null +++ b/doc/tutorial/categorical.ipynb @@ -0,0 +1,573 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": {}, + "source": [ + ".. currentmodule:: seaborn\n", + "\n", + ".. _categorical_tutorial::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plotting with categorical data" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "We :ref:`previously ` learned how to use scatterplots and regression model fits to visualize the relationship between two variables and how it changes across levels of additional categorical variables. However, what if one of the main variables you are interested in is categorical? In this case, the scatterplot and regression model approach won't work. There are several options, however, for visualizing such a relationship, which we will discuss in this tutorial.\n", + "\n", + "It's useful to divide seaborn's categorical plots into two groups: those that show the full distribution of observations within each level of the categorical variable, and those that apply a statistical estimation to show a measure of central tendency and confidence interval. The former includes the functions :func:`stripplot`, :func:`boxplot`, and :func:`violinplot`, while the latter includes the functions :func:`barplot`, :func:`countplot`, and :func:`pointplot`. These functions all share a basic API for how they accept data, although each has specific parameters that control the particulars of the visualization that is applied to that data.\n", + "\n", + "Much like the relationship between :func:`regplot` and :func:`lmplot`, in seaborn there are both relatively low-level and relatively high-level approaches for making categorical plots. The functions named above are all low-level in that they plot onto a specific matplotlib axes. There is also the higher-level :func:`factorplot`, which combines these functions with a :class:`FacetGrid` to apply a categorical plot across a grid of figure panels.\n", + "\n", + "It is easiest and best to invoke these functions with a DataFrame that is in `\"tidy\" `_ format, although the lower-level functions also accept wide-form DataFrames or simple vectors of observations. See below for examples." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib as mpl\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import seaborn as sns\n", + "sns.set(style=\"whitegrid\", color_codes=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "np.random.seed(sum(map(ord, \"categorical\")))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "titanic = sns.load_dataset(\"titanic\")\n", + "tips = sns.load_dataset(\"tips\")\n", + "iris = sns.load_dataset(\"iris\")" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Distributions of observations within categories\n", + "-----------------------------------------------\n", + "\n", + "The first set of functions shows the full distribution of the quantitative variable within each level of the categorical variable(s). These generalize some of the approaches we discussed in the :ref:`chapter ` to the case where we want to quickly compare across several distributions.\n", + "\n", + "Categorical scatterplots\n", + "^^^^^^^^^^^^^^^^^^^^^^^^\n", + "\n", + "A simple way to show the distribution of some quantitative variable across the levels of a categorical variable uses :func:`stripplot`, which generalizes a scatterplot to the case where one of the variables is categorical:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.stripplot(x=\"day\", y=\"total_bill\", data=tips);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "It's also possible to add a nested categorical variable with the ``hue`` paramater. Above the color and position on the categorical axis are redundent, but now each provides information about one of the two variables:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.stripplot(x=\"day\", y=\"total_bill\", hue=\"time\", data=tips);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.stripplot(x=\"size\", y=\"total_bill\", data=tips.sort(\"size\"));" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "With these plots, it's often helpful to put the categorical variable on the vertical axis (this is particularly useful when the category names are relatively long or there are many categories):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.stripplot(x=\"total_bill\", y=\"day\", hue=\"time\", data=tips);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Boxplots\n", + "^^^^^^^^\n", + "\n", + "At a certain point, the categorical scatterplot approach becomes limited in the information it can provide about the distribution of values within each category. There are several ways to summarize this information in ways that facilitate easy comparisons across the category levels.\n", + "\n", + "The first is the familiar :func:`boxplot`. This kind of plot shows the three quartile values of the distribution along with extreme values. The \"whiskers\" extend to points that lie within 1.5 IQRs of the lower and upper quartile, and then observations that fall outside this range are displayed independently. Importantly, this means that each value in the boxplot corresponds to an actual observation in the data:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.boxplot(x=\"day\", y=\"total_bill\", hue=\"time\", data=tips);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Violinplots\n", + "^^^^^^^^^^^\n", + "\n", + "A different approach is a :func:`violinplot`, which combines a boxplot with the kernel density estimation procedure described in the :ref:`distributions tutorial `_:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.violinplot(x=\"total_bill\", y=\"day\", hue=\"time\", data=tips);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "This approach uses the kernel density estimate to provide a better description of the distribution of values. Additionally, the quartile and whikser values from the boxplot are shown inside the violin. Because the violinplot uses a KDE, there are some other parameters that may need tweaking, adding some complexity relative to the straightforward boxplot:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.violinplot(x=\"total_bill\", y=\"day\", hue=\"time\", data=tips,\n", + " bw=.1, scale=\"count\", scale_hue=False);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "It's also possible to \"split\" the violins when the hue parameter has only two levels, which can allow for a more efficient use of space:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.violinplot(x=\"day\", y=\"total_bill\", hue=\"sex\", data=tips, split=True);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Finally, there are several options for the plot that is drawn on the interior of the violins, including ways to show each individual observation instead of the summary boxplot values:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.violinplot(x=\"day\", y=\"total_bill\", hue=\"sex\", data=tips,\n", + " split=True, inner=\"stick\", palette=\"Set3\");" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "It can also be useful to combine :func:`stripplot` with :func:`violinplot` or :func:`boxplot` to show each observation along with a summary of the distribution:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.violinplot(x=\"day\", y=\"total_bill\", data=tips, inner=None)\n", + "sns.stripplot(x=\"day\", y=\"total_bill\", data=tips, jitter=True, size=4);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Statistical estimation within categories\n", + "----------------------------------------\n", + "\n", + "Often, rather than showing the distribution within each category, you might want to show the central tendency of the values. Seaborn has two main ways to show this information, but importantly, the basic API for these functions is identical to that for the ones discussed above.\n", + "\n", + "Bar plots\n", + "^^^^^^^^^\n", + "\n", + "A familiar style of plot that accomplishes this goal is a bar plot. In seaborn, the :func:`barplot` function operates on a full dataset and shows an arbitrary estimate, using the mean by default. When there are multiple observations in each category, it also uses bootstrapping to compute a confidence interval around the estimate and plots that using error bars:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.barplot(x=\"sex\", y=\"survived\", hue=\"class\", data=titanic);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "A special case for the bar plot is when you want to show the number of observations in each category rather than computing a statistic for a second variable. This is similar to a histogram over a categorical, rather than quantitative, variable. In seaborn, it's easy to do so with the :func:`countplot` function:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.countplot(x=\"deck\", data=titanic, palette=\"Greens_d\");" + ] + }, + { + "cell_type": "raw", + "metadata": { + "collapsed": false + }, + "source": [ + "Both :func:`barplot` and :func:`countplot` can be invoked with all of the options discussed above, along with others that are demonstrated in the detailed documentation for each function:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.countplot(y=\"deck\", hue=\"class\", data=titanic, palette=\"Greens_d\");" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Point plots\n", + "^^^^^^^^^^^\n", + "\n", + "An alternative style for visualizing the same information is offered by the :func:`pointplot` function. This function also encodes the value of the estimate with height on the other axis, but rather than show a full bar it just plots the point estimate and confidence interval. Additionally, pointplot connects points from the same ``hue`` category. This makes it easy to see how the main relationship is changing as a function of a second variable, because your eyes are quite good at picking up on differences of slopes:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.pointplot(x=\"sex\", y=\"survived\", hue=\"class\", data=titanic);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "To make figures that reproduce well in black and white, it can be good to use different markers and line styles for the levels of the ``hue`` category:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.pointplot(x=\"class\", y=\"survived\", hue=\"sex\", data=titanic,\n", + " palette={\"male\": \"g\", \"female\": \"m\"},\n", + " markers=[\"^\", \"o\"], linestyles=[\"-\", \"--\"]);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Drawing multi-panel categorical plots\n", + "-------------------------------------\n", + "\n", + "As we mentioned above, there are two ways to draw categorical plots in seaborn. Similar to the duality in the regression plots, you can either use the functions introduced above, or the higher-level function :func:`factorplot`, which combines these functions with a :func:`FacetGrid` to add the ability to examine additional categories through the larger structure of the figure.\n", + "\n", + "While the main options for each plot kind are available either way, the lower-level functions have a bit more flexibility in the kind of inputs they can take. For instance, you can just pass a ``DataFrame`` to the ``data`` parameter, and the distribution or central tendency of each *column* in the dataframe will be shown:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.boxplot(data=iris, orient=\"h\");" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Additionally, these functions accept vectors of Pandas or numpy objects rather than variables in a ``DataFrame``:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.violinplot(x=iris.species, y=iris.sepal_length);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "To control the size and shape of plots made by the functions discussed above, you must set up the figure yourself using matplotlib commands. Of course, this also means that the plots can happily coexist in a multi-panel figure with other kinds of plots:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "f, ax = plt.subplots(figsize=(7, 3))\n", + "sns.countplot(y=\"deck\", data=titanic, color=\"c\");" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "The :func:`factorplot` function is a higher-level wrapper on these plots that produces a matplotlib figure managed through a :class:`FacetGrid`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.factorplot(x=\"day\", y=\"total_bill\", hue=\"smoker\", data=tips);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "By default it uses :func:`pairplot`, but the ``kind`` parameter lets you chose any of the kinds of plots discussed above:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.factorplot(x=\"day\", y=\"total_bill\", hue=\"smoker\", data=tips, kind=\"bar\");" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "The key advantage of :func:`factorplot` is that it's easy to add faceting by additional variables in the ``DataFrame``, such as along the columns:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.factorplot(x=\"day\", y=\"total_bill\", hue=\"smoker\",\n", + " col=\"time\", data=tips, kind=\"bar\");" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Any kind of plot can be drawn. Because of the way :class:`FacetGrid` works, to change the size and shape of the figure you need to specify the ``size`` and ``aspect`` arguments, which apply to a single facet:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.factorplot(x=\"time\", y=\"total_bill\", hue=\"smoker\",\n", + " col=\"day\", data=tips, kind=\"box\", size=4, aspect=.5);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Because of the generalized API of the categorical plots, they should be easy to apply to other more complex contexts. For example, they are easily combined with a :class:`PairGrid` to show categorical relationships across several different variables:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "g = sns.PairGrid(tips,\n", + " x_vars=[\"smoker\", \"time\", \"sex\"],\n", + " y_vars=[\"total_bill\", \"tip\"],\n", + " aspect=.75, size=3.5)\n", + "g.map(sns.violinplot, palette=\"pastel\");" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.10" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/doc/tutorial/categorical_linear_models.ipynb b/doc/tutorial/categorical_linear_models.ipynb deleted file mode 100644 index c845614974..0000000000 --- a/doc/tutorial/categorical_linear_models.ipynb +++ /dev/null @@ -1,470 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ".. _linear_categorical::\n", - "\n", - ".. currentmodule:: seaborn" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Linear models with categorical data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "import seaborn as sns\n", - "import matplotlib as mpl\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.set(style=\"whitegrid\")\n", - "np.random.seed(sum(map(ord, \"linear_categorical\")))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "titanic = sns.load_dataset(\"titanic\")\n", - "exercise = sns.load_dataset(\"exercise\")\n", - "attend = sns.load_dataset(\"attention\")" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ".. _factorplot:\n", - "\n", - "Plotting categorical data with :func:`factorplot`\n", - "-------------------------------------------------\n", - "\n", - "As with the quantitative functions :func:`lmplot` and :func:`regplot`, you can draw categorical plots with functions that operate at two different levels. In most cases, you'll want to use the :func:`factorplot` function. Like :func:`lmplot`, it plots onto a :class:`FacetGrid` and can visualize :ref:`a lot ` of data quickly. However, in some cases you may want a bit more control over the figure you're making, in which case you can use the lower-level functions :func:`pointplot` and :func:`barplot`. The :func:`factorplot` function is using these behind the scenes, and you can control which gets used with the ``kind`` parameter.\n", - "\n", - "The API for the :func:`factorplot` function will be familiar by now. It draws data from a tidy DataFrame, and the positional arguments specify the names of variables that will be placed on the x and y axes of the plot. :func:`factorplot` also takes a third positional argument. It is named ``hue``, as it plays a similar role as the ``hue`` variable in :func:`lmplot`, plotting subsets of the data for easy direct comparison. However, in some cases the ``hue`` variable will also affect the location on the x axis where data is plotted. Because these functions are intended for use with *categorical* data, the x axis is not quantitatively represented. However, there will be cases where the x axis has a natural ordering.\n", - "\n", - "The two main kinds of categorical plots show the same data, but with a different emphasis. ``point`` plots are better for comparing between conditions:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"kind\", \"pulse\", \"diet\", exercise, kind=\"point\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Whereas ``bar`` plots are better for understanding overall magnitude and how far it is from 0:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"kind\", \"pulse\", \"diet\", exercise, kind=\"bar\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "You can also plot a :func:`factorplot` with a boxplot representation (using ``kind=\"box\"``). While the above plots focus on the central tendency of the data (with a measure of the error associated with that value), the boxplot should be used when you care about the *distribution* of the data in different categories." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"kind\", \"pulse\", \"diet\", exercise, kind=\"box\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "When the ``kind`` is not specified, :func:`factorplot` uses a few heuristic rules to choose the appropriate kind of plot to draw. These are pretty rough, and may change over time, so it's better to specify." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"kind\", \"pulse\", \"diet\", exercise);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Naturally, you can specify the palette to render the ``hue`` variable in. Any seaborn palette definition will work, and you can also pass a dictionary mapping values of the ``hue`` variable to colors." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"class\", \"fare\", \"sex\", data=titanic, palette=\"Pastel1\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Options for grouping the categories\n", - "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - "\n", - "It's not necessary to use a ``hue`` variable:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"class\", \"fare\", data=titanic);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "And you can then use the palette to map the ``x`` variable:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"class\", \"fare\", data=titanic, palette=\"Greens_d\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "In fact, you don't need to provide a ``y`` variable either. When ``y`` is missing, the height of the plot shows the *count* of observations in each category:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"class\", data=titanic, palette=\"PuBuGn_d\");" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"class\", hue=\"sex\", data=titanic, palette=\"Pastel1\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Estimators of central tendency and their error\n", - "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - "\n", - "By default the height of the bars/points shows the mean and 95% confidence interval, but both can be changed." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"class\", \"fare\", data=titanic, palette=\"Greens_d\", estimator=np.median);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Remember, the 68% confidence interval shows the standard error of the estimator:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"class\", \"fare\", data=titanic, kind=\"point\", ci=68);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Plotting on different facets\n", - "^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - "\n", - "Remember, :func:`factorplot` is using a :class:`FacetGrid`, so all of the :ref:`options ` for structuring the plot into different subsets are available:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"class\", \"survived\", \"sex\", data=titanic, col=\"sex\", aspect=.5, palette=\"Set1\");" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"deck\", data=titanic, row=\"class\", x_order=list(\"ABCDEF\"),\n", - " margin_titles=True, aspect=3, size=2, palette=\"BuPu_d\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Choices in visual presentation\n", - "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - "\n", - "There are a few other choices for how the plot gets drawn when you use a ``point`` plot. Sometimes, the errorbars for different hue categories will overlap:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"solutions\", \"score\", \"attention\", attend);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "When this happens, you may want to \"dodge\" the different hue categories a bit so the extent of the overlap is more clear:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"solutions\", \"score\", \"attention\", attend, dodge=.05);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "It's also not strictly necessary to join the points for each of the different ``hue`` levels:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"solutions\", \"score\", \"attention\", attend, join=False, dodge=.2);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "The ``x`` and ``hue`` values are plotted in sorted order by default, but sometimes it makes more sense to provide a specific order:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"time\", \"pulse\", \"kind\", exercise, col=\"diet\",\n", - " hue_order=[\"rest\", \"walking\", \"running\"],\n", - " palette=\"YlGnBu_d\", aspect=.75);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Although by default the ``hue`` variable is only mapped to different colors (by the ``palette`` argument), you can also use different markers and linestyles for each level of the ``hue`` variable:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.factorplot(\"solutions\", \"score\", \"attention\", attend,\n", - " markers=[\"o\", \"D\"], linestyles=[\"-\", \"--\"]);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ".. _barplot::\n", - ".. _pointplot::\n", - "\n", - "Plotting with :func:`pointplot` and :func:`barplot` \n", - "---------------------------------------------------\n", - "\n", - "As noted above, :func:`factorplot` is a combination of a :class:`FacetGrid` and a lower-level plotting function. If you want to built up a more complicated figure with different kind of presentations in different subplots, you can use the :func:`pointplot` and :func:`barplot` functions directly. They take all of the same arguments as :func:`factorplot`, aside from those that control the faceting." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "f, (ax1, ax2) = plt.subplots(1, 2)\n", - "sns.pointplot(\"time\", \"pulse\", \"kind\", data=exercise, ax=ax1)\n", - "sns.barplot(\"kind\", \"pulse\", \"time\", data=exercise, ax=ax2);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Like :func:`regplot`, the lower-level categorical functions also accept their data directly in the form of a Series or array." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "survivors = titanic.query(\"alive == 'yes'\")\n", - "gs = plt.GridSpec(3, 1)\n", - "plt.subplot(gs[:2])\n", - "ax1 = sns.pointplot(survivors[\"class\"], survivors[\"age\"], label=\"survivors\", color=\".3\")\n", - "ax1.set(xticks=[], xlabel=\"\")\n", - "plt.subplot(gs[-1])\n", - "ax2 = sns.barplot(survivors[\"class\"], color=\".5\")\n", - "ax1.set_xlim(ax2.get_xlim())\n", - "sns.despine(left=True, bottom=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [] - } - ], - "metadata": {}, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/doc/tutorial/quantitative_linear_models.ipynb b/doc/tutorial/quantitative_linear_models.ipynb deleted file mode 100644 index c288c91051..0000000000 --- a/doc/tutorial/quantitative_linear_models.ipynb +++ /dev/null @@ -1,1085 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ".. _linear_quantitative::\n", - "\n", - ".. currentmodule:: seaborn" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Linear models with quantitative data" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Linear models are very common in statistical analysis. They are used to understand how linear combinations of *predictor* (or *independent*) variables relate to a *response* (or *dependent*) variable. Seaborn has several functions for exploratory visualizations that correspond with linear regression. This page will focus on the functions that can be used when the main predictor variable (or variables) are quantitative. They differ from functions focused on :ref:`categorical variables ` in that they fit and plot a representation of the model itself in the form of a regression line." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "import seaborn as sns\n", - "import matplotlib as mpl\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "np.random.seed(sum(map(ord, \"linear_quantitative\")))" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ".. _lmplot:\n", - "\n", - "Visualizing multiple regression with :func:`lmplot`\n", - "---------------------------------------------------\n", - "\n", - "The :func:`lmplot` function is intended for exploring linear relationships of different forms in multidimensional datasets. Input data must be in a Pandas ``DataFrame`. To plot, provide the predictor and response variable names along with the dataset:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "tips = sns.load_dataset(\"tips\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"total_bill\", \"tip\", tips);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "This plot has two main components. The first is a scatterplot, showing the observed datapoints. The second is a regression line, showing the estimated linear model relating the two variables. Because the regression line is only an *estimate*, it is plotted with a 95% confidence band to give an impression of the certainty in the model. You can plot different levels of certainty. For instance, it is common to plot the *standard error* of an estimate, which corresponds to the 68% confidence interval." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"total_bill\", \"tip\", tips, ci=68);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Hopefully, the default aesthetics are suitable for exploratory graphics. However, you can also control the aesthetics of the underlying plots separately through dictionaries of keyword arguments for the matplotlib ``scatter`` and ``plot`` functions." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"total_bill\", \"tip\", tips,\n", - " scatter_kws={\"marker\": \".\", \"color\": \"slategray\"},\n", - " line_kws={\"linewidth\": 1, \"color\": \"seagreen\"});" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Plotting with discrete predictor variables\n", - "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - "\n", - "Sometimes you will want to plot data where the independent variable is quantitative, but discrete. Although this works fine out of the box:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"size\", \"tip\", tips);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "And can be improved with a bit of jitter:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"size\", \"tip\", tips, x_jitter=.15);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "It might be more informative to estimate the central tendency of each bin. This is easy to do with the x_estimator argument. Just pass any function that aggregates a vector of data into one estimate. The estimator will be bootstrapped and a confidence interval will be plotted -- 95% by default, as in other cases within these functions." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"size\", \"tip\", tips, x_estimator=np.mean);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "It can also be useful to bin continuous predictor variable into discrete values and plot an estimated central tendency and confidence interval. This can be helpful when you have many datapoints and a reliable, but weak, effect. Note that the regression estimate will still fit to the original data; the binning only applies to the visual representation of the observations.\n", - "\n", - "With :func:`lmplot`, you can provide specific centroid values for the bins:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "bins = [10, 20, 30, 40]\n", - "sns.lmplot(\"total_bill\", \"tip\", tips, x_bins=bins);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Or, you can give a number of bins and it will find centroids so that they have equal numbers of datapoints in them:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"total_bill\", \"tip\", tips, x_bins=8);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Faceted linear model plots\n", - "^^^^^^^^^^^^^^^^^^^^^^^^^^" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "The :func:`lmplot` function is built on top of a :class:`FacetGrid`. That means it's easy to visualize how this relationship changes in different subsets of your dataset. You can read the extended :ref:`documentation ` for more details on how the :class:`FacetGrid` class works. The important thing is that you can supply the names of categorical variables that define subsets of the data to plot in different hues or along the row and columns of a grid of axes.\n", - "\n", - "Using a `hue` facet makes it easiest to directly compare the two subsets:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"total_bill\", \"tip\", tips, hue=\"smoker\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "To make plots that will better reproduce to black-and-white (i.e. when printed), you may want to let the scatterplot marker vary along with the hue variable." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"total_bill\", \"tip\", tips, hue=\"smoker\", markers=[\"x\", \"o\"]);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Plotting in different columns of a grid also makes a plot that's easy to understand, although direct comparisons between the subsets are more difficult as the data are separated in space. This might be better when you want the viewer to focus on the relationship within each subset independently." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"total_bill\", \"tip\", tips, col=\"smoker\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "You can also assign the same variable to multiple roles. This lets you plot the data separately and use color to semantically tag different subsets of the data. Using color in this way can be helpful when you are making multiple different kind of plots, and you want to help connect them visually." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"total_bill\", \"tip\", tips, col=\"smoker\", hue=\"smoker\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ":func:`lmplot` accepts all arguments that you would use to initialize a :class:`FacetGrid`, and it returns the grid object after plotting for further tweaking:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "g = sns.lmplot(\"total_bill\", \"tip\", tips, hue=\"day\", palette=\"Set2\",\n", - " hue_order=[\"Thur\", \"Fri\", \"Sat\", \"Sun\"])\n", - "g.set_axis_labels(\"Total bill (US Dollars)\", \"Tip\");\n", - "g.set(xticks=[10, 30, 50], ylim=(0, 10), yticks=[0, 2.5, 5, 7.5, 10]);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Plotting different linear relationships\n", - "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - "\n", - "By default, :func:`lmplot` shows the ordinary least squares fit to the data. However, you don't actually need to fit a regression at all:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"total_bill\", \"tip\", tips, hue=\"time\", palette=\"Set1\", fit_reg=False);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "There are several other choices for how to fit the regression. These options are mutually exclusive. \n", - "\n", - "Plotting nonlinear trends\n", - "~~~~~~~~~~~~~~~~~~~~~~~~~\n", - "\n", - "You might, for example, wonder if the relationship follows a higher-order trend:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"size\", \"total_bill\", tips, order=2);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "For an even more flexible fit, you can plot the *lowess* line, which is a nonparametric approach to regression. Note that, currently, lowess plots don't plot a confidence interval around the line." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"total_bill\", \"tip\", tips, lowess=True, line_kws={\"color\": \".2\"});" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Plotting logistic regression\n", - "~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", - "\n", - "Let's define a dependent measure that takes the values ``True`` and ``False``." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "tips[\"big_tip\"] = (tips[\"tip\"] / tips[\"total_bill\"]) > .15" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "It's possible to plot (and fit) these data as normal:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"total_bill\", \"big_tip\", tips);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "This plot suggests that diners with a larger total bill are less likely to leave a big tip, but it has a few issues. The first is that the individual observations are all plotted on top of each other, so their distribution is obscured. We can address this issue by adding a bit of jitter to the scatter plot." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"total_bill\", \"big_tip\", tips, y_jitter=.05);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "A more fundamental problem follows from using basic linear regression with a binary response variable. You might want to read the y axis as the *probability* of a big tip, but this regression line implies a negative probability when the bill is greater than $50. Because that doesn't make sense, :func:`lmplot` can fit a *logistic regression* and plot the resulting curve over the data:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"total_bill\", \"big_tip\", tips, y_jitter=.05, logistic=True);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Plotting data with outliers\n", - "~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", - "\n", - "Sometimes, data will have outliers. Because linear regression is based on the mean, it can be overly influenced by outliers, especially in cases with relatively few datapoints. :func:`lmplot` can also plot the regression model using robust estimation, which reduces the influence of outlying points.\n", - "\n", - "Robust estimation is computationally intensive, so you may want to reduce the number of bootstrap iterations when using it." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"total_bill\", \"tip\", tips, robust=True, n_boot=500);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Controlling for confounding variables\n", - "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - "\n", - "A major feature of multiple regression is the ability to \"control for\" confounding variables. Seaborn brings this ability into the visualization realm, allowing you to regress variables out of the predictor and/or response variables before plotting. Let's make a simple three-variable dataset where variables A and B appear related, but are actually independent once you account for variable C." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "c = np.random.randn(50)\n", - "a = 2 + c + np.random.randn(50)\n", - "b = 3 + c + np.random.randn(50)\n", - "df = pd.DataFrame(dict(A=a, B=b, C=c), columns=list(\"ABC\"))" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "If you plot the relationship between A and B, they will look related:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"A\", \"B\", df);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "However, if you remove (or \"partial out\") variable C from variable A before plotting, you can see that they are in fact independent." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.lmplot(\"A\", \"B\", df, x_partial=\"C\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ".. _regplot:\n", - "\n", - "Plotting simple regression with :func:`regplot`\n", - "-----------------------------------------------\n", - "\n", - "For most exploratory uses, :func:`lmplot` should easily produce an informative graphic. In some cases, though, you may want to draw a scatterplot and regression model in the context of a figure with different subplots. Because the :class:`FacetGrid` initializes its own figure and controls the whole subplot grid, it can't be used here.\n", - "\n", - "In fact, :func:`lmplot` is just using a lower-level function, :func:`regplot`, to draw the data and the regression line. :func:`regplot` is lower-level in the sense that it can plot onto an existing Axes and doesn't modify anything about the figure it will be drawn onto. In situations where you want more control, it might be advantageous to use :func:`regplot` directly." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "f, (ax1, ax2) = plt.subplots(1, 2, sharey=True)\n", - "sns.regplot(\"total_bill\", \"tip\", tips, ax=ax1)\n", - "sns.boxplot(tips[\"tip\"], tips[\"size\"], color=\"Blues_r\", ax=ax2).set_ylabel(\"\")\n", - "f.tight_layout()" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Because :func:`lmplot` is using :func:`regplot` behind the scenes, all of the options for binning the datapoints, fitting the regression, and modifying the aesthetics are shared. What's missing from :func:`regplot` is the faceting options, so each call can only draw a single scatterplot and regression line.\n", - "\n", - "The other difference is that :func:`regplot` also accepts data passed directly as numpy arrays or pandas series objects, if you don't have your data organized into a dataframe. You can also directly control the color of the plot:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "x, y = np.random.multivariate_normal([1, 5], [(2, -.8), (-.8, 2)], 80).T\n", - "ax = sns.regplot(x, y, color=\"seagreen\")\n", - "ax.set(xlabel=\"x variable\", ylabel=\"y variable\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ".. _residplot:\n", - "\n", - "Examining model residuals using :func:`residplot`\n", - "-------------------------------------------------\n", - "\n", - "The validity of linear models are based on a set of assumptions, several of which are about the distribution of the model residuals. Looking at the residuals of your model can also clue you in to interesting higher-order trends that you might otherwise miss.\n", - "\n", - "It's easy to visualize the residuals of a simple model in seaborn using the :func:`residplot` function. It looks a lot like :func:`regplot`, but instead of plotting the observations and regression model, it plots the residuals:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.residplot(x, y);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Ideally, the residuals should be scattered about the zero line, without any apparent structure.\n", - "\n", - "Let's see what happens when we use data that actually has a higher-order relationship:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "y = x + 1.5 * x ** 2 + np.random.randn(len(x))\n", - "sns.residplot(x, y, color=\"indianred\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Although the structure here is pretty obvious, you can help visualize it by fitting a lowess smoother to the residuals:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.residplot(x, y, color=\"indianred\", lowess=True);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ":func:`residplot` lets you calculate the residuals with a polynomial regression. Let's see if fitting a second-order regression removes this structure:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.residplot(x, y, color=\"indianred\", order=2, lowess=True);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ".. _jointplot_reg:\n", - "\n", - "Plotting marginal distributions using :func:`jointplot`\n", - "-------------------------------------------------------\n", - "\n", - "When you are interested in a single relationship, you may want to use :func:`jointplot` to draw a regression plot along with the marginal distributions of the two variables. By default, :func:`jointplot` just draws a scatterplot:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.jointplot(\"total_bill\", \"tip\", tips);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "However, if you set ``kind=\"reg\"``, it will fit and bootstrap a regression:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.jointplot(\"total_bill\", \"tip\", tips, kind=\"reg\", color=\"seagreen\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "There is also a mode for plotting model residuals with :func:`residplot`. This also fits a gaussian distribution to the marginal distribution on ``y`` to help you judge whether the residuals are normally distributed around 0:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.jointplot(\"total_bill\", \"tip\", tips, kind=\"resid\", color=\"#774499\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "The :func:`jointplot` function is using a :class:`JointGrid` to draw this plot. See the :ref:`docs ` to see more information about the options to this function and how you can use :class:`JointGrid` directly for more flexibility." - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ".. _interactplot:\n", - "\n", - "Describing continuous interactions with :func:`interactplot`\n", - "------------------------------------------------------------\n", - "\n", - "Hopefully, :func:`lmplot` is useful for a variety of visualization problems. However, one limitation is that it can plot, at most, a single continuous predictor variable. Although two-way interactions between continuous variables are often interesting, they can be difficult to visualize and understand.\n", - "\n", - "Let's make some fake data with a two-way interaction to show how you might approach this problem." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "rs = np.random.RandomState(1)\n", - "x1, x2 = rs.normal(size=(2, 100))\n", - "y1 = .5 * x1 + 2 * x2 + 2 * x1 * x2 + rs.normal(size=100)\n", - "y2 = x1 + x2 + rs.normal(size=100)\n", - "p = 1 / (1 + np.exp(-y1))\n", - "y_flip = [rs.binomial(1, p_i) for p_i in p]\n", - "df = pd.DataFrame(dict(x1=x1, x2=x2, y1=y1, y2=y2, y_flip=y_flip))" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "One approach is to bin one of the predictors and then plot the data as before, treating the predictor as categorical." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "bins = np.array([-1, 0, 1])\n", - "binned = np.digitize(x2, bins)\n", - "df[\"x2_bin\"] = binned\n", - "sns.lmplot(\"x1\", \"y1\", df, col=\"x2_bin\", hue=\"x2_bin\", palette=\"PuBuGn_d\", size=3);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "This is serviceable, but lacking in several ways. It requires several cumbersome steps, the choice of the bin size is arbitrary, and collapsing the continuous data into categories loses information.\n", - "\n", - "An alternative approach plots the two independent variables on the x and y axes of a plot and color-encodes the model predictions with a contour plot. This maintains the continuous nature of the data. The seaborn function :func:`interactplot` draws such a plot using an interface similar to :func:`regplot`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.interactplot(x1, x2, y1);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Naturally, you can directly pass a dataframe and adjust the aesthetics of the plot:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.interactplot(\"x1\", \"x2\", \"y1\", df, cmap=\"coolwarm\", filled=True, levels=25);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "The two underlying plot functions are ``contourf()`` and ``plot()``, both of which can be tweaked with a keyword argument dictionary.\n", - "\n", - "Note the appearance when we plot data that was not simulated with an interaction:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.interactplot(\"x1\", \"x2\", \"y2\", df, filled=True,\n", - " scatter_kws={\"color\": \"dimgray\"},\n", - " contour_kws={\"alpha\": .5});" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "This works for logistic regression too:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "pal = sns.blend_palette([\"#4169E1\", \"#DFAAEF\", \"#E16941\"], as_cmap=True)\n", - "levels = np.linspace(0, 1, 11)\n", - "sns.interactplot(\"x1\", \"x2\", \"y_flip\", df, levels=levels, cmap=pal, logistic=True);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Plotting many pairwise relationships with :func:`corrplot`\n", - "----------------------------------------------------------\n", - "\n", - "Sometimes, you want a birds-eye view of a large dataset to see what kind of relationships might exist in it. The :func:`corrplot` function will calculate a correlation matrix for a dataset and draw a heat map with the correlation values. By default, it also uses a permutation test to get p values for the correlation matrix in a way that corrects for the multiple comparisons." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "titanic = sns.load_dataset(\"titanic\").dropna()\n", - "attention = sns.load_dataset(\"attention\")\n", - "sns.set_context(rc={\"figure.figsize\": (8, 8)})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.corrplot(titanic);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Note that if you have a huge dataset, the permutation test will take a while. Of course, if you have a huge dataset, p values will not be particularly relevant, so you can turn off the significance testing.\n", - "\n", - "It’s also possible to choose a different colormap, but choose wisely! Don’t even try using the ``“jet”`` map; you’ll get a ``ValueError``. The colorbar itself is also optional." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.corrplot(titanic, sig_stars=False, cmap=\"RdBu_r\", cbar=False);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "By default, the colormap is centered on 0 and uses a diverging map, which is appropriate since 0 is a meaningful boundary and both large positive and negative values are interesting.\n", - "\n", - "Sometimes, though, you are only interested in either positive or negative values. In these cases, you can set the tail for the significance test, which will also change the default colormap to a sequential map." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.corrplot(titanic, sig_tail=\"upper\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "It’s also possible to specify the range for the colormap. Note that setting the test direction modifies the colormap and range, but the inverse is not true; the stars here will still correspond to a two-tailed test." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.corrplot(titanic, cmap_range=(-.3, 0));" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "You might also have many variables, in which case the correlation coefficient annotation may not fit well. In this case, it can be turned off, and the variable names can be moved to the sides of the plot:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "f, ax = plt.subplots(figsize=(10, 10))\n", - "cmap = sns.blend_palette([\"#00008B\", \"#6A5ACD\", \"#F0F8FF\",\n", - " \"#FFE6F8\", \"#C71585\", \"#8B0000\"], as_cmap=True)\n", - "d = np.random.standard_t(20, (100, 30))\n", - "sns.corrplot(d, annot=False, diag_names=False, cmap=cmap)\n", - "ax.grid(False);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Plotting model coefficients with :func:`coefplot`\n", - "-------------------------------------------------\n", - "\n", - "When building a complex linear model, it can be more effective to visualize the model coefficients than to preset them in a table. The :func:`coefplot` function takes a `patsy `_ formula string and fits the model specified by that string using `statsmodels `_. It then plots the model coefficients and confidence intervals:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.coefplot(\"tip ~ scale(total_bill) + size + time + sex + smoker\", tips);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "The intercept is ignored by default, but you can add it if it's relevant:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.coefplot(\"score ~ center(solutions) * attention\", attention, intercept=True, palette=\"Set1\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "When you have a dataset with repeated measures, :func:`coefplot` can fit the model separately for each unit and plot the coefficients so they can be easily compared across your sample:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.coefplot(\"score ~ center(solutions)\", attention, \"subject\", intercept=True);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "We hope that this collection of features will help you understand the relationships in your datasets. Please get in touch if you have a visualization problem in this domain that these tools don't seem suited for." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.9" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} diff --git a/doc/tutorial/regression.ipynb b/doc/tutorial/regression.ipynb new file mode 100644 index 0000000000..a914a16ee3 --- /dev/null +++ b/doc/tutorial/regression.ipynb @@ -0,0 +1,593 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": {}, + "source": [ + ".. currentmodule:: seaborn\n", + "\n", + ".. _regression_tutorial::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Visualizing linear relationships" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Many datasets contain multiple quantitative variables, and the goal of an analysis is often to relate those variables to each other. We :ref:`previously discussed ` functions that can accomplish this by showing the joint distribution of two variables. It can be very helpful, though, to use statistical models to estimate a simple relationship between two noisy sets of observations. The functions discussed in this chapter will do so through the common framework of linear regression.\n", + "\n", + "In the spirit of Tukey, the regression plots in seaborn are primarily intended to add a visual guide that helps to emphasize patterns in a dataset during exploratory data analyses. That is to say that seaborn is not itself a package for statistical analysis. To obtain quantitative measures related to the fit of regression models, you should use `statsmodels `_. The goal of seaborn, however, is to make exploring a dataset through visualization quick and easy, as doing so is just as (if not more) important than exploring a dataset through tables of statistics." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib as mpl\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import seaborn as sns\n", + "sns.set(color_codes=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "np.random.seed(sum(map(ord, \"regression\")))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "tips = sns.load_dataset(\"tips\")" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Functions to draw linear regression models\n", + "------------------------------------------\n", + "\n", + "Two main functions in seaborn are used to visualize a linear relationship as determined through regression. These functions, :func:`regplot` and :func:`lmplot` are closely related, and share much of their core functionality. It is important to understand the ways they differ, however, so that you can quickly choose the correct tool for particular job.\n", + "\n", + "In the simplest invocation, both functions draw a scatterplot of two variables, ``x`` and ``y``, and then fit the regression model ``y ~ x`` and plot the resulting regression line and a 95% confidence interval for that regression:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.regplot(x=\"total_bill\", y=\"tip\", data=tips);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"total_bill\", y=\"tip\", data=tips);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "You should note that the resulting plots are identical, except that the resulting figure shape is different. We will explain why this is shortly. For now, the other main difference to know about is that :func:`regplot` accepts the ``x`` and ``y`` variables in a variety of formats including simple numpy arrays, pandas ``Series`` objects, or as references to variables in a pandas ``DataFrame`` object passed to ``data``. In contrast, :func:`lmplot` has ``data`` as a required parameter and the ``x`` and ``y`` variables must be specified as strings. Other than this input flexibility, :func:`regplot` possesses a subset of :func:`lmplot`'s features, so we will demonstrate them using the latter.\n", + "\n", + "It's possible to fit a linear regression when one of the variables takes discrete values, however, the simple scatterplot produced by this kind of dataset is often not optimal:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"size\", y=\"tip\", data=tips);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "One option is to add some random noise (\"jitter\") to the discrete values to make the distribution of those values more clear. Note that jitter is applied only to the scatterplot data and does not influence the regression line fit itself:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"size\", y=\"tip\", data=tips, x_jitter=.05);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "A second option is to collapse over the observations in each discrete bin to plot an estimate of central tendency along with a confidence interval:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"size\", y=\"tip\", data=tips, x_estimator=np.mean);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Fitting different kinds of models\n", + "---------------------------------\n", + "\n", + "The simple linear regression model used above is very simple to fit, however, it is not appropriate for some kinds of datasets. The `Anscombe's quartet `_ dataset shows a few examples where simple linear regression provides an identical estimate of a relationship where simple visual inspection clearly shows differences. For example, in the first case, the linear regression is a good model:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "anscombe = sns.load_dataset(\"anscombe\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"x\", y=\"y\", data=anscombe.query(\"dataset == 'I'\"),\n", + " ci=None, scatter_kws={\"s\": 80});" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "The linear relationship in the second dataset is the same, but the plot clearly shows that this is not a good model:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"x\", y=\"y\", data=anscombe.query(\"dataset == 'II'\"),\n", + " ci=None, scatter_kws={\"s\": 80});" + ] + }, + { + "cell_type": "raw", + "metadata": { + "collapsed": true + }, + "source": [ + "In the presence of these kind of higher-order relationships, :func:`lmplot` and :func:`regplot` can fit a polynomial regression model to explore simple kinds of nonlinear trends in the dataset:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"x\", y=\"y\", data=anscombe.query(\"dataset == 'II'\"),\n", + " order=2, ci=None, scatter_kws={\"s\": 80});" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "A different problem is posed by \"outlier\" observations that deviate for some reason other than the main relationship under study:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"x\", y=\"y\", data=anscombe.query(\"dataset == 'III'\"),\n", + " ci=None, scatter_kws={\"s\": 80});" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "In the presence of outliers, it can be useful to fit a robust regression, which uses a different loss function to downweight relatively large residuals:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"x\", y=\"y\", data=anscombe.query(\"dataset == 'III'\"),\n", + " robust=True, ci=None, scatter_kws={\"s\": 80});" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "When the ``y`` variable is binary, simple linear regression also \"works\" but provides implausible predictions:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "tips[\"big_tip\"] = (tips.tip / tips.total_bill) > .15\n", + "sns.lmplot(x=\"total_bill\", y=\"big_tip\", data=tips,\n", + " y_jitter=.03);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "The solution in this case is to fit a logistic regression, such that the regression line shows the estimated probability of ``y = 1`` for a given value of ``x``:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"total_bill\", y=\"big_tip\", data=tips,\n", + " logistic=True, y_jitter=.03);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Note that the logistic regression estimate is considerably more computationally intensive (this is true of robust regression as well) than simple regression, and as the confidence interval around the regression line is computed using a bootstrap procedure, you may wish to turn this off for faster iteration (using ``ci=False``).\n", + "\n", + "An altogether different approach is to fit a nonparametric regression using a `lowess smoother `_. This approach has the fewest assumptions, although it is computationally intensive and so currently confidence intervals are not computed at all:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"total_bill\", y=\"tip\", data=tips,\n", + " lowess=True);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "The :func:`residplot` function can be a useful tool for checking whether the simple regression model is appropriate for a dataset. It fits and removes a simple linear regression and then plots the residual values for each observation. Ideally, these values should be randomly scattered around ``y = 0``:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.residplot(x=\"x\", y=\"y\", data=anscombe.query(\"dataset == 'I'\"),\n", + " scatter_kws={\"s\": 80});" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "If there is structure in the residuals, it suggests that simple linear regression is not appropriate:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.residplot(x=\"x\", y=\"y\", data=anscombe.query(\"dataset == 'II'\"),\n", + " scatter_kws={\"s\": 80});" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Conditioning on other variables\n", + "-------------------------------\n", + "\n", + "The plots above show many ways to explore the relationship between a pair of variables. Often, however, a more interesting question is \"how does the relationship between these two variables change as a function of a third variable?\" This is where the difference between :func:`regplot` and :func:`lmplot` appears. While :func:`regplot` always shows a single relationsihp, :func:`lmplot` combines :func:`regplot` with :class:`FacetGrid` to provide an easy interface to show a linear regression on \"faceted\" plots that allow you to explore interactions with up to three additional categorical variables.\n", + "\n", + "The best way to separate out a relationship is to plot both levels on the same axes and to use color to distinguish them:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"total_bill\", y=\"tip\", hue=\"smoker\", data=tips);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "In addition to color, it's possible to use different scatterplot markers to make plots the reproduce to black and white better. You also have full control over the colors used:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"total_bill\", y=\"tip\", hue=\"smoker\", data=tips,\n", + " markers=[\"o\", \"x\"], palette=\"Set1\");" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "To add another variable, you can draw multiple \"facets\" which each level of the variable appearing in the rows or columns of the grid:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"total_bill\", y=\"tip\", hue=\"smoker\", col=\"time\", data=tips);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"total_bill\", y=\"tip\", hue=\"smoker\",\n", + " col=\"time\", row=\"sex\", data=tips);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Controlling the size and shape of the plot\n", + "------------------------------------------\n", + "\n", + "Before we noted that the default plots made by :func:`regplot` and :func:`lmplot` look the same but on axes that have a different size and shape. This is because func:`regplot` is an \"axes-level\" function draws onto a specific axes. This means that you can make mutli-panel figures yourself and control exactly where the the regression plot goes. If no axes is provided, it simply uses the \"currently active\" axes, which is why the default plot has the same size and shape as most other matplotlib functions. To control the size, you need to create a figure object yourself." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "f, ax = plt.subplots(figsize=(5, 6))\n", + "sns.regplot(x=\"total_bill\", y=\"tip\", data=tips, ax=ax);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "In contrast, the size and shape of the :func:`lmplot` figure is controlled through the :class:`FacetGrid` interface using the ``size`` and ``aspect`` parameters, which apply to each *facet* in the plot, not to the overall figure itself:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"total_bill\", y=\"tip\", col=\"day\", data=tips,\n", + " col_wrap=2, size=3);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "scrolled": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"total_bill\", y=\"tip\", col=\"day\", data=tips,\n", + " aspect=.5);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Plotting a regression in other contexts\n", + "---------------------------------------\n", + "\n", + "A few other seaborn functions use :func:`regplot` in the context of a larger, more complex plot. The first is the :func:`jointplot` function that we introduced in the :ref:`distributions tutorial `. In addition to the plot styles previously discussed, :func:`jointplot` can use :func:`regplot` to show the linear regression fit on the joint axes by passing ``kind=\"reg\"``:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.jointplot(x=\"total_bill\", y=\"tip\", data=tips, kind=\"reg\");" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Using the :func:`pairplot` function with ``kind=\"reg\"`` combines :func:`regplot` and :class:`PairGrid` to show the linear relationship between variables in a dataset. Take care to note how this is different from :func:`lmplot`. In the figure below, the two axes don't show the same relationship conditioned on two levels of a third variable; rather, :func:`PairGrid` is used to show multiple relationships between different pairings of the variables in a dataset:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.pairplot(tips, x_vars=[\"total_bill\", \"size\"], y_vars=[\"tip\"],\n", + " size=5, aspect=.8, kind=\"reg\");" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Like :func:`lmplot`, but unlike :func:`jointplot`, conditioning on an additional categorical variable is built into :func:`pairplot` using the ``hue`` parameter:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.pairplot(tips, x_vars=[\"total_bill\", \"size\"], y_vars=[\"tip\"],\n", + " hue=\"smoker\", size=5, aspect=.8, kind=\"reg\");" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.10" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file From 428eb0569556bd907d96ff01112d267a1c4676b7 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 22 Jun 2015 08:55:05 -0700 Subject: [PATCH 0043/1738] Add link to API docs in header --- doc/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index cac6a776b7..96351fe710 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -127,7 +127,8 @@ 'bootswatch_theme': "flatly", 'navbar_sidebarrel': False, 'bootstrap_version': "3", - 'navbar_links': [("Tutorial", "tutorial"), + 'navbar_links': [("API", "api"), + ("Tutorial", "tutorial"), ("Gallery", "examples/index")], } From 64c40f87ba1c4c9152c6a9b4e9070625c7207c27 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 22 Jun 2015 08:55:47 -0700 Subject: [PATCH 0044/1738] Remove example notebooks execution from travis script --- .travis.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index cabdeb5c87..c462cf0a8c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,10 +45,3 @@ before_script: script: - nosetests --with-doctest - # Notebook tests are failing on old matplotlib - # because the figures have every so slightly a - # different size. Skip until I understand why. - - if [ ${DEPS} == modern ] && [ ${PYTHON} != 3.4 ]; then - python ipnbdoctest.py examples/*.ipynb; - fi - From f4c1d1cb7d653ee4effeedf2811a803e17bf433c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 22 Jun 2015 09:26:15 -0700 Subject: [PATCH 0045/1738] Fix annoying doctest return value issues --- seaborn/axisgrid.py | 54 +++++++++++++++++++++-------------------- seaborn/linearmodels.py | 29 +++++++++++----------- 2 files changed, 43 insertions(+), 40 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index f4d8956d8d..3056e35728 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -580,7 +580,7 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, ... data = kwargs.pop("data") ... data.plot(x=x, y=y, ax=ax, grid=False, **kwargs) >>> g = sns.FacetGrid(df, col="walk", col_wrap=2, size=3.5) - >>> g.map_dataframe(dateplot, "date", "val") + >>> g = g.map_dataframe(dateplot, "date", "val") Use different axes labels after plotting: @@ -1166,9 +1166,9 @@ def __init__(self, data, hue=None, hue_order=None, palette=None, :context: close-figs >>> g = sns.PairGrid(iris) - >>> g.map_upper(plt.scatter) - >>> g.map_lower(sns.kdeplot, cmap="Blues_d") - >>> g.map_diag(sns.kdeplot, lw=3, legend=False) + >>> g = g.map_upper(plt.scatter) + >>> g = g.map_lower(sns.kdeplot, cmap="Blues_d") + >>> g = g.map_diag(sns.kdeplot, lw=3, legend=False) Use different colors and markers for each categorical level: @@ -1525,7 +1525,7 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, :context: close-figs >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips) - >>> g.plot(sns.regplot, sns.distplot) + >>> g = g.plot(sns.regplot, sns.distplot) Draw the join and marginal plots separately, which allows finer-level control other parameters: @@ -1535,8 +1535,8 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, >>> import matplotlib.pyplot as plt >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips) - >>> g.plot_joint(plt.scatter, color=".5", edgecolor="white") - >>> g.plot_marginals(sns.distplot, kde=False, color=".5") + >>> g = g.plot_joint(plt.scatter, color=".5", edgecolor="white") + >>> g = g.plot_marginals(sns.distplot, kde=False, color=".5") Draw the two marginal plots separately: @@ -1545,12 +1545,12 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, >>> import numpy as np >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips) - >>> g.plot_joint(plt.scatter, color="m", edgecolor="white") - >>> g.ax_marg_x.hist(tips["total_bill"], color="b", alpha=.6, - ... bins=np.arange(0, 60, 5)) - >>> g.ax_marg_y.hist(tips["tip"], color="r", alpha=.6, - ... orientation="horizontal", - ... bins=np.arange(0, 12, 1)) + >>> g = g.plot_joint(plt.scatter, color="m", edgecolor="white") + >>> _ = g.ax_marg_x.hist(tips["total_bill"], color="b", alpha=.6, + ... bins=np.arange(0, 60, 5)) + >>> _ = g.ax_marg_y.hist(tips["tip"], color="r", alpha=.6, + ... orientation="horizontal", + ... bins=np.arange(0, 12, 1)) Add an annotation with a statistic summarizing the bivariate relationship: @@ -1560,9 +1560,10 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, >>> from scipy import stats >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips) - >>> g.plot_joint(plt.scatter, color="g", s=40, edgecolor="white") - >>> g.plot_marginals(sns.distplot, kde=False, color="g") - >>> g.annotate(stats.pearsonr) + >>> g = g.plot_joint(plt.scatter, + ... color="g", s=40, edgecolor="white") + >>> g = g.plot_marginals(sns.distplot, kde=False, color="g") + >>> g = g.annotate(stats.pearsonr) Use a custom function and formatting for the annotation @@ -1570,11 +1571,12 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, :context: close-figs >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips) - >>> g.plot_joint(plt.scatter, color="g", s=40, edgecolor="white") - >>> g.plot_marginals(sns.distplot, kde=False, color="g") + >>> g = g.plot_joint(plt.scatter, + ... color="g", s=40, edgecolor="white") + >>> g = g.plot_marginals(sns.distplot, kde=False, color="g") >>> rsquare = lambda a, b: stats.pearsonr(a, b)[0] ** 2 - >>> g.annotate(rsquare, template="{stat}: {val:.2f}", - ... stat="$R^2$", loc="upper left", fontsize=12) + >>> g = g.annotate(rsquare, template="{stat}: {val:.2f}", + ... stat="$R^2$", loc="upper left", fontsize=12) Remove the space between the joint and marginal axes: @@ -1582,8 +1584,8 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, :context: close-figs >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips, space=0) - >>> g.plot_joint(sns.kdeplot, cmap="Blues_d") - >>> g.plot_marginals(sns.kdeplot, shade=True) + >>> g = g.plot_joint(sns.kdeplot, cmap="Blues_d") + >>> g = g.plot_marginals(sns.kdeplot, shade=True) Draw a smaller plot with relatively larger marginal axes: @@ -1592,8 +1594,8 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips, ... size=5, ratio=2) - >>> g.plot_joint(sns.kdeplot, cmap="Reds_d") - >>> g.plot_marginals(sns.kdeplot, color="r", shade=True) + >>> g = g.plot_joint(sns.kdeplot, cmap="Reds_d") + >>> g = g.plot_marginals(sns.kdeplot, color="r", shade=True) Set limits on the axes: @@ -1602,8 +1604,8 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips, ... xlim=(0, 50), ylim=(0, 8)) - >>> g.plot_joint(sns.kdeplot, cmap="Purples_d") - >>> g.plot_marginals(sns.kdeplot, color="m", shade=True) + >>> g = g.plot_joint(sns.kdeplot, cmap="Purples_d") + >>> g = g.plot_marginals(sns.kdeplot, color="m", shade=True) """ # Set up the subplot grid diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index d9529fcd51..4781cf5fff 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -1495,7 +1495,7 @@ def pairplot(data, hue=None, hue_order=None, palette=None, >>> import seaborn as sns; sns.set(style="ticks", color_codes=True) >>> iris = sns.load_dataset("iris") - >>> sns.pairplot(iris) + >>> g = sns.pairplot(iris) Show different levels of a categorical variable by the color of plot elements: @@ -1503,58 +1503,59 @@ def pairplot(data, hue=None, hue_order=None, palette=None, .. plot:: :context: close-figs - >>> sns.pairplot(iris, hue="species") + >>> g = sns.pairplot(iris, hue="species") Use a different color palette: .. plot:: :context: close-figs - >>> sns.pairplot(iris, hue="species", palette="husl") + >>> g = sns.pairplot(iris, hue="species", palette="husl") Use different markers for each level of the hue variable: .. plot:: :context: close-figs - >>> sns.pairplot(iris, hue="species", markers=["o", "s", "D"]) + >>> g = sns.pairplot(iris, hue="species", markers=["o", "s", "D"]) Plot a subset of variables: .. plot:: :context: close-figs - >>> sns.pairplot(iris, vars=["sepal_width", "sepal_length"]) + >>> g = sns.pairplot(iris, vars=["sepal_width", "sepal_length"]) Draw larger plots: .. plot:: :context: close-figs - >>> sns.pairplot(iris, size=3, vars=["sepal_width", "sepal_length"]) + >>> g = sns.pairplot(iris, size=3, + ... vars=["sepal_width", "sepal_length"]) Plot different variables in the rows and columns: .. plot:: :context: close-figs - >>> sns.pairplot(iris, - ... x_vars=["sepal_width", "sepal_length"], - ... y_vars=["petal_width", "petal_length"]) + >>> g = sns.pairplot(iris, + ... x_vars=["sepal_width", "sepal_length"], + ... y_vars=["petal_width", "petal_length"]) Use kernel density estimates for univariate plots: .. plot:: :context: close-figs - >>> sns.pairplot(iris, diag_kind="kde") + >>> g = sns.pairplot(iris, diag_kind="kde") Fit linear regression models to the scatter plots: .. plot:: :context: close-figs - >>> sns.pairplot(iris, kind="reg") + >>> g = sns.pairplot(iris, kind="reg") Pass keyword arguments down to the underlying functions (it may be easier to use :class:`PairGrid` directly): @@ -1562,9 +1563,9 @@ def pairplot(data, hue=None, hue_order=None, palette=None, .. plot:: :context: close-figs - >>> sns.pairplot(iris, diag_kind="kde", markers="+", - ... plot_kws=dict(s=50, edgecolor="b", linewidth=1), - ... diag_kws=dict(shade=True)) + >>> g = sns.pairplot(iris, diag_kind="kde", markers="+", + ... plot_kws=dict(s=50, edgecolor="b", linewidth=1), + ... diag_kws=dict(shade=True)) """ if plot_kws is None: From 318f378f33f27943506b6c97f71d94670f1198d1 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 22 Jun 2015 18:27:01 -0700 Subject: [PATCH 0046/1738] Fix typo --- doc/tutorial/regression.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorial/regression.ipynb b/doc/tutorial/regression.ipynb index a914a16ee3..543e0d56af 100644 --- a/doc/tutorial/regression.ipynb +++ b/doc/tutorial/regression.ipynb @@ -122,7 +122,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "You should note that the resulting plots are identical, except that the resulting figure shape is different. We will explain why this is shortly. For now, the other main difference to know about is that :func:`regplot` accepts the ``x`` and ``y`` variables in a variety of formats including simple numpy arrays, pandas ``Series`` objects, or as references to variables in a pandas ``DataFrame`` object passed to ``data``. In contrast, :func:`lmplot` has ``data`` as a required parameter and the ``x`` and ``y`` variables must be specified as strings. Other than this input flexibility, :func:`regplot` possesses a subset of :func:`lmplot`'s features, so we will demonstrate them using the latter.\n", + "You should note that the resulting plots are identical, except that the figure shapes are different. We will explain why this is shortly. For now, the other main difference to know about is that :func:`regplot` accepts the ``x`` and ``y`` variables in a variety of formats including simple numpy arrays, pandas ``Series`` objects, or as references to variables in a pandas ``DataFrame`` object passed to ``data``. In contrast, :func:`lmplot` has ``data`` as a required parameter and the ``x`` and ``y`` variables must be specified as strings. This data format is called \"long-form\" or `\"tidy\" `_ data. Other than this input flexibility, :func:`regplot` possesses a subset of :func:`lmplot`'s features, so we will demonstrate them using the latter.\n", "\n", "It's possible to fit a linear regression when one of the variables takes discrete values, however, the simple scatterplot produced by this kind of dataset is often not optimal:" ] @@ -590,4 +590,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} From d384a653e93d5b6624f4058b42507dc10f45b7ad Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 22 Jun 2015 18:27:15 -0700 Subject: [PATCH 0047/1738] Add statsmodels to 3.4 and don't run doctests on with minimal deps --- .travis.yml | 5 ++++- testing/deps_modern_3.4.txt | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c462cf0a8c..070d5338ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,4 +44,7 @@ before_script: # fi script: - - nosetests --with-doctest + - if [ $DEPS == 'modern' ]; + then nosetests --with-doctest; + else nosetests; + fi diff --git a/testing/deps_modern_3.4.txt b/testing/deps_modern_3.4.txt index a95a91927c..a63b15f21e 100644 --- a/testing/deps_modern_3.4.txt +++ b/testing/deps_modern_3.4.txt @@ -4,3 +4,4 @@ numpy scipy matplotlib pandas +statsmodels From 8957b77a6174476b2e0788f09c9cd8d57c26fdca Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 22 Jun 2015 21:15:44 -0700 Subject: [PATCH 0048/1738] Add API examples for tsplot --- seaborn/timeseries.py | 80 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 5 deletions(-) diff --git a/seaborn/timeseries.py b/seaborn/timeseries.py index 4bbc371861..c08346f6b3 100644 --- a/seaborn/timeseries.py +++ b/seaborn/timeseries.py @@ -20,10 +20,12 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, legend=True, ax=None, **kwargs): """Plot one or more timeseries with flexible representation of uncertainty. - This function can take data specified either as a long-form (tidy) - DataFrame or as an ndarray with dimensions for sampling unit, time, and - (optionally) condition. The interpretation of some of the other parameters - changes depending on the type of object passed as data. + This function is intended to be used with data where observations are + nested within sampling units that were measured at multiple timepoints. + + It can take data specified either as a long-form (tidy) DataFrame or as an + ndarray with dimensions (unit, time) The interpretation of some of the + other parameters changes depending on the type of object passed as data. Parameters ---------- @@ -75,7 +77,7 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, must take an ``axis`` argument. n_boot : int Number of bootstrap iterations. - err_palette: seaborn palette + err_palette : seaborn palette Palette name or list of colors used when plotting data for each unit. err_kws : dict, optional Keyword argument dictionary passed through to matplotlib function @@ -93,6 +95,74 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, ax : matplotlib axis axis with plot data + Examples + -------- + + Plot a trace with translucent confidence bands: + + .. plot:: + :context: close-figs + + >>> import numpy as np; np.random.seed(22) + >>> import seaborn as sns; sns.set(color_codes=True) + >>> x = np.linspace(0, 15, 31) + >>> data = np.sin(x) + np.random.rand(10, 31) + np.random.randn(10, 1) + >>> ax = sns.tsplot(data=data) + + Plot a long-form dataframe with several conditions: + + .. plot:: + :context: close-figs + + >>> gammas = sns.load_dataset("gammas") + >>> ax = sns.tsplot(time="timepoint", value="BOLD signal", + ... unit="subject", condition="ROI", + ... data=gammas) + + Use error bars at the positions of the observations: + + .. plot:: + :context: close-figs + + >>> ax = sns.tsplot(data=data, err_style="ci_bars", color="g") + + Don't interpolate between the observations: + + .. plot:: + :context: close-figs + + >>> import matplotlib.pyplot as plt + >>> ax = sns.tsplot(data=data, err_style="ci_bars", interpolate=False) + + Show multiple confidence bands: + + .. plot:: + :context: close-figs + + >>> ax = sns.tsplot(data=data, ci=[68, 95], color="m") + + Use a different estimator: + + .. plot:: + :context: close-figs + + >>> ax = sns.tsplot(data=data, estimator=np.median) + + Show each bootstrap resample: + + .. plot:: + :context: close-figs + + >>> ax = sns.tsplot(data=data, err_style="boot_traces", n_boot=500) + + Show the trace from each sampling unit: + + + .. plot:: + :context: close-figs + + >>> ax = sns.tsplot(data=data, err_style="unit_traces") + """ # Sort out default values for the parameters if ax is None: From 9e92cdf6fd627d40389b0b27f4f55f0caf5462fd Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 22 Jun 2015 21:56:55 -0700 Subject: [PATCH 0049/1738] Add API examples for heatmap --- doc/index.rst | 18 ++------ seaborn/matrix.py | 105 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 16 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index d5af5de444..ba8d1a57e9 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -115,21 +115,9 @@ To check out the code or report a bug, please visit the `github repository tutorial/aesthetics tutorial/color_palettes - tutorial/plotting_distributions - tutorial/quantitative_linear_models - -.. raw:: html - - -
- -.. toctree:: - :maxdepth: 1 - - tutorial/categorical_linear_models - tutorial/dataset_exploration - tutorial/timeseries_plots - tutorial/axis_grids + tutorial/distributions + tutorial/regression + tutorial/categorical .. raw:: html diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 838d94d864..ed700dc37b 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -325,6 +325,109 @@ def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, ax : matplotlib Axes Axes object with the heatmap. + Examples + -------- + + Plot a heatmap for a numpy array: + + .. plot:: + :context: close-figs + + >>> import numpy as np; np.random.seed(0) + >>> import seaborn as sns; sns.set() + >>> uniform_data = np.random.rand(10, 12) + >>> ax = sns.heatmap(uniform_data) + + Change the limits of the colormap: + + .. plot:: + :context: close-figs + + >>> ax = sns.heatmap(uniform_data, vmin=0, vmax=1) + + Plot a heatmap for data centered on 0: + + .. plot:: + :context: close-figs + + >>> normal_data = np.random.randn(10, 12) + >>> ax = sns.heatmap(normal_data) + + Plot a dataframe with meaningful row and column labels: + + .. plot:: + :context: close-figs + + >>> flights = sns.load_dataset("flights") + >>> flights = flights.pivot("month", "year", "passengers") + >>> ax = sns.heatmap(flights) + + Annotate each cell with the numeric value using integer formatting: + + .. plot:: + :context: close-figs + + >>> ax = sns.heatmap(flights, annot=True, fmt="d") + + Add lines between each cell: + + .. plot:: + :context: close-figs + + >>> ax = sns.heatmap(flights, linewidths=.5) + + Use a different colormap: + + .. plot:: + :context: close-figs + + >>> ax = sns.heatmap(flights, cmap="YlGnBu") + + Center the colormap at a specific value: + + .. plot:: + :context: close-figs + + >>> ax = sns.heatmap(flights, center=flights.loc["January", 1955]) + + Plot every other column label and don't plot row labels: + + .. plot:: + :context: close-figs + + >>> data = np.random.randn(50, 20) + >>> ax = sns.heatmap(data, xticklabels=2, yticklabels=False) + + Don't draw a colorbar: + + .. plot:: + :context: close-figs + + >>> ax = sns.heatmap(flights, cbar=False) + + Use different axes for the colorbar: + + .. plot:: + :context: close-figs + + >>> grid_kws = {"height_ratios": (.9, .05), "hspace": .3} + >>> f, (ax, cbar_ax) = plt.subplots(2, gridspec_kw=grid_kws) + >>> ax = sns.heatmap(flights, ax=ax, + ... cbar_ax=cbar_ax, + ... cbar_kws={"orientation": "horizontal"}) + + Use a mask to plot only part of a matrix + + .. plot:: + :context: close-figs + + >>> corr = np.corrcoef(np.random.randn(10, 200)) + >>> mask = np.zeros_like(corr) + >>> mask[np.triu_indices_from(mask)] = True + >>> with sns.axes_style("white"): + ... ax = sns.heatmap(corr, mask=mask, vmax=.3, square=True) + + """ # Initialize the plotter object plotter = _HeatMapper(data, vmin, vmax, cmap, center, robust, annot, fmt, @@ -946,7 +1049,7 @@ def clustermap(data, pivot_kws=None, method='average', metric='euclidean', A ClusterGrid instance. Notes - ---- + ----- The returned object has a ``savefig`` method that should be used if you want to save the figure object without clipping the dendrograms. From 9d4a2c1516d9af4c889ff73f9559ffc535925299 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 23 Jun 2015 19:05:34 -0700 Subject: [PATCH 0050/1738] Removal of old example notebooks and testing infrastructure --- examples/aesthetics.ipynb | 463 ------------ examples/linear_models.ipynb | 330 --------- examples/plotting_distributions.ipynb | 971 -------------------------- examples/timeseries_plots.ipynb | 889 ----------------------- ipnbdoctest.py | 342 --------- 5 files changed, 2995 deletions(-) delete mode 100644 examples/aesthetics.ipynb delete mode 100644 examples/linear_models.ipynb delete mode 100644 examples/plotting_distributions.ipynb delete mode 100644 examples/timeseries_plots.ipynb delete mode 100644 ipnbdoctest.py diff --git a/examples/aesthetics.ipynb b/examples/aesthetics.ipynb deleted file mode 100644 index cd6a1561c8..0000000000 --- a/examples/aesthetics.ipynb +++ /dev/null @@ -1,463 +0,0 @@ -{ - "metadata": { - "name": "", - "signature": "sha256:799a615538efa18133b01b85f3cf563f0c25b09fb87df8201f1b0e627ed9ad05" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ - { - "cells": [ - { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "Controlling figure aesthetics in seaborn" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This notebook provides a brief introduction to the [seaborn](http://stanford.edu/~mwaskom/software/seaborn/) functions that let you manipulate the look of matplotlib figures and choose color palettes that will reveal interesting patterns in your data.\n", - "\n", - "For much more information, you can check out the detailed tutorials on [figure style](http://stanford.edu/~mwaskom/software/seaborn/tutorial/aesthetics.html) and [color palettes](http://stanford.edu/~mwaskom/software/seaborn/tutorial/color_palettes.html) at the seaborn documentation." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%matplotlib inline" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 1 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "np.random.seed(9221999)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 2 - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Setting the figure style" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's define a simple function to plot some offset sine waves to help us see the different stylistic parameters we can tweak." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def sinplot(flip=1):\n", - " x = np.linspace(0, 14, 100)\n", - " for i in range(1, 7):\n", - " plt.plot(x, np.sin(x + i * .5) * (7 - i) * flip)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 3 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This is what the plot looks like with matplotlib defaults:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sinplot()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n6NKpi0rX4GuZt3mnDXYJ+RRVFdHI7SPJf/0g6jx0C6UVZBMRkUwmo+jcaFp7bS3ZrLahlRdWklQm\nVTgWX86cIfL1JZLJ1DqsSAeguKqYFp9YTF2+7UKGE5ZQUGjR/WNSmZS2R24nux/saNqeaZRalKre\naxcTde5MlJ+v1mFFOgjHbh8ji+8tyHv+/+j9H0Oopr6GiIgqaitoX8w+emHPC2T/gz3dyLmh0nXu\n6U7ldS2fk4W8FCnzhIIE8lznSQuOLaD3PqijDz5o+bzM0kzqv7k/Tdw1kUqrSwV8LC0jlRK5uRFd\nuaK2IUU6AOU15TRg8wCauX8m7T2RRX5+LZ9XUVtBy88vJ4vvLSjqbpRa5/DCC0QbNqh1SJFHTL20\nnpYGLiXb1bZ0OPIiGRuzH+6W2HVrF5l/Z04nE08Kvt5jo8yvpF8hy+8t6ZeQX6imhsjCgig+Xv4f\nVl1XTa8dfo2813tTYWWhgI+mZVasIFq4UG3DiTxiquuqaeT2kfTKwVdIKpPSvHlE33+vWOavG3+R\n/Q/2lF6crrZ5HDlCNHCg2oYTecTIZDJ6ad9LFPBHAOWU5dAvvxC9+KJimYupF8niewvaEr5F0DUf\nC2WeU5ZDNqtt6Ej8ESIi+vdf5W/8BccW0Pid40mmJt9IbCyRjQ2z0kUeb+qkdTRp9ySavHsy1Unr\nqLKSyMSEKCOjddkfrvxAnus8Kb9CPb6R2loic3OipCS1DCfyiFl7bS1139CdKmsriYgoIIDowIHW\n5eLy4sh2tS2dSjzF+5p8lXm7R7PISIbZB2djTvc5GOM+BgCr3Tt1qnLyq55ZhayyLPwUrJ5UO09P\nVqfjyhW1DCfyCFl4fCHKa8uxY9IOaEo0cfQoS7O2tW1ddvGAxRjvMR6j/x6NqroqleeipcU6ELVF\nyJpI+3L1zlV8cfEL7HthHzppdcLdu0BEhHItLD3MPLBtwjbMOTSn7aNd+Gh+IS88ZJl/f/l7Grhl\nINXW1xIRs2BMTYnS0pT/xUopSiGL7y3o6p2rygspYMUKogUL1DKUyCMiMDmQHH50aLKnMm4c0R9/\nKD+GTCajybsn0ydnPlHLnIKD2Z6MuMH++JJbnkv2P9jT4bjD99/75ReiGTP4jfPhqQ9p7N9jeXkU\n0JHdLNcyrpHF9xZNogdOnSLq21fpv+8+B2MPkuOPjmpZFsfGEllbi66Wx5XK2krquqZrkwcuL49F\nlJSU8BsrqzSLzL4zU8uGqEzGlPm1ayoPJfIIkMlkNHL7SFpyZkmT95V1sTSmpr6G/Df50y8hvygt\nw1eZt5ubpaa+Bi/tfwkbRm+Ao/GD9M59+4ApU/iPN95zPMa6j8WSwCUqz83TEzA3By5fVnkokUfA\nl5e+RA+rHhjr8aBk3f79bBncuTO/sawNrfH50M/x1r9vQUYylebFcazc7q5dKg0j8ojYF7sPuRW5\nWBGw4v57fFwsjdHW0MaOSTvw6flPEZsXq+aZMtpNmW8M3QgPUw9M8nqQTy2VspoZkycLG3NFwAoc\niDuglg9n6lSxR+jjSFRuFDaFbcKaZ5sWqT98GJg4UdiYb/Z+E9X11dgWuU3l+U2YwOZCbZubJ6Jm\naupr8NGZj7D6mdXQkDxoqrB/P/D888JKNXiYeWDJoCX48MyHapxpI/iY8UJeAKikuoQsv7ekmzk3\nmywjzp0j6tmTx1qlBVZdXkXjd45XbRAiiosTXS2PG1KZlAZsHkAbrjcN6C4rIzI0JCoqkiOoBGFZ\nYWTxvQXlVeSpNEeZjMjBgSg6WqVhRNqZH678QKN3jG72/rBhRAcPCh+3uq6aHH50oKC0oFbPRUd0\ns6y6sgqjuo6Cr6Vvk/f37RNulTfwdt+3EZkTiUtpl1Qax8NDdLU8bhyIPYA6WR1e92/adPH0aaBf\nP8DYWPjYvax7YXq36Vh2dplKc+Q4YNw4lt4v8nhQWFWIr4O+xncjv2vyfk4OEBkJjBolfGwdTR18\nPvRzLDm7pMHYVRvtoszXX1+PFcNWNHlPJmNLFiH+8sboaupi5fCV+OD0Byp/OC+8wIpviXR8iAhf\nXvoSy4Ysg4RrehsfPswUqKosHbIUu6J3IassS6Vxxo1jcxJ5PFh5cSUme02Gt3nTjt/797MGFKpW\nw3y5+8vIrcjFyaSTqg30EO2izGd3n91k0xNg3TlMTJhFrCozfGegRlqDfbH7VBpnwgTWuEL0b3Z8\njiceh5Sk93MVGpBK2XeojvZdFvoWmOU3Cz9c/UGlcYYOBeLimGUn0rFJLkrG9hvb8fmwz5sdO3KE\n6QhV0ZRoYmXASiwJXKLyJntj1KLMOY7T4DguguO4Iy0dXzK4ecSJKhufDyPhJPj26W+x7NwylT4c\nb2+2YoiLU8+8RNoGIsIXF7/A0sFLm1nlV68CNjasH6c6eH/g+9gasRWFVYWCx9DWZktzsSxux2fV\nlVV4s/ebsDSwbPJ+ZSVzwY4cqZ7rTPKaBA2JBvbG7FXPgFCfZf4OgBgALdq0ZnrNe2gdO6bennkj\nXUZCR0MHp5NOCx6D49hO9bFj6puXiPo5l3oORVVFmOzV3BpQl4ulAXsje0z0nIi119aqNI7oN+/4\nFFYVYmfUTrzd5+1mx86dY9nERkbquRbHcVgZsBIrLqxQm+9cZWXOcZwdgOcBbAagVO3d9HQgNxfw\n91f16k3mgUX9F+Gna6ql+YvKvOOz8uJKLBm8pEnIWAOHDwPjx6v3eh8P+hjrrq9DWU2Z4DGeew44\nf55ZeCIdk1/DfsU4j3GwNrRuduz4caYb1Mkzrs8AYMaJOlCHZf4jgA8AKO3fOHECeOYZQKP5s6gS\n032mIyI7QqW48+HDgZAQsQNRR+XKHdaF6kWfF5sdi48HysqYBaVO3EzdMMJ5BH4N+1XwGCYmQO/e\nwJkzapyYiNqok9ZhXcg6LO6/uNkxIuYiU7cy5zgO8/vOx7qQdWoZT6W2yRzHjQGQS0QRHMcNk3vi\n//7H/qulBUyejBMnfAQndChCV1MXb/i/gTXX1mDDGGFddfX1Wb/IwEDhSScibcfPV37ESquXoPXP\nXpaKd+cO0LcvEBCAwyf9MHasBJI22Nb/ZNAneP7v57Gw30JoaWgJGmP8eOZqUacbSEQ97InZA199\nZ/Q4HgGE/sZSh42MAHNzJPhOhlRqjG7d1H/dmX4z8b+z/0NacVqzIBG+qNQ2juO4rwC8DKAegC6A\nzgD2EdGsRufQZwH3Wr5VV2NoXByorDd6bVsE4+nPQt1PXk55DrzWeyFpYZLgtk0//QTExAC/CjfE\nRNqAgnPHkfXSGHTTtoOkdx+gZ0+223ntGnD+PEqS8pD90gfw3PKB+pd9AIb8PgSL+i9qksXMh+Rk\nYOBAIDub7c+IdAwoPBxHFjyDZ6OqoT10OOvIXVEBlJQAKSmo+vcsAj3fxpgzi9gSS828e/JdaGto\n41nNZ3H+/Pn77y9fvhz0KNrGARgK4EgL7zfJarpwqpqWOW0n6tGDaORI+a06VODl/S/Tt0HfCpaP\njyeytRWr3XUYiouJ3n6bykwNadPioS1+MaWlRD6dEql+SADRgAFEt2+rfRrbI7fTs389q9IYrq5E\nN1TrJiaiTjZsoBozE/p6vClJszJbPGXmgERKHTGXlXfdvFntU0goSCDz78zv10pvAI84A7RVM/9Y\noA5o5svA9euAmxsweDBbKquRd/q9g3Uh61AnrRMk7+bGEgNu3VLrtESEkJYG9OwJqq7GsPfN0P3d\nb1s0ay9cAMz6uULj3Blg+nTmK9umem2Vxkz2noyQzBCkFacJHmPkSJahKvKIqa0F3nwTWLsW733a\nHwZLPofE2qbZaeXlwMFbrjA9uAUICgK++gr4+mu1JqN07dIVfWz7YFeUahXZ1KbMiegCEbXqDTxx\n4l7FMU1NYN06YPZs9uCFh6trKvC38YddZzucSDwhSJ7jWPSBGNXyiMnKYkveRYtwbukM1Bkboq9t\n3xZPPX36XgywRAIsXAhcugQsXarW6ml6Wnp40edFbI3YKngMUZl3AMrK2H2Vk4OCs/9ie8VlzPSb\n2eKpgYFA//6AgQFYedVLl4C//wY++ECtCn1B3wVYG7JWtTBFPma8kBcauVkyM1kbr7q6h9YZe/aw\nHlsJCaqsVpqwKXQTTflnimD5Y8eIBg9W23RE+JKbS+TtTfTVV0RENG3PNFp3bZ3c0729iUJCHnoz\nMpLdV4GBaptWZHYk2f1gR/XSekHyRUVEBgZEVVVqm5IIH6RSovHjiebOJZJKad21dTR973S5p7/+\nOtHq1Q+9WVBA1L8/0Wuvqc0XK5VJqeuarnQt40Hxe3Tk5hRbtxJNnSrnr1m3jsjXl6iiQoWP5AFF\nVUVk9LURFVQWCJKvrGQPnSqV90QEUlzMymkuXUpErNuL8TfGVFTV8peRkUHUpQtRfUv69fx5ptDD\nw9U2vT6/9qGj8UcFy/frp9bfFxE+/O9/RIMGEdXUEBH7Lo8nHG/xVJmMyN6eKCamhYPl5UTduxNt\n2NDCQWEsP7+cFhx70PKMrzJv1x6gx48z90WL/N//Ad27A2+8oZbli7GuMUZ1HYV/ooVVzurUiS2v\nLl5UeSoifFmwgAWLf/EFAGDbjW2Y4DkBxrotl0E8c4blB7QYwDJ0KLBxIzBmjNqKo7zW6zVsjtgs\nWF50tTwi9uwBtm9n5Vq1tRGbF4uM0gyMdGk5Rz8xkakiT88WDurrs6p8y5axEFk1MNNvJnZF7RK8\n19duylwqZQ+d3PKRHAds2gTcvAn88otarjnLbxa239guWH74cODsWbVMRURZ9u1jVdh+/hngOBAR\nNodvxmu9XpMrct9fLo9Jk9jezFtvqcVQmO4zHedTzyO7LFuQvKjMHwE3bjCD8eBBwMICADMSZvrN\nbDGTGGDP/vDhCsJI3d2BNWtYuVU1ZBm6mLjAzdRNeDVFPma8kBfuuVnCwog8PZVYayQmsmVxWBj/\ndcpD1NbXkuX3lhSfHy9IPjiYeX5E2onsbCJLS6KrDxp1h2WFkcvPLnIb4cpkTCQ5uZWxq6uZY33n\nTrVMde7BufT95e8FydbUsOYZear1vRBRlro65rbbuvX+W/XSerJZbaOw1+vUqUo2BH/jDaJp09Ti\nP99wfQNN2zONiDqwm+XsWaAhd0ghrq7At98yK0qmWnlILQ0tzPCdgT9v/ClI3t//QR0ZkTaGCHj1\nVfbq3//+27uidmF6t+ng5JhHt26xSANn51bG19EB/vgDeOcd1shRRab7TMfu6N2CZLW1mfcnMFDl\naYgow/r1rFPJnDn33zqTfAY2hjboZtFyWqdMxoprKaWzfvyRZRnuFnY/NGaq91ScSDyBkuoS3rLt\npsyV/mAAtiTW0AC2Cg8Ba2BW91nYfnO7oNK4mprAkCGsQJJIG7N1KwtF/PTT+2/JSIbd0bsx3We6\nXLFWXSyN6dMHmDuXLbdVdLcEOAcgvSQdSYVJguRFV0s7kZHB9l42bGjiL9l2Yxtmd58tVywqiul/\nBwclrtGpE3MNf/ghyxxVAVM9UwQ4B2B/7H7esu2izOvrWbz9sGFKCkgk7Nf0f/8DCoXXkQaAHlY9\nYKxrjItpwnYyhw8XLag2p6SExYRv3crM1nsEZwTDQNsAPhY+ckV5KXMA+OwzVrB+n2qNTDQlmpjs\nNVmwdd6gzNUYqizSEosWsR/vRl1wSmtK8W/Cvy0Wa2ugwV+uNIMGsdc336gwWcZM35n48yZ/b0K7\nKPOwMMDRkfXYVJqePYGpU9lDriIv+72MHTd3CJIVN0Hbge++Y2FOPXo0eXtX1C686POiXBdLTQ1r\nGMDrodPVBdauBT7+mGUBqoAqrhZPTxYUkJCg0hREFPHvv2zjc0nT5jhHbx/FYIfBMNUzlSvKW5kD\n7D7+5RcgJUXAZB8w2n00bty9wVuuXZS50v7yh/niC7b7HBqq0vUne03GwfiDqJfV85b18QGKi5nv\nXKQNyMhgoYP3whAbkMqk2BOzB9O6TZMreu0a4OUloHHz8OFsb2az8PBCABjkMAj5lfmCSi5zHJvG\nOfWUshZ5mLo6lgm8fn2zpp17Y/a22Nikgfp6FpLMW2fZ2QGLFwPvvy9gwg/Q1dRVOD95tIsy5+Uv\nb4yxMfDllyp/OM4mznAwchDkapFI2NzFh66N+PRTlltgZ9fk7QtpF2BraAs3Uze5ohcusI1EQXz9\nNbBypUo+TgknwQveLwi2zocOZX+DSBuwbRvg4sIaJzSivLYcZ5LPYLyn/A4m4eHMV34vgpEf773H\nBlDRN7tsyDLeMu2izK9eVeGhmzULyMxU+a6f4jUF+2KE+UlFV0sbcfMmWwp/9FGzQztv7VS48Qmw\nW2LIEIHX7tWLCf+kWmeqaT7TsCtql6CaGg3KXPSbq5naWvZDvXx5s0PHE45jgP0AheWxAwMFuFga\n6NSJReN98olKX6y9kT1vmXZR5u7uKpQB1tRkfvMWvhg+TPaejANxBwRFtTQoc/GhUzMffcQ2uR9q\nrFgrrcX+uP14odsLckVra5mbZfBgFa6/ciULKysoEDxEP9t+qK6vxs27N3nLurqy/yYnC768SEts\n3co2JQYObHZob+xeTPGaolBckL+8MVOmsHKL7Ryu1C7KXJCLpTEvvcRKoV66JHgId1N3mOqZ4uqd\nq7xl3dyYIk9MFHx5kYcJDmZRJW+8AQCNk8wQmBwITzNPOBjJjwsLDQW6dhXgL29M164se++rrwQP\nwXEcpnWbJqh8KceJrha1U1PDvs+HjL9qqRQlNRU4kXgCEzwnKBQPDlZhxQcw3+zSpcxYaEceD2Wu\npcV2pFW1zr0mY18sf1dLw2aVGKKoRr77DsUffIDdxcWYFRsLiytXoHHhAvQuXsTEDAmSXZfiy7Q0\npFVXtyiukr+8McuWAb//rpJ1PrXbVOyP4x8XDIjKXO1s3gzy88NZd3dMj45Gj+vXYRYUBKOgIFgG\nhwA91+Or7BIcys+HtIWldnAwM+pVMhIAYNo0ljfRjsWd2kWZq7QUbmDWLGYaX74seIgGZS7Uv6nC\nwkCkEdVxcVhuZQUnX19sz8nBgM6dEebvj7qhQ3F34AAY3ViAH50dkFFTA//QUAyLiEDoQ7Uv1KbM\nra1Z7RYV6gH5W/ujorYCcflxvGWHDBGVubqoq6zElitX0P3DD7EgIQFDjY3xu6cnYvr2RdWQIZhQ\nsA1zjephqaWFr9PS0Cs0FCcfymO5eFFN95WmJvObt6d1zif3X8gLD7WNU4lNm1irOYHIZDJyW+NG\nIRkPF75unfh4Vg5TRDVOFxSQ26FDNPGffyi9haLewXeCyWud1/1/V0ul9Ed2NlkEBdGnyclUK5VS\nXZ2aa5tER7MCLyoUGX/r6Fv0zaVveMvJZEQWFkSpqYIvLUJEd6qqaOCxYzR02zY6VVDQrJZPdV01\nGX9jTFmlWUTEdMGB3FxyDw6mpyMjKbGStWwbOZLo4EE1TaqmhimN4GBB4uiotVnUwpw5rAZCZKQg\ncY7jBLta3NyYPy1NeMew/zREhA+SkvBqTAxWr1uH/QEBsH8o/hcADsUfwniPB2FjOhIJZltZIaJ3\nb1wvK0P/8HAcCK2EoyNgZqamyXl7A717A38Kq+EDAOM8xuHw7cO85ThOtM5V5XhBAXqHhWHM6dM4\na2uLkV26NEs0O518Gj4WPrA2tAbAdMEEc3NE9emD57p0wVPh4bhQWILgYJbIqRa0tdkmfztZ54+X\nMtfWZgW41q4VPMQU7ymCXC0cx9xFoquFP/UyGebGx+NySQkiTpzAWE9PuZr4UPyhFmOAbXR08K+v\nL161tsa80kj4jC1X7yTffx9YvVpwcbcApwBE50bjbjn/Il6i31w4X6el4bX4ePxTVYVPTp+GRE4Y\nyqG4Q5joObHZ+1oSCd61t8c2Ly9MvBWFzhPuwlR+Yih/5s0DQkKAWP6JZXxpF2Wer2LadBNef53V\n1cjPFyTey7oXquqqcLvgNm9ZUZnzp1oqxdSYGGTV1OC0kxNMNmwA3n23xXMTCxNRWFUot88nx3F4\ny9YWXU92xYkRN3GjXI0KfehQVn7x6FFB4jqaOhjpOhL/Jvwr6NKiMufP6jt38EdODkL9/THkp59Y\nHZYWSj/ISIZ/E/7FWPexcsca1aUL5sV0R8m0ZKxWZ4N5XV3gtdfU1qNBEe2izMdGRaFSKlXPYObm\nwMSJglOxOY7DaLfROHqb/0MrKnN+1MtkmBAVBS2OwxFfX+j/8QfLyHNyavH8Q3GHMNZ9LCSc/NtS\nKgWSt1jgO/uuGHXjBiLLytQzWY5j1vmqVYKHGO8xHofiD/GW69aNlYzIzBR86f8cv2ZlYV1mJs50\n7w6rtDRm/c6Y0eK5EdkRMNQxVJhNDABJpw3wVWkvrMvMxF9q6koFgIXf7tjBGkm3Ie2TNNSpE6bF\nxKBexfrk91mwgNVcqOdfawUAxriPwdEE/srcz489cAIXBf853ktKAsdx2OntDW2OYzVY5s+Xe/6h\n+EMKY4ABVjfJygp4zd0C693d8dytW3LDF3kzZQorwiOwFtDzbs/jXMo5VNZV8pKTSJihIFrnyvH3\n3btYkZqK035+bN9l7Vq2Yu/UqcXzj94+ijFuYxSOScQqu45/SgdHfX3xblISLhQXq2fC9vYstlmF\nPRllaBdE7WPPAAAgAElEQVRlvtnDA3UyGd5KSBAUFtiMXr2YdXfwoCDxES4jEJYVhuJqfl+Wpibr\nmxAUJOiy/yk2Z2XhZGEhdnp5QYPjWFqdrm6LWXkAkF+Zj5t3b2K4s+LUu8ahY5PNzfG+vT2mREej\nWh0rP01NphQ2bRIk3qVTF/jb+ONM8hnesqKrRTmCS0qwODERJ7t3R1c9Pbak2bGDlbmVw9GEoxjr\nId/FAgDx8ey3wMEB6Kavj53e3nghOhrxlfx+mOUyfz6wbl2bppG3izLXkkiwt1s3hJSW4rdsYX0T\nm7FwoeCNUD0tPQx2HIxTSad4y4qultYJKi7GkpQUHPL1hbGWFntz40bgzTflNlQ8evsonnZ5Grqa\nzSNcmowd1DRv4V07Ozjp6mKhutJz584F9u4V3NNxvMd4HIrj72oZNEilFIr/BIV1dZgeE4NfPTzQ\nTV+fvbl1K/D884CNTYsy2WXZSCxMxFP2Tykc+9KlpvfVCBMTfOPigudv3kRRnbAGy00YOpQtwdqw\nYl+7RbMYaGpil7c3liQnI1bFbhwAgAkTWFELgWGKot+8bciqqcELMTH408sLHnp67M3sbNbNe+ZM\nuXJHbh/BOI9xCscmYgrvqUbPJcdx2OrhgUslJdiiDkPBygoYMYJZewIY5zEORxOOQirjt1Lo3p2F\nvRYVCbrsEw8R4ZW4OEw0N8f4hkgoIraKUmCVH0s4hlGuo6CloaVw/IeNBAB4xdoaz5ua4v/UUXSe\n4x5Y521Eu4Ymeunr40sXF8yIjUWNqv5zLS22JP71V0Hio91G43jicd4PXd++QHQ0q6Mj0hQiwuvx\n8XjN2hqjujSqSrdlC6uB0rlzi3K10loEJgfi2a7PKhy/oeb/w/unhpqa2N+tGz5OTlbPhugbbzAl\nIWBJ7GLiAjM9M4Rm8fO7a2mxrnbBwbwv+Z/g54wMZNfW4lsXlwdvBgUx19iAAXLljiYcxRh3xf5y\noLll3sC3Li6ILC/H32roG4uZM5kvrY2aI7R7nPnr1tZw0tXFEnWUipszhzVRrariLepo7AhrA2uE\nZIbwkuvUiTXEER+65mzLyUFmbS2WOjo+eFMqZT+4b74pV+7KnStwM3WDhb7iAtINVnlLnhovfX18\n7+KCV+LjUaeqoTBiBIs8COF3bzTwXNfncDzxOG+5p54SXS0tEVZWhq/S07Hb2xvakkYqa/Nm1gBc\njuuuur4aZ1POtmokZGYyr5qXV/Njehoa2OHlhUWJiUhXdaPdwIAVDVSxKYo82l2ZcxyHzR4e2J2b\ni9Mq9veEvT0zlfcLK3I0xn2M6GpRExnV1fgwORl/eHo2feCOHWP+zJ495coeTziO57o+1+o1rlxp\n6mJ5mNlWVjDX0lI9TlgiYbHBAld9z3V9DicST/CWE5V5c+plMrwaH4/Vrq5wbhytUlwMHDoEvPyy\nXNkLqRfga+ELMz3FqcKXLrE9Czm/CehlaIhFdnaYExcHmaobmHPnssYZ6orsa8QjyQA11dLCZg8P\nvHn7NqpUjUKYO5ct4wUw2m20oCQPUZk3hYgwLz4eC21t0d3AoOnBjRvvl7mVx4mkE0op84f95Q/D\ncRx+dXfHqjt3VI9CeOUVZiSUlPAWHeQwCLH5sciv5BfD2r8/i4pUx37bk8JPGRkw09LCTEvLpgd2\n7mQ5CwpqOhy5fURpF0trKfwfOTigVibDWlWTAXr0ALp0aZtuN3wKubT0AmAP4ByAaABRABY+dFxu\nIZnJt27Rp8nJgorQ3Ke6msjMjCgpibdovbSeTL81pfTidF5yhYVEBgZEdXW8L/lEsjkri/yvX6da\nqbTpgawsImNjovJyubIZJRlk+q0p1UvrFV6jqIhIX5+otrb1+ay5c4cGhYeT9KFiS7yZOpVo3TpB\nomP/Hkt/3/ybt5yPD1EI/zpwTyQplZVkeukSJVRUND/YqxfRyZNyZWUyGTn95EQ3c262ep3u3Ymu\nXm19PjHl5WR66RJlV1e3frIi1qwhmjGj1dPwCApt1QFYTETdAPQH8DbHcS14n5rzY9euWJ+ZiQRV\nrCgdHeaH+v133qIaEg2M6jqKt3/TxIR5eG7yby7zxFFUV4clycnY7OEBLclDt9OOHSxbtyGMrAVO\nJJ7ASNeR0JBoKLxOcDCrhaWlOCgBAPB/traQEWFjVpYyf4J8Xn0V+OMPQaKi31w1iAhvJyTgXXt7\nFk/emPBwVn/+6aflyicUJqBOWgcfCx+F1ykrAxISFHoB7+Olr4+51tb4UNX9vhkzWLtEdSUl3UNl\nZU5EOUQUee//ywHEAmg56PMh7HV18bGDAxaomkw0bx576AS4bEa5jsLJpJO85QYOZL1N/+ssT03F\nRDMz9DA0bHqAiPkGZ89WKH88UTl/eWsulsZocBx+9fDA56mpKFDFZzFiBGswEBPDW/TZrs/iZNJJ\n3m0KRWXO2JOXh7Tqarxv30IvzC1bmHv1YeOhEScTT2KU66hm1RMfJiSEKXIdHeXmtczREWeLihCk\niiI2NQVGjgR28e9OpQi1+sw5jnMC0BPANWVl3rGzQ0ZNDfbl5Qm/sK8viw8W0HPvGddncDblLOpl\n/EoDDBzINuT+y8RUVGBHbi6+cHa+/560UorioGLkrQ5BVnZvpF12RNbmLBSdL0J1RjVI9uBHu05a\nh8CUQIxyHdXqtfgoc4Bl8U01N8eK1FQ+f1JTNDTYqk9AGraziTNMdE0QkR3BS65Bmf+X+81WSqV4\nLykJG93d72+mk4xQlVKF/H3ZuPN7BTJoIrJ/z0buP7kov1XezBg8mXQSo7q2fl9duaIwsrEZhpqa\nWOXqivkJCaqVJ3nlFUHeBEVoqmsgjuMMAOwF8M49C/0+n3/++f3/HzZsGIYNG3b/31oSCX5xd8fM\n2FiMNjVFJw3Fy225zJvHssGeVRyG9DBWBlZwNHJESGYIBtq3nGreEgMGtHuLvw4FEWFxYiKWOjjA\nqBjIOpCF/CP5KLlYAj0vPejkJ0HLYSQ0i6WoSijF3W13UZVYBXCAxXQLWM60RHiXcLiauMLSwFLh\nterqgOvX+T10APC5kxO8r1/H/9naPkhg4susWcBzzwFffqnQEmyJBleLv42/0jLOzkyRp6ay//8v\n8lNGBvp37oynDI1QcKIA2ZuzUXSyCJrGmtA3K0MnU29Qng5kqSWoL6tHeWQ5pOVSmAw3gekYU3Se\n2BkX0y5i+8TtrV7r6lUWuMSHaRYW2JSdjY1ZWZhvZyfsj3zmGXbh6GhWaQ3A+fPncf78eWHjAerp\nNARAC8BJAItaOKbUnsCEW7fou7Q0pc5tkcJCos6diYqLeYt+eOpD+vTsp7xkpFIiExO2x/df5HBe\nHvU+EUy330+gSyaXKHpGNOXszKHawlrWYcXcnCgxsZlcRVwFJS9LpqsuV+mw02H6aflPzbrCPExo\nKFG3bsLm+X1aGo252fommEJ69iQKDOQtdjLxJA3cMpC33OTJRH/+yVvsiSC3poasz1yksM8T6Ir9\nFQrtHUqZGzOpNv/ezvf48US//95MrjKlkrI2Z1HE8Ag6a3OW3pnxDtVXKt5Ub3iGs7P5z/NWWRmZ\nBQVRkTI78vL4+GOi996Texg8N0DVocg5ANsB/CjnuFJ/V0x5OZkFBVGhKh/OhAlEW7bwFjuTdIb6\n/daPt9zzzxPt28db7LGnuqKO3nvlEp01uUjx/xdPVXceard28CDRU08pHEMmk9G0BdPovMd5ihgW\nQaURpXLP/flnotdeEzhXqZRcrl6l0wUFwgYgIvrxR6LZs3mLVdVVkeFXhlRYWchL7ocfiN58k/fl\nngi+3HiDjtpdpFuTblFZZFnTgwUFzGArKVE4xsqfV9I/T/1Dl60uU87fOXKNhZgYImdn4XOdGxtL\nSwRE0d0nNpbIyoqovuUfHb7KXB0+86cAzAQQwHFcxL0XP18H2E7xeFNTfKtKqutLLwmqqTHIYRBi\n8mJQWMUviWnAgP/eJmh5VDnO9w6BaxqHARF94L7eHbp2DxXHUmLjM6c8B6dsT2HgzYGwmG6Bm8/e\nRNKHSZDVNfdD8vWXN0ZHIsF3rq54LympxW7sSvHiiyxBhWdNIV1NXQx2HIzTyfz2cv6Lm6A1OTW4\n+nwkHL8shPdGD/js84FB94dyFvbsYW5UOWUhGvhH+x/Y7rSFz2EfpK1MQ8y0GNTmN2+Qc+WK3CKe\nSvGZkxM2ZmUhp6ZG2ACeniyhThXXSiPUEc0SREQSIupBRD3vvfinv4H5OH/Lzkam0A9nzBggIoJ3\nlX8dTR0MdhyMwORAXnL/pU1QIkLG2gxEBkRi20QZeu73ga5jCxUOCwqAwEBWi0UBZ5LPIMA5AFra\nWrB5wwZ9o/uiIqoCN4bfQE1W0++/tczP1phkZgZ9DQ3szs0VNoClJfuyDxzgLTrKdRTv6pw9e7Ia\ncgLylR5LSq6WILxPOILsa5BxxgnOo+WUdfjrL4XF2gBWJTG9JB19bfuic5/O8A/zh46DDkL9QlF4\nsqmxxnfz82EcdHUx28oKK1VpDDxjBvD338LlG9GheoDa6eriNWtrfC40AkFXl1VTFBDyIyREsW9f\nVrRR6G/P44KsVoa4WXG4u/0ubu23Qfl0I/Q3Mmr55H37gFGjAHnH73E6+TRGuoy8/28tUy34HvWF\nySgThPUOQ9E5Vj4wIwOorgZcXYXPn+M4rHBywvLUVOERCLNmAdtb31B7mJEuI3E6+TSv0FstLabQ\nr1/nfbnHjqxfsxA1Pgq0yhZr5siw0LWFUESAVVmLi2P3lgJOJZ3CCOcR0JSw2A4NXQ10XdUV3ju9\nETcnDlmbHuQeXL2qmmUOAJ84OGBXbi6SBdSHAgBMn86MBDU0WOlQyhxgabMH8/MRJ7RMrkBXyzOu\nz+Bk0kleD52BAeDuzhYDTyr1ZfW4NfYW6kvr4XbWFys0crBCTts3ACzN+sUXFY5JRDiTfKaJMgcA\nTsLB6X9O8PrTCzHTY5C7NxfBwSzNvZVw4VYZYWICS21t/C3UOh83jmlXnmV2Pc08IZVJkVDIr4zq\ngAFPdjE3IkLCogRk/JSBnkE98aVXMZY4OMiPZvv7b7ba09ZWOO7JpJMthroaDzVGj0s9cGf1HSR9\nnISCfMKdOyyqWRXMtbWxwM4OnzaU9OSLrS1L8T92TLWJoAMqcxMtLSyys8OXQn3nw4YBd+/y7obt\nYeoBCSdBXH4cL7kn2dVSe7cWkQGR0HXURbd93fBLYTaGGBk1TxBqICuL9XV7TnESUHReNHQ1deHa\npWVz22SECfxO+iFxQSIytuSotBRuoME6X5GaKqyqYqdOwNixrHEFz+uOdB2J00n8/Ob9+z+5+zEk\nI9x+8zbKrpWh19VeiLaqx82KCsy1tpYjQEq5WGQkw+nk03Ljy/W66qHnlZ4oCSpBxJRY9POXQVMN\nwdnv2tnhTFERbgmti60mV0uHU+YAMN/WFscLCoSl+WtosKULT+uc4zhBrpYnVZnX5tUiclgkTEeb\nwn2TO8ogw+qMDCxXZJX/8w8wfjxzdyngdNLpZlb5wxj2MET3c93hdDYF/XPU0+l4mIkJHHR18afQ\n2tTTpwty4TW4WvjQvz+zzJ+05CGSEuLnxaMythJ+p/ygaaSJ5amp+MTBATry4vgjIoDaWvahKCAi\nOwJmemZwMHKQe462mTa6n+mO4sx6zMuPA0lV/4ANNTXxrr09vhTqO588mSU8qrhJ0iGVuZGmJubb\n2uJrodb5Sy+xXzqeT4IQZd4Q0fIkPXT1JfW4+exNmE8xh/NyZ3Ach7UZGXiuSxd4Kqizgp07mcJr\nhdPJpzHSVbEyBwAtF328K+kB3SN3kPWrinVW7rHCyQlfpKWhVoh1/vTTrFkkz4d2hPMInE89zyvL\n2MaGufHU1Q2vI0AyQtycOFSnV8PvuB80DTVxvbQUNyoqME+eVQ6w+2rGjFZ9bWeSz+AZl2danYeG\nrgZ+s+0GC81axL8R3yQrWShv2dggsLhYWLVOExMgIEDQBntjOqQyB1ia/6H8fKQI2VhoKLbA0+kY\n4ByAy+mXUSttHsYkD2dnVhKmjZqHtDvSSilujb0Fo4FGcFrhBACokEqxJjMTSxzkWzxITmabVCNG\nKBy/pr4GQelBrTZuBtjmsqF7J/Q45YfUz1KRf4hfSdmWGGRsDPdOnbAtJ4e/sLY2MGkSW4HwwNLA\nEo7GjrieyW9Hs8E6f1JI+jAJ1WnV8D3qCw195htfkZaGjxVZ5UTs8542rdXxz6ScwQgXxfcfwJ7X\nq+Ea8Dvsg8rYSiQuSlS50byhpiYW2tria6HWeYMBqgIdVpmbaGnhTRsbfCNES3Ic+/J5PnRdOnWB\nh5kHgjOUf4I47snZrJLVyRA9NRq6jrro+nPX+0WKfsvKwlBjY8VW+a5dwJQpaM0JeTXjKjzNPNGl\nUxeF5wG4v/mp56YHnyM+iH8tHiWXVY/X+5+jI75NTxcWd97OrpYnxW+e8XMGCo8VwuegDzQ6MUUe\nWlqKiLIyzLOyki8YHMyqbt5LeZdHVV0VgjOCMdRxaKtziYoCrK0BcwdN+B3zQ8nlEqR9qUJ44T0W\n2NriSEGBMAN0zBi2wS7EyLhHh1XmALDYzg578vKEtWuaOpUlGfBcTo9wHsE73rxfvydDmScuTgQI\n8NjqAU7CFHmNTIZVd+7gE0VWOcAUXCtRLIBy/vIGGpQ5AHTu3Rlef3ohalIUKmJUawg+yMgIltra\n2CukuNvQoWyj9/ZtXmJClPmTYiTk7ctD+nfp8DvuB60uD2oYf5mejg8dHKCrqB5Tg1Xeiovlyp0r\n8LXwhZGu4pBYALh27UF8uaaRJnyP+iL712zk7hUY6XQP43sGqKDEx06dgNGjBXdNAzq4MjfT1sar\n1tb4TsiH060bYGzM27R52uVpnEk5w0umf392gzzOZG3KQnFgMbx3ekOi9eC2+DMnB74GBuglL4IF\nYMWCioqUyuxR1l8OsK+u8Z5Xl1Fd4PqdK26Nu4W6IuGlbTmOwycODvgmPZ3/8lpDgxkKu3fzEhvs\nOBiROZEoq1G+4XTPnsxFLzRKtyNQeq0Ut9+8Dd8jvk2SzOIqKnC5pESxr1wmYwZZKwloAPOXP+0i\nv755YxobCQCgY60Dn0M+SHgrAWVhqjUEX2Rnh3/y8pAhxAB94QXe3oTGdGhlDjDrfEduLvJrlfdj\n30eAq+Up+6dw8+5NlNaUKi3TuzeLyHtck4eKLxQj5dMU+Bz2gabRAzdJvUyGb9LTFfvKAWaVT5vW\nalXBwqpCxOXHYYBd67GGOTmsdr+HR9P3rWZbwWysGWJfilUpEuF5U1PUE+GkkD6006ezTTkePwR6\nWnroa9sXF9IuKC2jo8PioEND+U+xI1CbW4voKdHw2OwBw15NjYFVd+7gbVtb6Cuyyi9fZi3WWuq0\n/BBnUvgp8379mr5n2NMQ7pvcETUhqlkGMh/MtbXxipUVVgnpQ/vMM0yR8MxlaKBdlHnB8QLBstY6\nOphsZob1QrrGCHC1dNLqhH62/XAx7aLSMgYGQNeu7Ht43KhKrUL0tGh4/eUFPbemZWL35OXBWkcH\ng42N5Q/QsEGlhPV0LuUcBjkMgo5m650Arl1jD1xLvw8u37lAViVDyqcCEzUASDgOH9+zznnTvz8z\nl2/d4iU20kVYvPnj6GqR1csQMy0GlrMtYTa+aZ/OrJoa7M/Px3xbW8WD7N6t1MZnYVUh4vPj0d9O\ncegiwAyE9PSWk4XMJ5nD5i0bRE2MgqxWeK3yd+3tsf3uXRTxbYyiq8t85wJdLe2izONeYeFIQnnf\n3h7rMzNRybeTkKcnYG7Ou2rRCOcROJP85LtaZDUyRE+JhsNHDugysumGJBHh2/T01n3lUVFsSdKn\nT6vX47MUftjF0hiJlgTeu71x96+7yNsnvKnJNHNzpNXU4Crf+F6JhP147dnDS+y/5DdP+SQFnA4H\n5+XNi7L/lJGBly0tYaqoB6BUyhK0lDQSnnJ4CtoairNDAbbH6O8vf5/e4RMHaFtpI+nDpFbHkoet\njg7GmppigxADVAVXS7soc/v37BE9NVrwr52nvj4Gdu6MP4Ts9Ar4cJ52eVqQMn/cHrqkj5Kga68L\nu0XNC+wHFhWhngjPdWkl6mTPHhbFokS+fWBKIEY4tx46BrDPUlHmp7aFNrrt64bbb95GZaKwHrKa\nEgk+sLcXZp1PmcL+dh6ulh5WPXC34i6yypR/yBsiWh6nPIbcvbnI25sH7x3e4DSa3hfFdXXYkp2N\nd1tqB9eYixdZsL2bW6vXC0wJxNPOwl0sjeE4Dp5/eKLgUIFKhsL79vZYm5mJGr75DM88w5oLC/gh\n4FSNr2z1AhxHMpkMUROjoOugC7c1rX85LXG5pASzYmNxu18/aPAp1HH7NotAyMhgm1dKIJVJYf69\nOWLejoGVgYKwqUbExrIVUpLwH/R2Je9gHpIWJ8E/3B9aJg8sJCJCdXUqPonej6HaafDQLEBt7V3U\n1d1FfX0ZOE4TEokWOE4HOjq26LT/KnSffhn63mPQuXN/aGq2vFF6p+QOev3aC3ffvwsJp9iGqK9n\neRTp6ey/ishYm4G72++i5+WekGjzt02qpFI4BgcjqGdPuPPpRkQEODqymho+ipsGN2byP5MxyXMS\nXvJ7SenL2Ngwha4o+bajUJVShfB+4fA77gdD/6b3Qm1tHn5PPIiSyniMNyxDVVUS6uryQVQDmawG\nRDJoaZlCS8sc2hHJ6KThBIPnFsLQsCe0tW3k9vN0X+uOPVP3oLtV91bnN2YMax86aZLi80qvl+LW\n6FvodbUXOrl2Uvrvb8xzN29iirm54k3elpg1C+jTB9zChSAipZWd2trGKaLh1y6sVxiMhxvDfII5\n7zGeMjKClbY29uflYaqFnBKZLeHuzvqDXrrE6rYogYZEAwHOAQhMDlT6ofPwYNVf8/KYZ6cjU5Va\nhdtv3IbvYV9omWihrq4YhYXHUVBwBIWFpyDjtGFf7wLfLiNgoNcb2tqW0Na2goZGZxDVg6gOMlk1\nahIuoyrzCqotCflpK1BWFg49PS8YGw+FuflkdO7cH9w9xX025SwCnAJaVeQAc0Xb2bWuyAHAdr4t\nis4UIXlJMrqu6sr7s+ikoYE3bWzwY0YGNri7Ky/Iccw637uXlzIf7jQcgSnK31cNeQyPgzKX1csQ\n+1IsHD52gKG/IWSyOhQVBaKg4BCKiy+ipiYDNTJPDLHoAwMDP5iZTYS2tgUkEl1IJDoAONTVFaKu\nJgd1K2eg4uMAZGauQXl5BCQSXXTpMhqmpqNhYjICGhrshzetOA3F1cXwtWy9YhYRs8x//bX1v6Vz\nn85wXOaI6Bei0etKL0h0+BsK79vbY35CAl6xsoKEjwH6wgvAd9/xvp5a2sYpeqFRp6Hiy8UUZBlE\n1VnVAtpyEB3IzaU+oaGtthlrxldfEb31Fi+R9SHrac7BObxkRo4kOnKEl0i7I62TUlj/MEpblUx5\neYfpxo3RdPGiId24MZoyMzdRVdUdeiU2llamprY+2GefES1efP+f9fVVVFwcRMnJn9G1a1505Yo9\nJSS8S+XlMfTy/pdp4/WNSs1xwwaiOTw++tr8Wrpid4Xyj+crL9SI7OpqMr50ifJqavgJXr7Mu59d\nbF4sOfzowOse/uYbonfe4Te1R0HysmSKGBVBRYWXKC7uDQoKMqOwsP6UlvY9lZaG0h9Zd+iZyMjW\nBzp3jrXqu4dMJqPy8lhKT19FEREBdPFiZ4qJeZmKii7QlrDNNH3vdKXml5BAZGen/N8jk8no1oRb\nlPh+8/aHysr3vH6djuTl8ROsriYyNm7/tnGtXuChtnHJnyZT5KhIkkl5KmQikspk5BYcTBeLivgJ\nxsez9kxSqfIi+fFk/4M9r4du2TKipUv5Ta29Sfwqgq4uf4uuXLajsLD+lJX1O9XXl98/nlVdTSaX\nLlG+Mu37vL2ZQpNDeXkUJSUtoaAgS/r5kA7dTPmNZLLWv4M5c5hC50PhuUK6bHWZqrOFGQpK/4A1\nRiolsrVl/ceURCaTkfUqa0osUF5BnDtH1L8/v6m1NwXn79LFKZ9SyFV/Cg7uSqmpX1NlZfL94zKZ\njLqHhNDxfCV+cN9+m+jLL+UerqnJo/T0H+jaNS86cMaQ/rn6ItXXV7Y67F9/EU2ZotSf8+BaeTV0\n2eYyFZ7l1/qvgb9zcmhIeDh/wVmzHknbOF44LnNEfXE9Mtfxr4Qn4Ti8Y2eHnzIy+Am6uzPfB4/y\nhm5dmG+fTx3qfv06bkRLbW0+Yi+/hzs+Q2E4phK+fv+iV6+rsLaeAw2NB2n66zIzMcPCQnGkAQDE\nxLAqbwoq2enrd4OLy5cwdTuN4CJ91OT/guvXfZGXt09hsk5DWCIfTIaZwGqeFW6/dltQnY137eyw\nju+GlUTCKt7xKIvLcRxGuIxAYIryWca9e7M9MSGpFm2NTFaH9MT1uJnvC903z8K566fo2zcOjo4f\no1OnB5Es54qLUUuEUa1tqMtkLDRvyhS5p2hrm8HefjF6947CTwlacNDOw7VrLkhPX4X6evllaB9O\nFlIGbTNteGz2QNzsONQV809Um2pujtTqaoSWKp+3AgD48EPe12p3ZS7RlMDrLy+kfZGG8ij+9X9n\nW1riQnEx//oHU6awLjhKwnEchjsPx9mUs0rL9OsHhISwqKqOglRagZSUzxFyzQP5Z1LQtToQ3Xr9\nAQMDv2bnVkil+DU7G4vsmke3NGPvXvaZtpIoBABn0y5Bx3gc/P3D4Or6PdLSvkRYWG8UFDTvLqgo\nDrg1nD51Qk1GDXK28o968jEwgJ++PnbyLY87dSrvGucjnPkpcwMD1mmpI+UxEElx9+4OhIR4IuPa\nTphFrEefEZdgZjYOHNc80ODHjAwstrOTu4l5n6tXAVNTZoC1QlxBHNJqOqNfr9Pw8zuBsrLruHat\nKzIzN0LWQoVKIcocAEyfM4XpOFMkvM2vwQjAIqbm29riZ56tLFurRdMSjyQDVK+rHly+cUHcrLgW\nGz/pAz8AACAASURBVPgqwkBTE3OtrbGW74czeTJT5jysNr7K3MwMsLBg3a0eNUQy5OT8hZAQT1RV\nxcP06D6Y3l4Ju6m95cpsz8nBU507o6syUR179zJFpgQNIYkcx8HU9Hn4+4fC0XEJEhMX4ebNMais\nfFDn9fp1lsYupGmARFsCzz89kfxxMqpS+Rc7es/eHj9kZPCz7AcOZLvePGq1DHcejnMp5yAj5e/9\njpTHUFoagrCwfsjMXAer/NXQ+Hw1vD6ZKPf825WVCCktxUxLy9YHbzASlOBsylkMd2LVNw0MuqNb\nt93w8zuOvLzdCA3tgcLCB+Wsq6rYYrJXL6WGbobrd64oDy/H3V38a+G/am2NowUFyGrjFPFHls5v\nNdcK2lbaSP+af4zvfFtbbMvJQWm98vWh4e3Nqq/xaKwY4BSAc6n8HrqOUHSroiIaERGDkZm5Bt7e\nu2GVuR7FO3Xgtk5+WKiMCGsyM5Wzym/fBvLzleqGK5VJcT71fJOStxwngbn5ZPTpcxPGxkMRHt4f\nycn/g1RaKcjF0hgDHwPYf2CPuDlxvOtUjzQxAYHF2CuNRMLi3HhY5w5GDjDSNUJUbpTSMh3BhVdb\nm4/4+NcRFTUBdnaL0M3uPLJes4DnH573S9q2xE8ZGXjdxkZ+S7gGiJjBNXmyUvM5m3K2WSllQ8Oe\n6N79LFxcvkJCwnxERU1BTU0WIiJYVYBOwqIMoaGnAc/tnkh8JxE1OfyUsomWFmZYWAhLIuLBI1Pm\nHMfB4zcPZK7LRFkkv+I2Drq6eNrEBFv51DDgON7+TXsje5jomvB66B6lBSWT1SAl5TNERg6DpeXL\n6NUrGPqSvoh/LR4ev3k0iSd/mFOFhdDhOAxVlLrfwL59wMSJSrlYbty9AUt9S9gY2jQ7JpFow8Hh\nA/TpcwNVVQkIDe2OjIyLKilzgCWpUT0hYw2/vRWO47BQyJJYgKtluNNwXtU5H6UyJyLk5u7G9es+\nkEj00LdvLCwtX0LC/yXA8mVLGD0lv1JhYV0dduXm4m2b5t9/M65fB/T0lHIxtGQkNMBxHMzMxqF3\n71vQ1/dGaGh3JCZuRL9+wlP0ARauaD3PGglvJfDel1loZ4dNWVmoakMf7CMttKVjqwPXVa6Imx3H\nOzt0sZ0d1mRm8qtJ3Q6ulkf10JWWhiI0tCfKy2+gd+9I2Nq+CY6TIPnDZJg8bYIuoxRvPP2cmYl3\nlPFpAmyDSknrKTC59axPHR1bdOu2Gy4uq/DMMy/C1XWBwo2s1uA0WF5D2so03tmhL1laIri0FIl8\nOsYMGgRkZrLmHEoywmUEzqYqf195ebHiYwXCyxwJorb2LqKjpyA1dTl8fQ/Dze0naGoaIXdXLipj\nK+83MJHHb9nZGGtqCiud1uvxYO9edl8pcQ9G5kTCysAK1obyE3I0NHTh7LwCPXqch5bWnxg7dhiq\nqoTX8wEAp8+cUJlQidyd/MrleujpoY+hofCG4krwyKsmWr5sCV1HXaSt5Fccvr+RESy0tHA4n0f3\nmR492H957CQNdx7Oa7Oqe3fW6ktob1e+yGT1SE39ArduPQ9Hx0/h43MAOjqsgFFRYBEK/i1A19WK\nk2niKioQXlaGF5VJxkpLA1JTgSFDlJpfYEqgUl2FAKC8fDw++igKnTqVITS0O0pKhHdm0OuqB8el\njoifx68tmJ6GBl7luyejocF6n/IokBTgFICLaReVbiWnocGiWkJClJ+WquTl7cf1637Q03OHv384\nOnfuC4BVQ0xcnMjcK7ryXSf1MhnW3zMSWqXBxcLHX67kfaWv3w1LllyEufk4hIf3RXb2VsGdhSQ6\nEnj+4YnExfzdLYvvReIJvXarc2uTUXnAcRzcN7kja2MWb3fLonvWOY+L8Xa1BDgF4FLaJaUfOm1t\nwM8PCAtTflpCqapKQkTEIJSUXELv3hGwtJx+37KuL6tH/KvxcN/k3qSsbUusyczEGzY2ipsENLB/\nPzBunFI7lLXSWly5cwXDnIYp8+fg2jXAx8cEXl5/wNV1FaKiJiA1dUWLkQnKYLfQDlRHyNrIz1f5\nfzY2+PPuXX57MpMm8YqWMtc3h5OxE69Wcu216pNKKxAf/xqSkj6Aj88huLh8DQ2NB7XIExYmwGqW\nFTr36axwnEMFBXDU1VVcC7+BGzeYQm8wuFrhbKryyvzuXaC4WAM9e76P7t3PIiNjDaKiJqK2Vlgb\nws69O8P6VWvcfpNfGOwIExMQEb89GR48cmUOsOLwLt+5IH5ePGT1yrtbJpub43ZlJW7yMYN5hiia\n65vD0dgRYVnKa+e+fdv+obt7dyfCw/vD0nIG/PxO3LfGG0j+JBnGw4xh+pypwnGK6uqwMzcXbynj\n0wR4uVhCMkPgbuoOk05K5OWjaXy5uflE9O4dgZKSS4iMHIrqav5tvTgNDh5bPZD6WSqv6BZ7XV2M\nNDHB73wKuw0fzsKYeGxyjXAegXOp55Q+vz2UeVlZOEJDe0Emq0Xv3hEwMmoay5d3IA/l4eVwWu7U\n6lg/Z2RgYWtlbhto2PhUwsVSK63F5fTLvIyEvn3ZFo+BgS/8/a9BT88NYWE9UVysfH35xjh96oSq\n21XI26N8MS7uXp4MLwOUBx1CmQOs6YCWmRburFK+qLuWRIK3bGz4LYn79GE+kJgYpUWGO3Ucv7lU\nWoG4uHlITf0Mfn6nYGe38H79kwaKLxUj/0A+XH9wbXW8LdnZGN2lC6yV8Wnm5LCuQq00bW7gXMo5\npa0noHmykI6ODfz8TsLMbALCwvoiP/+w0mM1oO+pD/v37XH7dX5W1Dt2dlibkaH8noy2Nmv7xaPD\neoBTgKA8hrZYpRMRMjPX4+bNUXBy+hxeXtugqdnU8q4rqkPC/AR4bPG438dTHhFlZUiprsYEMzOF\n592Hp5HgZuqmVB9ZoPl9JZHowNX1e7i7/4qYmOlISfkcRPw2JiU6Enhs8UDiO4moK1A+meglS0tc\nLS1FkpA+oa3NSe0jCqTB3XJn1R1Uxiu/+fSGjQ325uUp34mI49iSmId/c7jzcF6bVW2lzCsqYhEW\n1hdEtfD3D4OhYc9m50irpYh/NR5u69wURq8AgJQI65T1aQJMUT3/PGuBowRnU1lxLWWorWUr7d4P\nhcFznAQODh/Ax+cgEhIWIjHxPchk/FIh7d6zQ11hHXL+UN7SHtC5M4w1NXGMz47j5Mm87qshjkMQ\nnBGM6nrlav1bW7MEosTE1s/lQ319CWJiXkB29hb07HkVlpYt93JNejcJ5pPMYTy49YinNZmZeNvG\nBlpKRDwhLo5li/Xtq9R8G8eXK4O8cFdT0+fg7x+OkpJLuHFjJGpq+CWbGQ0wgvk0c9Y7V0n0NDQw\n18oK69vCOueT+9/SC8CzAOIAJAD4qIXjvEoS3Flzh8IHhfOq3TInNpa+5lNX4/z5JoV8WqO4qpgM\nvjKg6jrl6n7IZERmZkQZGcpPqTVycnZSUJAZZWVtVlgvJmlJEt2afEupMQ/k5lL/sDDlJzFiBNH+\n/UqdWllbSfpf6lNpdalS51+/TuTjo/ic2toCunlzLIWF9aeqqnSlxm2gNKKUgsyDeNVu2Z6dTSMi\nIpS/SGUlUef/Z++8w6Oqtjb+nlRKeoGENJKQHkKA0KQI0nsVEEFs2AEr6hXU6xVBBbmIqJ80pQpc\n6aJ0hBRIr6QX0ivpZSYzs74/NoGQZGbOOTNRyvyeJ8+jM3vvsxPOrNlnlXeZEQkQVhq8bTBdyr7E\ne/yTTxLt2cN/S+qoqYmhsDB3Sk19jWSyRqXjKs5UUKhLKDXXNqtds0QiIQu++j5ETIfl9df5bpke\n3/U4nU47zWusXM7+SUpLlY9RKGSUlbWGQkJ6UWXlZd77ICKS1ckozDWMyk/zF3nLaWwkq6tXqbZZ\n9d8Sf6c2C8fqdr+7bdB9ATzFcVy7hn2NjVm813R43QGkIBT+wN/3uNzBAVsLCyHjq6vRkkqWxW9f\n5l3M4Wvri7B8ftkVHMcOGdrIPFAoJEhLewM5OWsQEHAO9vYvKE0frI2tRdG2IpXFQa35tqCAv0+z\nooLlAU+cyGt4aF4o+tn1g6kxj+AX2OlJXam1oaEV/P2Pw8ZmNqKjB+PWLf5de0wDTWH/oj0ylvM/\nRc3v0QOJ9fW4wbejcteurLnA8eO8r/FPuvCKinYhPn48XF0/h6fn1nuCnK2R18uR9nIaPH/0hIGJ\n+sD3T4WFmGdrq17fpwUBLpaG5gZEFkZihPMIXuNTU5k6gCpZao7Th6vrZ/Dy2oGkpAXIzf2St0tO\nv7s+PH/yRNoraZDV8guYu3TpgtEWFtgtVDpCDZq6WQYDyCCiHCJqBvArgJltB0VHD0NFxe+8FuT0\nOHht90L2J9loyuP3+DnA1BS9u3TBMb5pii2pZAL8m/9EkUdTUy5iYkZBKi3EwIGRMDVVHulXyBRI\nfSEVbl+5wdhOvRskoa4OKQ0NmMtXfP3ECWD8eFbUwQNtPQq3heM4ODuvgo/PAaSkLEVOzmcgnhW6\nLh+7oC6+DmXH+AWtjPX08LLQmIxAV4uYOgZNK4zl8iakpi5DXt7XCAy8gp49F6ocn70mG+YjzGE9\nSXUwHQCaFQr8UFiI5XwPCTdvsp+RI3kND80LRaBdoKBDAt8iNGvrSRg4MBxlZUeRmDgbMhm/doJW\n46xgOdYSWR/yP7SucHTEloICKLQYANHUmDsAaB2xzL/92j34+x9FWtoryM5ewyvQ0N2nOxxXOiLt\nVf5Bq5VCK/dE+M3/zsyDW7fOISpqMGxt58HP7zcYGCivsgOA/G/yYWhlCLul/DojfVdQgFd79YIR\nH58mIOj0BAhLHQOEKyVaWo7GwIFRqKw8h4SE6WhuvqV2jn4XfXht80L6G+m8FfBe6dULv5aWoopv\nc94pU1gjFJ59RYc7D0dscSzqpPwysgYMYDHoJpEtdRsbsxETMxwyWS0GDAhH9+7tHqTvoSa8BiX7\nS+C+SX0wHQB+KyuDZ7duCDAx4behI0fYwYqnGI+Q/HJA+H3VpYsz+vf/C8bGjoiKCkJdXTyvee4b\n3VF+pBzVIfz+3UeZm8OI43Bem2mKQnwybX8AzAWwrdX/Lwawpc0Y+uSTT2j16nfp5Zd707ZtA0ki\nUe9TlEvkFO4fTsUHivm4oahZLien0FCKruHnoyWJhMjSkqiggNfwemk9dV/bnWoltbzGV1QQmZoS\nyWT8ttOCQiGnnJzPKSTEnm7dusRvb2n1dNX6KjVkqdd0JiKqkErJ4upVKubbjKG6mv0y1dX8hjdV\nk8kXJtTYrNwH25pbt8T9rYiI5HIppae/RWFhrlRTw8//n/pKKqW8mML7GouSkmhjrgAf/bRpRPv2\n8R4+atco+iP9D97j+/cnCg3lv50Wyst/p+DgHpSXt5mXTv+dz+B+fp9BIqJhUVF0RJWDui0jRhD9\n/jvv4UO2DaGLWRd5jxf7tyIiKi7eS8HBNlRUtJvX+JLDJXTd5zrJm/j1TdheWEhT4uLu/P+lS5fo\nk08+ufODv7M5BYChAP5s9f8fok0QFK0CoHJ5M2VkvE+hoc5UXX1N7S9bfa2aQuxCSFrOL5CyLieH\nnktO5jWWiIgWLybaupX3cKEfOg8Povh4/tuRSm9RXNxUiop6jJqa+EVPFQoFxYyOodyN/I3NVzdv\n0jMCGirQgQNEU6bwHn4q9RSN+XkM7/F//kk0ejT/7XREScnBOwFidTRXN1OoYyjvhgPXqqvJNSyM\nZHwblezcSTRnDr+xRPTppU/pvbPv8R7/yitEmzbxHn47wPcxhYY6UlVVMO952Z9lU9yUON4NWiKq\nq8klNJSa+TaBKSoisrBgnXV4UNVYRd3Xdud9SKivJ+rWjaiR3/AOqa2Np2vXPCg19RWSy1Xvs6Uz\nUdaaLJXjWmiQycg2OJjS6us7fF+oMdfUzRIJwIPjuN4cxxkBWABAaTKwnp4B3N3Xo0+fzUhImI6C\ngq0q3ShmQ8wEpf68aG+Po+XlKOObpiiwaq8zg1W1tdGIihqIbt08ERh4uV0RkDKKthdBXi+H40p+\n6YUtJda8fZqAICU7oPMfhTuiR4/5CAy8gry8b5CS8jzkcuXprQZmBvD43gOpy1Ihb1Dv9htiZgZb\nQ0P+aYozZgDnzwM89V0604UnlZYhPn4yqquvYMCACJibD+c1r/5GPQq+LYDnj5789HoAbCkowOsO\nDjDg67o7dkxQquvV3KsY4jgEXQw6DtS2JTqaiaV24Te8Q1iRUQSk0hLExIxUWbzGcRw8vvNA4Q+F\nqEtQ7zbrels6QltpihoZcyKSAXgDwBkANwAcJKJkdfNsbWehf/9QFBZuQ3LyIshkysv4XT93RfXV\nalT8of6DZGNkhDk2NtjGV01x4kQgMpLJufKgM0S3iAiFhf+H+PiJcHP7En36fAM9PX5ZAJJCCbL/\nlQ2v7V7g9Pl94E5UVMDB2BhBZqpLse/Q2AicPcsMFE8621+ujO7dfTBgwHUoFBJERw+7Rye9LTbT\nbWAaZIqcT3N4rb3C0RGb+Xa4srZmxWl/tm++0RFDHIcgpTwFlY38/Kd8jXl1dSiiogbC1DQIAQHn\nYGzML55CckLqi6no/VlvdHHiZwlLpFKcqKgQ1on+t9/YgYonl7IvdUpQXR0GBubw8/sNtrbzERU1\nWGUyh7GDMVzXuiL1xVSQXH2879Xb0hG1QqQjlCHkGC/mByryzGWyBkpJWUbXrnlSba1yf0TF2QoK\ndQ6l5hr1Oa4xNTXkEBJCUr6PenPnEu3YwWuoRCYhky9M6FYDv8fz8HCivn2Vv9/cXENJSU9ReHhf\nqq9P5bVmCwqFguJnxlPWan6PdC08Hh1Nv5aU8J9w9CjRGP4uk/L6cjJbZ0ZSGT/XmEJBZG3NO3TB\nc00F5edvpeBgWyopOah0nKREQsE9gqk6Qn0sQCKXk11ICCXW1akdS0RE339P9PTTfLdME/ZMoGPJ\nx3iNlcuJzM2V504rFHLKzd1AwcE9qKxMeIfxvM15FD1SWK3HZ9nZtCyFfxyCystZAjjfvycRBf4Y\nSKG5/B3gTz5JtJufu5s3VVXBFBrqSBkZ75Nc3rE9Ushvuz438HN9PpmYSN/m5bV7Hfd7Q+eOKCra\nTcHBNlRQ8KNS/1zy88mU+ho/gzcqOpoO8jVY+/YJ8gdP2DOBjiYf5TVWImE+u9oOYqa1tbF07ZoX\npaS8yKsZbVtKfi2h6778gy1EIr7oiIiWLCH67jvew/+X9D+atHcS7/EZGcI6pguhpiaKrl3rQykp\nLyv9GxftLqLwgHCSS9T/TT7NzqaX+BqswkJB/uB1V9fR8tPL+a1NROPGEZ3swE5LJGUUFzfldmFV\nDu/1WmjIbqCr1lepPrVjP25HSORysg8JofiObnRl7NolKK4g9JBAROTsTCTk+4UvEkkpxcZOpOjo\nEdTYeLPDMQ0Zt/+O6er/jlcrK8nj2jWSt7F9Qo35fVHOb2e3BIGBV1FQ8AOSkp5Ec3P7x033De4o\nP1aOqqtVatdb4eiIb/k+Ek+bJiiV7InerOUXH1oUFCMj775GRMjP34y4uHFwcVkNL69t0NcX1v5E\nWiZF+sp0eO/0hp4x/3/CLQUFeM3BgV+JNcBq7E+dAmbN4n2Ni9kX1eqXt0Zbj8IdYWo6AAMHRkEm\nq0J09FDU1ye1G9NzcU8YOxgjd736jlev9OqFQ2VluMUnTdHenjVZuMjPLacNF15V1V+IiuqP7t39\nEBh4BV26uPBeD2D3ZtpLaXB6xwndPPnVEwDA4bIyeHfrhr580xEBwXGYyzmXMcJ5BAz1+bkgi4uB\n2lperUQFY2Rki4CA07CymoqoqCCUlbWPu3V17wqXf7mwBuNqJJiHm5vDRF8fZ26pT69VxX1hzAGg\ne3dvDBhwDcbGDoiM7I+qqiv3vG9oaQiPrR5IfSEV8kbVQauZ1tbIk0gQVctDUtfMjGlznzrFa59C\ndVpadx6SSkuQkDAVJSX7MWDANdjZLea9TmsyVmag59M9YTaEp98bQJlUiiPl5XhJiE/z0iXAywsQ\nECxt6ffJl8405gBgYGAGX98DcHRcgdjY0cjP33JP0J3jOHj+5ImCLQVqg1Y9jYwww9qaf0xGQIB9\ngP0A5Nfko6SOX1Vga2OuUEiQmfk+btx4Cp6eP8Hd/SvecZfWFO0oQvOtZji958R7DhFhc34+v3aD\nLdTUAFeusIMUT8QUoQ0ezEuEURQcpwcXlw/Qt+8pZGauQmrqS5DL760UdlzpCHmDHEXbVN8vLR2u\nNFVTvG+MOcA6g3h4bIan51bcuPEUMjLehlx+V13MdpYtTAaYIHu16m4hLR2x/8v3dN7SgYgH/e37\ni/rQlZX9hsjIQJiY9Ef//sHo2pVfEUZbyk+Uoya8Bq7/cRU0b1tREebY2MDGyIj/JIGnp4KaApQ1\nlKGfXT/eczrbmAPsw2Jv/wL69w9FSclexMdPhkRyVy6ii2MXuK5zRcpzKWolmFc6OmJrQQE/6Yg5\nc1hpP4/gloGeAR7v/Tjv0/mQIUxdobY2CVFRQ9DQkIqgoDhYW0/mNb8tTflNyP4wG967vKFnwN8s\nXKupQUVzM6Zaq68OvcPvv7OKT75BeAgPql+71vn3FQCYmQ1GUFAMFIomREYGoro65M57nD4H753e\nyF6djaZc1VVeC3v0QHRtLVL4Skd0hBCfjJgfCBTaakEiKaPExCfp+nUfqq6+fvf1MgmF2IVQVXCV\nyvm3bhfGFPDxWZaXs6oVnsGYGQdm0K8Jv/Iam55eTmvXLqRr1zypqkpk9cJtpBVSCnEIocrLlcLm\nyeXkEBJCsUJ8ms3NRLa2RJmZvKfsjt1Ncw7y94M2NbGYgoAYmMbI5VLKyvqEgoNtqbBwx50YjUKh\noNhxsXRzfcc+0NaMiI6mQ3xjMgMGEF3kV+Sy+dpmevH4i7zGyuVSeuONz+nyZWu14mvqUCgUFDc5\njrI/yxY8d35iIv23g+CdSgQkHRARFdQUkNWXViRX8I/1jBlDdJqfFpfWKC09QiEhdpSe/s49omU5\na3Modlys2n+jNVlZ9Grq3bggHkSfeUcYGdnA1/cgXFzWICFhBtLTV0Imq4GRjRE8tnog5bkUlTnC\ngjpiW1uzr/E//uC1Nz755kSEkpIDKC3ti1u3esHOLhbm5uq72asifXk6bOfawuJxHk2XW/FbWRn6\ndO2KfkJ8mlevAk5OgJsb7ykXc4T5y+PiAA8PoHt3/tvSFD09Q7i6foqAgLN39LsbG3OYu2Ubk2Cu\nv6H6dLTCwYF/mqKApz6+Lrza2ihERQVh0KBg5OVFqxRf40PxL8WQFknh/IGzoHl5TU04V1mJ5+z4\npTwCYLn3586xEn6eXMq+hMddHocex89cyeUsTsVTUVdrsIYq8ZBIchEZ2Q+VlUzLyWmVE2RVMhT9\npNrd8mqvXjhQWsovJtMB960xB9jjcc+eT2HQoETI5bWIiPBDWdkR2My2gelAU7XuFkEdsQV0IFLX\nF7S+PgVxceOQl/cV/PyOIC1tIyIihAU521J2pAy1EbVwW8ffuALsS+Wb/Hy87cTfDwqAtdbj2Y+x\n5Tr/RLGQWExNAzFgwHVYWo5FVFQQcnI+h5ETB9fPXZH8TDIUzcrdKLNtbJDLNyYzbx7TH+HhlvGz\n9UOdtA45VTkdvt/cfAvp6SsQHz8FTk7voqLiNEJDhRngtjTlNSFrVRa8dnlBz1CYOdhaUIBnevaE\nGU9dFQAs937QIHaA4onQOExyMtCzp6BLaA0jI1v4+R2Cu/vXSEl5ATduPI1meSm8f/FG1kdZaMxW\n3pTC3thYWEymDfe1MW/ByMgG3t474eOzF9nZaxAX9wTsv65F6a+lKrNbBHXEnjWLncx5KBj59fBD\nrbQWN6vurQZrbr6FzMz3EBs7EjY2M29X3A3VWOlOWipF+uvprIFuNx59OlsRWlODSpkM04Tc2QoF\nU5QU4C/PrMyETCGDl7UX7zn/pDEHWEWys/P7GDgwEnV1sYiI8IXhnGsw7GGIm2uVV/oZ6OlhuYMD\nNuXx6Irl6cn0V0ND1Q7lOK7DrBaFQoaCgh8QHu4DIhkGDUqCnd0SDBnCaSTmRgpCynMpcFzpCNNA\nfiqELTTI5dhRXIzlQgKfgOA4DBExY+7G35hfu6ZeTrmzsbGZgcGDk2Bs7ITIyL4o774Fju/3UNtg\n/E1HR3xXUIBmvnLerXggjHkLFhaPIygoDj16LERy7ix0/eW/uPHOBchqlAeY3nR0xKa8PJWyAQDY\nV3m/fuwRUA16nN49p3O5vAE3b65HeLgXZLJaBAXFw9FxBfT02IlFEwVFIkLaq2no+UxPmD+mWjmx\nIzbl5WGlgwP0hDyGh4YyAyQgr6vlVC7kcf+fNuYtdO3aG/7+/4OX1zZkZ3+M5o9fQX7wcVSHK09X\nXWZvj9O3biGfj3zhvHm8m4iPdR17574ikqO09CAiI/uhrOwQ+vU7C0/P72FkxFqx9e/PTqFiO5AV\nfFcARb0CTu8LfGoDsKekBMPMzODeVcATp0QCnD4tKNX1QTwktKCv3x3u7uvRv38wamsjUTR8LJp8\njyL/O+VpsP1NTeHepQt+K+PfW/QOQhzsYn4gMgCqjubmGsrK+pgu/2lOYdunKq0gVSgU5Hf9Op2r\nqFC/6ObNREuX8rr+tqht9Oxvc24rHNpRYuKTSqs4q6qIuncn4tt4pTVFPxfRdb/rJGsULimY1dBA\n1jw6mrRj5Uqizz4TNGX+4fm0M3on7/EtBYBilBI7E4VCRsXF+yjknDv99VMglRWeVhq4WpGWRqsy\nMtQveuMGq4ziUayVeSuTem3oScXF++n6dV+KjBxCFRV/Kt1DUBBRMH/trDvU3aijYJtgXkUtbZEr\nFOR57RpdrhQWiKcTJ4hGjRI05ceIH2nJkSWC5vTty6qv7zeqqsIoMmQEXTrQizLDvyGZrOO//dHS\nUhocGfnwBEDVYWBgClfXf2Po0CzIYl0Qe30c4uMno6zst3t6RHIchzcdHbGRT8BqzhzWhEGFP3rm\nWwAAIABJREFUUBcRoa4uDv0Nr2Cu2VE0NmYgIOAc/PwOoVu3jk+y5uaAiwuQmCjsd2zIaEDmu5nw\nPeAL/S7C3CsA6yT0gr09TIT4NBUK9igswF+uIIWo5s2DBrE+IfcTHKePnj0XYegTKeiePx8p4W8h\nPNwH+fnftmtWsNLRETuKilCnLvXQx4el4alpPSWRFEKveg++CyhHxs0NcHffiAEDwmBlNVHpE0/r\nOga+KJoVSF6SDNfPXdGtD//ioBZOVVTAVF8fo8wFPikePgw8+aSgKUL95XV1QGYme8i+3zA3H4qB\nj12Fk+J7FIQdw7VrrsjO/hiNjZn3jJtuY4NyEUHQB9aYt2BsboWAZ9YBSw/A0mAu8vO3ICzMAenp\nK1FZeQkKhRSLe/ZEbF0dEuvUKJk5OjKZtfPn73mZiNDQkIHc3C8RGRmAhIQZsDJxwZpUR8is3oGJ\nib/afQr1myuaFUhelAyXj11g0ldAFsptqmUy/FJcjDeEqCMCzOCYmjIDxJOk0iSYGZvBxYJ/xeH9\n4NdUhZ6eAQLefBd6K3fCrvxrVFeHICzMBYmJ81BScgAyWQ3cunbFaAsL7Czm0Qh43jxmzNoglZag\nsHAb4uOnICLCD1JpMYKbpiMGz8HaepJat9XQoUAYv26Gd8j5OAdGPY1g/5KAArJWbMzLw7tOTsIy\naCQS4ORJQcJaClLgUs4lQf7yyEhmyIWUU/zduD01A1ZXv4fVn7shk1UjOnoYoqOHo6DgRzQ15UOf\n47DKWXhg+4E35gBgNsgMTq+6o+KNIAQGXMKAAddgaGiFrKwPEBJii/Qbc/CFyWkcyDwIiaRQtf/8\nySchO7YPNTXhKCr6GSkpz+Hatd6IjX0cjY2Z8PD4HkOHZsPV9T8IcprIu5Wc0BNUzic5MLQ1hMMb\nAo3xbXYUFWGSlRWchOp/CjyVA8JPT8D9b8wBVnXsd8AP+S9Ywd38FwwZkgFr6ykoKdmLsDBHxMSM\nwkq97fjr5j7UN96EQqHihD5vHhRHD6OhPg0lJfuRnv4moqKG4vp1L1RWXoCd3VIMHZoLT88fMLD3\nk7yLh4YOFXZIuHXmFor3FMP7Z29R6YwRNTXIaWrCPL7tBls4dw7w9wd69eI9Jb4kHlZdreBoxj/I\n+ncVC2kCx3Hw/D9PVP1iBuvsTzBsWAGcnT9AdfVfiIwMxPXr3hhT94XwdVUaNi3AcRx19jUAJtsZ\nNz4O5iPN4frvu9WRUmk5KivPorQqDJeLQxGonwtQMwwNbWBoaAkDA0sQySGX10OhqIe0qQTyujJ0\nswlEt+4+MDcfAUvLsejatb2u86+Jv2J/wn6ceEqphPsd4uKAhQtZwEodlZcqkfx0MoJig2DUQ/gR\nQ6pQwP36dRzz98dAUwFZCkQsr/z4cSYqw5PpB6Zjcd/FWOC/gNd4hQKwsgLS01U32r1fuPnFTdw6\ncwuBFwPvSA3LZDWoqbmOmpownMk/Cw9kQl9eASOjnjA2dgST9+fAcXqQyaohkRRA1lQCIwM7mFoP\nh5nZYJiaDoa5+TDo6d2r511UWwS/7/1Q9l4Z9PVU+6GIgB49gJgY9mCpCkmhBFEDo+D7q6/gWoUW\nFiYlYYiZGd4Smuq6dCkwcCCwYgXvKRtDNyKzMhPfT/2e95zZs4EFC9hn7X6n5XM+MHIgjHuxe4BI\ngbq6OFRWnoOLy/sgIv7fuEIc7GJ+0EkB0I5oKmqiEIcQqjjTcbBzeVoavZ+eTlJpBdXXp1F19XWq\nqPiTKirOUlVVCNXWxlJjYx4phj/Gq5VVSV0Jma8zp2YlUpitaW4mMjFhLdJU/g75TRRir/x34MPP\nRUU0NiZG+MTwcNYeSUA1oVQmJbN1ZlRWr74VYAtJSUTu7sK390+hkCkoZmwMZX3Ssdzw4ZISGhoV\nRTKZhBobc6iqKphu3bpEt25dpFu3zlN1dTg1NRWQfPWHRKtW8bqm31Y/Cs/nF8WbNo3of//j8TuM\njqHsf2fzWrMjshsayOrqVaoWGlBvamItGvP5dc9qYfLeyfS/JDW/WCsUCiJ7e6LsbGHb+yfJ/k82\nRY+IJrm0fXAcj0oAtCOM7Yzhu88Xyc8koym/fcrYW46O2F5cjCbODN26ecDMbDCsrCbCymo8zM0f\ng4lJP3Tp4ghu/gLg0CG11+vRvQeczZ0RWRipdqyBAWvGGxGhfIxCqkDS/CQ4vO4AqwlWatfscA0i\nfJWbi/dF+Nxw8CA71ghJLyy4DndLd9h0s+E950FwsbSG0+fgs8cHRT8VoeLP9k1SZtvaory5GSG1\nDejSxQXm5sNhaTkalpZjYGk5FmZmg2Bs3At6c+czvzmPJ9VxbuNwPuu82nEAP1dLzn9yAA5w+UiY\nkmJrNt8OqAsqEgJYDMrXV5Bgm1QuRXBuMMa4juE9Jz+fVX+6iP8V/3Zc/uUCfTN9ZH2YpfFaD5Ux\nBwCLxy3guNIRNxbcaFfF59q1K8ZZWmK7ugqruXPVZrW0oM0PXea7mTC0NoTzh+Kr+n6vqICxnh7G\nWVoKm6hQsC+wBfxcJS2czzqPcW7jBM150Iw5ABjbG8P3oC9SlqagIePednD6HIdVTk5Yl6tGRrdf\nP/atruob/Tbj3cbjfLZ27quyY2Uo3lEMn30+vDtStaWiuRm/FBdjhdCAOiAqi+V6/nV4WHvAqiv/\nQ01YGPtbdJZSYmfA6bGDQtn/ylB2RERueSseOmMOAM7vO8PQ2hDpr6W3C3a+6+SETfn5kKqqsHJw\nYFrUPAqIWhd5qGPYMOWZByX7S1BxugLeu73B6Ym/G7+8fSoXHNy6do1lsfirz8xpzfms8xjvNl7w\npR40Yw4AFiMt0PvT3kiclQhZ7b3Bzmfs7BBfV4dYVSX+HMecuQcPqr3WKJdRCC8IR0Oz+j6igwYx\nn3lH2Wx1iXVIW5YGvyN+MLbn12uzI7bk52OurS0chQbUpVJ2MBJQ9QmwoLoQyVuA3VfDNJM/+kcw\ntDKE32E/pL2Shvpk8aqJD6Ux5/Q4+OzzQU14DfI33ZtfHmRmBu9u3bC3RI2E7ZNPdphK1pZRLqMQ\nURDB60M3bBjLaGn7PVITXoOMlRnwP+IPQwvhOtQthFRXo0gqxVwb/i6PO7S4WARQI6lBbHEsRjiP\n4D2nthbIyhIUX72v6PVKL5gPM0fKsyn3HBSM9fTwlqMj1qs7nS9YwP7Wasq1TY1NEWgXiODcYLV7\nMjMDevcG4uPvfb25ohmJMxPhvskdZoP4y822pVYmw9bCQqwSGvQEmIvFx0d9dLYNQkv4gbsn8wcR\ns0FmcP/aHQlTEyAt5dmQvg0PpTEHAANTA/Q90Rd5G/JQfvLehs2rXVywLjdXtSZ1i6tFIlF5HVNj\nU/Sz68frQ9ezJ2BhAaSm3n2tMasRibMS4bXLCyYBwvPJW/Nlbi7edXLi3x29BbmcfXEJNOZ/5fyF\nIY5D0NWQf0l3RAQQGHh/5wGroqUDu7RI2k7o7eVevXC+shIZDSq+2P38AEtLXlot493G41ym+qdD\noL2rRdGsQNKCJNjOsYXdYgGqhh3wY2EhxllawqOb8AIjHDggOLWkRlKDmKIYjHIZxXuORMK+zAYN\nErrB+we7pXbo+XRPJM5MVNuApyMeWmMOAF1cusD/qD9Sn09FXdzdgqFRFhawNzLCIVX6Bw4OQN++\nwJkzaq8zwW2CqA9dc0Uz4ifHw2WNC2ymiThNtyKurg4RtbV4VogcaQvBwSy/zYu//gVw21/u+vD7\ny9uiZ6wH/+P+KDtchvwtd5/8TA0M8KqDAzaoE+BauBD49Ve11xnnNk6U35wUhJRnU6DfVR9u64Wp\nbLalSS7Hpvx8fCgmoN7QwAqF5s8XNO1yzmUMcRyCbob8vzyio9nt+3fKKXcGvT/rjS69uyDl2RTB\ncx9qYw4AZkPM4PG9B+KnxKMh7e6JabWLC9bevAmFqsyCRYuA/fvVXmOC+wSczTrLaz8tfnN5kxyJ\nsxJhM8sGDq+KKwxqzWc5OXjPyQldxdTHi3CxAMD57PMY7/5o+MvbYmRrhICzAcj9MhelB++qcq5w\ncMChsjIUqHqiW7CACW+pkQEY1GsQsiuzUVqvXvWzxZgTEdJXpEOSJ4HvIV/RAc8WdhUXI8jUFAFC\ntPBb+P13dlTu2VPQtHOZ5zDBbYKgOWFhD6a/vC0cx8Frlxck+ao9Ah0iJI9RzA/+xjxzVRTuLKRQ\np1BqyGRd2hUKBQ2KjKTfSkuVT2pRg6qpUbl2s7yZLNZbUFFtkdp9REQQ9feVUezEWEpalEQKufgO\nMS3E1daSXUgI1YtRrWpuJurRQ1BHIaK73V9kcv7XVChY8yKhjWnuZ2rjainYNpgqzt2tC3gnPZ3e\nSEtTPXHgQKLz59WuP+PADDqQcEDtOJmM3aqJ72ZRRGAENVcJzAXvAKlcTr3DwiisSnVXL6XMni2o\no1ALnls8KbowWtCcefOI9uwRfKn7FkmZ5NHOM1eF/XP2cP7QGXFj49CU2wSO47DaxQWf37ypvLzf\n2pr1Kjx+XOXaBnoGGNN7DK8URX8POZakJAJmhvD+RbPMlRY+y8nBu05O6CbmVH75MuDsLKijEABc\nyLqAMb3HqK1QbE12NvOVC5XAvp8xCTCB329+SF6UjIo/WA76Kmdn7C8pQZ4qedwFC/i5WlzH8XLh\n6esDK2xzUbK/FAFnAmBgLjAXvAN2l5TArUsXDBUqqAUA1dXAhQuCtFgAILc6F5WNlYL6yAIPzxNf\nC0Y2woNKj4wxBwCHVx3gsMIBsU/EoiG9AdOtrSEnwsmK9oUgd1i0iAVx1DDBfQLOZqp2tcjr5UiZ\nnQDOxgilz/sIapyrjPi6OgRXV+MVAZoX9yAiQAUA57LOCc4vDwu7/3UzxGAx0gL+x/2R8mwKSg+V\nooeREZbZ2+MLVZkt8+ezBiBqahnGu4/HuaxzKvWESEHIeDcDQ6qLETK3nygJiLZIFAp8lpODz12F\nNQ6/w9GjwJgxLOIvgHOZ7L7i2yIOYMVCTU2Au7ge6Q8Nj5QxBwCnt5zg/L4zYkbGoDq4Gv9xdcXq\n7GzlvvMZM1iAUI1YfIsxV/ahkxRJEDcuDl1cuqBwiTfCwrVT2fBZTg7ec3ZGdzGn8sZG9qF76ilB\n04hIVLFQWBjw2GOCpjwwmA8zR79z/ZDxVgaKdhThXScnHCotRY6yzhEuLixid1b1AcDL2gsKUiD9\nVnqH7yuaFUh5NgU1YTXQ+64/LicJzANXwk+FhejbvTuGiTmVA+yQIPC+AoCzWWcF1y20+MsfpGKh\nzuCRM+YA0GtZL/js8UHS3CQMPiODib4+DihrLWdiAkyZorZTjJulG7obdUdCaUK792qu1yB6cDSs\nplrBa4cXhg3nNGoj10KCpqfykyeBoCBBSnYAkFiaiK6GXeFuKewoFBoKDB8uaMoDhUmACQIvB+Lm\n2puoej8Xr9nYY62q0/nixcCePSrX5DgO49w6drVIy6VInJGI5lvN6HeuH4aON0R4uNq4qlrq5XJ8\nkZuL/4g9lZeWsoKK6dMFTZMr5LiQdUFwUP1hCX5qyiNpzAHAarwV+l3sh+yPsvHFT0b4z40s5VWh\nfLNa2qQoEhGKdhYhYXoCPL73QO/VvcHpcRg2jPn4RLT5u4cPsrLwvthTOcAMyZIlgqf9mfEnJrmr\n19puTV0dkJbGWp09zHTz6IaBkQPRmNmI6UsrcTWhFJnKTufz57MGx9XK29MBwKQ+k3Am894U2cpL\nlYjqH4Xu/t3hf9Qf+t30YWUFODkBCe3PE4L4rqAAI8zN0V+I4mZrDh8Gpk4FBOalxxTHoEf3HoIk\nbwGdMW9BI2POcdzXHMclcxwXx3HcEY7jRD6T/TOY+JsgKDoIPer0sG5pM/YfUyJ2M3EicOMGoKa6\nr3WKYmNWIxKmJSBvYx4C/wqEzfS7eeR2dqz7UFqa+L1frKxEckMDXhOjlQEwt9HVq0wzVCB/Zv6J\nSX0mCZpz/TorFjIWX1H+wGBoZQj/4/6wn98T375C+GVLcsdNfK2tgbFj1T71jXcbj79u/gWJTAKF\nRIGsj7KQvDgZXju84P61O/QM736MH3uMVz2SUqplMmzMy8NnvXuLX2TvXnYAEsi5zHOY4C4sJbGl\nWCgoSPDlHjo0PZmfBeBHRP0ApAH4UPMt/b0YWhnCd68ven3lCvOX8pH0Ugoas9ucpIyMWEWomtP5\nGNcxiMiKQMa/MxA1OAoWoywQFBOE7j7tKxnEdIhpQUGEdzMzsc7NDcZCqz1bOHSInZ4E5g/XSesQ\nXhAuSM0OYAbmYfWXdwTHcXB6xwkBx/vCZVctrgyKQNWVqvYDlywBdu9WuZZ1N2v4Wfoh5JsQXPe6\njoYbDQiKCepQWXP4cM2M+ca8PEy2soKP2Oqb1FSWtjRxouCpYvzlMTGs77iYNPiHDY2MORGdI6IW\nZ8F1AA9s0tmQhU44etwaMQaNiBoUhRuLbqA2pvZuQHPpUuDnn5XKlzakNaB8dTl2b9iN3NBcBEUF\nwfl9Z+gZdfwnViW6pY79JSUw5DjM16Szg0gXy+WcyxjUaxBMjIR9eh41Y96C3XBL6J33wK9zCMnP\nJCN+ajxK/1cKecPtcu0pU4CkJCAnp8P50nIpinYUYfXa1ajcWwnffb7wP+qvNGPlsceAkBBxe73Z\n1IStBQXifeUA8MsvLBYgUCa3XlqPyMJIPN77cUHzdC6Wu2iejHqX5wGoz+G7j/m4vzuGUjSi/90f\n+LkCiTNZB2aL0RaweNwVXevdobcrDHqDAkBSQl18Hepi61AbUYvGzEbYPWuHpB+SUG5bjidcVCu+\nPfYY8NNPwvfYJJfjo+xs7PXxEdX2CwBr8ZOTA4wTlo0C3PaXC3SxKBQsRvDLL4Iv91CwtJc9tkws\nxJglLhh5nlD0UxFSX0yF9RRrmA4yRZdBb6LLhmPQX/kSpCVSSIulaMpqQsXvFaiLrYPleEuYrzPH\nB9UfYO5w1eqDnp5MzKygQJB8OABgVWYmljs4wFmoMmILcjk7JJw+LXjqpZxLog8JM2YIvtxDiVpj\nznHcOQAdCX78i4hO3h7zEQApEXXoh/j000/v/Pfo0aMxevRoMXvtdPp064aXe/XCB2U3sf89Xzi9\n64TG9EZUXa5C5YUqFOu/DMUHRVD0NAT0gO59u8Mk0ATW061hMdICesZ6eDz/cSw7uQxf42uV1+rX\nD7h5E6isZLpLfNlSUID+JiYYKTB/9x727mVpY0KbDAA4k3kGv83/TdCc5GTAxobJvzyK6HEcNrq7\n4/nUVCQvHQT75+0hLZWi/Fg56pPqUVU3Ck3bciH/Ix5GdkYw6mkEY0djOK9yhsVYC+h30YdcIUfB\nhgIU1BTAwUy5leY4dlAICxPWyvVKVRWu1dRgl7e3+F/04kX2j9y3r+Cpp9NPY4rHFEFziNhTyFdf\nCb7cfcnly5dx+fJl8QsIKRft6AfAswBCAHRR8n7n1Lt2EnUyGTmHhtKljvq75eWx9lf19Urny+Qy\nsv7Smm5W3VR7rSeeIDp1iv/e8hobyfrqVUpRcX21yOVErq5EkZGCp2ZUZJD9BntSCGgrR0T0f/9H\n9Mwzgi/30DEzPp7W3+zgvlAoiPr0Ibp+XeX8BYcX0I5o9eXxX3xB9NZb/PclUyioX3g4/VpSwn9S\nRyxaRLR5s+BpCoWCXDa5UFJpkqB5mZmsTZzA2/GBAX9nOT/HcZMAvAdgJhGpqF1+cOiur4+N7u5Y\nnpGB5ra5g46OwODBwLFjSufr6+ljUp9JOJ2u/lFz+HBh/s3lGRl4w8EBXmKkSFu4dIkJYA8YIHjq\nmcwzmNhnomD3zqPqL2/L1+7u+Do3F7lty/w5jsUv1PihJvWZhD8z/lR7HaFB0B1FRTAzMNAsBlNd\nzYS1RGSxJJcng0DwsfERNC84GBgxQlcs1IKm2SxbAJgAOMdxXAzHcfzbaN/HzLW1hZ2REb4vLGz/\n5rPPArt2qZw/1WMqL2M+YgS7IflwrKwMN+rr8YEYKdLWbNsGLFsm6hPQkl8uFJ0xZ3h064a3nJzw\nSlpa+0rhpUuZVosKLfSJ7hNxPus8ZArVVUFBQSzXXFl6e2tKpVJ8nJ2NzX36iI/BACy3/IknmD9N\nIH+k/4EpfaYIvn5ICPsM6biNkGO8mB88YG6WFpLr6sgmOJjyGhvvfaOxkcjKiqijx+XblNeXk+kX\nptTY3Kh0DBFRdTVR9+6sebnKcc3N5KjM9SOEsjIic3MiEetIZBIyW2dG5fXlguaVlrJLyts3H38k\nkcrlFBAeTnuKOlDYnDyZ6OefVc4P+CGAQnJD1F5n0CCiK1dUj1EoFDQ3IYFWZWSoXU8tI0YQHTsm\nauoTvzxBJ1JOCJ7n6yvKW/jAAJ1qonbw7t4dKx0c8GxKyr26LV26MMU7FbnB1t2s0bdnX1y5eUXl\nNczMAA8PJqyvijXZ2RhvaYnRQps0t2XPHhb6F7FOcG4wvG28Yd3NWtC8llZeYtPhHzYM9fSw09sb\n72RmoqStyNbLL6tNcZrkzs/Vwqd46GBpKW40NODfmhQIAUBKCquAmzxZ8NRaSS3CC8LxhKuwfp+3\nbgF5eSyRQAdD9xFTwQfOzmhQKPBt/r19RPHcc8zVoqIef0qfKbxdLar85leqqnCwtBRfayoJRwRs\n3w68+KKo6afSTmGaxzTB83QulvYMNDXFs3Z2WJHeRjxr6lSWMpqYqHQu33iMOmNeLJFgZUYGfvH2\nRhexchAt/Pgj8MILonoBXsi+gGGOw9DdSFiRUmgoC1+JSMh6aNEZcxUY6Olhj48PPr95E4l1d9vO\nISiISXuqaCk3xYOfMR8+XLnfvEwqxdPJydjl7Q1rQ/GNngGwI7JMxvTZBUJEOJl2EtO9hAknAeyL\nSmfM2/Np796IqavDgdaNxQ0MgOefZ3ENJYxwHoGsyiwU1nYQz2lFSxC0oxo3IsIraWlYZm+PQWbi\nGz0DYD7+PXuAl14SNV1MSiKg85d3hM6Yq8G9a1esd3PD4uRkSFpO4hwHvP46sHWr0nmBdoGok9Yh\nvaJj6dIWWk7mbT90CiI8k5KCp3v0wGRrYa6NDtm2jZ3KRQS5UitSIZFJ0K+nsGfapiZWbv0wNQ3Q\nFl319XHI1xcrMjKQVF9/940XXmB1AEqil4b6hpjYZyJOpZ1Sub6DA2BqyjwgbdlWVISspias0dS9\nArCg7bBhgIi1iEi0MW/JZNFxF50x58EL9vbo3aUL3srIuJuFsHAhK2vM6lici+M4TO4zGX9k/KFy\nbUdHJi7XVnTrq9xc1MhkmpVWt1BdzXTLly4VNf1E6glM85wmONsgPJw1o9fpZnRMoKkpNri7Y05i\nImpadGt792b+AxXiWzM8Z+Bk2km1648aBVxpE7a5WlWFNdnZ+J+fn3hdn9Z8/z3w2muipiaUJsDY\nwBgeVh6C5kkkLM70MDY60QSdMecBx3H4xccHV6qq8N8W/3m3bixN8YcflM6b6sk/RbG13/xqVRU2\n5efjV19fGGrjA/fzz0z4SGQJ5sm0k5juKdzFcuUKMyg6lLPUzg5PWFri2ZSUuweFl15SGQid1GcS\n/sr5Cw3NytMYgfbG/GZTE+bfuIHdPj7w1KRWoYWICKCiQpSoFsBcLJP7TBZ8SIiKAry92ZOHjrvo\njDlPzA0M8HtAADbk5eFYS9ehV19lhlLJI/E4t3EIzQtFraRW5dqt881ja2sxLykJe3x84CRWI6M1\ncjmweTPw5puiplc0VCC+JF6wSiKgM+Z8+W+fPiiQSPD5zZvshWnT2BNfbGyH4y27WiKoV5DanrMj\nRwJ//cVceHUyGWYmJGCVkxMmWrVXWxTFDz+wDByRAdTjqccx02um4HnBwQ93kxOx6Iy5AFy6dMFx\nf38sS0tDRE0Nazo4aJDSxrxmxmZ4zOkxtalkLUHQ1IYGTElIwFYPD0zQ1gfuxAl2IhcpLXc6/TSe\ncH0CXQyEfbE0NzMvlM6vqR5jPT0c9ffH7pISfJWbCxgaAsuXA998o3TODK8ZOJF6QuW6ffqw7/LU\nbDmeSk7GAFNTvKmtbtqVlcCRIyxgK4Ki2iKklqcKVkkEdMFPZeiMuUCCzMyw3csL0xMSEFJdfTcQ\nqkQad5b3LBxLVV7+DzC/cjEaMTY6Duvc3DBPm4pUmzYBb70lerpYF0tMDODqKiql/ZGkl7ExLgcG\nYntREdbfvMlOvKdOMfnDDpjuOR2n0k5BQcrTYzkOGDZWhjmpCeiqp4cfPT01q/JszY4dLJVS5L16\nPPU4JntMhpG+sHRGhYIZc93JvD06Yy6CmTY2+NnbG7MTE3Fw4EBWwaBEnHym10z8kf4HpHLlXdjj\nGmoh+zoOEyudsNSuI4FKkURFsbzluaplU5UhlUtxLuscpnpMFTxX52IRjoOxMS4FBmJXcTHW1tSA\nFi8GtmzpcKy7lTusu1kjoiBC6XrlUimino4D5XXFAV9fGGmrcksiAf77X+Cdd0QvcSzlGGZ7C+9y\ndeMGywoW22DrYUZnzEUyydoa5/v1w3vZ2Vi3bh1o/foOx9mb2sPbxhuXsi+1e4+IsLWgABPj4zG3\nwg1d/tRyb49Nm9jjusjKiis3r8DL2gs9TXoKn3tFVEr7I0+LQT9cWorZTz+N0kOHWAPVDpjuOV1p\nVkt0bS1GxsZigpUlZBs8oa9NNap9+9jjpAixNgCobqpGaF4oJroLD5xevgzcpwra/zg6Y64BASYm\nCBswAEfd3DByyhSERUV1OG6W9ywcTTl6z2vFEgkW3LiB7UVFCOvfH2/26wFNpIzbUVDAVOxEVnwC\nwMlUcS4WhYLFAHTGXBy9jI1xfeBA+Nraot+WLTiqJE2xI795jUyGlenpmBwfjw+cnfF/g9xwq4JD\nUZGWNieXMwHxDz4QvcQfGX9glMsomBoLT0fRGXPl6Iy5hjgYGyMsKAgvGhlhflER5iUmIrym5h75\n3Nnes3E89TgUpEBkTQ2WJCfDJyICTsbGCOvfH326dUNgILO/paVa2th33wFPPy3aaa3+20OsAAAa\njklEQVQgBY6kHMEs71mC5yYlMfE8e3tRl9YBFhT9ws0Nv1lYYFXXrhgVHY3thYWolt1VTBziMATF\ndcXIrsxGWkMDvsnLg094OOrkciQNGoSldnbQ02PBwqtXtbSx48dZN3INLOrRlKOi7iuFgmXnPC48\nZvpIwJGSwJ3WLsBx1NnXuC+orUWDtzc2nzyJfTIZbkokGGhign4mJqiUyXAk6wosLbyhp2eENxwc\n8KK9PSzblOhPnw488wzw5JMa7qWsjCXiRkWJqswDgLC8MLxw4gXceP2G4Llbt7IA6Pbtoi6tow3S\nkSNx+p13sKdPH5yvrMQQMzOYGxigm54eruWHocKwF4yNzDDV2hrP2tnhMXPze+Zv2MBCJ999p+FG\niFilzocfArOF+7sBQCKToOeGnkhbnoYe3YUFTxMTgVmzgIwMUZd+4OA4DkTE2z+mk6nRFqam6LZs\nGT78/nt8uH07qpqbcb22Fon19ehvYAB51wYYN/yFn8auhoGSQNTo0ewxUmNj/tVXTNlRg3LtwzcO\nY77ffFFzr1xhiQ46tIPRv/6FWW+/jVkJCbhFhNDqajQoFGiQy2HV3BtnYzYj8ZljSjNVRo1SKfLJ\nn0uXgJoaYKbw3PAWLmZfRN+efQUbckDnYlGLEL1cMT94QPXMRVFeztrK5ea2eyuyIJL6fNtHZcu1\nyEim0awRhYVsD/n5opeQK+Tk+I0jJZYkCp6rUBDZ2RFlZ4u+vI62KBREI0cS7dzZ7q1meTPZfmVL\nmbcylU6XSolMTYkqKjTcx9ixRDvUt61TxbITy2hDyAZRc+fOJdq7V6PLP1BAp2f+D2JtzYooOshs\nGWA/AFK5FEllSUqna8Vvvm4d02DRIHfrev51mBqZwq+Hn+C56eksecbFRfTldbSF49i/66efsrTA\nVhjoGWCOzxwcTjqsdLqhIRM708hv/uefTEB8yRLRS8gUMpxIPaHzl3cSOmOubT74ADh0iCXEtoLj\nOMzxVv2h09e/W4ItitxcprinQaYBwFwsT/qK8/VcuACMHavry6h1hg9nXe//7//avTXfbz4O31B+\nXwGso9uFCyKvLZMB777L3HcaSDFfyr4EZ3NnuFsJ1+ZvyS/XVgHrw4jOmGsbGxvgo4+At99uVxW6\nqO8i7EvY177/Yyta/OaiWLuWiTT1FJ4X3oKCFMyY+2lmzHV0AmvXAl980S7vfJTLKOTV5CHzVqbS\nqePGaWDMd+1i9/WMGSIXYOxP3I+n/J8SNVfnL1ePzph3Bq+/ztIHTt+rmBjUKwh6nB4iCpVX7Yk2\n5vHxTCvjvfdETL7LHReLrXAXi1zOYmQ6Y95J9OvHjtj//e89LxvoGWCuz1yVp/P+/YHiYqCjHuUq\nqa0FPvkE2LhRo8etxuZGHEs5hgX+C0TN1xlz9eiMeWdgaMhEkt5+mylO3YbjOHY6j9+ndKoov7lc\nzk7ka9cyv70GtLhYxGh4xMYCdnZAr14abUGHKv7zH2bM2+joz/ebj0NJh5RO09dnxvDiRYHX+/pr\n9u08cKDwvbbidPppDLAfgF6mwm8Onb+cHzpj3llMnsyUptp0I3q679M4mHQQMoWsw2mi/OY//si+\nQDSo9gQ0d7GcP687lXc67u7AqlXsy7uVu26k80gU1haq7Gw1bhz7N+LNzZvs/l27VoMNM/Yn7sci\n/0Wi5ur85fzQGfPOguPY6XztWhaYvI2HtQeczZ1xMVv5EUlQsKqggD0G//QToKGQ0tWbV2FubC7K\nxQIwQzFunEZb0MGHt98GqqqYcuFt9PX0Mc93Hg4mHVQ6bexYdl/xquGTy1kF26pVgLOzRtutbqrG\n+azzmOMzR9T8CxeAMcLl9B85dMa8M/H1ZVkAixaxjIDbLOq7CPsT9iudNmEC6xXN60O3fDnz0fv4\naLzdnbE78Xz/50W5WJqamH657lH4b8DAANi5k1VitpLIXRKwBL/E/aI0wO7hwc4YbVsUdsiGDWzw\nu+9qvN2jKUcxpvcYWHYVJy1x5gz7TOhQjc6YdzbvvcdazH322Z2XFvovxPHU42hs7rhDka8vc7Wr\nLVs+cIAJoXz4ocbbrJHU4HjKcSwOWCxqfmgo4O/PZDt0/A0EBLDem6++eudbf7DDYBjpG+FqbscJ\n5Rx393SukuhoFvDcvVt0F6HW7E/Yj0V9xblYmpqYaJvOfacenTHvbPT02Idi+3aW6gHAzsQOg3oN\nUtphnePuns6VEhEBrFjBuhxpob3coaRDGOM6RlSZNaBLSfxH+Ogj5sK73ZGI4zg8H/g8dsbsVDpF\nrd+8oYE9SW7erLF7BQCK64oRXhCOaZ7TRM0PCWFqu7omJ+rRGfO/Azs71it0yZI7aSqL+i7Cnvg9\nSqdMnKjCmOfnM6GjbdtYzpkW2BW7C88FPid6vs5f/g9gZAScPMmyWw6xTJYl/ZbgWMox1EhqOpzy\nxBMszU8u7+DNlqyogQOBp8Tlg7dld9xuzPaZjW6G4hpInzkjul/0o4eQ2n8xP3iUtFnU8emnRP7+\nRMXFVCupJasvrSi3qr2OCxGTeTEzI5JI2rxRV0fUvz/R+vVa21ZKWQr1/LonSWVSUfMrK5n2R1OT\n1rakQwixsUS2tkRXrhAR0exfZ9NPkT8pHe7nRxQR0eZFuZzoueeIRo8mqq/XyrbkCjm5bXaja3nX\nRK8REEAUGqqV7Txw4O/WZuE47h2O4xQcx2mpA/FDzMcfM0nEUaNgUlqFp/s+jZ+ifupwqLU14OXF\nfNF3qK5m8wMCWJaBltgVuwvP9HsGhvriSrUvX2b9oo2NtbYlHULo1491/5k3D0hKwgv9X8DOWOWu\nlrFj27haFArWczQjg/Ud7SbuFN2Ws5lnYW5sjsEOg0XNLypicjCDBmllOw89GhlzjuOcAIwHcFM7\n23nI4Thm0JctA0aNwgrb6dges11pf9B7XC2Jieyu7t2bpSFqSfxEppBhd9xujVwsZ8/qXCz/OOPH\nM3fL6NGYFFKC3KqbuFHWsRb9+PGt7qumJuCVV4DkZNaZqnt3rW3px8gf8WrQq6KbSJ87x9xCIrse\nPnoIOca3/QFwGEAAgGwAVkrGdP7zyIPI1q1E1ta0ba4rHb7+c4dDrlwh6h+oINqzh8jGhmj3bq1v\n41TqKRq6fajo+QoFkbMzUVKSFjelQzxxcUS+vhQ7xoc++u31DofU1xOZmiiodt9xIldXojlziKqr\ntbqN3KpcslxvSbWSWtFrLFpE9JNyb9FDDwS6WTQx5DMBbLr93zpjLoa0NMqdNIzKLIyIvv2WOTLz\n85kAdVISyf61htL1PKjZy5d9SDuBCXsm0K6YXaLnx8cze6BCpl3H301DA1W+8DQVm+pR87IXiI4d\nI6qtZVY8JobowAEKt51M1b28ic6e7ZQtrLm4hl7/veMvEz7I5SwMkJOjxU09YAg15irbxnEcdw6A\nXQdvfQTgXwAmEFENx3HZAIKIqKKDNUjVNR51muXNmPZuLxzKGgjzvFKmhlRWxpQPFyzAu5EL0X9Z\nEJ5erH1N2fiSeEzeNxlZK7JgbCDO4b1uHfNtfvutljenQ2Ne/3oMFudbYVhiFavoUiiAPn0ALy9c\n1R+NXYYvYedeI61ft1neDJf/uuDskrPw7+Evao2oKNbCNiVFy5t7gNBq2zgiGq/kIv4AXAHE3faH\nOQKI4jhuMBG1k4j69NNP7/z36NGjMVonf3YHQ31DDJ3xGv7VUI6tU/9kL8rlLD+d4+D+A3DmLPC0\nuFoelWwI3YAVg1eINuQAi5d98okWN6VDazy14D9YcuxZpG5Khb5EylIZbxcBueQCJweyW00LdUH3\ncCL1BNyt3EUbcuDRTEm8fPkyLovWv4Z2UhOhc7NoRF51Hlmst6DqpvZ+y8xMoh49iGQy7V/T6ksr\nqmysFL1GWRlLn9SlJN6fKBQKGrZ9GB1OOtzh+337aj/tT6FQ0IidI2h//H6N1hk5kuj337W0qQcU\n/ENt43R+FA1wNHPENM9p+PZ6e1+FmxvQowd7StYmm69txtJ+S2HRxUL0Gn/8wdLcdCmJ9yccx2HV\n8FX4MuTLloPVPUybxp6stMnF7IsorS8VrbwJMC9jfDzLZNHBH60YcyJyI6Jb2ljrUeXjUR9j8/XN\nqGqqavfe7NnA0aPau1Z1UzV2xu7EyiErNVrn1ClmEHTcv8zwmoFaSS0u51xu9562jTkR4dO/PsWa\nUWtgoCc+n/DECSZnoQWVikcKXTn/fYKHtQemeU7DprBN7d5rMebaiiNvi96GSX0mwcVCfNfl5maW\nXz5linb2pKNz0OP08N5j7+Gr0K/avTdkCOs81EqhWSNaTuUL/RdqtM7Ro8As4T2fH3l0xvw+Ys2o\nNfgu4jvcarz3IScwkAWqEhM1v0atpBabrm3Cu8M0kzYNDmaSqnYd5TrpuK9YHLAYccVxiCmKued1\nfX3WQ+X33zW/BhHhk8ufaHwqr60FrlwBpk7VfE+PGjpjfh/hZumGOd5zsDF04z2vc5z2XC1fXP0C\nY13Hor+9ZgJdOhfLg4OxgTFWj1qNN8+82c53ri1Xy4XsCyhrKNP4VP7HH8Bjj+mklMWgM+b3GatH\nrcaPUT+ivKH8ntdnzdLcmGfeysS26G1YP269RusQMbE+3enpweHlgS+jRlLTrhPRhAnA1atAXZ34\ntYkIn17+FB+P+lijUzkAHDvGDi46hKMz5vcZLhYuWOi3EJ9cujd5e8QI1lQmO1v82u+cfQfvDHtH\nVFPd1sTFMZ/5gAEaLaPjb0RfTx9bJm/Be+feQ530ruW2sACGD2dfzmLZG78XtdJajU/lUik7mc+c\nqdEyjyw6Y34f8vkTn+N46nFcyLrbEkZfH5g+HTh+XNya5zLPIaE0AW8Ne0vj/R04ACxcqDWtLx1/\nEyOcR2B079FYe+XeBs0LF7IeJ2Ioqi3CO2ffwa6Zu6Cvp1n10cWLrMuWLg4jDp0xvw+x7GqJbdO3\n4YUTL9zTZECs37xZ3ow3z7yJjRM2oouBZvleCgX74Gupd4GOv5mvxn2FbdHbkFZxtxHo7NlMxriy\nUthaRIRXfn8FLw98GQPsNX9MO3pU52LRBJ0xv0+Z7DEZE9wn4O0zb995bdw4IDaWFVUI4f3z76O3\nRW/M9NL8+fXaNaaS2revxkvp+AewN7XHByM+wLKTy+5IL5uZsXvryBFhax1IPICsyiysHrVa433J\n5eypU5eSKB6dMb+P2ThhIy5kX8Dp9NMAWBHFlCl3OoTxYmfMTpxKO4W9s/eK1pVuzYED7FSuc7E8\nuLw19C2YG5vjtd9fu5Pd8tRTwlwtxXXFeOvMW9g1c5dG2j4tXLnC3Ct9+mi81COLzpjfx5gam2LX\nzF144cQLSChJAAAsXQr88gu/+SG5Ifjg/Ac48dQJWHbVvCOuTAYcPqxzsTzo6OvpY//c/YgsjMSG\n0A0AWGZSRAQT7VRHRUMFJu+bjNcHvY6gXkFa2dPPP7N7W4cGCBFyEfMDndCWxhxIOEA9v+5JEQUR\nJJMR9epFlJioes7Nqptkv8GeTqed1to+zp4lCgrS2nI6/mFyq3LJYaMDHU0+SkREixczWX1VlNaV\nUsAPAbTq7CpSaEnEvqaGyNycqKREK8s9NOAfEtrS0Yks9F+In6b/hCn7piCsIBjPPMNOMsr4M+NP\nDN85HO8Pfx+TPSZrbR+6wOfDhZO5E44tPIZlJ5fhh4gfsGChQqWrpbS+FE/sfgJTPaZi/bj1WnHb\nAexpb/RoJiinQwOEWH4xP9CdzLXG2YyzZPOVDS3/37/J1q2Qmpvvfb+mqYZePvkyOW9ypvOZ57V6\n7aYmIisrorw8rS6r4z4gqTSJhm4fSiN2jCQL99R23X3qJHW05foWctnkQqsvrNbaibyFESNYMyQd\n9wJtdhrSBrpOQ9oluSwZ317/FtuuHcSwHuMwf8hI5FTlILMyE+EF4ZjUZxI2TdwE8y7arYc+cgTY\nvBn46y+tLqvjPkGukGNrxFa8f/ozuOqPwJiBDuhl2gvVkmrsit2FUS6j8PbQtzHcebhWr5uRwYqW\n8vMBQ0OtLv3AI7TTkM6YP6Bs/rEGP0fvxZAZ8XCzdIO7pTt8bH3ga+vbKdd74gngpZdYgYmOh5cz\nYflYtOo61nxZiJKGQgDAiwNehLuVe6dcb80aoL4e+OabTln+gUZnzB8RqqsBFxcgMxOwtu7cayUk\nAJMmATk5utPTo8CoUcDy5cCT4vtL8EIuB1xdmdBXQEDnXutBRKgx1wVAH1DMzVk62f79nX+tLVuA\nV17RGfJHheXL2b95Z3PpEmBrqzPk2kJ3Mn+AuXyZGdmkJO035W3h1i3A3Z11Se/Zs3OuoeP+orn5\n7ok5MLDzrjNzJnvie/XVzrvGg4zuZP4I8fjjgKWl8DJsIezYwQS+dIb80cHQEHjttc49nScksCKl\nZ5/tvGs8auhO5g84J08CH38MREdrv8ReLmen8sOHgUGDtLu2jvubsjLA0xNITwdsbLS//lNPMQnl\n997T/toPC7qT+SPGtGlMyfCPP7S/9smTgL29zpA/itjaMjfI9u3aXzs9HTh/nrkIdWgPnTF/wOE4\n4MMPgbVrtdfwGWBrffklsHKl9tbU8WCxciVztdTXa3fd9euB118HTE21u+6jjs6YPwQ8+SRQWsqU\n57TFr7+yzi/z52tvTR0PFv37s7jMl19qb83cXKZbvny59tbUwdD5zB8SduwADh4Ezp7VfK2GBsDb\nG9i3Dxg5UvP1dDy45OWxjJaYGMDZWfP1VqwAjI2Br7/WfK2HHV3R0COKVMpabn3zDTBjhmZrffYZ\nkJgoTDddx8PLJ58wP7emNQ2JicCYMUB8PIvF6FCNzpg/wly9CixYwNK+xFaF5ucD/foBUVFA795a\n3Z6OB5T6evakdvAg8Nhj4tZobgaGDGE55cuWaXd/Dys6Y/6I89ZbrMHAgQPi5i9ZwmQCPv9cu/vS\n8WCzdy/w7besbaCeiEjbv//N5p4+retSxRedMX/EaWxkPs61a4F584TN3b8f+Ne/2OOwiUnn7E/H\ng4lCwYKhY8YwN5wQoqNZpWdMDODg0Dn7exj5W/PMOY5bznFcMsdxiRzHaTHmrUMsXbuytnJvvAGU\nlPCfd+ECO9X//rvOkOtoj54e8Ntv7Av///6P/zyJhFV5btyoM+SdjWhjznHcGAAzAAQQ/X97dx9a\nVR3Hcfz9QTMSIdsfauVAcxo2K9KsUZQmDjRCBf+oSLIcIdmDhZSpSIJQQtkDhYLVZJpZaiIGZo5q\nEkhRtpa4ZAk9OMMHtAcK0sm+/fE7g+va7u7ZdnbuGd/XX/fcxw9n5373O+f8vufaBODlXktVROrq\n6tKOEFtFRZjHO3UqbNlS1+Xz6+tDR9727VBenni8WLK4/nNlOX/77MOGwd69sGoV7N7d9evPnoWZ\nM2HCBJg3L5GIeWV53XdHT0bmjwIvmlkLgJmd7p1IxSWrG8TKlWEu78KFdXnnnzc2hi7S9evDpU+L\nTVbXf5ss5+8oe1lZKORVVaGLszNNTWFQMWkSbN6cznHyLK/77uhJMR8L3CnpS0l1knrnZ7pdr1m0\nCObMCcfO164Ns1xaWsJjBw+GhqApU8LJzrlz083qsmPy5HC4paoq/GhJbW3oGG5tDb8cVFMT+hOW\nLg3zyZO6oqe72MB8D0qqBUZ08NCK6LVXmFmFpMnANuCa3o/oeqKsLJzUXL0aNmwITSClpWG62ZIl\nUF3tx8hdfJWVoXBv3Rra/s+dCxfnKikJU1u3bQsDBdd3uj2bRdLHwBoz2x8tHwVuNbMz7Z7nU1mc\nc64b4sxmyTsy78IuYBqwX9I4YFD7Qh43jHPOue7pSTGvBqolHQLOAw/2TiTnnHNxJd405JxzLnmJ\nXgJX0gxJRyT9KGlpkp/V2ySVSvpc0uGoKerJtDPFJWmApHpJH6WdJS5JQyXtiJrSGiVVpJ0pDknL\nom3nkKT3JF2adqZ8JFVLOhntabfdVyKpVlKTpH2ShqaZMZ9O8r8UbT8NknZKujzNjPl0lD/nsSWS\nWiWV5HuPxIq5pAHAm8AM4Drgfknjk/q8BLQAT5tZOVABPJax/ACLgUYgi7tfrwN7zGw8cAPwQ8p5\nCiZpFPAIMNHMrgcGAPelmakAGwnf1VzPAbVmNg74NFouVh3l3weUm9mNQBOwrM9TFa6j/EgqBSqB\nX7p6gyRH5rcAR83s56ix6H1gdoKf16vM7ISZfRfd/ptQTK5KN1XhJI0E7gbeBjJ1EjoaQd1hZtUA\nZnbBzP5MOVYcfxEGA4MlDQQGA8fTjZSfmX0B/N7u7llATXS7BpjTp6Fi6Ci/mdWaWWu0+BUwss+D\nFaiT9Q/wCvBsIe+RZDG/GjiWs9wc3Zc50UjrJsIGkRWvAs8ArV09sQiNBk5L2ijpW0lvSRqcdqhC\nmdlZYC3wK/Ab8IeZ5emXLFrDzaztCj8ngeFphumhBcCetEPEIWk20Gxm3xfy/CSLeRZ37f9H0hBg\nB7A4GqEXPUn3AKfMrJ6MjcojA4GJwDozmwj8Q3Hv4l9E0hjgKWAUYW9uiKQHUg3VQ9GlTzP5nZa0\nAjhvZj38eY2+Ew1elgPP596d7zVJFvPjQGnOcilhdJ4Zki4BPgTeNbNdaeeJ4TZglqSfgK3ANEmb\nUs4URzNhRPJ1tLyDUNyz4mbggJmdMbMLwE7C3yRrTkoaASDpSuBUynlik/QQ4XBj1v6ZjiEMBhqi\n7/FI4KCkYZ29IMli/g0wVtIoSYOAe4ECrrVWHCQJeAdoNLPX0s4Th5ktN7NSMxtNOPH2mZllpg/A\nzE4Ax6JmNIDpwOEUI8V1BKiQdFm0HU0nnIjOmt3A/Oj2fEKjYGZImkE41DjbzP5NO08cZnbIzIab\n2ejoe9xMOKHe6T/UxIp5NCJ5HPiEsCF/YGaZmZEA3A7MA+6KpvfVRxtHFmVx9/gJYIukBsJslhdS\nzlMwM2sANhEGNG3HOzekl6hrkrYCB4BrJR2T9DCwBqiU1ETo9l6TZsZ8Osi/AHgDGALURt/fdamG\nzCMn/7ic9Z+ry++wNw0551w/kGjTkHPOub7hxdw55/oBL+bOOdcPeDF3zrl+wIu5c871A17MnXOu\nH/Bi7pxz/YAXc+ec6wf+A4ciTcWBrJtqAAAAAElFTkSuQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 4 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To switch to seaborn defaults, simply import the package." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import seaborn as sns\n", - "sinplot()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 5 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "By default seaborn plots with the `\"darkgrid\"` theme, but there are a number of other options:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.set_style(\"whitegrid\")\n", - "data = 1 + np.random.randn(20, 6)\n", - "sns.boxplot(data=data);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAeAAAAFXCAYAAAB6G51YAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAE8RJREFUeJzt3W+MpVd9H/Dv/vH6H2Z3drMQB1zMRumBQhJg0giVUMyk\nE0GCW6IKUaQ0aSLtm6YVitSIDpWi9k1XVlWFplWjZgmWnaTUYhCJVsQJ04xMXamNykAUYO2Dw41d\nQwO72ZnZjL1e27O7fTGzrQXr2dl7n7nnzr2fz5u5c2fOc3732bv3O+c553mePVeuXAkAMFx7WxcA\nAJNIAANAAwIYABoQwADQgAAGgAYEMAA0sL+LjZRS9iX5QpJv1Frv7WKbADDOuhoBfzjJ6SROKgaA\nbRg4gEspr03yk0k+nmTPwBUBwAToYgT8q0l+OcnlDrYFABNhoAAupbwvyZla65di9AsA27ZnkGtB\nl1L+dZJ/mGQ9yS1JXpnk07XWn73W7y8tLZkjBmCiTE9PX3OAOlAAv1Qp5V1J/tlWq6CXlpauTE9P\nd9IfAIy6paWllw3grs8DNsIFgG3o5DzgJKm1fj7J57vaHgCMM1fCAoAGBDAANCCAAaABAQwADQhg\nAGhAAANAAwIYABoQwADQgAAGgAYEMAA0IIABoAEBDAANCGAAaEAAA0ADAhgAGhDAANCAAAaABgQw\nADQggAGgAQEMAA0IYABoQAADQAMCGAAaEMAA0IAABoAGBDAANCCAAaABAQwADQhgAGhAAANAAwIY\nABoQwADQgAAGgAYEMAA0IIABoAEBDAANCGAAaEAAA0ADAhgAGhDAANCAAAaABgQwADSwf5DGpZRb\nknw+yc1JDiT5vVrrXBeFMVquXNn4umdP2zoAxsVAI+Ba68Uk7661viXJDyV5dynlxzqpjJHywAPJ\ngw+2rgJgfAw0Ak6SWuuFzYcHkuxLsjzoNhktq6vJRz6y8fj9708OHmxbD8A4GDiASyl7k3wxyfcn\n+fVa6+mBq2KkOOwM0L0uRsCXk7yllHIwyR+WUu6ptT4ycGWMjIMHk/vu2whio1+AbgwcwFfVWs+X\nUj6b5EeSPPJyv7e0tNRVlwzRm9+88dU/H0A3Bl0F/T1J1mutq6WUW5PMJvlXW7WZnp4epEsA2DW2\nGnQOOgK+M8kDm/PAe5P8Vq31jwbcJgCMvYECuNb65SRv66gWAJgYroQFAA0IYABoQAADQAOdnYYE\nAINYXFzMwsJCX21XV1eTJIcOHeqr/ezsbGZmZvpq2y8BDMCut7y8cRXkfgO4BQEMwEiYmZnpexQ6\nN7dxI74TJ050WdKOMgcMAA0IYABoQAADQAMCGAAaEMAA0IAABoAGBDAANCCAAaABAQwADbgSFnyH\nSbseLdCGAIYO7cbr0QJt7PoANlqha5N2PVqgjV0fwIMwWgGglV0fwEYrAOxGuz6AAUadqTKuRQAD\njDBTZeNLAAPsMFNlXIsLcQBAAwIYABoQwADQgAAGgAYEMAA0YBU0AJ05efJker3e0Pu92ufVVePD\ndOzYsRw/fvyG2wlgADrT6/Vyuj6Rmw8fHWq/6/tvTpJ8/ezqUPt9fvls320FMACduvnw0bzuvR9o\nXcZQPPXwp/puaw4YABoQwADQgAAGgAYEMAA0IIABoAEBDAANCGAAaEAAA0ADLsQxQRYXF7OwsNBX\n29XVjavLHDp06Ibbzs7O9n0zcoBxJYDZluXl5ST9BTAA300AT5CZmZm+R6JXL3B+4sSJLksCmFjm\ngAGggYFGwKWUu5I8mORVSa4k+Y1a6691URgAjLNBR8AvJvmlWuubkrw9yS+WUt44eFkAMN4GCuBa\n67dqrX+y+fiZJI8l+b4uCgOAcdbZHHAp5e4kb03yx11tEwDGVSeroEspr0gyn+TDmyPhl7W0tNRF\nl51YW1tLMlo1jSr7anvsJ7q2295TV+udJGtra339+wwcwKWUm5J8Oslv11p/93q/Pz09PWiXnZmf\nn08yWjWNKvtqe+wnurbb3lPz8/M5c3G1dRlDdccdd7zsv89WwTzQIehSyp4kv5nkdK31Y4NsCwAm\nyaAj4Hck+Zkkf1pK+dLmc3O11j8YcLsAMNYGCuBa63+Pi3kAwA0TngDQgAAGgAYEMAA0IIABoAG3\nIwT6sri4mIWFhb7arq5unCfa7/2lZ2dn+761JowKAQwM3fLycpL+AxjGgQAG+jIzM9P3KHRubi5J\ncuLEiS5Lgl3FHDAANCCAAaABAQwADQhgAGhAAANAAwIYABoQwADQgPOAd5mTJ0+m1+sNvd+rfV49\nf3NYjh07luPHjw+1T4BhGIkAnrRQSfoPll6vl68+fjr7Dx7Ygape3uW9l5Ik9S/+bGh9rp9/YWh9\nAQzbSARwr9fLV07X7LtluJelu7y+L0nyWO/bQ+330sXVgdrvP3ggh975mo6qGV2rj36zdQkAO2Yk\nAjhJ9t1yKK+4+57WZQzFM08+0roE4AZN2pE60z87b2QCGGCU9Xq9PPG1x3L0yG1D7ffmmy4nSVbP\nPTW0Ps+euzC0viaZAAbYpqNHbssH731D6zJ23EOnHm9dwkRwGhIANCCAAaABAQwADQhgAGhAAANA\nAwIYABpwGhIAnVlZWcnzy2fz1MOfal3KUDy/fCYr+6/01dYIGAAaMAIGoDNTU1NZXt+T1733A61L\nGYqnHv5Upqb6u4+BETAANCCAAaABAQwADQhgAGhAAANAA1ZBM5Ym7ebpiRuow24jgBlLvV4vX/vq\nV3Nk33Df4jdd3rh5+rnH61D7PXdpfaj9AYMTwIytI/v2595Dh1uXMRSnVpdblwDcIHPAANCAAAaA\nBgQwADQggAGgAQEMAA0MvAq6lPKJJD+V5Eyt9QcHLwkYphbnTDtfGro5Den+JP8+yYMdbAsYsl6v\nl8dO19x+69TQ+ry8vi9J8r///MzQ+kySZ59bGWp/sJWBA7jW+mgp5e4OagEauf3Wqbz5B36idRk7\n7itPfK51CfD/mAMGgAaGfiWspaWl73pubW1t2GU0t7a2ds19sZ12k8R+2j77anvsp+2xn7av3301\n9ACenp7+rufm5+eTsxeGXUpTd9xxxzX3xfXMz88nz3x7ByoaTYPsp3M7UM8oG2RfrfzlcztQ0Wjq\ndz+dPHkyZ89dyEOnHt+BqkbLmXMXcvToK/t+P525uLoDVY2urd5TWwWzQ9AA0EAXpyF9Msm7khwp\npTyd5FdqrfcPXBnXtLKykvXzz2f10W+2LmXHrZ9/Piu3WLXKaJiamsqey3+VD977htal7LiHTj2e\nQ1PDWxU/qbpYBf2hLgoBgEkyErcjXFlZyaWLq3nmyUdalzIUly6uZmXlQF9tp6amcubiuRx652s6\nrmr0rD76zUz5KxwYU+aAAaCBkRgBT01N5VsrL+QVd9/TupSheObJR4zsACacETAANCCAAaABAQwA\nDYzEHDB0bWVlJefW13Nqdbl1KUNxbn09e1f6O2d6ZWUlzz63MhE3Knj2uZWsrNzUugxIYgQMAE0Y\nATOWpqamcvnbZ3LvocOtSxmKU6vLfa+sn5qaytrqixNzO0JnIDAqjIABoAEBDAANOAS9C62ff2Ho\nN2O4/PylJMnem/cNrc/18y8kdw6tO6Ajzy+fzVMPf2qofa4/92ySZP+ttw+13+eXzyZHD/XVVgDv\nMseOHWvSb6/X2+j/ziH2f2e71wv0p91n1MYZD8eODvk6+UcP9f2aBfAuc/z48Sb9zs3NJUlOnDjR\npH9gd/AZtX3mgAGggZEZAbe4HeHl9YtJkr37bxlqv5curiZ59VD7BGC0jEQAN5/XPDbsMHy1uU2A\nCTcSAWzOAIBJYw4YABoQwADQgAAGgAYEMAA0IIABoAEBDAANCGAAaEAAA0ADAhgAGhDAANCAAAaA\nBgQwADQggAGgAQEMAA0IYABoQAADQAP7WxcAtPfscyv5yhOfG1p/L7z4XJLkwE23Dq3PZON1Jq8a\nap/wcgQwTLhjx44Nvc9er5ck+WuvH3YYvqrJ64VrEcAw4Y4fPz70Pufm5pIkJ06cGHrfMCrMAQNA\nAwIYABpwCBpgm86eu5CHTj0+1D6fvfBikuT2224aWp9nz13IoSND625iCWDG1rlL6zm1ujzUPi9c\nvpwkuW3vcA8unbu0Hp+XO6vV4q3l8xsL1l5z5HVD6/PQkXavd5IIYMZSqw+P85ure48Muf8j8YG5\n01osVkssWBtnAwdwKeU9ST6WZF+Sj9da7xu4KhiQD0tg1A10nKyUsi/Jf0jyniR/I8mHSilv7KIw\nABhng05U/WiSP6u1PllrfTHJf0ny9wYvCwDG26AB/JokT7/k+29sPgcAbGHQAL7SSRUAMGEGXYT1\nzSR3veT7u7IxCn5ZS0tLA3bZnbW1tSSjVdOosq+2x37aHvtp++yr7dmN+2nQAP5Ckh8opdyd5P8k\n+WCSD23VYHp6esAuuzM/P59ktGoaVfbV9thP22M/bZ99tT2jup+2+oNgoEPQtdb1JP8kyR8mOZ3k\noVrrY4NsEwAmwcDnAddaH07ycAe1AMDEcDMGAGhAAANAAwIYABpwMwagL4uLi1lYWOirbW/zphVX\nr519o2ZnZzMzM9NXWxgVAhgYusOHD7cuAZoTwEBfZmZmjEJhAOaAAaABAQwADez6Q9AWggCwG+36\nAB6EhSAAtLLrA9hCEAB2I3PAANCAAAaABgQwADQggAGgAQEMAA0IYABoQAADQAMCGAAaEMAA0IAA\nBoAGBDAANCCAAaABAQwADQhgAGhAAANAA7v+fsBs3+LiYhYWFvpq2+v1kiRzc3M33HZ2dtY9mwG+\ngwBmWw4fPty6BICxIoAnyMzMjJEoMLJaHaVL2hypE8AA7Hq78SidAGZbrlzZ+LpnT9s6gPE1aUfp\nrIJmWx54IHnwwdZVAIwPI2Cua3U1+chHNh6///3JwYNt6wEYB0bAXJfDzgDdMwLmug4eTO67byOI\njX4BuiGA2Zaf+7nWFQCMFwHMtjgMDdAtc8AA0IAABoAGBDAANCCAAaABAQwADfS9CrqU8oEk/zLJ\nG5L8zVrrF7sqCgDG3SAj4C8n+ekk/62jWgBgYvQ9Aq61Pp4kpZTuqgGACWEOGAAa2HIEXEpZSPK9\n1/jRR2utp3amJAAYf1sGcK11tusOl5aWut4kjIy1tbUk3ud0x3tqfHV1LehtXyl4enq6oy5h9MzP\nzyfxPqc73lO721Z/OPU9B1xK+elSytNJ3p7ks6WUh/vdFgBMmkFWQX8myWc6rAUAJoZV0ADQgAAG\ngAYEMAA0IIABoAEBDAANCGAAaEAAA0ADAhgAGhDAANCAAAaABrq6GQOMjcXFxSwsLPTVttfrJUnm\n5ub6aj87O5uZmZm+2gK7iwCGDh0+fLh1CcAuIYDhO8zMzBiFAjvOHDAANGAEDLDDrCvgWgQwwAiz\nrmB8CWCAHWZdAddiDhgAGhDAANCAAAaABgQwADQggAGgAQEMAA0IYABoQAADQAMCGAAaEMAA0IAA\nBoAGBDAANCCAAaABAQwADQhgAGhAAANAAwIYABoQwADQgAAGgAYEMAA0IIABoAEBDAANCGAAaEAA\nA0ADAhgAGtjfb8NSyr9J8r4kLyT5epKfr7We76owABhng4yAP5fkTbXWH07ytSRz3ZQEAOOv7xFw\nrXXhJd/+cZK/P3g5ADAZupoD/oUkv9/RtgBg7G05Ai6lLCT53mv86KO11lObv/MvkrxQa/3PO1Af\nAIylLQO41jq71c9LKf8oyU8m+fHtdri0tLTdXwWAsbXnypUrfTUspbwnyb9N8q5a6192WhUAjLlB\nAviJJAeSLG8+9T9qrf+4q8IAYJz1HcAAQP9cCQsAGhDAANCAAAaABvq+EtZuV0r5RJKfSnKm1vqD\nresZVaWUu5I8mORVSa4k+Y1a66+1rWo0lVJuSfL5JDdnY4Hi79VaXaL1ZZRS9iX5QpJv1FrvbV3P\nKCqlPJnkr5JcSvJirfVHmxY0wkoph5J8PMmbsvFZ9Qu11v/ZtqqtTfII+P4k72ldxC7wYpJfqrW+\nKcnbk/xiKeWNjWsaSbXWi0neXWt9S5IfSvLuUsqPNS5rlH04yelsfFhybVeS3FNrfavwva5/l+T3\na61vzMb/v8ca13NdExvAtdZHk6y0rmPU1Vq/VWv9k83Hz2TjTf19basaXbXWC5sPDyTZl/9/mh4v\nUUp5bTYu4vPxJHsalzPq7J/rKKUcTPLOWusnkqTWur4b7s43sYeguXGllLuTvDUbN9/gGkope5N8\nMcn3J/n1WuvpxiWNql9N8stJXtm6kBF3Jcl/LaVcSvKfaq0nWxc0ol6f5Gwp5f4kP5xkKcmHX/IH\n8Uia2BEwN6aU8ook89l4Uz/Tup5RVWu9vHkI+rVJ/nYp5Z7GJY2cUsr7srH24ksxurued9Ra35rk\nvdmY/nln64JG1P4kb0vyH2utb0vybJJ/3rak6xPAXFcp5aYkn07y27XW321dz26wefjrs0l+pHUt\nI+hvJfm7pZQ/T/LJJDOllAcb1zSSaq1/sfn1bJLPJDEPfG3fyMZivv+1+f18NgJ5pAlgtlRK2ZPk\nN5OcrrV+rHU9o6yU8j2bKzFTSrk1yWySL7WtavTUWj9aa72r1vr6JP8gyWKt9Wdb1zVqSim3lVLu\n2Hx8e5KfSPLltlWNplrrt5I8XUr565tP/Z0kX21Y0rZM7BxwKeWTSd6V5Egp5ekkv1Jrvb9xWaPo\nHUl+JsmfllKuhslcrfUPGtY0qu5M8sDmPPDeJL9Va/2jxjXtBlZBX9urk3ymlJJsfFb/Tq31c21L\nGmn/NMnvlFIOJPl6kp9vXM91uRY0ADTgEDQANCCAAaABAQwADQhgAGhAAANAAwIYABoQwADQgAAG\ngAb+LziCFnI5UAoEAAAAAElFTkSuQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 6 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Seaborn also has some utility functions to easily manipulate the figure outside of what can be done through the matplotlib rc parameters." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.set_style(\"ticks\")\n", - "f, ax = plt.subplots()\n", - "sns.boxplot(data=data)\n", - "sns.despine(offset=10, trim=True)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nzjOJSinnkvx5kktJXq61/kivgSZYKeVQko8neWPWP6t+rtb63/pNdXVm4MP7ZJJ39R2iAS8n+YVa\n6xuTvDXJz5dS3tBzpolUa30hyTtrrW9K8oNJ3llK+bGeY02yDyc5k/UPWza3luTuWuublfc1/esk\nv19rfUPW//092nOea1LgQ6q1PpJkue8ck67W+p1a6x9tPH4m6/8ovrffVJOr1vrcxsMDSfYlWeox\nzsQqpdyR5D1ZnzE1c/OJnnh/rqGUcjDJ22utn0iSWutqrfViz7GuyS50OlNKOZrkzUm+1HOUiVVK\n2ZvkK0m+L8mv1VrP9BxpUv1Kkl9M8l19B5lwa0n+UynlUpJ/X2s91XegCXVXkvOllE8m+aEk/yPJ\nh1/xP9QTyQycTpRSXpXkM1n/R/FM33kmVa318sYu9DuS/J1Syt09R5o4pZT3Zv3Yk6/G7PJa3lZr\nfXOSd2d9+ertfQeaUPuTvCXJv6u1viXJs0n+eb+Rrk2Bs+NKKTck+Z0kv1Vr/d2+87RgY/fd55L8\ncN9ZJtCPJvnJUsrZJJ9KMltKeaDnTBOp1vqnG1/PJ/lsEuvgm3sq6wdD/veN7z+T9UKfaAqcHVVK\n2ZPkN5KcqbV+rO88k6yU8t0bR8KmlHJzkrkkX+031eSptX601npnrfWuJP8wyWKt9Wf6zjVpSim3\nlFL+2sbjW5P8eJKv9ZtqMtVav5PkyVLK39j40d9L8o0eI22LNfAhlVI+leQdSY6UUp5M8ku11k/2\nHGsSvS3JTyf541LKlTKar7X+QY+ZJtXtSe7fWAffm+Q3a61/2HOmFjgKfXOvSfLZUkqy/ln/27XW\nL/QbaaL90yS/XUo5kORbSX625zzX5G5kANAgu9ABoEEKHAAapMABoEEKHAAapMABoEEKHAAapMAB\noEEKHAAa9H8B4CWtqiSdP+EAAAAASUVORK5CYII=\n", - "text": [ - "" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Scaling figures for different contexts" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The seaborn defaults are tailored to make plots that are well-proportioned for vieweing on your own computer screen. There are several other plotting contexts that let you quickly change the scale of plot elements for use in contexts where larger or smaller figures are used. you can also independently scale the fonts within each context." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.set_context(\"talk\", font_scale=1.25)\n", - "sns.boxplot(data=data)\n", - "sns.axlabel(\"Category\", \"Score\")\n", - "sns.despine(offset=10, trim=True);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nSUVORK5CYII=\n", - "text": [ - "" - ] - } - ], - "prompt_number": 8 - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Using color palettes" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Seaborn also tries to make it easy to choose color palettes that are attractive and informative. The default palette is based on the default matplotlib cycle, but with flatter colors:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.set()\n", - "current_palette = sns.color_palette()\n", - "sns.palplot(current_palette)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAV0AAABGCAYAAABv7kdbAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAAYZJREFUeJzt2DEuRVEYRtH7PAyIUq+QiELCCBTqF61CK2qFEZAoRKLQ\nKxkQkWsCorv7Cmu1f/NVOydnMY7jAEBjbe4BAP+J6AKERBcgJLoAIdEFCIkuQGj9p+P7x+e4ubGs\ntgD8CSdPb8PN3tbiu9uP0d3cWA77q4dpVv0Cj1cHw9Ht6dwzJnN3fD28HBzOPWMyOw/3w8Xqce4Z\nkzi/2h9en8/mnjGZ7d3L4eTpbe4Zs/C9ABASXYCQ6AKERBcgJLoAIdEFCIkuQEh0AUKiCxASXYCQ\n6AKERBcgJLoAIdEFCIkuQEh0AUKiCxASXYCQ6AKERBcgJLoAIdEFCIkuQEh0AUKiCxASXYCQ6AKE\nRBcgJLoAIdEFCIkuQEh0AUKiCxASXYCQ6AKERBcgJLoAIdEFCIkuQEh0AUKiCxASXYCQ6AKERBcg\nJLoAIdEFCIkuQEh0AUKiCxASXYCQ6AKERBcgJLoAIdEFCC3GcZx7A8C/4aULEBJdgJDoAoREFyAk\nugAh0QUIfQHQuR6AY7gr5QAAAABJRU5ErkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 9 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It's also easy to get evenly spaced hues in the `husl` or `hls` color spaces." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.palplot(sns.color_palette(\"husl\", 8))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAcwAAABGCAYAAABBh6SMAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAAcNJREFUeJzt2DFqFVEAhtE7khSSXbgBUUFwEzY2gqlSCHYRC9dgIdoJ\nKawi2Ni4CUEw4gayi0cKhbG3eHzNeDWcU85M8d/q486yrusAAPa7MXsAAPwPBBMAAsEEgEAwASAQ\nTAAIBBMAgoN9L9efv9blcO8nAHCt7E4ux9H7W8ufz/fWcDk8GFfPX223arKbb16O72/vzp6xmTun\nF+Pd+fU937Pji3Hv89PZMzbx7eHZuP/pw+wZm/n66Ml48PHH7Bmb+fL49jh/vZs9YzPHL47G7uRy\n9oy/zi9ZAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwAC\nwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgE\nEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBM\nAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwCCZV3X2RsA4J/nhgkAgWACQCCYABAI\nJgAEggkAgWACQPAbQxckyJWg960AAAAASUVORK5CYII=\n", - "text": [ - "" - ] - } - ], - "prompt_number": 10 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can use the `color_palette` function to get discretized colors from any matplotlib colormap." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.palplot(sns.color_palette(\"coolwarm\", 7))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAZQAAABGCAYAAADrRGIwAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAAbBJREFUeJzt2CFuVVEYRtHzmtdUgGUCtAPoAPCYigp8kxo8kgTVpLIe\nQ4JHVGDwHQAD4DEBLBUEwmUAJDXs2xOatewxn9v5z2ZZlgEA/2pv9gAAHgZBASAhKAAkBAWAhKAA\nkBAUABLbux5//lqW/e3mvrYA8B/YnZ2Mw/cf/4rDnUHZ327G+cW39VZN9u7Nk3F1/Xv2jNW8Ot0b\nnz7/mD1jNc+PD8bX3ZfZM1bx9PBo3N58mD1jNY+evRjf376ePWM1j19ejt3ZyewZ986XFwAJQQEg\nISgAJAQFgISgAJAQFAASggJAQlAASAgKAAlBASAhKAAkBAWAhKAAkBAUABKCAkBCUABICAoACUEB\nICEoACQEBYCEoACQEBQAEoICQEJQAEgICgAJQQEgISgAJAQFgISgAJAQFAASggJAQlAASAgKAAlB\nASAhKAAkBAWAhKAAkBAUABKCAkBCUABICAoACUEBICEoACQEBYCEoACQEBQAEoICQEJQAEgICgAJ\nQQEgISgAJAQFgISgAJAQFAASm2VZZm8A4AFwoQCQEBQAEoICQEJQAEgICgAJQQEg8Qdo5iEbu2ev\npAAAAABJRU5ErkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 11 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When working with matplotlib palettes that are naturally qualitative, seaborn handles them properly." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.palplot(sns.color_palette(\"Paired\", 8))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAcwAAABGCAYAAABBh6SMAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAAbxJREFUeJzt2KEyhFEAhuHz/8tVWIEbMIouK6qi0iRB2yZIGlXZqsi6\nYtwAwboKs3tcgJmdb8z8DrPPU0/5TnrnnK7WWgCA5frWAwDgPxBMAAgIJgAEBBMAAoIJAAHBBIDA\n2rLD+aLWUd/91hYAaK5O+tJNFt/itzSYo74r0+fZcKsaO9odl62Lh9YzBvN2eVDuX69azxjM4fZ5\n2bvbaT1jEE/HL+Xz9qb1jMGsn5yW2cZm6xmDGX+8l/njWesZgxntX5c6Wb0PytW7MQD8gGACQEAw\nASAgmAAQEEwACAgmAAQEEwACggkAAcEEgIBgAkBAMAEgIJgAEBBMAAgIJgAEBBMAAoIJAAHBBICA\nYAJAQDABICCYABAQTAAICCYABAQTAAKCCQABwQSAgGACQEAwASAgmAAQEEwACAgmAAQEEwACggkA\nAcEEgIBgAkBAMAEgIJgAEBBMAAgIJgAEBBMAAoIJAAHBBICAYAJAQDABICCYABAQTAAICCYABAQT\nAAKCCQABwQSAgGACQEAwASAgmAAQEEwACAgmAAS6WmvrDQDw53lhAkBAMAEgIJgAEBBMAAgIJgAE\nBBMAAl+nhiQQHo3ctAAAAABJRU5ErkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 12 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Many seaborn functions use the `color_palette` function behind the scenes, and thus accept any of the valid arguments for their `palette` parameter." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.violinplot(data=data, inner=\"points\", palette=\"Set3\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nOvj/AcwNzxGmwf45AAAAAElFTkSuQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 13 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There is also a flexible interface to the cubehelix system." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.palplot(sns.cubehelix_palette(8, start=2.5, rot=.75))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAcwAAABGCAYAAABBh6SMAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAAb9JREFUeJzt16GKVVEYhuF1Nt7GBIMMZpNpRBDnIsSoGDQIk8U8YNAg\nThQvQhFEk8ksYjB4H2fbDYe3bJfi88S1ytde/t26rgMAOGyZPQAA/gWCCQCBYAJAIJgAEAgmAASC\nCQDBpUOf+/1+XRZNBeD/cXR8Mn5++7T7/f1gMJdlGT++f9xs1GyXr9wYX9+/nj1jM1dv3R1fXr6Y\nPWMz1x48HO/Ons2esYnb54/Hqzvns2ds5v6bs/Ho9MnsGZt5/vbpuHn93uwZm/nw+WIcHZ/MnvHH\nOR8BIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwAC\nwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgE\nEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBM\nAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAh267rO3gAAfz0XJgAEggkAgWACQCCYABAI\nJgAEggkAwS8aASMSB5hCnwAAAABJRU5ErkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 14 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Two other functions allow you to create custom palettes. The first takes a color and creates a blend with a very dark gray." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.palplot(sns.dark_palette(\"#5178C7\", 8))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAcwAAABGCAYAAABBh6SMAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAAcJJREFUeJzt2CGqlUEAhuE5IvwLOcmkTVFsgsEiBxdgVHADN4nJDQga\nXYBcLAbBJsq1aTLdhZz02w2Ht/x3FJ4nzpSvvczs1nUdAMBp12YPAID/gWACQCCYABAIJgAEggkA\ngWACQHD91OXxeFyXZbmqLQAw3eHsYpy/vrP7+/xkMJdlGfv9frtVk11eXo4bN+/OnrGZ37++j1v3\nDrNnbObnt/Nx++Hz2TM28ePz23H/yavZMzbz9cPL8eDpu9kzNvPl/bPx6MXH2TM28+nN43E4u5g9\n48r5kgWAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBM\nAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDAB\nIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSA\nQDABIBBMAAgEEwACwQSAQDABIBBMAAgEEwACwQSAQDABINit6zp7AwD887wwASAQTAAIBBMAAsEE\ngEAwASAQTAAI/gCdySQxvxaqYAAAAABJRU5ErkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 15 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A more general function for making custom palettes interpolates between an arbitrary number of seed points." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.palplot(sns.blend_palette([\"mediumseagreen\", \"ghostwhite\", \"#4168B7\"], 9))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAgQAAABGCAYAAACzDYzYAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAAcRJREFUeJzt3KFOllEAx+HfB8K+brJrYWYMzkKRQPAiMDCYwXEZzOB0\nBr0IgwGKhRkkO4p2k53p2OsNMNrLcfg89ZR//O2c7SymaQoA+L+tjB4AAIwnCAAAQQAACAIAIEEA\nACQIAIDqznWHvy//TOuraze1BQCY0dbecZ/fbS+uOrs2CNZX13ry6eU8q/4BpzuvOvjydvSM2bx5\nvN/785PRM2bzfONpZz9/jJ4xi81797u4uL1/hCyXi86+/Ro9YzabD+/24eP30TNms/vsQS+Ovo6e\nMZvXh4/a2jsePePGeTIAAAQBACAIAIAEAQCQIAAAEgQAQIIAAEgQAAAJAgAgQQAAJAgAgAQBAJAg\nAAASBABAggAASBAAAAkCACBBAAAkCACABAEAkCAAABIEAECCAABIEAAACQIAIEEAACQIAIAEAQCQ\nIAAAEgQAQIIAAEgQAAAJAgAgQQAAJAgAgAQBAJAgAAASBABAggAASBAAAAkCACBBAAAkCACABAEA\nkCAAABIEAECCAABIEAAACQIAIEEAACQIAIAEAQCQIAAAEgQAQLWYpmn0BgBgMDcEAIAgAAAEAQCQ\nIAAAEgQAQIIAAKj+AtTVJvphNSUxAAAAAElFTkSuQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 16 - } - ], - "metadata": {} - } - ] -} diff --git a/examples/linear_models.ipynb b/examples/linear_models.ipynb deleted file mode 100644 index 9473817efd..0000000000 --- a/examples/linear_models.ipynb +++ /dev/null @@ -1,330 +0,0 @@ -{ - "metadata": { - "name": "", - "signature": "sha256:58c25f973e469d6ecb73ca8f6d93fdce09aac799c1da92c5b7f9ff5cc157f595" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ - { - "cells": [ - { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "Graphical representations of linear models" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This notebook provides a brief introduction to the plotting functions in [seaborn](http://stanford.edu/~mwaskom/software/seaborn/) that help visualize linear relationships between variables.\n", - "\n", - "For much more detail, you can check out the documentation on graphing [quantitative](http://stanford.edu/~mwaskom/software/seaborn/tutorial/quantitative_linear_models.html) and [categorical](http://stanford.edu/~mwaskom/software/seaborn/tutorial/categorical_linear_models.html) linear models." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%matplotlib inline" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 1 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import seaborn as sns\n", - "import matplotlib.pyplot as plt" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 2 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "tips = sns.load_dataset(\"tips\")\n", - "iris = sns.load_dataset(\"iris\")\n", - "exercise = sns.load_dataset(\"exercise\")\n", - "titanic = sns.load_dataset(\"titanic\")" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 3 - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Linear models with quantitative variables" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Seaborn visualizes linear regressions with `regplot` by plotting a regression line and confidence band over a scatterplot of the data:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.regplot(\"total_bill\", \"tip\", tips);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAfEAAAFkCAYAAAAwmQKNAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3Xlw5Ol52Pfv7+gL99E9mHsGM8D2zOzsLEBSvERSZEmk\naknLpKgjSugoG5YcVaSoVI4tOZIrrlQqqbjKji1ajhNHNkMnkWOL9IqmRNJFiiLFc7VaEphjZ6an\ngcEcmAPTjauBPn/Hmz9+3T0NoAE0jr6A5/PPzgLoX7/4Aein3/d93ufRlFIIIYQQov3ozR6AEEII\nIXZHgrgQQgjRpiSICyGEEG1KgrgQQgjRpiSICyGEEG1KgrgQQgjRpsx6Xjwajb4L+AexWOxD0Wh0\nDPingAPkgV+OxWLP6vn8QgghxEFWt5l4NBr9beAPgEDxQ78H/DexWOxDwGvA363XcwshhBCHQT2X\n06eATwJa8f9/KRaLXSv+2wdk6/jcQgghxIFXtyAei8VeA+yK/38KEI1G3wv8OvBP6vXcQgghxGFQ\n1z3x9aLR6H8C/C7w0VgsNr/d1yullKZp232ZEEIIcVDsKOg1LIhHo9G/AfxXwAdjsdhiLY/RNI1E\nYqW+AzsAIpFuuU81kntVG7lPtZH7VDu5V7WJRLp39PWNOGKmotGoDnwG6AJei0aj34xGo/9DA55b\nCCGEOLDqOhOPxWL3gPcW/3ewns8lhBBCHDZS7EUIIYRoUxLEhRBCiDYlQVwIIYRoUxLEhRBCiDYl\nQVwIIYRoUxLEhRBCiDYlQVwIIYRoUxLEhRBCiDYlQVwIIYRoUxLEhRBCiDYlQVwIIYRoUxLEhRBC\niDYlQVwIIYRoUxLEhRBCiDYlQVwIIYRoUxLEhRBCiDYlQVwIIYRoUxLEhRBCiDYlQVwIIYRoUxLE\nhRBCiDYlQVwIIYRoUxLEhRBCiDYlQVwIIYRoUxLEhRBCiDYlQVwIIYRoUxLEhRBCiDYlQVwIIYRo\nUxLEhRBCiDYlQVwIIYRoUxLEhRBCiDYlQVwIIYRoUxLEhRBCiDYlQVwIIYRoUxLEhRBCiDYlQVwI\nIYRoUxLEhRBCiDYlQVwIIYRoUxLEhRBCiDYlQVwIIYRoUxLEhRBCiDYlQVwIIYRoUxLEhRBCiDYl\nQVwIIYRoU2Y9Lx6NRt8F/INYLPahaDQ6AnwOcIEbwK/HYjFVz+cXQgghDrK6zcSj0ehvA38ABIof\n+sfA78ZisQ8AGvDxej23EEKI1mbZLm/cmuONW3NYttvs4bStei6nTwGfxAvYAG+LxWLfLv77q8BP\n1fG5hRBCtCjLdvncV2/xldfv85XX7/O5r96SQL5LdQvisVjsNcCu+JBW8e9VoLdezy2EEKJ1TcQT\nPEqm0TQNTdN4lEwzEU80e1htqa574utUvs3qBpZqeVAk0l2f0Rwwcp9qJ/eqNnKfaiP3qXale9Xz\nKIXP1NE0b26nlKKnJyT3chcaGcQnotHoT8Risb8AXgG+UcuDEomV+o7qAIhEuuU+1UjuVW3kPtVG\n7lPtKu/V+aEujvSFeJRMA3Ai3Mn5oS65l+z8TWEjgngpA/1vA38QjUb9wE3gCw14biGEEC3GZ+q8\n+srF8hL6+GgEnyknnnejrkE8FovdA95b/Hcc+GA9n08IIUR78Jk677w41OxhtD156yOEEEK0KQni\nQgghRJuSIC6EEEK0KQniQgghRJuSIC6EEEK0KQniQgghRJuSIC6EEEK0KQniQgghRJuSIC6EEEK0\nKQniQgghRJuSIC6EEEK0KQniQgghRJuSIC6EEEK0KQniQgghRJuSIC6EEEK0KQniQgghRJuSIC6E\nEEK0KQniQgghRJuSIC6EEEK0KbPZAxBCCNEeLNtlIp4AYHw0gs+UeWCzSRAXQgixLct2+dxXb/Eo\nmQbg6lSSV1+5KIG8yeTuCyGE2NZEPMGjZBpN09A0jUfJdHlWLppHgrgQQgjRpiSICyGE2Nb4aIQT\n4U6UUiilOBHuZHw00uxhHXqyJy6EEGJbPlPn1VcuSmJbi5EgLoQQoiY+U+edF4eaPQxRQd5GCSGE\nEG1KgrgQQgjRpiSICyGEEG1KgrgQQgjRpiSICyGEEG1KgrgQQgjRpiSICyGEEG1KgrgQQgjRpiSI\nCyGEEG1KgrgQQgjRpiSICyGEEG1KgrgQQgjRpiSICyGEEG1KgrgQQgjRpiSICyGEEG1KgrgQQgjR\nAlxX8TN/+z8YO3mMWa/BCCGEEGJ7ecshnbXIFWyAHmCx1sc2NIhHo1Ed+JfAC4AL/M1YLBZr5BiE\nEEKIVpDOWWSyNpbjoOs6ur7zxfFGL6d/BOiMxWLvA/5H4H9u8PMLIYQQTeO6iqXVPE/m0yynCzhK\n7Sp4lzR6OT0L9EajUQ3oBQoNfn4hhBCi4SqXzHVdR9M0tH24bqOD+PeAIHAbGAR+psHPL4QQQjRM\nOmeRztrYe1gy30qjg/hvA9+LxWJ/LxqNngT+PBqNXo7FYpvOyCOR7saNro3Jfaqd3KvayH2qjdyn\n2h2We+W4itRqnkzOwgz46Av66/ZcjQ7inUCq+O9FwAdsmU6fSKzUe0xtLxLplvtUI7lXtZH7VBu5\nT7U7DPcqX3BYzVrkLXvfZ9ybaXQQ/4fA/xWNRr+DF8B/JxaLZRs8BiGEEGJfKKVIZ20y+fotmW+l\noUE8FostAT/byOcUQggh9pvjuqTS5bPdaJrW0OBdIsVehBBCiBrlCzarWZuc5WDoGpq2HznmuydB\nXAghhNiCt2RukcnZ2K5C1zUMvbnBu0SCuBBCCFGF7TisZOx1S+atEbxLJIgLIYQQFVptyXwrEsSF\nEGIblu0yEU8AMD4awWdKA8iDppWXzLciQVwIIbZg2S6f++otHiXTAFydSvLqKxclkB8Qlu14s+68\nDVprLplvRX4LhRBiCxPxBI+Saa/WtabxKJkuz8pF+8rmLeaXcySWc+QtB63Fl803IzNxIYQQh4JS\nitXikrnrKjRdQ2/DwF1JZuJCCLGF8dEIJ8KdKKVQSnEi3Mn4aKTZwxI7YNkOiyte+8/VrIUCtDZa\nMt+KzMSFEGILPlPn1VcuSmJbG8rmvQ5iBbvx5VAbRYK4EEJsw2fqvPPiULOHIWqglGIl4y2ZK1Vc\nMj+AwbtEgrgQQoi2V8oyz+YtL2gXM80POgniQggh2lapMEvedtCb1ISkmSSICyGEaDvpnLffbTsu\n+gHIMt8tCeJCCCHagqsUK2mLTN7LMNfbrDDLdlaz1o4fI0FcCCFES7Nsh9VMcb/b0L3CO80e1D7J\nFxzeurfAZDzJ9OPlHT9egrgQQoiWlCtYrGYqjogZB2O/23EVU7NLTE4luTmziOW4u76WBHEhhBAt\no1ojkoOQrKaU4lEyzWQ8ydXpedLrls51DUZP9RF7sLSj60oQF0II0XSuq0hlCmTzrdu7ezcWV3JM\nxueZnEqQWMpt+PzJiFcB8KXzg3SFfPzu//n6jq4vQVwIIUTTFGyH1YxFrmCj6/qBONudzdtcvzvP\nZDzJvacrGz7f3x1gbDTM+EiYcF9oT88lQVwIIUTDpXMWmayN5RyMkqi243Ln4RITd5LcfrCI46o1\nnw8FDF46N8j4aITTQ1379mZFgrgQQoiGKB0Ry+ZtXFTbF2dRSvFgbpWJeILrd+fJ5p01nzd0jQtn\n+hkfDfPCqT7MOiTmSRAXQghRVwXbIbmUZW4+jVYsiaq38SGx5FKWiakkk/Ekiyv5DZ8/e7Sb8dEw\nl88NEgrUN8xKEBdCCFEX6ZyXZV6wHSKm6QXwNrWatbg2Pc9kPMFsIr3h8+HeIOOjEcZGB+nvDjZs\nXBLEhRBC7Bu32EUsm7NRKDRNw2jT4F2wHW7dW2RyKkn84RLrtrnpDPl4+fwg46Nhjoc7m5KUJ0Fc\nCCHEnlnFLPNs3vaKsmjQjnXVXFcx8yTFRDzJWzML5K21+9w+Q+fScD/joxHOn+jFaPIxOAniQoiW\nYdkuE/EEAOOjEXxme87gDqpqP59MziKds7HavKra04UME3cSXJ2eJ5UurPmcpsH5472MjYZ58ewA\nAb/RpFFuJEFcCNESLNvlc1+9xaOkt994dSrJq69clEDeIip/Pkop/ur2HB9/3zkMXSsWZmm/n9Ny\nusC1qSQT8SRPFzIbPn9ssIOx0TAvnw/T0+lvwgi3J0FcCNESJuIJHiXT5X3FR8k0E/EE77w41OSR\nCfB+Pg+fraCUhovicTLDzXsLXDkfbvbQdqTUcGQinuDuoxTrtrnp7fTz8sggY6MRjg50NGWMOyFB\nXAghxJayeYtUuoDteLXMNTTUhvDXuhzXZWp2mYl4klv3NjYcCfgMLg8PMPZCmOFjPW3Vm1yCuBCi\nJYyPRrg6lSwvp58IezWlRXN4jUhsMjkLx1VcON3PzXsLzC1mARjqD3HpzECTR7k5pRSPEmkmppJc\nm0qSztlrPq9rGi+c8va5L54ZaNttGwniQoiW4DN1Xn3loiS2NVmpqlom73XZ0jQNTdcwdY1PfuA8\nN+8vAHDpzABmC/58FlI5JouFWJLLGxuOnDrSxdhomJfOeQ1H2p0EcSH2qBUzqltxTLXwmbrsgTeJ\nd0TMJlew0DZpRGKaekvugWfztleIZSrJ/SoNRwaKDUfGRsOEe/fWcKTVSBAXYg9aMaO6FcckWle+\nYLOatclbxS5ibZJlbjsusQdLTMQTxB4sVWk4YnLl/CBjI+F9bTjSaiSIC7EHrZhR3YpjEq0nnbNI\nZ21sx0XX2+OImFKK+3MrTMaTVRuOmIbGhdP9jNWx4UirkSAuhBCHRGVJ1OddxFp/hppYyjIZTzI5\ntUnDkWPdjI9GuDw8UPeGI63mcH23QuyzVsyobsUxieayHYeVjE02b3kz7jboIuY1HPES1Ko1HIn0\nhRgfDfPySJj+7kATRtgaJIgLsQetmFHdimMSzZEvOKxmLXKWg9EGS+blhiPxJPHZjQ1HuooNR8Ze\niHB8sOPA7nPvhARxIfaoFTOqW3FMonHSOYtM1sZyvHrmzW7SsRXXVdx9kmIynuDGzAIFa20hFp+p\n8+LZAcZGwy3RcKTVSBAXQogDQCnFatZLViu1AG3lmfeT+TST8SRXp5KkMtaaz5UajoyPhrk0PEDA\n1zoNR+pBuQo08JsGwMa9gy1IEBdCiDbmuC6ptEUub3vtPzWtZVuALq/muTo9z+QWDUfGRyNcOT/Y\nsg1H9ovjupi6jt9n0BEwCPi9cPwn/+vHC9s8dA0J4kII0YYKxf7duYLj1TNv0WXmXMHmrZkFbtxb\n5M79xU0ajniFWNqh4chuKaVwXYXfZ5QDt8/c+wqDBHEhhGgj2bzFatamYDsYut6SR8Qc1yU+u8xk\nPMnNewvYztrQHfAZXD7n7XO3W8ORnSgtkwd8BgG/QShg7vv3KkFcCCFanNeMxCKds3FdhaZrGC22\n362UYjbhFRa6Nj1PZn3DEV3jhZN9xYYj/Qf2xITruhhVlsnrpaarR6PRIeB9gAV8JxaLLe72CaPR\n6O8APwP4gH8Wi8X+9W6vJYRoTe1au73VuK4ilSmQzXsBsdSMpJXU2nDkA28/hZWzqlyhvSmlUK7C\nV1wm7wyaDa0Ut20Qj0ajfwP4R8D3AB34P6LR6N+MxWJf3umTRaPRDwLvicVi741Go53Ab+/0GkKI\n1ia12/fOKu9325s2I2mmTM7m+l0vQe3+XJWGIz0BxkbCjI9GGOwNAtDd4WfhgATxRiyT16qWmfh/\nD7w9Fos9AohGo2eAPwV2HMSBjwDXo9HoF4Ee4Ld2cQ0hxB7Ue5Ystdt3L1ewWM14+92t1ozEdlxu\nP1hicpOGIx0Bk5fODzI+GubUkYPXcKTRy+S1qmUUKeBJ6X9isdj9aDS6sXhtbSLAKeCvAeeALwEX\ntnxApHuXT3W4yH2q3WG+V5bt8Pt/NMmDpykAYrPL/MYvjlXNkt3tfep5lMJnPp89KqXo6Qkd2Pu+\nH9/XaqbASqYApklPX+scrXKVYnp2iTfeesoPbz0jk1+7z20aOldGwrzr8lFePDe47TLywEBnPYe7\nr1ylQIHf1PH7DXo6/Bgt2FClliA+AXwpGo3+AeAA/ynwKBqN/iJALBb7ox08XxK4FYvFbOBONBrN\nRaPRcCwWS272gERi41KNWCsS6Zb7VKNWuFfN3C9+49Yc07NL5QA7PbvE174/s2GWvJf7dH6oiyN9\noTW1288PdTX9vtfDXu6TqxQraYtM3ltibqWZ67Niw5GrmzQcGT7Ww/homBcrGo6klrNbXnNgoJOF\nhR3VMWk4x3UxNN1bJg/ohPwmmuti59yGbQXs9E1hLUHcjxd8P1H8fwtYAF4p/v9Ogvh3gd8E/nE0\nGj0OdALzO3i8EG3tMOwXS+32rXn73TbZgteMpFWC90qmwLXpeSankjzaouHI2GiYvq6D0XDEm20r\n/Obzve12a1+6bRCPxWKv7teTxWKxL0ej0Q9Eo9E38JLkfi0Wi60/+y/EgdXs/eJGdTiT2u0b5QoW\n6axD3rLRdb0lSqIWbIebxYYjU5s1HBkZZGz04DQccRwX0/D2tkN+L3i38/e1aRCPRqNfjsViH4tG\nozNVPq1isdi53TxhLBb7u7t5nBBi72SW3Fil892ZnI3tKvQW6CTmuoq7j1NMxBO8de/gNxxxXYVW\nrEse8Ol0BH0tWSBnt7aaif9K8b8TwN+q+LgGfLZuIxLiAGuFXt8yS66/cj3zwvPz3c0OHE/m00wU\n97lXqjQcGTnRy9hI+zccUUrhKoXPMPCZOh1Bg4CvNTLJ62Gr7+yfR6PRMeA4ML7uMQ/qOioh2kyt\nyWoyEz7Y8pZ3vjtvFeuZN3mZdnk1z9Upb5+7WsOR44MdjI1GuDIySE9H62TF75Trumia5iWk+QxC\nwead2260rYL4q0A/8E+B34ByWxwbeFrfYQnRPnaarCYz4YMnnfNagNrF/t3NnHWXGo5MxJPMPE5V\nbTgyNhpmbCTMUJs2HClXSTOLzUSC+9NMpB1tGsRjsdgysAz89cYNR4j20+xktXqR0qlbc5ViMZXj\nyXwaBehN7N9dajgycSfJrfvVG468VGw4crZNG448P/7lndvuCJhNX+loBQd3o0AIsWuW7Rz4o3C7\nVbAd0hmLbN4mHNGL/bsbb9uGI5pG9LTXcOTC6fZrOFJ5/Mu3j607DxoJ4kLsUSskq+231288PZCr\nC3uRyXldxKxiSVS9SeeJt2s4cnqoi7GRMC+dH6Qz6GvCCHevsrRp0K8T9MtsezsSxIXYI0lWO7iU\nUqxkvCNiSnktQJuxZJ7JWVy/u8BEPMGDudUNnx/oCTA+GmFsNMxgT7Dh49ut9cVWOoJmy7VYbXUS\nxIXYBwctWe3dl4/ynR89PFCrCzthOw4rGZts3quqhtb4sqiW7RJ7sMhEPMmdh9Ubjlw5P8hYmzUc\ncV0XXfa2940EcSHEBj7TOJSrC/mCzWrWJmc5GE2YdbtKcf/pChPxJDfuzpMrOGs+bxoaF870Mz4a\nYfRkb9uUCHVcF12DoN8gFAi09Tn0ViNBXAhR1UFbXdiMV1XNJpOzylXVGl2l7Nlilsl4gsmpJEur\nhQ2fLzUcuXxugGCLtMDciqsUmgK/73mVtKFwFwklVbb3W+v/NgghRB24riKVKZDNN6eqWrnhSPx5\nUmSlI/1ew5GXR9qj4UhlB7BgQJLSGkWCuBDiUCkfESvYDe8iVrCKDUemEkzNLm9oONId8vHyiNcp\n7FiLNxxRSqGK5U0Pe8GVZpIgLoQ4FHIFi9WMTaF0RKxB+92uq5h+vMxkPMlbMwsU7OoNR8ZfCHPu\neGs3HCk1EzmM5U1blQRxIUTL2o+qcemcRTpjNbSLmFKKpwuZbRuOjI9GuHi2v2UTvZ43E9HxmcaB\nbybSjuSn0UBSxlLUolm/J5XP+5H+5tfUtmyXz375JlOPlwGYuJPg0x+7VNP9cIvnu7M5GxdVLIla\n/xnj0mqeq8VCLHOL2Q2fPx7uZGwkzMsjg3S3aMORymYiftM7u93sDmxicxLEG2SnTTLE4dSs35P1\nzxubXeaXPjTS1N/PN2/PcX1mAcvyjlldn1ngzdtzvOfysU0fU+18t17noqi5gs2NuwtMTlVvONLX\n5S/vcw+1wJuj9aSZSHuTIN4gB7VJhthfzfo9mYgnmE2skst7AfP+k1TTfz9nnq5gWU75XliWw8zT\nlapBPJv3uog1ar/bcV3uPFzm5rfvcjWe2NBwJOg3uHxukLGRMGePdbfcvnFlJrnfr0vBlTYmQVwI\ngeMo5pdz5WCULdg4TnPP9A4f7eH75pPymHymzvDRnvLnXaVIZ72SqK5b/5KoXsORVSbuJLl2d2PD\nEUPXeOFUazYcUe7zexjwG4QCZtsUihFbkyDeIAexSUa728necz33qSuvfXl4sKbfk/0fjwJNQyl3\n7ce2GW8979s7Lhzh2t0k07Penvjw8V5A8f0bTxg90YdV7N0NoNVxz3Y+lWMy7u1zz6c2bzhy5fwg\nHRUNR2zb5eb9BQAunRnA3MH3vpfHljiO6yWk+QxCfoNgYG8v95LT05okiDeINMloLTvZe67nPnW1\na3/qw1FuzMwD1X9P6jEew9AZ7AmQKS6n93b6MKrM1Bp533ymzqc/eomJeALHUUzcecafvn4f5SqO\nDnTwyQ+cp14T70zO4tpdrxBLtYYjg71BxkbCfPAdpzCqVCGzbZfXvj1dTm67fX+RT37gfE3BeLeP\nVUpBRZW00D42E5GcntYlQbyBDksZy3awk73neu5TV7v2jZn5La9dj/GsXyk6c6y36gpAo++bz9R5\ncXiA719/wmwy7RVn0TXmFrPcvL/AlfPh3Xy7VVm2y+0Hi0xu1nAk6DUcGR8NczLiNRwZ6O9gYWFj\ntbWb9xeYW8yWv/edjHcnj61s3Rnye53A6rG3LTk9rUuCuBBiw0rRR947zNJipmnjWdMCFIWiPl3E\nXKW492SFyanNG45cLDUcOdXb9DaZla07/T6DzpC07jzsJIiLQ2knOQr1zGfYzbXrNZ7KlaLNjhjV\n+745rksqbZHL2+X2nxoal84McPv+YnmJeag/xKUzA7v6PgHmFjNMFguxrG84ogHDx3sYG9l9w5G9\njHf9YyN9QS6dHSBgNq8mueT0tC5NtXZXGZVIrDR7DC0vEulG7lNtKu9VKya21XrteicZbfU7VY/7\nli84rGatcgvQavaa7FVqODIRT/J4nxqODAx0Vl1O38t4lVIUbIf4w2VMQ+ddF48QqkiYa5a9/s7J\n61RtIpHuHb1DkyB+AMgfR+3kXtWmEfep3AI0b2O7bl3OUlc2HInPLrP+5a67o9hwZGR3DUe2CuI7\n4bouuqYT8On4/caBPLctf3u12WkQl+V0IZrssB3dKS+ZFypagO5jwNqu4Yi/mCw3Nhrm/PHePZUU\ntW2Ha9NJYOezbdd9vrctVdLEbkkQF6KJDtPRndKSed5y0HVtzzPNyuXqi6f7SSznyvvcK9mNDUdG\nT/YyNhLh0tl+/PvQcMS2Xf6fr97iwVNvdrndUbA1HcCKBVdarZKbaD8SxIVoosNwdCed80qi2k6p\nJOreA1fpLPXjZJpsweGL35nZkFkOXgLW2KhXiGW/G47cvL/A48TqlkfBHNct9tvWCQXMlu1WJtqX\nBHEhxL5zlWIlbZHNV3YR25/VhVzB5qt/+YCb9xY3LJWD13BkbCTM2GiEI/2hfXnOWrnFZiL+Yr9t\n6QAm6k2CuBBNdNCO7li2w2qpi5ixf13EbMclPrvMRDzB7fuLGxqOaBoMH+vhJ99+kjNHG9Nw5NKZ\nAWaernD/SQpNg+ODnbzv8nG6OpqfSS4ODwniQjTRQSnHu6GL2D4011BK8fDZKpPxJNem58nk1zYc\n0fBKjIYCBqePdPHzHxzZVY3xHY+rWMktFDT5r3/uCt+ffISuaW37sxPtTYK4EE3WruV4lVKsZi1S\n6QLXZ+bRNa8oy15XzeeXc0xObdNwZDTMpdP93Jvzksp22ySkVqXypgF/qbyp99I52NvBuy8drdvz\nCrEdCeJCiB2xHYeV4pK54yj++Dt3d9Xoo1I6Z3G9WIjl4bPqDUdKhVgGe4Llj1+psSjLTlWWNw34\nvb1tKW8qWpEEcSFETfIFm9WsTd52yolqN2aSu270UdlwJPZgyQucFZ43HIlwMtJZ9+InjutiGjp+\n0yDob055UyF2SoK4EGJTXlU1rxGJ7Sp0fW+FWcoNR+IJrt9dIG81r+GIqxS4Cr/fyyQPBUzMfdjL\nF6KRJIgL0SJaqXKb47gsruTXVlWrclSq1kYfpYYjk/Eky+nqDUfGR8O8OLy7hiO1OgzlTcXhIkFc\nNEUrBaxqGjW+0vM4juLadJInC177z2ZVbis3InEVecvZNsCZps4nP3C+aqOPVKbAtal5JqeqNxwZ\n6g8xPhrhyshgzQ1HdkophVKqWHBFypuKg0eCuGi4Vi812qjxVT5PJmuRKTiEe4Nomtbwym0bq6rV\n/r2apl7eA89bDtfvJJicSjL1qHrDEa8QS5hjg537+S2Uua6LpmlS3lQcChLERcO1eqnRRo2v8nk0\nTcOyHNI5m65QY4qFlKqqZfIWCnAdVZ5Rv68iA3w7jquYflRsOHJvAauODUeqKc22zXJ504CUNxWH\nhgRxcaCtXxZvVaGgSTpnAV5AqmflNq+qmtdFTNN1NE3DKdYiL+1tzzxd4WPvOrPpUTGlFI/nM0zG\nE1ybmt/QcETXYORkn3ee+8z+NBypVGom4jeL5U1DMtsWh5MEcdFwjSo1Wm1Z/Ld++cdaZnzrn+el\n84NcOTeIYeh12YfPF2xWMla5qppWsWR+8/7CmqNijxOrVY+KLa3mvQS1qSTPigG/0olIJ2Mj9Wk4\n4rgKn67hK+5tB3zy8iWE/BWIhttNqdHdJJpVWxZ//cZTLpzo2fK6jSqF2qjneb7f7XpHxHZ4bCub\nt3lrZoGJeIKZJysbPt/X5WdsNMLYSHhfG46Uypt6zUR0OoI+aSYixDoSxEVT7KTU6H4nmtWSEd6o\nUqj1ep71+936JkfEStYfFTs62IWuafybP7tTteFI0G/w0rlBxkbD+9pwpFTe1O/zypsGA/ISJcRW\nmvIXEo0leUqOAAAgAElEQVRGjwA/BH4yFovdacYY2lUrHc3aj7FUBlRQVZeS18+oZxOrfP5bU4yc\n6OXy8CA3ZuarjqHasvjbL0T4vX/zI2YTqyyt5MlbLkMDIXRd33UC237+THZyLct2efP2HDNPVxg+\n2sM7LhxBoUhnvBaguuHtd9cSXk1T52fff47vXH/MvSer3Ly3wF/derbmawxdI3q6j7GRMNHT/fvy\nu6eKrTt9xdadnaHGljdtpb8nIXaj4UE8Go36gH8BbDw4Kra02Yy0lcaykxfB0jVmE6vML+dA0xjs\nCWx5LaUU86k8k3cSxB4s8oVvTRMKGGiatuFx1Zarf3g7wWxilYWUV8jEcWFuIcvRwY6m3YfdXMuy\nXT775Ztcn1mgULD5rqHxxu05fvb9w/h95o66iCWXs+V97oVUfsPnzwx1MzYa5qVzg3QE9/6SoVwF\nGuUjYM0quNLqRx2FqEUzZuL/EPjfgd9pwnO3tc2OPh0/1tsyY9nJLLZ0jVzewXYUSrlk8s6Ga1XO\nqNM5C5SiI+QjnbNJrebRCNAR8lUdQ7Xl6kzepmA5GLqOUi6265LOWbxwso/x0ciOZmf7eRxtJ9ea\niCeIP1oin7fRNLAdxYMnKW4/WKqpbnkmZ3Fti4YjQwMdvHRugLGRMAM7OG62mcpl8o7A8y5gzdTq\nRx2FqEVD/5Ki0eirQCIWi30tGo3+Dmy/0heJdNd9XO2i51EKn6mXX3SUUvT0eIlEjb5Pm41lJ+Mo\nXSNv6HiX0TANDZ+pb7jWb/3yj/H6jafcvrfAjekkuq4VK4qBbnh72LWMoa+/gz/97l1W0gU0DTpC\nPjoCJu++fIxX/9olAH7/jyZ58DQFQGx2md/4xbFNq3ztx33Y6bWyeRul66Dw9rk1DVBohk5XV5CB\ngepFVAqWw7WpJG+89ZQbd+dx3bX73F0hH++4OMS7Lh/l7LGePc2OXaVQCgKmTiBg0h3yYbRYXfL9\n+tnJa1Tt5F7tv0a/Hf4vARWNRn8KGAP+dTQa/XgsFpvb7AGJxMZs2MPq/FAXR/pCa/Z4zw91AY2/\nT5uNZSfjKF2jYK1iFIOR39Q50heqeq0LJ3q8jy+keZRM4zd1ejoD+E2NguXUNIZIpJtf+/hlPvOF\nqyymcoSCJicjXXzsXadZWszwxq05pmeXyi/s07NLfO37M5vOzvbjPtRyrfWNSE70hzgZ6eLO7DK2\n7WAaOicjnZwe7GBh4flOlddwJMVEPMmNTRqOXDrrFWIZPfm84YimaWuuU4uqdcldFytbYCFb2P4C\nDbYfP7tIpFteo2ok96o2O32jo6n1dREbJBqNfhP41W0S25T80NeqttTbrD+ORiW2bfW8WyW2VVO6\nV5uN/Y1bc3zl9ftrZmcfffeZLZdY65nYpmmKlYxNLm+DxprZsW27XL+bZDaZ5mS4i5fODZaLs8wt\nZJic2rzhyLkTPYyNbN5wZGCgs6Yg7jguPsML2qGA2XaV0vb6s5PAVDu5V7WJRLp3tAQmQfwAkD+O\n2m13r9YnO50IdzYl2Smb9852523XW6WoQbnhSDzB4/nMhs8fHehgbDTMy+cH6d2m4chmQbzcUMQ0\nCPoNOoKNzSZvNfK3Vzu5V7XZaRBvWnZJLBb7ULOeW7Qv71jVM2aephg+2s07Lgzta4BtVAEW2DgL\nNAyNlYxFNmejlELTtW0DeN5yuDmzwEQ8yfTjjQ1Hejp8vLzHhiOuUqAUAZ8pJU6FaDHNTxEVh9Ju\nljEt2+WzX7nJ9el5LNvl+z6Da9PzfPpjl/Y9kNc7Q7lyxu+6Lm/cmuNnfvysV0p03bL5ets2HPHp\nvHh2gPHRCOeO9+yqypnruiilmn4MTAixNQniouF2ez53Ip5genYZ21Hlrl9Tj5fb8ljQRDzBg2cr\nFCe5PJnPENvieFi54cidBFen51mt0nBktNhw5OLZfvy76Jldqk3u9xsc6e8g2OQSp1KIRYjtSRAX\nDXeYz+fmLYcf3HhKfHYJ23YxDANN84J0NYsrea5OJZmIJ0ksbWw4cjLSydhomCvnwztuYbq2E9ja\n2uTNPscthViEqI0EcdE2xkcjTMQT5eV0n89g5HhvS7cYLc0mbcfl7NEevvDNKeaWsiilKFgugWIv\n8aH+EJfODADeOfAbxYYj96o0HOnvDjA2Eubl0TBH+nbWcGRN0ZUW7gR2mN/oCbETrfkXLA603bb6\n9Jk6n/7opbomtu0ny3b5l3/6FrPJNMr19pfzVrENqKYR8MPw8R7ODHXzwsk+7swuMRlPcvvB/jUc\nUUrhugq/z8DvM+hqcG3y3XIcRSZroWkaoX0o9SrEQSV/HaLhNssAX3/+++pUYk1zj1KwNgyNkRO9\nW+6T1rqfWi3bHdj0sbVc11WKlYzF62895eGz1WLvbo3Uah6lQUfweZ/tzqDJzJMV/sN375HN22uu\nU244Mhrhwuk+zBornpVqk/tNg2DAO7/dTtnklu1ybTpJpuBgWQ7pnMVL5wdbesWl1Ug+weEhQVxs\nqx4vCOszwCv3QJVSfP6bU2TztpeFbj7h2t0k//lHLvCHX49tu09a635qtWz3yXgS3dB4Mr+xPWmp\n6cjU42UAJu4k1mTGW7bDasYmm7eKHcTWZpn7/QaOq1jJ5HFd7/n/7M1HG+7NmaPdjI3srOGI47oY\nmk7A3/4tPCfiCZ4sZAj3BknnbEBx5dygBKIaST7B4dK+f+miIRr1glC5B5rO2Syt5EEDQ9exHcX0\n7DJf/O7dmvZJa91PrZbtfuvBIkGfQWeHf8Nj37w9x/WZBaxi6dLrMwu8eXuOsdEw6axNwfaWyksd\nxCp7dDuOy0rGwnZcLGdjElu4N8jYaHhHDUcc18VneC08O4LGpvXd25WmaXSFfCilWq7ueiuTfILD\nRYL4IbKbGXUjXhAs22Xq0TLpnEVncGcZ1o0083QFy3LQNA2lFPm8zVszC5w92oOma+jr9poV8MLJ\nPp4tZUks5VgfugM+g7dFI4yPhDkR6dz2HHbBcrh1fwHT0HnbC0fo7Qq11TJ5rXabMyHEYSRB/JBo\n1SW2yp7i2ZxNNu8w0O2H7kB5Od1n6pw/2csn3nduzXL6Zi/utQQBy3ZxHJe+7iCZ0vP4DC6e7l+z\nnF752OGjPXzPeFwurmIaGiciXWgV56ldpZh5kmLyTpIbMxsbjgDl5e4Pv+MkY9sEp9L+tq5p/OkP\n7jG3kEHTNO4+TvHqKxfRzYMXxBtZNe8gkjdBh4sE8UNitzPqer8glMal6zrhvhCZrMWFMwN84n3n\nqia21fLivt3Xrd9/Pxbu5MxQF+eP926a2Ja3HM4d62HkZB+zc96xr1NHu3np3CDgNRyZiCe5OlW9\n4cjZYz3YjkPBctF170jZ5eHBqvek8hhYKeC/cWuOZ4vZ8mz/oC+RNqJq3kElb4IOFwniYkuNfEHQ\nNI2OkI+RE710BE3ec/kY77l8bMM2QC0v7lsFgco3NJqmYdkuL5zqX/P177w4VGz/abO4ksd2vP3u\nX/jgCDfvLwBwKtLF6zfnvESsKg1HTEPjSF+I/+ynXmCgN4htu+XHXjozUO44ppRCuQqfT5qKiP0h\nb4IODwnih8ReZtS1viDsdM/dW9L2zk/nCjaapm0YV7VtgE99OLqj9qM75bqKVKZQPvKlac/3ux1X\nYTuKiTsJ/t03pjbsc4cCBrqm0RH04TN1lFLMJlcZ6A1imnq5rKpy1Y5qk8sSqRCiGgnih0S9Z9Q7\n3XNfv6Qd9Jt8cOz4huIt67cBZhOrfOYLV8t7zbU8T+X3bNnOpm8c8gWbdM4mV7DLBVnAC9xTs0tM\nTiW5ObOI5axtOKJrcOV8mLdFI6xmC3zn6pOqAdlxXcxStbSAsaPSpq2+RCrnkoVoDgnih0g9l9i2\n23Nf/yK/fkk7bzkYhr7ti38mb5PN2VWPgK23/o3C1//qIX6/wWrG27MO+k1+4uVjXDgzwEIqh+Mq\n9GKWuVKKx8m0t889PU96XcMRoHi0yyTg07lwpo+RE73YtsudB0vMLXqlVYf6O7g8PEgoYNIR2Nsx\nsNLPr9UCZqsmTQpxGEgQF3VX7UV+s6Su9dYvIw90B8kV7G0e5Sm9UQBYSOXJFmw0vODd3+0nnbNY\nyVoUitnmuq6xuJLj6tQ8E/EEiaXchmuejHRypD/EbCJdrqBW2bzE0DV+9v3nuDO7hN/UeeelowR8\n+3d+u1SgZnp2ufw9fvqj+9uKdadqSZpstTceQhwUEsTFjlV7Qd5qz7bai/zl4QFOhDvXfP3l4UHe\nuDW35rqlZeQ3b88x83SF05Eu3rq/UPUI2GbSOZtCcfldKcjlbVYMb99a0zSv4cjdeSbiSe493aTh\nSLEQS6QvhG27vPbtaeYWva5iR/qCvHh2wJuZF5fJTw117+keb+bN28+4Pj1frq1+fXqeN28/4z2X\nj+7bc1i2y3cmH5FKZfcl4MpMXYj6kSAudmSrF+Sd7Nkaxtqvvzw8uGVJ1RszCzxKprnzcIljAx38\n9I+dxjC0LZ+n9Mbi9oOFYhMQvVyZDbx98WvT83z+m9M47toUtVDAazgyPhrh9FDXmj1uw9D4+PuH\niT9cxmfqvOvSEMEGte6ceZrCst3yeCzbZeZpat+CeOnn+2wpi2W7NQXc7ZLupIKYEPUjQVzsyEQ8\nwWxilUzem9nOJlY3vCA7jtdUBBSOo7g3l8JnaCyu5tGAvu4gTnEmWXrcG7fmykEgnbO5M7vEX771\nBL/fZOrRMrOJVW/WnLOZerRMR8DEMHUcx+XlkciabHWAH8aekc7aHB/oxHUVT+bT5C0HF8jkNNI5\nh+X02lm3psHxwQ7ef+U4l4YH1jQcKfXeLmWT+wyduQVvJt7I42DDR7v5vs8ol371+QyGj26c9W+2\nfL3dsnYp4Pp9Bpqm1RRwWz3pbivV7sdulv5lu0A0iwRxsSOO4zKfypeDSDZv4Djumspr86k8yvX2\nmQvFWaPrKkzDSxjL5h3+4xv3uTEzv2aWp5RiIZWnYDleE5RvTdPXHSCbs8kUj3tZjsK2Xb41+RhD\nh+/5DF779t1yo5A3bs3hOIq5hTRLqwXQNLpCXhevdM4uNtRYqyNoes/pKuZTeW7eW+DS2YG12eQV\nvbebuTz8jgtDXJueLzdhGakoUFOy2fiAuo17q6TJVj0et9nxxVqa7Gx3HdkuEI0iQVzskAZKPV9e\nVgrQyjO4TN5rH+kqhet69cN1TaEUWI6Lrnkvepm8s2aWNz4a4Rs/nKVgZcoZ69mCQzDv0BHykcpY\nOI7rlT/Du64CCgWHbN4L+oGAyd3ZJdA1lOu9gXAV5AobS5+ahkYoYKIpheMq77IaWJbDg2crTD1a\n5sdfOlo1m3wny8P7PUPzmTqf/tilmmbT68dX+vdW4y4F3GdLXnb9fgTcVp2pV7tPtTbZ2e46sl0g\nGkWCuNgRw9AY7A2SKy6nBwMGhrH3+t0+U+eDYyf441TOm7mjSK16R8E0TaO7w0fectCATM7GUeBW\nHNdWxahuOYp83iknflUK+AxGTvXxdH4Vv+ktF2cyeWylyhnmmubt13cEzXIAXx+Ia1WvGVo9jwqW\nAu703Oq+JbaVritBTYj91/y3w6KtjI9GOBnpIhQ0CQVNTka6GB+NMD4a4US40zsL7TPwmzpBv46h\nUzx7DUG/93FfMYt7/SzvHReOMHqqj1DQpCNg0tsVIBgwUEoxcrKXF88O0NPpw2dqaHhFVnymRmfQ\nIFdweLqQIV0lgAf9BueOdfPffept/MrHX6QzYJLNWSjlMnKqjxfPDZb3gP1+k5HjveVxlQLxV16/\nz1dev8/nvnqLy8ODnAh3euVSt5itrj8LXzkjrqfSz2L9+Db7+Ho+U+f9Yyd458Whlpgx10u1+/GJ\n952r6R5td51W2C4Qh4NWeca1BalEYuORH7FWJNJNI+/TdklTjuPirXt7iW0PEqucOtKFqWvljxuG\nXnWWV3nty8OD3JiZx3ZcRk/0kbds3rq3gOvC4kqem/cXWEzlya5bLteAI/0hTg91cSLcSTBgcuF0\nH7qu8x/feMD9J8vkLJeB7iC/+fMv4zN13rz9jJmnKYaPdq+pGvfGrTm+8vr98lKpUoqPvvtMuWDN\n+ntQabPHNmJGutvEtpJG/041y14T20r3SRLbtndYfqf2KhLp3tHSpiyni01t9sK02dLofiyZlq7h\nKkU6a3H2aA+W4+AoRSbvkEpbTMSTPF3Y2HDk2GAHYyNhroyE6e30r+kG1hE0uTqV5Ol8hoKl0NHI\nFWxuzMzzzotDvOfy0R0d06rle21mQlc9f0YHSbX7sZt7JPdVNIsEcVFVZbZ5Jm/zjR/O8ps//3I5\nC7xesnmLhVSeL79+H02D9185zt3HKSbjSaYfLW9oONLT6WdsZJCx0QhHBzpwXBefruP3G3QGA2sS\n0xxH8WwxQ8HyNtPTOa181G0ze20c04oJXUKIg0OCuKiqdB68dOQrtVrgM1+4yt/5pfF9D0SW7bCa\ntckXHDI5m3/22lVSGQul4Ac35jZ8fcBncHl4gLEXwgwf6wGlMHWdgN+kI7hVfXIvYJe3kDS9/LHN\n7DUQywxNCFFPEsTFpjJ5r1xpaU93MZXbt6MzjuuWA7ftOOXEr9e+fZel9MZmI5oGoyf7eNsLYS6e\nGcDQvSIrAb9BZ0Um+VYMQ+dIf4jl4vU7AgaGsX1AlkAshGhVEsQPAMt2NtQc36vSue3l1QJucaYb\nDOzt16UycFu217VscSXHZHyeyanqDUfAy0Dv7w4S8utcOjtAZ9BHV6i2wL3+e4rNLjM9uwS0TtER\nIYTYLQnibc6yXX7/jybLgWk/zyL/2ide4u//q9e9c9m45ApOzd3Hno/P8RqQFBwsx8UwdLJ5m+t3\n55ncpOGIrhXPfRcN9AQwdZ35VI7HyfSuZ8U+U+c3fnGMr31/Bjgce9SSNS3EwSZBvM1NxBM8eJra\nc7Woai/2sYeL9HUH8Bebe4QCRjmbeyt5y9vbzhccXOWi6zq24xJ7sMRkPMntB4tbNhwJ9wb45o8e\nkVzO8WxhFdcBw1c6nrY3PtNo2NJ4swOolAMV4uCTIH4IbBdMSj2qpx4uUbBdvv7mQ/7WL4xtez0v\ns9s7833p7ACW7RYDt0LXNRSKh8+8NxXX786Tza89z61r0Ncd4MKpPj709pN0BnwE/AZdIZP/4pWL\nfPbLN5lNpkmv5knnLF467wX5ZgfHWrRCAJVyoAdXO/wNiMaQIN7mttvnrSWYvHn7GdemkuQtr9b4\n6qMUv/f5q/z6z77EF741TWo1D4BGgOip/vLRs8RiBjSN3i4/P7jxhJ/7iRFMU2dhOcfEVJLJeJLF\nlfyGMZ852o3tOCQWMiym8rxxa45cweZXP/7SmiIrTxYyhHuDxaYliivnvKX8/QqO9XwhlAAq6qUV\n3iCK1iFBvM1tt89bSzCZeZqiUAzgpQXrpwtpvvz6PUIBA40A4NVJ/9L373J/boVszi6WN1XkCy5P\nFrJ88bt3ebaYZTaR3jDOSF+Q8dEIL48Mcv/pCl974wGlQmu26/ULf/P2sw0FVzRNoyvkQylvxr9f\nwfEwvBC2avcwsTfyBlFUkiB+AOx1n3f4aDffNnQc2yuCUuqb7f1bIxQ0cRyF7bhkc86apDNXwWrW\nwl4t8Gwxu+a6nSEfY+cHGRsNc2ywA03TCPpNllby2KXOYXgntR1HMfM0VQ7imwWg/ao9Xu8XwlYI\noFJsRoiDT4L4AVdLMHnHhSEm40muTs9juy5Bn8HwsW4+NH6C5FKWpwtee9Ch/g5+8u0n+bffiBcD\nsfd4t6Lqmc/QuTTcz9hImJGTfWgoAj6TUMCgI+grP983Jx4x/SiF18gU/H6D4aPdz69TEYBKe+8T\n8QSXhwebHhxr0SoBVM64Hzyt8AZRtA5pgHIAbNdYoFpTEVgbWAqWww9uPGH6cYpjA51cOtdPwGdi\n2y437y+wtJpnJWNx/e4CqXRhzfU14NzxHsZfiPDi2QH8pu7N4AMm3R0+dH1jVnkmZ/NP/t0Es8k0\npqFx6cwAv/IzL1ZNuqtc9j4R7uRTH46Wv4fNvp/t7lW16x605fS9kGYVtWnWfWrHxDb5naqNNEAR\nG5RmY+sD12Q8wc//xAiOUliWw+jpfqJnBsqPW04XuFpMUNu04chomJfPh+kpNhwJ+LzSp6GAr+pY\nKjudDfSGWC6+IdA36Ulebdm7dMxtL/vaPlPnUx+O8sXv3gXgE+871xYvhEKArLCI5ySIHyIT8QQP\nn62glIaL4t6TFP/+O9OcGerm0pkBTE0jnbX4sx8+5N6TFZ4tZjdUFu/t9PPySJix0TBHBzpQSqGh\n4dN14k9T6JpWdWnPsl3evD3HtyYfkyvYZPI22ZxNuC+Epmk8mc/seE96L/valu3yh1+Pld8A/OHX\nYzITF0K0HQniB5zrKq/wiuV4+9iOd4YbBUtpi9szC8w8TvHGzTlCQR837y2wfoel3HBkNMzw8R50\nTcN1XUxdoyPow2caW86ISzPmO7NLpFYL+H0GwYCBZbtkczYdoeqzdqjf/t9+JLa145KmEOJgkSB+\nAFm2QybnkLccLMfB0L3gcunsALcfLDK3mCWTK+A6LnldY3kxi1slNSLgM3h7NMxPv/MMPlMvd/8K\n+Ay6Qs/bfL5xa27LgFgKmKUDbJbtEvIb+HwGSimUUpsG560SxJqZ4HMYjqgJIVqfBPEDQClFNm+R\ny7vkreelToFyAAcwTZ0PjZ/kz374kPhKnoKjKDj2mmv5TJ2OgEkwYKJrcHqoG0MHXfNm3Z0hsxys\nNxtLJmsx9Wh5w+y0M2iSK3ZGA3hpeIAr58MYhrblTHaz/b+9ZIDv9Q2AnNUVQrQCCeJtynFd0lkv\nIOZcWFwtoGsaaKBrawNZNm9zbXqeyakk96s0HDF0jXBvkJ/74Hm+f/0Jc8Xz3kf6QoydD9PXE8C/\nRcewUkCcTawyv5wDTSP2YJHPffUWr75ycU3AHOgJEPSbfHDsBO+4cGRfGrXsJnC2yhGwRpBlfyEO\nroYeMYtGoz7gs8AZIAD8T7FY7E+2eIgcMatQaixiWQ62+3y2PTDQycLC2ipppYYjE/EEsQdLVRqO\nmFweHqC308dgb5AXzw5imjqZrM2f/+ghhqHxCx8apWuL/epKlu3y+W9NMRFP0Bn0oWkaSil++sdO\nYRh6+ay3R9t29l0Plu0yPbdKKpXd83Pv5IhaM4Pobo/SyXGg2sh9qp3cq9rs9IhZo4P4q8CVWCz2\n30aj0X5gMhaLndniIQcyiNfyom7ZLj+68wzLdome6sdx1Zpl8hLbdnkwn2FhMcOjxAorWYtszmE2\nmcYqVWMp0jW4cKaft70Q4dyxHu7MLuE4LqChaRA91c8f/lmsmHyms7hSoL/bz0+8fIxEqsDw0R4u\nnR3gy6/fw3Vdzg714PcbRE/18+XX7zG3kGFxJYdheLN213UJ+k3yxeXzof4O5lNZ5orH1TqDPi6d\n7Wf0ZP+2s/LdBsLKZi3XppMkUzks293TufBqDWA2ayzz5u1nfGvyEbmCt23hrUIc5x0XhvYczGu5\nJ2/cmuMrr98vL/srpfjou89sunpRumZPT4jTgx07OoN/GElgqp3cq9q0+jnxzwNfKP5bB+wtvvZA\n2i4hylWK5dUC/+/XYjxZSIOCH91J8MkPnMc0Nwbw1749zZP5THkJvBpN85bMB7oD+E2d4WM9fOl7\nMzyZT7OULqApxWBviK++fp9swQEUdrGueXY+y//353cxdPieaeC4Csd1cV3Q9ccM9YdILudxi28G\ndU0j0hcgX3AxTR2lbAzDS4q7OpWgYD9/05jOOTybfMJf3pzj2t0kn/7opU3f0OwmiazycZmsRabg\nlMu/7lfN9c3eDJS+Lv5wieV0AZ/PAKWwbJfXVnLcmFnYUyJcPRLrKq9pGjqpdMGrna9pkrgnRItq\n6F9kLBZLx2Kx1Wg02o0X0P9eI5+/FVQmRJWCyQ9jz1jJFEgsZXk6n+YHNx4z82SZXN5B0zTmFrPc\nvL+w5jqrWYsvfvcub80sbhrAdQ2Cfh3D2yrHsl3mFrN8/a8eMreUpWC7uI7CVRqpjEUmZ+Oq5wG8\nkuN6y/kF2y1nsjsuPFvKUbBdFF4Ad5ViNWujNC9LfiGV8xLvcjaWXX3Vp2C7TM8ub1oXvdo9q6WG\n+vrHWZbDatba9nE7ueZmY1n/dfmCXV6RgNq/h72OY3w0wolw57anANZfM52zWV7Nkyn+Du51vEKI\n+mh4Yls0Gj0FvAb8b7FY7N9u9/WRSPd2X9JWeh6l8Jk6rlIo15t5W0CgI0CgA2zbYXJ6gXTWAjQK\nlsNAb5CuriBd3UGuxhO8fuMpt2YWyrPfavymtydtFvejFd75cL+pM9gXIrGcxXUVmmajaaDrGroO\nhqGTK7jVL6ppoLzGJariQ+C9SdA0DeW6KAX93UGUUuStDLmCg6Zr+H06eWvjtTUNdEOnpydU9edd\numeVS8Kbfe1mj+vpCpAt2CgFpqFx+mgvH3nvcPmYXK1qHUvp63xdAXKWQybnfd8Bv0Ffl9/7mhq+\nh72OA+C3fvnHeP3GUwDeffnopt9z5TVzBQdN8+5V6XjhXsZ7kMk9qZ3cq/3X0CAejUaHgK8BvxaL\nxb5Zy2MOyh6KUopM3qY/ZNIV8jFXbioSYjjSVU5MuzadZDVTwDQMbMclX3AoFBzefOsp//dXblJY\nFwQ1zWs6UqjY/9Y16OsOYFkufp+G7bigIBQwOB7u5JV3nmZhOUvBcrwleqXoDvnwGTpBv85qtsDS\n6tqdDkMHv6mXl9OV8j4W6Q2Wl9Nd16UjYNLT6Svvx/d3B4ie7mf4aA+T8QQ3ZubJVrxJ0PDGP3ys\nm/NDXVV/3ueHujjSF1qzhL3Z1271uBeHB/jxsZNk0nnGRyMsLW4sJbudWsdS+XV9XX6G+jtQKAqW\nU96Tr+V72Os4Si6c6AHY8nuuvGZn0MRxvO2XguXsebwHlezz1k7uVW12+kan0YltnwF+AYhVfPiV\nWPxxTdEAABKhSURBVCyW2+QhbZ3YZjtuuVqaZXmzUU3Tyk1FAK/cacU+47XpJH8x+RjLdlnJFLyl\n6nU/Ik2DkRO9vHRukNiDRZ4sZHAcF9PQGDnex4lIJz6fzkvnwtx7miqWWFmbEV5Zw9x2FQ+frXI6\n0oVhaBiGzpmhbv75F6+jXMX7rxytKbEN4GPvPssffTNedc+4VHp1+nHKWwVAoek654/31j2xrfS4\n48d69/xCUutY1n8dsK9Z6vXIepfEtp2RwFQ7uVe1aens9F1oqyDuKkU2b1MoOOQtF0e5a4qtbGd5\nNc9EPMm3rz4mV9i4MX1ssIPx0QhXzg/S0+kvB3yfqWM73kz4Q+MneM+LR+kMece8tnuhr0c3r/0O\nLvt5PXkhqY3cp9rIfaqd3KvatHp2+oFj2Q6ZvOMtk1bMttHA0LYPNvmCw40ZrxDL3WJ/7Uq9nX6v\nU9iI13CkxLZd7s+tkM1b6LqfXM4CFF0hP10d/uLYts9grkflsf3ssCTlTYUQYnMSxHehVHSlUCy6\nUppt60ZtgcVxXeKzy0zGk9y6t4jlrN3nDvgMLp8bYHw0zNljXsORSrbt8u//YoqnixlyeZuVtAUa\naGh86Xt3uXR2gN4u/74F6GYWK5HypkIIsTkJ4jVQSpEr2OTyDjnLQSkvmxuoeblcKcVsIs1kPMm1\n6STp3NrEMV3TiJ7uY2w0zIXT/ZsGSlcpbt1fJJnKEfT7cDpgcSWH9z5AMbeY4+//q9f5X371vZuO\npTIoXx4e3LKG+F5mwlLuUwgh6kuC+Ca8Fp4Wecslb9mgaejFM7lb9P/YYCGVY3IqyWQ8SXJ5Y/7e\nqSNdjI+Geen8IJ3BzUucukoRMA26Qia9Xf7ymwcNyue2S0e/VrI2/+JLN/jVv355Q4C+PDy4ISh/\n6sPRTROYJuIJZhOrZPLeHv1sYrWmmfB+LYM3s1OZEEK0OgniFSzbIZ2zKVjumhae60udbieTs7l+\nd57JeJL7cxsTOQZ6AoyNhBkfjTDYG9z0OkopUBAMmHSFzPL53sqGI65S5SNmlfvpM09S/OHXYxsC\ndLXl6Rsz85sGZcdxmU/lsYqFSrJ5o1iqdWvP249CNmcTf7jEm7ef8Z7LR7d9bKXD1KhECCF26tAH\n8VpaeNbCdlxuP1hicouGI1fODzI+GubUka6q7TxLR89cV/HS+UF6O/zlLPNKPlPnUx+O8pkvXCWT\ntzkW7uDRszS2Wyp9CgGfyWxilatTSQzjeR/vqUfLpHMWHQGTXN4pFmRxeOPWHFAtSBYLvJTGoBSl\nvuDbUUqxkMpj2S6u6/In358B2HH3sloT5Q7q8v1B/b6EEHt36IK4WywBmit4GeVolLPJ17fwrOVa\n95+uMBlPcv3u/IZjYaahceF0P2OjYV441Ye5ReJbKVltbilLIW8zEU/ymz//8qa9u2/MzJO3HLpC\nfnymztHBDkDxZN4rIrO0mmMlo/GNHz3EdrySm1/41jRBv0E2Z7O0kkcD/D6DL313ho6gWbVGtmFo\nDPYGyRWX04MBo/ymYCvjoxG+8cNZClbGK/mJRnI5xx9/e5obM/P7nmF+ULPYD+r3JYTYH4ciiOct\nxzu/bbnYjlOuN63pOzqOV/ZsKctkPMnVqSSLK/kNnx8+1s34aIQXhwcIBTbeYtt2uX43yWwyzYnB\nTl46N8jUo2WSy1lW0pZX6OVJis984Sp/55fG17xgl2ZlU4+WURUzZF3XGDnRx3wq782wAcdVzD5L\nc3Swg0zeIbWaR+sK0BH0kbMcQgETv88glfZ6kXeEfBuyv3e7J+0zdT44dpzXVnLkCw75wvP7Xo8M\n84OaxX5Qvy8hxP44kEG8NNvOWw75ddnkO93fLlnJFLg27Z3nfpRIb/j8kf4Q48Xz3H1dgU2vY9su\nX/jWFLGHS9i2i8/UePBshZfPh8kX3DXtQxdWcmtesCtnZUopsnmHUACU0ryymMd7uTqVIG853rE0\n5e1pr8+E1wBD0wj4t//x72VP+h0Xhrgxs8Cd2SXyBQefqRMKHshfOSGEaIoD84q66Wx7h9nklQqW\nw837i0zGk0zNLrFum5vukI8rI4OMj/7/7d1rcFz3Wcfx7960F0l2LUt1rCbxRZKf4qS23BRoKDjO\nUC51KLSd9hVTmqRhygCdzLRDgUynzDBMWqYDQztABwqNKFN4EU96oaYlTMAJZKYXp1bi1PY/kuNL\nsVsnsmTLkiVH0h5enLPKSqtdnZWl9Tnr32dGM3v2rHVOnuzuo//16ZovcbmcF0+PcvaVK8zOFkml\nkhQ9ePncOLt7utiwLsflydcAv5u7sKgVv7hyVT4LdvsG3vpTt9CzqQ2AQ4PnGJ+cIZFIkMn4hSvA\no5BNkyBLLutPjltPlnzwuPR8tSpXK928pfQHwOETFzg0eH6+pvZqzTCvZ6lcXGl2vojUEtskXvQ8\nrk7NBuUxV6e1Df7SspPn/Y1Yfnh6tKLgSCad5I6tHezZ0cn27vWkQnbJF+eK5LMZ1hUypFLJ+Xst\nSaUSPPz+3XzuwPOMXpmmkE1za1fbkl/YnucFrWuPbbe08wv9b5rfzrD0O8bGp8nn0nR3trJr+0ZS\nqSR3bts4P1O92uPVnjiVSSe5+87NvO3Nm1Z9K9Z6lsrFlWbni0gtsdo7/drMLFPX5uaXgJXWbV/3\nRTx/QtjgsD/OfeXqwprTpYIj/X2d7NzaQTYTrnyl53kkSJDPpWkvZEgGe5l/6eAxjp4aZWbG72J+\nS89GHty/c0FhEqj8wq72b//wQz+zoDrVzTCb+XvHL/Dv3zmzoBTn/rdvWbbHQPs3h6M4haM4hadY\nhdNUe6fPFT0mrs4s2dqudwnYUi5PXOP54YscGXqVC2NTFee7O1vp7+1kd+9G2oP9yMMoFotk0ikK\nuUzFBi6ZdJIH79vJ4RMXOPWTK2y7ZV3oJVeZdJJdPZ2cPHeZRDZNPpfm/MgkA988RndHfk1b1CIi\nEj2RTuLnXrnCxLTfKr6ese1y06/N8sNToxwZGuHU+eoFR/p7O9lUVnAkDL/LPE1rIUtLunprvdTF\nfPedmxc8H2Y5USrlzyJPJBJ4nsfF8Wt858Ufk80kOXDoJPlsasmlYs1GY8UiIhFP4qmQBUWWM1cs\nMvSjyxwZGuH4mVFm5xam7mwmxVu2d9Df18XWze0VBUdqKQ1HFLIZ2lszdf3bxcIsJypPXpPTM+B5\ntBdaGJ+4xuWJa3hkaVtiqViz0VhxfNwMwzsiN0qkk/j1KBUcOTL0Ki+cvMjVFRYcqaZY9EgnExTy\nLbTm06syNh9GefIaPncZd3ZsVXoo4mg1S57K2tBmNSJrq+mS+Oj4NEeGRhgcHuHiEgVHbt/URn/v\n8gVHqpkreuQyKdra0qHWWdcjbBdxKXnt6eti4FvHeeXSFLlsan7ZWLWlYmGp5SSrRZvViKytpkji\nV6dnOPryaM2CI3v6uujv7axZcKSaUpd5riVNeyFNOhVudnq96u0iLr3+5IUJxsenVmVim1pOIiLx\nEdskPjNbxJ0dY3B4ZMmCI4Wg4Ej/ooIjpSIjADu3dJCukZzCdJnX02oN89p6u4gz6eSCdeLX08KZ\nmS3y+KFhXvq/S7TmMmu2RWqt66sHoLloAqLI2opVEi8VHDkyNMKL1QqObNnAnt5O+pYoODI7W+SJ\nZ07OLyc7cWaM9+3tqUjkxaJHNkSXeT2t1qi3cEv3N/SjS4xPvsb0tbkV9Vpc7/WjGh9ZGU1AFFlb\nsUjir4xNMTj0KoPDI1yaeK3i/LbN69jT18md2zvI1Ui6x86McmFsar5FfWFsimNnRtnV0znfZZ7P\n+huzhFmHXs94X9THBkv3V8hnmAoqvE1Oz7Dj1jc0pOUU9fjIymkCosjaiXQSf+r7Z3n2+fOcH1l5\nwZHleEXPr+CVyzR0lnlUJRJ+6dGrUzP093XxgX29ajmJiERUpJP4408NLThuz2fY3dtJf19n6IIj\n5XZu6eDEmbGgO91j88ZW3rGrm7Z8/bPUob7xvqiPDS6+v77b3tDQBB71+IiIRFGk905/98e/7rWk\nk9yxrYP+vk56utdXFA6ph+d5zM0VGT53mVw2zV07wm13WstqT2xbidXak/hGTyxrxPW1f3M4ilM4\nilN4ilU49e6dHukk/rWnh73bNhZoCVlwpBqv6JFMJmjNpWkNtixtJvpwhKdYhaM4haM4hadYhdNU\nBVB+euctjI5WjoeHVSwWaUmnaG3NkM+urMtcREQkqiKdxFeqWCySb8nQVsiSqVGIREREJM6aJol7\nRY9EIkEhl6atcH2FSEREROIg9klcXeYiInKzimUS9zwPz/PIZzO05dVlLiIiN6dYJfH5Web5DG1N\nOMtcRESkHrFI4qXyn61tKXIt6jIXERGBiCdxLyhEspblP0VEROIq0kn8TW9sY2TkRt+FiIhINEW6\nsoXGvEVERKqLdBIXERGR6pTERUREYkpJXEREJKaUxEVERGJKSVxERCSmlMRFRERiSklcREQkppTE\nRUREYqqhO7aZWRL4W2AXcA14yDl3spH3ICIi0iwa3RJ/D9DinPs54I+Av2jw9UVERJpGo5P4O4Bv\nAzjnvgu8rcHXFxERaRqNTuLrgPGy47mgi11ERETq1OgqZuNAe9lx0jlXrPH6RFdXe43TUqI4hadY\nhaM4haM4hadYrb5Gt4KfBfYDmNnbgRcafH0REZGm0eiW+FeBXzKzZ4PjBxp8fRERkaaR8DzvRt+D\niIiIrIAmlYmIiMSUkriIiEhMKYmLiIjElJK4iIhITDV6dvqytL/68szsZ4HPOOfuNbNeYAAoAi8C\nv+ecu+lnK5pZBvgSsAXIAn8GHEexqmBmKeCLwA7AA34H/7M3gGJVwczeCDwH/CJ+fAZQnCqY2Q+A\ny8Hhy8CnUawqmNkfA+8GMsBf4y/FHiBknKLYEtf+6jWY2Sfwv3CzwVN/CTzinNsLJIDfuFH3FjG/\nCbwaxOVXgb/Bfy8pVpV+DSg6534e+CTwKIrVkoI/Dv8OmMSPiz5/SzCzHIBz7t7g58MoVhXMbB9w\nd5Dv9gHbqfOzF8Ukrv3VaxsG3of/Pxfgrc65Z4LH3wLeeUPuKnoeBz4VPE4CMyhWS3LOfR34SHC4\nFRgD7lKslvRZ4AvAj4NjvaeWthsomNl/mNlTweZeilWlXwaOmtnXgH8DvkGdn70oJnHtr16Dc+4J\nYLbsqUTZ4wlgfWPvKJqcc5POuQkza8dP6J9k4ftdsSrjnJszswHgc8BX0Puqgpndj9+782TwVALF\nqZpJ4LPOuV/BH575yqLzipWvC7gLeD9+nP6FOt9TUUyO9e6vfrMrj007cOlG3UjUmNltwH8BX3bO\n/SuKVU3OufsBA/4ByJWdUqx8D+DvOPnfQD/wT/hfwiWK0+teIkjczrkh4CKwqey8YuUbAZ50zs06\n514CplmYtJeNUxSTuPZXr88RM7snePwu4JlaL75ZmNkm4EngE865geBpxWoJZvbBYHINwBQwBxxW\nrBZyzt3jnNvnnLsXGAR+C/i24rSkBwjmM5lZN34yelKxqvC/+HN2SnEqAE/VE6fIzU5H+6uHVZqt\n+HHgi2bWAhwDDty4W4qUR/D/ov2UmZXGxh8GPq9YVTgADJjZ0/gzZB8GTqD31XI89Pmr5h+Bx8ys\nlIAewG+NK1ZlnHMHzWyvmX0Pv1H9u8Bp6oiT9k4XERGJqSh2p4uIiEgISuIiIiIxpSQuIiISU0ri\nIiIiMaUkLiIiElNK4iIiIjGlJC7SBMxsvZl9dZnXPBbsYlfrNYfKNppY6vxWMzta5dxBM9tsZveb\n2WPBc6fN7PYw/w0iUr8obvYiIvXbgL8VaC37WP4Pd4/XNxKqi3PuPgAzK/8d2ohCZA0piYs0h88D\n3Wb2BH41pI/hJ9DngN8HPgp0AwfNbC9+LeyPAfng5yHn3P+EvFZbcJ0e/D2yP+ycGzez08A9VBYG\nEZE1ou50kebwUeA8fvnVR4C9zrld+NWk/sQ595ng/H78ggofAe5zzvUDfw78QR3XuhV41Dm3GziF\nXyEO1OoWaTglcZHmUGr53gN8wzk3Fhz/PX6re15QFfC9wLvM7E+BDwGtdVzrqHPucPD4nxf/fhFp\nHCVxkeaSZGFXdpJFw2Zm1gYcBrYAh/C74uv5LiivZ59cdCwiDaQkLtIcZvGT9SHg181sQ/D8b+PX\nVC+9JgPswC83+ung9fuBVB3X2m1mdwSPHwT+83puXERWTklcpDn8BDgL/BXwKPC0mR0H1vH6mPU3\ngYP4Y+KDwHHgaeAFIOwyMA9wwKNm9gLQEVyvdK78R0TWmEqRioiIxJSWmInIAmbWAxyocvoh59xz\njbwfEalOLXEREZGY0pi4iIhITCmJi4iIxJSSuIiISEwpiYuIiMSUkriIiEhM/T/3LiLnZVv40gAA\nAABJRU5ErkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 4 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The higher-level function `lmplot` can draw this plot separately for different parts of a dataset." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.lmplot(\"total_bill\", \"tip\", hue=\"smoker\", col=\"sex\", data=tips);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nN/ISEZHZ7XvxDD997gyFumGV61Z18Om7NnLj2u5Ff08YhnR3JLWPiohIi2n6BODf/NrHuDCam/N1\n5xzZdJxOdTuLiCzKD/afmnzcm03y4J0buOPGFYseMllt9fdZqVZ/EZGW1PQJwNwTfEPSqbhWmRAR\n+RDSyRj371rHXbf1L2plnwkudHR1aPd0EZFW1vQJwExhGJJMBHR3qOVJROTD+Cd/ZwfdqRjp5OJv\nAaFzJOM+vZ1pfF+NLiIiraxlEoCJ5eW0qYyIyNJsu2kl5wYuLfzGGuccPR3aS0VEpF20RAKgcf4i\nIsvPhSHJRIyerJZUFhFpJ8ueABhjfOBPgG1AAfiStfbtud6fScaI92U0zl9EZDk56O1KamU1EZE2\ntPiZX1fPLwAJa+3HgN8Bvjnfm3u7Uqr8i4gsk4mx/v19aVX+RUTaVCMSgI8DPwSw1j4P7GnANYiI\nSB3nHJ4HK7tT9GbV8CIi0s4859yyntAY8y3gf1lrf1h7/i6w2VobzvGR5b1AEZHmdVVq5WeHxly5\nMhVawzCkM5OgN5u6Gl8vItJqItfi0YhJwBeAbN1zf57KPwADAxev7RU1wKpVWZWrhahcraWdy3W1\nDA2NVXdRDzx6O5OU8yUG8qWr9v2N0K6/d2jfsqlcraWdyxU1jRgC9DTwGQBjzN3AoQZcg4hIpLkw\nJJuOs7onQzympZVFRKKkET0A3wc+aYx5uvb8Cw24BhGRyEolYqzq1WaKIiJRtewJgLXWAb+23OcV\nEZGq7s4kxVyx0ZchIiIN0oghQCIiIiIi0iBKAEREREREIkQJgIiIiIhIhCgBEBERERGJECUAIiIi\nIiIRogRARERERCRClACIiIiIiESIEgARERERkQhRAiAiIiIiEiFKAEREREREIkQJgIiIiIhIhCgB\nEBERERGJECUAIiIiIiIRogRARERERCRClACIiIiIiESIEgARERERkQhRAiAiIiIiEiFKAERERERE\nIkQJgIiIiIhIhCgBEBERERGJECUAIiIiIiIRogRARERERCRClACIiIiIiESIEgARERERkQhRAiAi\nIiIiEiFKAEREREREIkQJgIiIiIhIhCgBEBERERGJECUAIiIiIiIRogRARERERCRClACIiIiIiESI\nEgARERERkQhRAiAiIiIiEiFKAEREREREIiS2nCczxnQDfwFkgQTwm9ba55bzGkREREREomy5ewD+\n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\nTkSuQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 5 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also use the `jointplot` function to show the marginal distributions of the two variables." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.jointplot(\"total_bill\", \"tip\", tips, kind=\"reg\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nOAkhhCg5/z/zDpmcT6WGqAAAAABJRU5ErkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 6 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `interactplot` function can be used to help understand complex interactions in your dataset." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.interactplot(\"age\", \"fare\", \"survived\", titanic.dropna(), logistic=True);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n29wAJQAAAABJRU5ErkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Linear models with categorical variables" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The low-level `barplot` and `pointplot` functions, and the higher-lever `factorplot` function, can draw similar plots in cases where you have categorical predictor variables." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.factorplot(\"time\", \"pulse\", hue=\"kind\", col=\"diet\", data=exercise);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 8 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.factorplot(\"sex\", \"survived\", hue=\"class\", data=titanic, kind=\"bar\", palette=\"Purples_d\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n/X+KkIbQizkWHQAAAABJRU5ErkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 9 - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Linear relationships in large datasets" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To explore many relationships simultaneously, you might want to look at scatterplots for each paired variable." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.pairplot(iris, \"species\", size=2.5);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAx8AAALJCAYAAADVtJ4jAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3Xl8VOW9+PHPJJOFkCEkZIAgAQTJk7CGsFmlAalLW+u+\nQKDe1lJEbb33V9rbq/TXltvftdpL7b3a1gXELrcSwH2remtVQK2KrMrygKASJWAiAYaErDO/PyYz\nzL5kzmzJ9/168SJnzjnPPDPznOfMM+d7vo/J4XAghBBCCCGEEPGWkewKCCGEEEIIIfoGGXwIIYQQ\nQgghEkIGH0IIIYQQQoiEkMGHEEIIIYQQIiFk8CGEEEIIIYRICBl8CCGEEEIIIRLCnOgnVEplAA8D\nZYAdWKy11h7rfwAsAhq6H1qitd6X6HoKIYQQQgghjJXwwQdwMdBfaz1LKXUhcCdwrcf6KuAGrfW2\nJNRNCCGEEEIIESfJCLs6DRQopUxAAdDus34qsEwptUkpdXvCayeEEEIIIYSIi2QMPt4EcoG9wEPA\nb33W1wJLgLnALKXUpYmtnhBCCCGEECIekhF29WPgTa31T5RSw4FXlVITtNauKyD3aq1PAiilXgCm\nAC8EK8zhcDhMJlPcKy36jLg2JmmvwkDSVkU6kfYq0oU0pDhLxuCjP3Cy++8mIKu7Hu1KqQJgp1Jq\nHNCC8+rH6lCFmUwmGhpsMVXIarXEVEay95c6GLO/q4x4MqK9+jLidUuZ6VlmPBnZVo16/alWjpFl\n9YU6xZP0rVKmkWWK+EpG2NUK4Fyl1Cbg78AdwBVKqcVa6xPA7cBrwEbgA631S0mooxBCCCGEEMJg\nCb/yobU+DlwVYn0tzvs+hBBCCCGEEL2ITDIohBBCCCGESAgZfAghhBBCCCESQgYfQgghhBBCiISQ\nwYcQQgghhBAiIWTwIYQQQgghhEgIGXwIIYQQQgghEkIGH0IIIYQQQoiEkMGHEEIIIYQQIiFk8CGE\nEEIIIYRIiITPcK6UygAeBsoAO7BYa6091l8G/BToBB7RWj+c6DoKIYQQQgghjJeMKx8XA/211rOA\nXwB3ulYopbKA3wAXAbOBm5RSg5NQRyGEEEIIIYTBkjH4OA0UKKVMQAHQ7rGuAvhQa31Ca90BvAFU\nJ6GOQgghhBBCCIMlPOwKeBPIBfYCg4DLPNYNAE54LNtwDlCEEEIIIYQQac7kcDgS+oRKqWU4w65+\nopQaDrwKTNBatyulJgJ3a60v7d72N8AbWusnQxSZ2BcgejtTnMuX9ppErQ2NAORai5NcE0NIWxVR\nSXL7l/bah6VZ3xvvttrnJePKR3/gZPffTUBWdz3acV4NGauUKgSacYZcrQhXYEODLaYKWa2WmMpI\n9v5SB2P2d5URb7HW0ZcRr7svlNm29R0OPbQSgBFLbiKnambMZUYqXmXGm1F1Nur1p1o5RpYV7zpF\n2v7jWad4S5fjtq+V2ZO2F6l07Vv7umTc87ECOFcptQn4O3AHcIVSanH3fR5LgZeBt4DVWuv6JNRR\nCNGLmGwnOPTQShxdXTi6uji0chUm24nwOwrRC0j7F8kibU8EkvArH1rr48BVIdY/DzyfuBoJIYQQ\nQgghEkEmGRRC9HoOSwEjltyEyWzGZDYz4qbFOCySy0L0DdL+RbJI2xOBJOOeDyGESLicqpmUrSgH\nkJOf6HOk/YtkcbW9fEsuNnKSXR2RAmTwIYToM+RLl+jLpP2LZHFYCsi1WrAZfHO4SE8y+BBChOS6\nOTDYF5dw613btNIGSf7VK5K6CtEbRNPWTY1HnNsWD5VjRMRFuHNAoHbn2S5F7yKDDyFEUOFSJEaS\nQjGeaRajkSr1ECLejr76Ogd+dz8Qvq23bniFujW1AJTOu55Pn3wKR0cHI5bcBJdcmJD6it6tJ+cR\nr3a5oIbc2dIWexO54VwIEVC4FImRpFBMlTSLqVIPIeLNZDvBgd/dH1FbNzUeoW5NrXvbuvWPUTB+\nnHs/18RwQvRUj84jR+q822XtWvdVENE7yJUPIYQQoi/q7Eh2DUQfJT/+9G1y5UMIEVC4FImRpFB0\nWAooXVDj3qa0Zn5SYskl3aPoKxyWAsZ8/9awbb1t6zt8+Kv/ZNhVV545Pq+/jhO797j3y7UWJ+EV\niN4kUN/bemAf+360lH0/WkrrgX3+ffPQUv/zhtz30avIlQ8hRFDh0nOGW2+yneDTxx5n4ORJAHz6\n+BOMrZqelC/+kmpU9BVD5s4hc9Q5QPDj0hXqUv/XFxl2xWXkT6nCMbSUsdPPDbqfED3hmWr31KlW\n9v1wKY6uLgAOPfgQZb++h7IV9wBn2l3u7AspGz/B+ZgMPHqdhA8+lFLfAr7dvdgPmAwM0Vqf7F7/\nA2AR0NC9zRKt9b5E11MI4RTuS0jY9R0dNG3ZCoDJnNzfO+QLlegrIm3r9pYWDj/zHGWz5kS1nxDR\ncKXaPWX7NMDKwO1OBh29V8LDrrTWf9JaX6C1vgB4D7jNNfDoVgXc4NpGBh5CpC8JdxIi9chxKZJF\n2p6AJIZdKaWmAeO11t/3WTUVWKaUGgq8oLW+O/G1E0IYRWa3FSL1SBiiSBZpe8LkcDiS8sRKqSeB\ne7XWG3we/ynwe8AGPAU8oLV+IURRyXkBorcyxbl8aa/CKNJWRTqR9irSRbzbap+XlCsfSqmBQJnv\nwKPbvR73f7wATAFCDT5oaLDFVB+r1RJTGcneX+pgzP6uMuIt1jr6MuJ1x1JmpDMiJ7uevbHMeDOq\nzka9/lQrx8iyjK5T40FnbH0svywbXad4S5fjti+UGei8kIr1DFamiK9khV1VA3/3fVApVQDsVEqN\nA1qAucDqBNdNCBEhmTVciNQTzQznQhhNzgsinGTN81EGHHAtKKVqlFKLtdYngNuB14CNwAda65eS\nVEchRAgya7gQqSeaGc6FMJqcF0QkknLlQ2v9a5/lWp+/a/12EkKkjUhDsYQQCdB2GhNyPIr4kQGG\niIbMcC6E6JFgKRPbtr7jnr22bes7ya6mEH2K7wznpTXz2b98uRyPIm48+/yAM5bLoFf4kBnOhRA9\n5psy0fOSO8ChlasoW1EuJx8hEsg9w3nbafYvX469tRWQ41EYz6/PDzJjuRCeZPAhhIiJnFyESD0O\nS4Ez1KqjI9lVEX1NkBnLhXCRsCshhGGChWKZbCdobWgMup/JdiLmmGEjyhAi3fi2e9exZjrlfGzE\nLTdLCIyISai+1WEpYMQtN1M4YxqFM6Yx4uYlCWtj4c4rInXJlQ8hhKF8Q7HCpV00Ii2jpHYUfZFv\nuycjg0MPPAiA9YI5NL7xJqXfuZGyX98jv0aLHomob7XbOb5lGwADpk1PnXqJlCVXPoQQhnNYCvzu\nAQmUdtGItIyS2lH0RYHa/cl33nEvN7y+gYLx4zi0cpUMPESPRNK3JqP/lT4//cngQwghhBBCJIyE\nyfZtMvgQQsSNw1JA6YIar7Sfnr/ABrtHJNrnkNSOoq/xbffWC+aQXVR0ZnnObE7s3iPHg+ixSPrW\nnvS/saZjlz4//ck9H0KIuDHZTvDpY48zcPIkAD59/AnGVk33OlH43iPSE0aUIUS6yamaSdl/jOTz\nJx6n4fUNAAysqqS0poY2k5nCSy+X40HEJJK+NZr+16h07K7nzLfkYiMnqn1F8vV48KGUmgH8CCgG\nTN0PO7TWc8Ps9y3g292L/YDJwBCt9cnu9ZcBPwU6gUe01g/3tI5CiORzdHTQtGUrACZz4C7nVH/n\nRdj+sTyPfMkSaaDZbgOgf4bFmAJz+nF86zb3l7njW7cz+qbFtMoXMhGhcG0ykr41Gf2vw1JArtWC\nrcGW8OcWsYnlysefgd8CuwFH92OO4Js7aa3/BPwJQCn1O+Bhj4FHFvAbYBrQAryplHpWa/15DPUU\nQiSJ6/L4oZWrAAJeHt/RtIPV22sBWFRZw+TCyQmvpxCJEI+2HugYy7UWyxcyEZFE97+RnBNE7xfL\n4KNFa/37nu6slJoGjNdaf9/j4QrgQ631ie5t3gCqgcdjqKcQIolCXR5vtttYvb2WLocdgNU71nJX\n9WjjfhUWIkXEs61L2KHoiWT1v9JeRdSDD6XUCJxhVtuUUkuBp3GGSAGgtT4UYVHLgOU+jw0APNMf\n2ICwLdNqjf1AibWMZO8vdTBm/0SIRx1TvszusnJ9Hja1+M++nG/JpTgv8udO+dcexzLjzcg6G1VW\nqpUTaVmRtPWY6uSzbyq+T/GWLsdtqpQZrk3GtZ4p2LeIxOnJlY+NnAmvmgvc5rP+7HAFKKUGAmVa\n6w0+q04Anq3IAjSFK68hxsvLVqslpjKSvb/UwZj9XWXEW6x19GXE6050mV2NdQBkFpeyeMoC9h50\n3hNSProKR3MWDc2RPXc6vnYjy4w3o+ps1OtPtXKiKyuLRZU1rN6xFoBFk+fjaM7iY9thAEYNGUbj\nwU8B/1+DXSlJPR8P9Fj0dQrN6Pcp3tLluE2dMoO3yXxLLo7mrLjVM1T7dTE1HnFuUzw0ojKNIoOZ\n+It68KG1HgWglCrSWh/zXKeUGhVhMdXA3wM8vhcYq5QqBJq7t1sRbR2FEKmrecPLHF6zHoBhC66n\nrLCY3Ic2ATBiSQVUJbN2QsTP5MLJ3FU9GnDe3OuKt8/KMPN/875C48N/AbxnbA40k7PM7iyMEqxN\nQvzuAYmk/bZueIW6Nc56lC6oIXf2hYbXQyRP1PN8KKVKlVIjgY1KqREe/8YAL0VYTBlwwKPMGqXU\nYq11B7AUeBl4C1itta6Pto5CiNTU1VjH4TXr3TPTHq59jM7PPpWZakWf0T/DQv8Mi1e8/fT+Y2h8\n+C9+x0HAmZwbj8jszsJQgdpkl8PO6h1r3ZmwjBLRrOmNR6hbU+vepq52rfsqiOgdehJ29QtgDjAM\n8Ayb6gSej6QArfWvfZZrPf5+PtJyhBDJF0nqUJPtBK20uXNye63LyKBwqvNyx4nde+JRRSFSTpu9\nlcqh49l5NMo23+kfp48psjAWIQJx9eGB+udkMWVlMbDSedVFzgu9T0/Crm4EUEr9m9b6V8ZXSQiR\nLiK5RO91if17tzJswfUcrn0MgLMWziODDI5v3wE4L6/LlyfR23keN9WjzuW9z3ZwyXe/SePqRwHv\n9KOeaUmtc2bz4a/+k9IFNdTVOuP0R9y8hNZ9e73CWLhEQlREZHz78MVTFrDKtTx5vuGZrxyWAq/2\nW1oz33/W9OKhDL/6KurWOcNzS+ddH/K+D5F+Ykm1m6uU+pnHsgM4DezRWr8QW7WEEKkukjSNfrPZ\n3v8AZffcw+jx4wAw5wxg34+WutfX1a6lzGcGdCF6E9/jZuMn77B81lLKSkZTVO68AujZ/r1mMd+w\nEUdHB58+/gRl//EfkNMPwOsYOrRyFcVVlSCTDIowAvfhy7irellcbjgH5znh08ceZ+DkSQB8+vgT\njPXp8022E9StW3/mvLD+McqmnyvnhV4klsHHGGAsUIvzat01wElgllJqttb6xwbUTwiRYK4QKaO+\nvPhdPndAW5bzAn+oDiiSTCdCpKpg4Yie4VYd9k6yMpxHQWPLMfeXK78Qqu5ZzOkOUczIzT0TfmVK\npWAZke5MhJ8tOlDbNjrsL1DYle9zGH2uEokTy+CjHPiy1roNQCn1ALBRa32uUmonIIMPIdJMNFl0\n+mdY/NI0+n7RclgKKLnuKq/sVg17ttD0sDO8pPC7CwLOdiuZTkQ6CxaOGCjc6jJ1Ecs33QPA4ikL\nKDt42u8YdFgKGHHLzZzeu5fGN96keNb57Pv5cgDOuvpKBl90IZ+/4kwgKTOci0j1z7Awb8LlrNv1\nHAA1E65g/4mDIUNpA7XtaM4bDksBw6+71qt/9wu7CrBN60cfcuj39zuf45abwW6XjG9pLJbBx0Ag\nC2jrXs4B8rv/lp9iwmhvb6eu7hMAmpryOXbsVMDtSktHkp2dnciqiT7KL0Rq5SrKVpSH/CXLN02j\nr3bbUXd2K4DDtY8xcEqle7lpdS15//nvlK1wfvlyWAq8Mp1AdyjW+AlyBUSkhWDhiIBfuNVPZv0z\nd2661/3Y3oNbyX1oU8BjMHdMGYceeJCBlZNpeO119zafPfUMw664jIFVlQy++lo5TkTEmu02ntz9\nVyYNqQBg3xcH2HL4/aChtIHa9q+nDo3qvGGynfDv3wOFXfls43neOPnuOxzfsi2qc5VILbEMPn4H\nvKeUeg7IBL4O3KeU+j/ATiMq15vV1X3CWz/4Z0ry8vgoyDb1LS2c91/3MWbM2ITWTYhoxHxDosOB\nY4CcNETfkxXTKdiD3c7xrdsZXHODMeWJPqPD3sm2+g8AmDpsYpJrI/qKqOf5cNFa3wdcDxwGPgau\n0VrfD7wA3GhI7Xq5krw8RuRbgv4ryctLdhVFH+KwFDBiyU2YzGZMZrNXxp1oNdttNNttZFuGMGjR\nQneZgxYtwFRV4V4eeOM88gqGe9ejeCilC2rc25TWzJdfc0Xa6J9hYfGUBUwdNpGpwyayuLKG/hkW\nTMC3J19PZkYmmRmZLJo8nyKzlUWVNe7HykdXBT0GXcfnid17sF4wx73NsCuv4MjfX4vpeBV9h6tv\nBv+2OrOk0qs9+obSukJtPddnW4ZEdd6I5DwTaJsBM2e6lwfMmGnYuUokR49/dlFKZQEjgC9whllN\nU0pN1Vr/2ajKCSESK6dqJmUrysm35GLr4U18njHBi6csIENZOfnD6wFoH2xlfMFE8soUgN/AwyV3\n9oWUjZ8AyA3nIv3YHQ621+8CYOqQSbx/fCertq0hK8PMrdO+RWneWe4vda7QRXd2oSooW1EO+N+8\nm1M1k7Fjy8EEhRde5HywXz/OOb9avnyJsALdr+HbVv3ao49Aobau8wZEdsN5JNsH2qZsRZnPcmzn\nKpE8sVzzXYNz8LEH7+QIYQcfSqk7gMtw3jPyO631nzzW/QBYBDR0P7REa70vhnr2OZ73k4Qj95QI\nXw5LAblWS49uWPWNCd58ZAfb63e5lzM/y+Su6mX0DzLo8KqHDDpEGgoUF185ZJxz1uiudu5/70/c\nVb3Ma5/+GRaK8yw0NDuPuXC/HAOQL4MNEblA7XL5rGFB06V7tkdfgUJtox38RrJ9oCsivss9PVeJ\n5Ipl8DERqNBah8vK5kUpNQf4ktb6PKVUf/yzYlUBN2itt8VQtz7N836SUOSeEmGEcDOcD8jOZ4l1\nLgAPNb4WcBuZnVmI7uPAI9ep7/EQ6DiRY0cYrdluw9TSgfP34ciEa4emxiPYbF+AZVCP9he9SyyD\njz1ACc57PqJxMfC+UuppYADwrz7rpwLLlFJDgRe01nfHUMc+y3U/iRDxFOgyvmf63fPOqmL+YSuf\n3bMagGXzriPPd96DKNI0CpHqAqWgbuk6zfajuwGYN/6ygAP1o6++zsGHVlE863waXnsd8D4eAh0n\ncuyISAVql657jjwfO3jyI1ZtW+NcDpBqN5Bw7TBc6nRpx31PLIOP/oBWSn0AtHY/5tBazw2znxUo\nBb4BjAaexTlniEst8HvABjyllLq0N86Y3tHRQX1LS8ht6ltaGN7RkaAaCRGdYClFPWOC8z8/zr51\nj51JC7r+ccrGjccxtBToWXpfIVKd5zFgAn666VfudKZP7nmRykET/SZoO/C7+/3S6LqOB8D/OPmP\nkXLsiKgEul/Dt63evuHOoKl2AwnXh4dLnS7ngL4plsHHL7v/d3BmXo9IQrAagT1a605gn1KqVSlV\nrLVu7F5/r9b6JIBS6gVgCs4MWkFZrbH/wh9rGdHuf/Rof9ZMMpNXFPyyZssxMxcX9o+4bNd2TU35\nQdP3+ioqynfvl47vo9H7J0I86pjIMlsbnIeqyZJLnjmX64qcv1I91vQu+ZZcivMs3ZfsIetYpt/+\nWeZMclzTA1ly/WayzbfkkhvF60n39zOVGVlno8pKtXJ8y2psOQbAqLxhAHzcVOeVzjQzI9N9nLi0\nuqfL8pfV1kxO8SBMWVkUzpjOgHEVdJ48SU4//3v1PI+dVHyf4i1djttklunqmz3bnxXn366268m3\nrfpqpS1gH+6aAq4jx/87Tk5OFlkRnANc55pca3HI15RObVQ4mRyOqG7Z8KKUmgVMAP4IzNBab4xg\nn0uBf9FaX6yUGgZsAJTW2q6UKsA5R8g4oAVYD6zWWr8UokhHQ4w3G1mtFmIpoyf7Hziwn3//xwos\nwwYG3cZ2+Dg//9K/RnRPhmcdDhzYz0c/uT1s2NWhUzbOvvNuxowZG/N74FuHdNy/u4x4T5AZc3v1\nZcTrjrRMr8vj37uVzqZGr9nL+8++xCsU65ap3+Ts9z6jbv1jAJTOuw5TYRGHHnjQWcaSm+hqPsln\nf1nrVUas9YxFGpWZNm3VqNefauX4luUbhphhMvHHHev50oipbPzkXefjk+cHDGWxv7+Fgysfpvj8\n82h4fYOz7DmzaXzjTYbXzAd7F3V/cYbDWC+YQ+5ZZ2EqKODQ/Q8AzpnNXeEqKfo+pU17dUmjviCi\nMgOFyfp6s+Et94zn88ZfxvnW88KW6xtWZSos9JqN3HHyBHVr1navn49pQMGZc4Br/aPeYVmRhmKl\nad/a58WSavf/AFcAZwFPACuVUqu11itC7ae1fkEpVa2UehfnPCO3AvOUUvla61VKqduB13AOm18J\nM/AQQiSI7+Xxzs8+5fDTz3rNXj56/DhWv38mFOuBrY9y95xllI0bR5Y5k47c/uz74VKvS+yeM9ce\nrn2MkVWVZFuGJOEVCtFzwbJctXa188Ynm6kcOo4rz/kqRWZrwP2HzJ1D5qhznGl058zh82eeoWHD\nRhwdHdh2feA1o3PD6xvcM5qXrbgHkBt1RWjBwmQ9Q6p8ZzwPFCLoK9xs5IcefIiyX99D2bgJ5ORk\n0WYye50DfGcrd4VlSShW7xZL2NW3gZnA21rrBqXUdOBdIOTgA0Br/W8h1tXivO9DCNELOADH0FIK\nrBYaD36a7OoIkXAd9k62H9nNvLIrQ27njpNvPc3xrWe+kEWyjxBG8A0RNITDmTrdYrXQJucAQWyD\njy6tdZtSyrV8GuiMvUpCiHgJlxYXoN12lMb2Y5Bd5PW4w1LAiFtu5uS77wBgLh1B6TcXYtvjzOJj\nqRhHZnEpi6csYO/BrQCUj67yei7XzLWHVq4CnKEiLfbTmLZtB2DQdxa4r3q0244CuJclFaNIZZ7Z\nhLIyzCyuWkgGsKthHx32Tvds0b7HYPupz8lsbaOVwZhOtbq/qJUuqKGu1hmqYqkYx4Bp093HTck3\nLsVcWBhwLhyT7UT3PSQy8Zo4wzWb+eYjOwCYPnSy33mgf4aFJVULOdlQD8AAa0nANuspUJ+O2ey+\nE3jAjJlefbbv9gNmzPRq2yNuWoyjeKhfmQ5Lgd85QNp6+opl8LFBKXUPkK+UuhK4CXjVmGoJIYwW\nSbzvsXdf5YvVjwIwaNFCimb4JK+z2zm+xTkFz4AZMzH1739medp0AMoOnib3oU0AjFhS4Zy5x4Pv\nzLU5QH/lvMzvGmh41qN48Q3kZfSTVIwi5U0unMzd1aPZe3w/92/+IwDfnnw95QPHkpdh8ToGl1Qt\nZNj+LzB9WOdOrWu9YA6Nb7xJ6XduJHf2hZSNnwCcmXDz7CXQ9O5mjrz0MsPnXef3/JKyVITiO5t5\nICW6gezV6wDnOWCXep+Htjr74mDnDUdzMwOnVDr/Pn0aU79+fucFT4FnL/de9t3Gt22TkeF176C0\n9fSSEcO+/wrsB3YA/wT8FfihEZVKBe3t7Rw4sD/sv/b29mRXVYiwPON9uxx2Vu9Y6/41y6XddpQv\nVj+Ko6sLR1cXXzyyxn31Abzv+XB0dXHy3Xe8lg+tXIWp8Yj/Y92/VnlyWAq8fg3LtgzxuuLhWY/O\nrR9EVKYQqcAB/HHHevex9sedj+HA/xg82VBP55YP3Kl1HV1dNLy+gYLx49xt3FE89ExK0sYjfPTg\nSpo2v4e9tZW6R2sxNR5xP6/v8SnHifDU03PAyc/rQ+5jajxC3V8epWnzezRtfg/brsj6a99zgO+y\n52OB2vbJd96Rtp7Gor7yoZQa4bH4Yvc/l2HAoVgrlQoimSXcNUP4WWcFnrFTiGTzPVGkK99UjEKk\nqjZ7K5VDx7Pz6B467J3ux3Izct3bZGWYKcmwkDeilONbt4Ut02Q7AZ0dmLKyIroPRIhAsjLMTBpc\nBsCehv2GlevZP5uMuk+EM6G2SO6pXqcnVz424kyPuwF43effBmOqlRpcs4QH+xdqYCJEsu1o2sEd\nG+7kjg13cvDkRyyqrCEzI5PMjEx3/LmnbMsQBi1aiMlsxmQ2e91/AWdidV3rB5z7JWdaxe7l0pr5\nOIqH+pUR7T0avvXImjaJ4dddy/HtOzi+fQfDr71G7vsQKWlH0w6Wb7qH7Ud2MWvkDHLNOVSPnMGd\nb97L/hMHWTxlAbnmHBbbJ9Hxq4epf+FFhl11pbutW+fM5sTuPe4Yd3CGUu370VL2/Xw5w6++iox+\n/byONxff49OzDCH6Z1i4etzX2XlkNzuP7Obqiq8FPAcM/M58dxsaeOM8Bg4ZFvK84SgeyvCrr3L3\nz/nnnGNIO3S3+x8tpXXfXkbccrNXmQNmzpS2nsaivvKhtR4Vbhul1E1a65U9qlGKkBnIRTrzTau4\nanstd1cv467qZUDwG86LZswlv2I8OTlZOHxuOAfvOFyA/Sv/jYGTnbHDnz7+BCOqJnLn6VeZ/t3z\nAdjc8hq/sE8PeYN7qHoA5JDLvh8t9U7FWDVdTjYipfgecxs/eYdvjP0Kf93/Kh32zu7Upsu4a8r3\n+eRHy9whI/Uv/JVzlv+EAUOHcepUK4WXXu51Q61nytG69Y9RtvznYM4KeLO56/jMt+Rik5twhYdm\nu411Hzzrbp/rdj1HZfVEv1S797S/yWU/dN5PtProW/w0f2nI84bJdoK6devPtNG165ypdWNIAR14\n1nP/MstWlElbT1Ox3HAeyi1AWg8+gIhmIPe/lUqI1OQgdJYrl2zLEIo9Jm7yzTDi+cXI0dFB0xZn\nZiuT2dmddNg72XTSGRqVmZGJieAZtMLVAwCJ5RUpqtlu654xOvB54tOT9XTYO53hLkMraLO3kmE/\nkxTSlJWRl1XhAAAgAElEQVRFwcTxZJj7kWstxkYEYZLmLMjpF3S1w1JArtWCzeCJ10R6iiT0tuWE\nM/2tyVJAS2cra+r+DpxJtRvuvOEXFuuIT1bCQPeESFtPT7HccN6rZWVlYS0vYWjliKD/rOUlZGUF\nH5wIkSyutJ+hLpdHwvPSd9vWd7zWOSwFfpfosy1DmDfhcvfzLphwBa3vbeaTHy1j323/yrF3o0+I\nJ+EkIhW5whpve/6n7GjaEfCYm14ymVxzDrNGzmB7/S6Wb7qHl5reo2PexWT064d1djXHt2xj309+\nwtFXX/d7Dt+2X1ozn/3Llwc8HoXw5Rt6O9+jb543/jL6Z1hoeOdvfPbjn/PZj39O8+a3WVK1MKrz\nhsNSYHhYrPT5vV+8rnwIIZJscuFk7qoeDUR2xcNX4EvfZ2aZPdbZwN2tGzxCrDby484JXjPkmk7Y\n+GL1WncZXzyyhvyK8VHPYB4oNaMQyRJstuhAx9xPzith+aZ73Nu+9vHbHB82gcpbrsBx72PuY+PA\n7x+gbMU9fu3b3fbbTrN/+XLsra2AzPosQgsUejt16ESv2csnZQ/n+CNn+ufjf1jH2b9azl3Vy8i3\n5OJoDv/jaqAZzo0Ii5U+v3dLyuBDKXUHcBnOa9W/01r/yWPdZcBPcU5Y+IjW+uFk1FGI3qAng45o\n+IZYuR5zzZA7foR/XHpPyQlIpAPfYy7HI8uVS5ejiw9bj1AZYZkOSwEmwCH3GIoYdDm6Ipq9vH+G\nheI8Cw3NyQ1nkj6/90p42JVSag7wJa31ecAcYLTHuizgN8BFwGzgJqXU4ETXUYjeot121GuujmgE\nu/TdbLfRbLdRZLZ6hVjNG38ZQ8wlLJ6ygKnDJjJ12EQGDh5G8eIbKJwxjcIZ0yj+7jejvurh4npe\nIZItmrDGdnsb/zT5Wve21SNn8uEXHzF9WCWj/+U2d/aqs2/6Ls39M4K2cQlFEYE02200thzzezxY\nGKDnsqVghF/obF7BcOc9ep994i7rWGcDxzobAj6/w1LAiFtudvfxI25eIu1ShBWvKx9NIdZdDLyv\nlHoaGIBzskKXCuBDrfUJAKXUG0A18Hic6ilErxV2tvII+F769p0l/XzreVTMGgtAkdkKeM+iO33o\nZOfs5N2z3Y4IMNttJCKZnV2IRMowmagcMs79dyBvNrzFug+eJSvDzKIp8znHMpoOeztfO1bM4Z//\nloPAiBsWYtur+fiRP9J29WweydzFP026LmAbl1AU4SlcvxgoDPCu6rO9lq0zL6J/uTMUK69guPd5\n47sL0WfnUfv+0wDMm3A551vP86+I3R5yRnMhfEV95UMp9fMQ/34GoLUO9S3HCkwFrgVuBh71WDcA\n8ExtYwOkhxUiSuFmK4+Ga5bZYDPkFpmt7oGH7zZ7D26NedblSGbmFSKRmu02Vm1bw5b699lS/z6r\nttf6tcljnQ3u1KatXe2s3raWNnsrhafNHF75hzPHxF/W4OjowN7aSvb6vzO9/5iQbTzQTNCi74m0\nX+yfYfG6Kue7DM5Bh+uKh9d5Y/UaPv1kr/s51u16zu8KSKDZx2W2cRFOT658mHBm7XT9TfdypHNQ\nNgJ7tNadwD6lVKtSqlhr3Yhz4OF5VFgIfRUFAKs19rh23zKamvIj2q+oKL9HdYim/EjLdm3X1JTP\nRxHWw7P8eLyP6bZ/IsSjjr5lNrb7X4bPycmiOIrn9i3TmVLUW74ll+I8S8htfNMw5ltyyfWtb3fY\nQHGefyrecM+biPczVcuMNyPrbFRZqVBOsDYJzseL84pobjrut01Obhb5Zv97QKD7OJkymbH9hrK5\n5aDfsRWtVHifEi1djlsjyoykP45WoPOGr5zcLKyFZ56jlbaI+niXVH0/RWL1ZJLB5YEeV0plAGdH\nUMQbwL8Av1FKDQP6A64WvxcYq5QqBJpxhlytCFdgQ4w5nq0ecxq4HDt2KqJ9XdtFW4doyo+kbM/X\nEGnZnuUHeg+iFWsZyd7fVUa8xVpHXwFfd3YRgxYt5ItH1gA4ZxrPLor4uQO/l1ksqqxh9Y61ACya\nPB9Hc5bPTYne24wbM5WS60o4vGY9AMMWXI+NHK+87OFDqoI/rxGfua90KjPejKqzUa8/dcrxb5M7\nP9vDqm3O483VjudNuJx1u54DcKY27RyIDbyOzWE113HkmRewzq6m4bXXGbBlGz9ZtDDAsZXI12ds\nOa6y4i1djltjyoykP45SdhEDvzOf439YB0DhjfMZPjKfzA80cKYNe9c/h5LrrgrZx7uk9vvpXaaI\nrx7f86GUug24E+fgwXXVYw8wPtR+WusXlFLVSql3cYZ93QrMU0rla61XKaWWAi93r1utta7vaR1j\n0dHRQXOYBt3cYKNDso+IFOU5S3hPb/L2FUn6Xs9tsptb+HjNf7vTMB6ufYyRVZXu+gRLWepbdqxp\ng4UwmqtN5ltyaba1cvuGO/3acaB7oprtNu48/ao7RfXq1re5c9m/cvAny71SUg+qmCrhVSIkzzYY\nSVrccPxnOH+TnxUtRc0aA5xpw57abUc5vGZ90D5eiEBiueH8h0AlzgHIHTgzV5VHsqPW+t9CrHse\neD6Gehnm+Htn02YJPiPzadsx+FoCKyRElOJxAojky79rm3ZaEvq8QiSSKyXpKdvhoNsE+sLmm6K6\nKyc7bnUUvZvRaXF9Zzh3ELgNCxGLWFLtfq61PgjsACZqrf8IfNmQWqWArKwsBg2vYPCoqqD/Bg2v\nkBnORa/nm+K2o+FjOho+di+bbCeC3mCYbRnCoEUL3akcB31ngdeAyKiZ2I1ia+2kocm4AZPwZmvt\nxNbamexqxKzZbqOl+7hobDmGCbh1+rfJNeeEbceB2ny2ZQhjvn+rpNFNI6nSloOl2nUJlSbXV0/6\n42B9vKnxCKbGI1G9lnBCnWtEeonlyscppdQFwPvAFUqp9wDjZhQTQiSd5/0YS6oWMnLbYeprnbG9\nJQuuJ6ugiEMPPAjAiCU3kVM1068MV/hXTk4Wjmz/K4mpElK1dX8jDzz1PgC3XDWRqrHFSatLb9Rb\n3t8dTTv4887H+NKIqWz8+B0AqkfN5B+HtrBw4tWUDxxLXph2HKjNtx87xsApzmkHHSdPxvEViFil\nSlsOd7+cK9UzhEiT66MnoVy+Ib6tG16hbo2zXqULasidfWHErymYtq3vcOihlUDwc41IH7Fc+fhn\n4HLgRWAQzpvFf2dEpURitbe3s2/fPg4c2B/yX3t7e7KrKhLIN5Vj+2efUV+73p1Ssb72MU6+805E\nKRazLUMoPmtk0OcKlP4xkWytnTzw1Pt02R102R08+PT7KfGrZm/R0NTSK95f1zFRMbiMjR+/4z42\nNn7yLhWDy/jjzsfcqSDD8WzzpiN11K1ZS9Pm92ja/B51a9dhOlIXvxcieixV+opwqXY9Uz0HS5Mb\njDOUK3jIeSDZliHuKx51a2rd54W62rUxXwGRdL69T4+vfGitP1BK/RjnfR+/AK7TWtsNq5lImLq6\nT3jrB/9MSV5e0G3qW1o477/uY8yYsQmsmUgGk+2EM31iDGW4ToKuL1fNdlt3asjkhim6viRYcuM1\nv2pinqO38XzPUuX9823DwWRlmJk0tIKRBcMZNXA4p+ynvPaJqJyu9BuIiTNMBD7u492WszLMTBpc\nBsCehv1h12eQwdFOZw6fIeaSuNQpGNd5BXIS+rwiNcWS7eoi4E9APc4rKAOVUvO01u8aVTmROCV5\neYzIlxt6+zrfS9s1E6+k9oNnAOgYUkRJzfXUr30MgJKa62jNy8K0bTsAA2+cF3AW9AyTyS8FaTKE\nCpWw5Jq55aqJPPi0c/3NV07s0ReGVAnHSDXWwryg76/rPcsyZzDvwjL+8tJeIDXDWVwx8X9+/3Gq\nR53LP+q28KXSqWz8+G221++ietRM/vPN33P1uK9zvvW8CNJIO4+5T//nLwy76koOP+081kqvvw7H\n0NIEvVoRDd++4tarJrGv7rjfcR/vvqB/hoWrx33dK6zKc4BbZLZyRcUlPL7rBQDmT7yCncd2u5ev\nHX8p1YONv03XUTyU0gU11NU6UwCXLphP66d1YcNzQ5ZpKWDEkps4tHKVswy5JyrtxTIc/2/g61rr\n7QBKqWnAg8A0IyomhEgsz0vbAIdWruLwktlMGlIBwLpdz/H/qn/MqPEKgOZCC3e9+V/ulKGbWzby\n484JfqlzK4eMC5tKN948QyUAHnz6fe657cteA4yqscXOx/JzoLMrLs/Rl7neXzjzS7DnezZlTDF/\neWlv0t+/cOmfJxdO5hezRmMC5paez/JN97i33fjJu0waUsG6Xc9xzqyzw6aR9jzm6v/6IsOuuIz8\nSZNwnBXJlFkiWTzbMsDS+zZ5tdtf3nxe3PuCZrvNHVYFzv65snqi19Xmx3e94F5/qr2Z5/Ur7uXH\nd/8VVXROXK6A5M6+kLLxE7oX+rHvh0u9zitlK8qjHjzkVM2kbIUzoaoMPNJfLEdCq2vgAaC1fk8p\nFUukhhAixdjtXWyr3w040y4CtA8a1L2uNWn1ihdLrhlrYZ7hk1YJp3QdiLXZW/0GzA4gJyPwbOUu\n4cJiMMHAqilgt3N85/scfuY5ymbNMajWIp48B9CpxPO+j1jKiDVM1lHszD9ksp3wmwG9x2XKoKPX\niOWG87eUUg8opSYrpSYope4CDiqlZiilZhhVQSFEYjgsBX4pE8vHVLnTLi6urGH/iYPcseFO7thw\nJ/XNR7g9dzZTV73B1FVvcHtONUPMJcybcLl7n/kTLqeseLR7+dpxX0/KjeWuUAlzpglzpqnHYVXJ\nfo7exvM923WwkW9+tTzp759vutHqkTO488172dG0A3CGZLmOgY9tHzN/4hUe285kT8N+rh33dYaY\nS7h63NfZeWQ3O4/s5uqKr/m1/dZ9ezm+dRvHt+/AOruaETcvkS9YaSbQcV9syYl7XxAoLe7Bkx+5\n2+bBkx95rT+r/xCuGXepe/maiq8HvOrhat+3Pf9Td5uPhcNSwPDrruX49h0c376D4ddeI21cxHTl\nYyLOH3/+u3vZ1L38q+7lC2IoW3hob2+nru6TkNs0NeVz7NgpSkuDZxQSIhTfmZc3t7zG/xvwY+6q\nXube5g6PWZx3H9hC9iOb3JfTj/9hHQXjJvHk7r+6Q7VaOk7z4r5X3cvP7P1fJhSVJ2XSqkBhP+n4\nHL2N73tWVWZ1/50skwsns3zWMJ7e/xJvfLKZDnsnq3esZfmsYV6hVO/Ub2fXUc2lY+fS0PwFBTkW\nLh5TzV/3vcqEovKQYTG+YY4Nr2+g7NLLI86WJVJHoOM+EX2BZ1rcZlsrt3v0z6u213J39TJ3/91s\nb+Z/dj7BpWPnAvDi/lcpH+QddhUu5LAnTLYT7uxXAHW1aymrmi4DkD4ulmxXc3q6r1JqK+DKk3ZQ\na73IY90PgEWAKyfcEq31vp4+V28QSTaqjziTkUqInvKdedmBd8aqSMvYVv8BACMKhnktu0K3kiUR\nX2hl0BE9z/csVd6/nIxcth/Z5f4iFkyHvZO6k/XsPLLbvW2y27lIvEDtNhFt2TXD+SnbYb91nv33\nKfspWjpbeVb/DZA2KpKrx2FXSqlRSqm/KaU+VEoNU0q9ppQKe5ecUioXQGt9Qfe/RT6bVAE3eKzv\n0wMPF1c2qlD/Qg1OhAD/2crhzAy4/TMsLJ6ygKnDJjJ12EQWV9bQP8Pi3sf3Mn/56CpGLLnJa1bm\nbMsQv0v9nmFY88ZfZthVj2TNRp4qMxunM9d7eCqF30tXe8815zB12ES+P+1b5GbkumcyzzXncN7w\nqdw641t8+MVHVI861ysExnPbQLNFuzL4uI6fMd+7RX4NTiONtjYabW3JroZ7hvNw/fcQcwnXjf+G\nu41eN+5Sv7CrnsxwHo5vO5dMVQJiC7t6CPg1cDdwBHgUZ+rd6jD7TQbylFIvdz//Mq31Ox7rpwLL\nlFJDgRe01nfHUEchRLdAaT89Z8CtmXgleZn92F6/C4DpQycH3MdrZuYq/DKQBJq9uWLWWHJys+jf\nOdCQ15KslLaSSjd2nu/h3GmlbNr+Gd+9bHxKvpeTCyezcEIXf9yx3p1K9x+HtnDDpGs43dXG/Zv/\nDEDNxCuYXDSBr438CgAHT37E7RvuBODbk68POuu5Zwaf4tHDJdFBmtiws96dEvqbXy1n9qTEzpnh\nEiiteaj+Oyczm8oh4wBnMoRAejLDeTiudp5vycUm83wIYrvhvFhr/TKA1tqutX4YiGQ42wys0Fpf\nAtwMPKqU8qxHLbAEmAvMUkpdGkMdhRAEng33aGe91wy4exr3e22z+ciOgDPo+s5G7rAU+P2S5btN\nkdnKqEJj5i1I1gzDqTKzcTrzfQ9f21LHhDHFKfteNttt/HHHer+ZzLce/YDa9592P177wbO0emTE\nWrVtjXtduFnPAx0/InU12trcKaG77A4efXlvUq6ABOrTN9fvCNl/f/C5Zkv9+2ypf581HzwTdMbz\nnsxwHo7DUkCuNfV+YBDJEcuVjxallPvbhFJqFhBJ7s19wIcAWuv9SqkvgBLgs+7192qtT3aX+QIw\nBXghVIFWa+zZc3zLaGrKj2i/oqL8HtUh2vI/irDceG5fVJQf9nXG+lkke/9EiEcdw5XpTJvoLcsc\nfcxvviWX4rzo69/YcozGlmNYrT0/oR387DiAcx4OH5b8HKyF3mGHrpAs38cDlTn6LO8rMgHfzwAh\nXoGeN5h0aJu+jKyz1WoJ+B66tHV2MdpaGPQzMbpOkZQT6LgJJic3C2uhJfA+2V1YC8M/n+HvdwqV\nkwiJ6Ftt7f5zAOXkmAM+956PGgGoONv7S7cR9YymbQbjarO+jOivg0nG+U+knlgGH0uB54AxSqkd\nQCFwfQT73QhMAr6nlBoGDMAZtoVSqgDYqZQaB7TgvPqxOlyBsV6qtlotfmUcO3Yqon1d20Vbh2jL\nN7rcnmx/7NipkK8z0PsYjWTv7yoj3owOrYjsdWexqLKG1Tucs84umjyfIgYzb8LlrNv1HAAVxWOZ\nOmSSe5vpQyd7LS+aPB9HcxYNzdHVP5JZnsPxDHP4p69X+M2WTWeX13sQSXhUsNCJUO9nuOcNxoi2\nGajMeDOqzp6v/5tfLefRl53v+wVTS3ljx2dcMLWUn618m2suGMsTr+2no9MeMJzFqPcx8nK8j5vq\nkTP5R90Wvj3pOsoHjWFt97Ezb/xl9O8c2F2m7z4z+Nnff80/TbouZNs3so0k/n2KrKx4S0TfasnO\nZP5FinWvaADmXaiwZGf6bffqtsPU/s25Tc1FirlThgUts2f8+/QMk4ntR53zMgXqv1u6TrvXe7fZ\nM4zor4OJVz+Yjn1rXxfL4CMD530eLwK/BUqB4RHstxr4g1JqY/fyjcD1Sql8rfUqpdTtwGtAG/CK\n1vqlGOqYMO3t7bz55saw251/fjXZ2dkJqJEQ3gLdi3G+9TwqZo0FcN8I7hvv67tPNIxI3egZ5gDw\nPy/u4a5bzgs6G3kkM437lvnoy3sZf3YRxZbQ8ciSSjc2ttZO1r2yj8qywWRmQOGAHCaMKeb1rZ/S\n0Wln3SuayrLBvLfnaMSfSbx5Hjcm4Gsjv+Juv+VB7mUKlqo31rSlIvkabW08u+kAl1ePAeC5Nw4w\n+ZxBXu20/vhpav+m3f3L2lc0FWcXUjKwn6F1CXR/xl3Vzrw/rnbm23+rWc56B0r8EY9Uu0IEEsvZ\n8z7gxzivYpwEKoEngcdD7aS17gRu8Hn4bY/1tTjv+0grdXWf8J9/v5e8ov5Bt2k51szvRoxkzJix\nCayZEGcEOon4noRcqRtdVzhS8cTjcISejTzLnMGUMc6rHbsONhr63DLoiE1Hp5339hwFnJ/j+x82\nMnmssw0a/VkZJdgxUGS2Yi0M/MtrpKl6Rfrp6LRz6IjN/XcyheuvfZeTMceSEL5iueE8Q2u9AbgU\neEJrfQjo04mjreUlDK0cEfSftTw5GTGESBYjUjcWW3K8Zr5eeEl5yF/DLblm5l1Yxjb9Odv051z/\nlTK/AUO0ZQpj+M4Gfd74Eq6dO9b9WV01Zyz7Dh3rFZ9JPNKWiuQrtuRw7QVn2uw1c8b6tdOSgf2o\nuUi52/n8C5XhVz3iQdqsSJRYbzj/EfAV4Dal1L8AkidQCOHFiNSNsyeVMP5s582P4b6Q2lo7/UKq\nqsqsfgOQaMoUxvEMXQP47X073J/V+lc0/774XLIyM3rFZxIo1FGkN1trp19I1fSKwX79y9wpw6g4\nuxAgLQYeLvFItSuEr1iufCwE8oCrtdbHgKHAAkNqJYToVYxI3VhsyTH8C2k8yhThWXLNQcPX8nOz\netVn4pt2WvQdJQP7pdXAwyUeqXaF8NTjwYfW+lOt9S+01m91L9+htf7UuKoJIVJRoFnSE8F3ZvFg\nM5z7hvbcfOXEHt+nIbOZx5cJuOmKCYZ8VomUrGNAJI+rLzCyf0kV0p5FoqX3ESOESKh4pmEMxTd1\nbobJxO+f3Ole9k2la0RWKpnNPL627m/k4ed2MadqOFPUYACa21J/oOd7DFxonZXkGol48+0LTnd0\npVWbDSVZfbro22IJuxIeOjo6aG6wYTt8POi/5gYbHR2xTwwkRDIEmlE3Eb+WBZpZ/B+7j4SdaTxU\naE9PnlOugBjH9f5OGFPM3949xObdR9m8+yj/8+KepMwWHalAx0Bjy7FkV0vEUaC+YOeHjWnTZkNJ\nVp8uhFz5MNDx986mzRI8TvK07Rh8LYEVEqIHmu227tlzk3uzoeeX/Z6kznXt7zkAqT9+GkivG0B7\nO9dnm5lpIgNo7ejiVGsn+d2fW6DPMRap0r5F+srOzuDqC84B4JV3PwFwD0A871fyfczW2gkBQkWN\n5Bo8yH1GIpXJ4MMgWVlZDBpeQX7hWUG3OdX0GVlZcsITqSvUJXhXGkbPGXPjdYLzDHO47ZrJzLuw\nzGs2cpPJxDb9OQDzL1R+X0wDhUwFm3E4EFdct+ds5uke151KLLlmai5SPPvGAS778mie23SQL1ee\nxavv1fHu7qPMnVbK+JFFdNrtXp/jJTHOPBxriEmgY6A4r8g9x4LofXz7gu9fPZmGk62s7e5L5l2k\nOPDZSR5+9gPA2T/NnlTChp31Z/qsr1VQ0C8rZKioEaJt34ns04XwJGfTXqijo4P6lvC/rtS3tDC8\no0MGRAKIbHbbRKQO9Z2h/K1d9WzZ+7lX6txpFUOoLHPGXD/x+n4mecwwHGiG858tmhn1jMMym3n8\nNNraePy1/Sy5eiK/W7+DKWowr75X5/58XttSR0F+Nk+9fsDrc6yqGNLj5zRq9mZJn9v3ePYFp1o7\n+O0T3umhr5g9xqt/GjO8wDvd90t7mKIGe7Xle277sqH9Sk/bt7RnkQxJOaMqpbYCJ7oXD2qtF3ms\nuwz4KdAJPKK1fjgJVUx7ayaZySsKPahoOWZmeoLqI+Ij2hASIy7J92TfcOEGvqE1nmFWmZkmv+0z\nzSaGWfMB2HfomFcZRpJBh7FsrZ2YgM4uO5XKigkTVeWDycjw/4wTzffYCHWsyJe0vsfVF5xq7fAL\nA80wmZjWPTCONCzUhDEhhcc6G2huOk4sczxLexaJlvAzq1IqF0BrfUGAdVnAb4BpQAvwplLqWa27\n4ytERLKysrCWl2AZNjDkdrbDx+WqRxqL9hJ7uO3jdQk+XNaoQOs9w6xu+FoFY0sLWfPymRCGjg67\nO+xh/kWKIw3N3Pv4DgC+d/Ukv5Cp4UV51FykWPtK9z5pMuNwb+LKbvXlyrPYtP0zvlx5Fr9dvx2A\nC2eM4NqvjOWJV/cDcP2FZZxV2M/vc7QW5tHQ0LMQp1Dt2/fYyDCZWLVtjXtZMgAJl5KB/bj6grGs\n8wi7ysnOcIeB1lyk/PubixTFllz3NrdeNYl9dcdjzqb3ZsNbrPvgWefzTrxSQqhE2kjGz3qTgTyl\n1Mvdz79Ma/1O97oK4EOt9QkApdQbQDXweBLqmTIkjEr4ivYSe6TbGz27baAQKM9wg0Drf3nzeV4h\nC395aY9XmFVzSwdPvv6he/06n7CH+5/ayW9u+7JfyFS6zjjcGzQ0tfDAU++7w6t8w6z+vvmQ12f8\n1Osf8n9vnEHFwH6Ghr4Fat+Bjo3KIeNiDs8SvVPdFy2s8wjhXP+K9gqpWvuKZtI5g3j8tf1nQkNf\n28/dt5zv7Pvyc7CdamPpfZtiCsM61tnAug+edbfT2g+e4d9n/ZC7qpcBcjVDpLZkDD6agRVa69VK\nqbHAi0qpMq21HRjAmXAsABtQEK5Aa4w3IQYqo6kpP6L9ioryvf6PZHur1RJV+e3t7RGHUV1c2J/s\n7OyIynaVD/BRhNuGe69j/SySvX8iGFFHZ6iVt3xLLsV5gcuOZnsr3Y/lxVZHIGColSU/B2thXtD1\nOTn+3VKX3cF7e44CMKok/PuX7/kcHiJ97+PRjtKhbfoyqs6BJoP05fkZmzNNZJkzsFotWA2uk2/7\nDnRs+Ap1bBlRJ6PLMbKsdGq3iThuPz0Wvi3n5Jjp6LR7tWdLkD7JJdx6X85QK2/ZuWZGFQZPpBGt\ndOkH06mNCqdkDD72AR8CaK33K6W+AEqAz3AOPDxbkQVoCldgTy/Du1itFr8yjh07FdG+ru2i2b6h\nwRZ1+ZGGUZ061Q60R1S2Z/mRbhvqvQ70PkYj2fu7yoi3WOvolOV3id3RnBUi607w7X1j2022E+Rb\ncrGRE6Ss6PiGztDZxZ7uuOhiS45/VqnsTL/Hmts63SELBfk5fPOr5TzaHYa18JJyBuXnMGOcM+b6\n3HFDobOrx++zEe0oUWXGm1F1tlot3HLVRFY/v4u500p5Y8dnzJ1Wymtb6gBnmFW2OdMrg1lxfo7f\n8xv1PnqX439sZJhMbD+6270c6tgKVyeTzfl7msMS+nc0I9tIfN6n2MuKt3get660ucOL8ph/kWKd\nRwhnkSXH3XaD9WGuPslqtUBnV9D1kerPQOZNuJx1u54DYN74y+jfOTAhbSjSNh1NmT2Vrn1rX5eM\nwa5bb+4AACAASURBVMeNwCTge0qpYTivdhzpXrcXGKuUKsR5haQaWJGEOgqR8qINkQqU1cQz1n3x\nlAWUHTzNoYdWAjBiyU3kVM2MuZ4ZJpN7NuDszAzvFJTdaSl9Q2sCZZqqGOkMmXJltRp/dhE5OWYs\n2Zls3d/Ilr3Ok/+MGDIiifipGlvM2FvOxwR847xRHDh8EltLO112B8dOtPLmzsPces1khhb1Y2iC\nw+IyTCYqh4xz/z1x4CTuqj4biC18pW3rO4YfTyLxfNPm5vfL4orZYwDol2um8pxBEfVhnozIpjcw\n28I3xn7F/XciSJsWRkjG4GM18Ael1Mbu5RuB65VS+VrrVUqppcDLOGdfX621rjfiSR0OB4sWf5fT\nraf91mVlmenocGadUGWK5T/7uRFPGZJrRvRQXDOiyz0cIpj+GRaK8ywRzzPg+UXKN9Z978Gt5D60\nCUdXFwCHVq6ibEV51L9uebK1dvL7J3e6Y5tHDrV4pU999OW9jD+7yGtiLhffE7LvNsWWHKxWCwfr\nmkLeVyJSh+szabS18fvHz6QrNWeaqCwbzP1P7OCXN5+X0Do1222s2rbGfRxsP7qbu6rPjjlm3mQ7\nwaGHVhp6PInEa7S1BUybu3n3mZCqscMLIurDol0fSrPdxkNbH3W328yMTO6qXhbXez2kTQujJPzs\nrLXuBG7wefhtj/XPA8/H47nbBkyg3zmBs5a43ojTjgPxeOqAZEZ00Rfk5Zq5aOZIADIzM3pcTqAZ\nhEX6CjZrfWtHl9d2Rs9wLoTRTCZpp0JEo+ffBERMXDOiDx5VFfTfoOEVctVDxI0r9WhmRiaZGZmU\nj65ixJKbMJnNmMxmRty0OOZftCy5Zq748hie2XCAZzYcINucwcKvlmPONGHONDlj+yMYTGzYWc8d\nD7zFHQ+8xYad3hdDXTMQu8qU2chTX7Elh2suGMs2/Tnb9Od8Y9Zo9h06xgVTS/nlHze7P+Ot+xtZ\net8mlt63ia37I5s/IVq+x4FRKUodlgLDjyeReMWWHGouUu7+pebictTIojN92MXlfFJvi3s79RWv\ndhuKtGlhFDlDC9GH+d0HUgVlK8oNu+G80dbmNbN47f9qv9nJp1cMDjlY8At7CBCqJbORp5dGWxtr\nPdrFU69/yBWzx/DsxoN0dNp59OW9lI0Y6BdOF8sM56HEa5bnnKqZlK0oB6K/OVekBltrp1fa3LaO\nTp7deNC9fKCuiXd3H01K2KfRqdEjIW1aGEHO0kL0cb5fthyWAnKtFmwGZxBx8U2pahQZdKS3Q0ds\ndHTak/b88frVWL6gpT/PtLmjSixey64se8kS7X1/RpA2LWIlYVdCiLgptjjT4rpCFBZ+tYIvjRvq\nFyLVaGtz39MRtoxLyuW+jzTXLyuThZd4t4uJY4q9lksG9vMLp4tmHgQhjOAb1jl8UD41l5wJwyof\nVSRhn0JESY4QIURczZ5U4pUWF/AKkQqUejdYGSA3nKe7rfsbeeCp98nLNfOvC6dSaMmh2JLD9g+/\ncKdkLujnDCGRcDqRCnzb4Ru7jrrbqjkzQ9qpEFGSo0TQ0dFBfUvoWVvrW1oY3hF+JmAhAnGlxXVN\nBuWZcjXc/RyeZYj0ZmvtdN/HYWvpYMWjW7jnti/7pWTepj93x83LlzmRCjz7rD+9sNurrZaPGCj9\nkxBRkF5dALBmkpm8ouA3rLUcMzM9gfURvZsrLSUET7kabJ9gX0Yl1WVqsrV2QpP/jxt5uWa+ft4o\nTrV2kG2WCGCRPgL1WdL/CBE5OUoEWVlZWMtLsAwbGHQb2+HjkvZXGMIVdgNw2zWTuXbuWGr/VwNQ\nc1Hg1Lue+9xy1USqxhZHtV4kR6DP5ZarJvLnl/ZwybmjePK1D3nq9QNcc8E5XDxzBH979xCAxM2L\nlFVsyeHqC8ay7m/OPmveRYojDc3c+/gOQPofISIhvbuIWnt7O3V1n/g93tSUz7Fjp9zLpaUjyc7O\nNqTsQHpSvkguz7AbgLd21bNl7+fu5bWvaL/Uu777+KayDLdeJEewz6VqbDEl1mn8bOXb7nVPvv4h\nV80+hyo1mGsvOEdCWETKqj9+mnUeaaLXv6K5YvYY6X+EiELSjg6l1GBgC/AVrfU+j8d/ACwCGrof\nWuK5XiRfXd0nvPWDf6YkzzvzzEcef9e3tHDef93HmDFjDSnbV0/L7+skNECkqi6Hg636cxZerJJd\nFSGikmEyMa17DppQoaNCCKekfANRSmUBDwHNAVZXATdorbcltlYiGiV5eYzIj09e/HiW3ZelQmiS\nK23lg08763HuuKHMqBjiXg4UbuO7j+824daL5Aj2uWzd38jDz+1i7rRSXttSB8BVc87h5bc/ls9O\npLySgf2ouUix9hVn2NX8ixVZ5ky26c8BZ8Y+acNChJasI2QF8ABwR4B1U4FlSqmhwAta67sTWjNh\nuEhDqQoKJiSgNn1TKoUmBUpLGS5NZbhUlpLqMjW5PhdLfg50dnm1w9e3fkqVGswV1aMZkJvFrIkl\n8tmJtDB3yjAqzi4EwJKbxQ/u2+SVsa+qzCptWYgQEn50KKW+DTRorf9XKXUH4DvFcS3we8AGPKWU\nulRr/UKoMq3W8L+SOxwOMjLCZ1TJzjZjtVpoasoPuy1AUVG+1/+RbN+T8iMVz+096/5R+M3d2+/b\nty9sKFV9SwtFf3qEoqLIyvYs31MkbSHZ4lHHsGUGyDZkyc8JOWlbPOtp9X08kn2jLDMWSfmMUpAR\ndfb6XDzaYUenna36c26+elJUkwca9T4a+XlInVJDIo9b1+MNKda3SpkiHSRjaH4j4FBKXQhUAn9S\nSl2udfc1S7hXa30SQCn1AjAFCDn4cM0dEIrD4cBut4fdrr29k4YGm9eN06G4totm+56UH6l4bt+T\nuru2jzSUqif1cfGcR6KnEtGJxVpHX5G+bt8QGDq7gu4X63vpe2+JrbXT/et3JNtHyojPPJ3LjDej\n6uz5+r939STe3n0EgOnlQzh1qi1ouwhVjlH1SZWy+kKd4i2Rx2398dOAMwwrkX2rlJmYMkV8JXzw\nobWe7fpbKfUazhvKP+9eLgB2KqXGAS3AXGB1ousoRG+UqNAkz3tLvnf1JOwOh6TJFW4nTnewZa/z\nt6ZRJQP488o9fOtrFfK5i7Tx6rbD1P7tTHrwuVOGSdinEFFIhZmdTEqpGqXUYq31CeB24DVgI/CB\n1vql5FZPiN4j3jNGe8b0d9kdvL37iNfyg0+/7zXBoO/2vutF79Joa+MvL+5xf95Pvv4hF80cKZ+7\nSBv1x09T251qt8vuYO0rmvrjp+PetwrRmyT1SNFaX+D60+OxWpz3fQghhOgFGppaZHAh0pq0XyGM\nkwpXPoQQvYQrvao504Q508R540v45lfL3csLLykPmCbXtV5SrfY+W/c3sviXr7D0vk0c/rzZqz1c\nNeccXnn3E/ncRUrbur+RpfdtYul9m2j44jQ1Fyt3G55/oaJkYL9kV1GItCK9vRDCUJ73lgA8eP+b\nVJYNBmD93/f5paGUNLm9l2+K5989uYP/uu3LjD+7CIB+WZmSYlektGBtuGKUM9WuDDyEiJ70+EII\nw3lmuerotPPenqMAmDN9M2t7by96PztQbMlJdjWE6DE7MugQIhYSdiWEiBsJq+rb5PMX6U7asBDG\nkyNIRK2jo4P6Fv+JlTzVt7QwvKPj/7N35/FtV3e+/1+SJduxrSR2bLI6kITkxNkXkrAmkNJSWtbS\nkA1mKCkEaGmn3N+d2zIzHTp3OnAv085vOm1ZQhhmgCQQtrK00FIopdCGJGQjy2ELxCROsLMqdhxv\nun/IUiRZm20tlv1+9pFHbX3P9+jIHH30Pfqecz4ZapH0ZJFZrqVvmTG2nAd/cDHe4yd10SY5SVND\nRVJL7yLpklVTXBSVuWMebzjkYlYG2yM9m6fQRUVpUcqTQUluqCgt0sBTcpoGHSKpo3eTdJrb7aZi\n/FA8wwbGLOPddwS3O/bgRERERET6Hq35EBERERGRjMjanQ9jzGnARuAL1tr3Qx6/HPgHoAV42Fr7\nUJaa2GM0NzdTn8R0lfpaL83NzbrjICIiIiI9UlYGH8YYN/AAUB/l8Z8CZwENwFvGmOettZ9nvpU9\ny5ENozjpKYtb5oT3EFyaoQaJiIiIiHRStu583AvcB/wg4vEq4ENr7VEAY8yfgLnAU5ltXs/idrsZ\nNKKKktLhccsdP7xXdz1EREREpMfK+ODDGHMDUGut/a0x5gdAaNax/sDRkN+9wIBUPXfDsUO0uGvi\nlinJPzW9qeFo/BsukcfTWT5R2cgyyU7TSrZ85PHOlk9ma95kywbKjEpYSkRERER6EofP58voExpj\n3gB87f+mARa4wlr7uTFmMnCPtfar7WV/CvzJWvtMRhspIiIiIiIpl/HBRyhjzOvA8sCC8/Y1H9uB\nOfjXg7wNXG6tjX+7QkREREREeryekOfDYYxZDJRYa1cYY+4AXsG/DfBKDTxERERERHqHrN75EBER\nERGRvkNJBkVEREREJCM0+BARERERkYzQ4ENERERERDJCgw8REREREckIDT5ERERERCQjNPgQERER\nEZGM0OBDREREREQyQoMPERERERHJCA0+REREREQkIzT4EBERERGRjNDgQ0REREREMkKDDxERERER\nyQhXNp7UGPMucLT914+ttctCjl0O/APQAjxsrX0oC00UEREREZEUc/h8vow+oTGmEHjbWjsjyjE3\nsAM4C2gA3gIus9Z+ntFGioiIiIhIymVj2tVUoMgY84ox5vfGmDkhx6qAD621R621zcCfgLlZaKOI\niIiIiKRYNgYf9cC91tpLgFuAx40xgXb059R0LAAvMCDD7RMRERERkTTIxpqP94EPAay1HxhjDgJD\ngb34Bx6ekLIe4HC8ynw+n8/hcKSpqdIHpbUzqb9KCqmvSi5Rf5VcoY6UZtkYfHwDmAJ8yxgzDP/d\njv3tx3YBY40xpfjvkMwF7o1XmcPhoLbW260GVVR4ulVHts9XG1JzfqCOdEpFf42UitetOnOzznRK\nZV9N1evvafWksq6+0KZ0UmxVnamsU9IrG9OuVgL9jTF/BNbgH4xca4y5qX2dxx3AK8DbwEprbU0W\n2igiIiIiIimW8Tsf1toW4PqIh/8ScvxF4MWMNkpERERERNJOSQZFRERERCQjNPgQEREREZGM0OBD\nREREREQyQoMPERERERHJCA0+REREREQkIzT4EBERERGRjNDgQ0REREREMkKDDxERERERyQgNPkRE\nREREJCM0+BARERERkYzQ4ENERERERDJCgw8REREREckIDT5ERERERCQjNPgQEREREZGMcGXriY0x\npwEbgS9Ya98Pefx7wDKgtv2h5aHHRUREREQkN2Vl8GGMcQMPAPVRDs8ArrfWbspsq0REREREJJ2y\nNe3qXuA+oCbKsZnAncaYN40x389ss0REREREJF0cPp8vo09ojLkBGG6t/bEx5nXgFmutDTn+D8Av\nAC/wLHCftfalOFVm9gVIb+dIc/3qr5Iq6quSS9RfJVeku6/2edkYfLyBP0j4gGmABa6w1n7efry/\ntfZY+8+3AoOstf8cp0pfba23W22qqPDQnTqyfb7akJrz2+tI+wdkd9sYKRWvW3XmZJ0501dT9fp7\nWj2prKsPtCln+mtADsUC1ZnaOjX4SLOMr/mw1s4L/Nx+52N5yMBjALDVGDMBaADmAysz3caewtvY\nAoCnMGv7AoiIpJ1infRG6tci0fWEd4TDGLMYKLHWrmhf5/E6cBJ41Vr7cnablx3vflDHfc9uA+DW\nqyczY2x5llskIpJ6inXSG6lfi8SW1cGHtfaiwI8hj60GVmenRT2Dt7GF+57dRmubf0rc/c9t4ye3\nX6BvT0SkV1Gsk95I/VokPr0TRCStmpqaqK7+lMOHSzh06HjUMpWVp5Ofn5/hlomIiEimafDRA3kK\nXdx69WTuf85/y/aWqybrGxPJWdXVn/L2977D0KKiqMdrGho4999+xpgxYzPcMsk2xTrpjdSvReLT\n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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 10 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For a similar visualization, you can draw a heatmap of correlation values." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "f, ax = plt.subplots(figsize=(9, 9))\n", - "sns.corrplot(titanic.dropna(), ax=ax);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nAAAASUVORK5CYII=\n", - "text": [ - "" - ] - } - ], - "prompt_number": 11 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also fit a linear model and plot the model coefficients." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.coefplot(\"survived ~ pclass + scale(age) + sibsp + parch + scale(fare)\", titanic.dropna());" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n1ET/H4cwhkmfgz00AAAAAElFTkSuQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 12 - } - ], - "metadata": {} - } - ] -} diff --git a/examples/plotting_distributions.ipynb b/examples/plotting_distributions.ipynb deleted file mode 100644 index 2bc5bf5040..0000000000 --- a/examples/plotting_distributions.ipynb +++ /dev/null @@ -1,971 +0,0 @@ -{ - "metadata": { - "name": "", - "signature": "sha256:a24259c8ad60e2b49aeceaf7c120e038f91e8e167cbcfe13c6461ccaeabca51f" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ - { - "cells": [ - { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "Visualizing distributions of data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This notebook demonstrates different approaches to graphically representing distributions of data, specifically focusing on the tools provided by the [seaborn](https://github.com/mwaskom/seaborn) package." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%matplotlib inline" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 1 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import numpy as np\n", - "from numpy.random import randn\n", - "import pandas as pd\n", - "from scipy import stats\n", - "import matplotlib as mpl\n", - "import matplotlib.pyplot as plt\n", - "import seaborn as sns" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 2 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.set_palette(\"deep\", desat=.6)\n", - "sns.set_context(rc={\"figure.figsize\": (8, 4)})\n", - "np.random.seed(9221999)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 3 - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Basic visualization with histograms" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The most basic and common way of representing a distributions is with a histogram. We can do this directly through the `hist` function that is part of matplotlib." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "data = randn(75)\n", - "plt.hist(data);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n00H77UVlAAAAAElFTkSuQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 4 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "By default, `hist` separates the data into 10 bins of equal widths and plots the number of observations in each bin. Thus, the main parameter is the number of bins, which we can change.\n", - "\n", - "The more bins you have, the more sensitive you will be to high-frequency patterns in the distribution. But, sometimes those high-frequency patterns will be noise. Often you want to try different values until you think you have best captured what you see in the data." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "plt.hist(data, 6, color=sns.desaturate(\"indianred\", .75));" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAeQAAAECCAYAAAAvs6RmAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAEF1JREFUeJzt3X+M5Hddx/Hnttdtwd3tcbrBq9mUpJ7vaGJJWyMGSaGC\nKBICNBKjpVGCIkKk/FDSXLFNCTWN2BKaVBJKawVNkdYWrYQCQsOVRitobSXguz0J16s9dWW33dtD\ne73r+sfMwXZ7OzM7Nz/eM/N8JJeb+X6/M9/3ez6z85qZ73c/O7W2toYkSRquk4ZdgCRJMpAlSSrB\nQJYkqQADWZKkAgxkSZIKMJAlSSpgW6uVEXEKcBNwJnAq8AHgUeBvgYeam30kMz/VzyIlSRp3LQMZ\nuAhYzMyLI+J5wAPAlcA1mXlt36uTJGlCtAvkW4HbmpdPAp4CzgMiIl4LPAy8MzNX+1eiJEnjb6qT\nmboiYhb4a+CjwGnAA5l5f0TsBp6Xmb/f3zIlSRpvbU/qiogF4EvAxzPzk8AdmXl/c/WngXP6WJ8k\nSROh3Uldzwc+D7wtM+9uLr4rIt6RmV8FXg58rd1O1tbW1qampk64WEmSRsSWQ6/lV9YR8WHgDUCu\nW3wpcA2N48kHgLd0cAx5bXHx4FZrK2V+fpZR7wHGo49x6AHso5Jx6AHGo49x6AFgfn52y4Hc8hNy\nZl4CXHKcVS/Z6o4kSdLmnBhEkqQCDGRJkgowkCVJKsBAliSpAANZkqQC2k2dKamAw4cPs3//vmGX\ncVzLyzMsLQ1u9tyFhTOZnp4e2P6kQTGQpRGwf/8+9lx1BTvn5oZdylAdWFnh/Muu5Kyzdg27FKnn\nDGRpROycm2Nh+/ZhlyGpTzyGLElSAQayJEkFGMiSJBVgIEuSVICBLElSAQayJEkFGMiSJBVgIEuS\nVICBLElSAQayJEkFGMiSJBVgIEuSVICBLElSAQayJEkFGMiSJBVgIEuSVICBLElSAQayJEkFGMiS\nJBVgIEuSVICBLElSAQayJEkFGMiSJBVgIEuSVICBLElSAQayJEkFGMiSJBVgIEuSVICBLElSAdta\nrYyIU4CbgDOBU4EPAN8EbgaeBr4OvD0z1/pbpiRJ463dJ+SLgMXMPB/4ReB64Bpgd3PZFPDa/pYo\nSdL4axfItwKXr9v2KeDczNzTXPZZ4BV9qk2SpInR8ivrzDwEEBGzNML5fcAfr9tkFTi9b9VJkjQh\nWgYyQEQsALcD12fmLRHxR+tWzwKPd7Kj+fnZ7iosZBx6gPHoYxx6gM77WF6e6XMlo2PHjpm+jP+k\nPacqG4ceutHupK7nA58H3paZdzcX3x8RL83MLwOvAr7YyY4WFw+eUKHDNj8/O/I9wHj0MQ49wNb6\nWFpa7XM1o2NpabXn4z+Jz6mqxqEH6O5NRbtPyLtpfCV9eUQcO5Z8CXBdREwD3wBu2/JeJUnSM7Q7\nhnwJjQDe6GV9qUaSpAnlxCCSJBVgIEuSVICBLElSAQayJEkFGMiSJBVgIEuSVICBLElSAQayJEkF\nGMiSJBVgIEuSVICBLElSAQayJEkFGMiSJBVgIEuSVICBLElSAQayJEkFGMiSJBVgIEuSVICBLElS\nAQayJEkFGMiSJBVgIEuSVICBLElSAQayJEkFGMiSJBVgIEuSVICBLElSAQayJEkFGMiSJBVgIEuS\nVICBLElSAduGXYDUyuHDh9m/f98zli0vz7C0tDqkinpnK3088si+9htJGmkGskrbv38fe666gp1z\nc8MuZagefOwxzj7jjGGXIamPDGSVt3NujoXt24ddxlAdWFkZdgmS+sxjyJIkFWAgS5JUQEdfWUfE\ni4CrM/OCiDgHuBN4uLn6I5n5qX4VKEnSJGgbyBHxXuCNwLHTQc8Drs3Ma/tZmCRJk6STr6z3AhcC\nU83r5wGvjogvR8THImKmb9VJkjQh2gZyZt4OHFm36D7g9zLzpcC3gCv6VJskSROjm5O67sjM+5uX\nPw2c08N6JEmaSN38HvJdEfGOzPwq8HLga53caH5+totd1TIOPcBo9bG87BERPdOOHTN9eQ6P0s9F\nK+PQxzj00I2tBPJa8/+3AtdHxFPAAeAtndx4cfHgFkurZX5+duR7gNHrYxymyFRvLS2t9vw5PGo/\nF5sZhz7GoQfo7k1FR4Gcmd8GXty8/ADwki3vSZIkbcqJQSRJKsBAliSpAANZkqQCDGRJkgowkCVJ\nKsBAliSpAANZkqQCDGRJkgowkCVJKsBAliSpAANZkqQCDGRJkgowkCVJKsBAliSpgK38PWRJGqoj\nR4/yyCP7en6/y8szI/e3txcWzmR6enrYZaiHDGRJI2Px0CEWP3ETT87NDbuUoTqwssL5l13JWWft\nGnYp6iEDWdJI2Tk3x8L27cMuQ+o5jyFLklSAgSxJUgEGsiRJBRjIkiQVYCBLklSAgSxJUgEGsiRJ\nBRjIkiQVYCBLklSAgSxJUgEGsiRJBRjIkiQVYCBLklSAgSxJUgEGsiRJBRjIkiQVYCBLklSAgSxJ\nUgEGsiRJBRjIkiQVsK2TjSLiRcDVmXlBRPwocDPwNPB14O2Zuda/EiVJGn9tPyFHxHuBG4BTm4uu\nBXZn5vnAFPDa/pUnSdJk6OQr673AhTTCF+DczNzTvPxZ4BX9KEySpEnSNpAz83bgyLpFU+surwKn\n97ooSZImTUfHkDd4et3lWeDxHtWipsOHD7N//76+3Pfy8gxLS6t9ue9+eOSR/jwOklRNN4F8f0S8\nNDO/DLwK+GInN5qfn+1iV7UMqoeHHnqIPVddwc65uYHsr7IHH3uMs884Y9hlSOXs2DFz3NckX2tH\n11YC+diZ1O8BboiIaeAbwG2d3Hhx8eAWS6tlfn52YD0sLa2yc26Ohe3bB7K/yg6srAy7BKmkpaXV\nZ70mDfJ1ql/GoQfo7k1FR4Gcmd8GXty8/DDwsi3vSZIkbcqJQSRJKsBAliSpAANZkqQCDGRJkgro\n5teeJElDdOTo0eP+jv6ozTNwPN30sLBwJtPT032qaHAMZEkaMYuHDrH4iZt40rkKOLCywvmXXclZ\nZ+0adiknzECWpBHkXAXjx2PIkiQVYCBLklSAgSxJUgEGsiRJBRjIkiQVYCBLklSAgSxJUgEGsiRJ\nBRjIkiQVYCBLklSAgSxJUgEGsiRJBRjIkiQVYCBLklSAgSxJUgEGsiRJBRjIkiQVYCBLklSAgSxJ\nUgEGsiRJBRjIkiQVYCBLklSAgSxJUgEGsiRJBRjIkiQVYCBLklSAgSxJUgEGsiRJBRjIkiQVYCBL\nklTAtm5vGBH/DDzRvPqtzHxzb0qSJGnydBXIEXEaQGZe0NtyJEmaTN1+Qn4h8NyI+FzzPnZn5n29\nK0uSpMnS7THkQ8AHM/MXgLcCfxERHo+WJKlL3X5CfgjYC5CZD0fEd4CdwH9sdoP5+dkud1XHoHpY\nXp4ZyH4kaRzs2DEzFhnTbSC/CTgbeHtEnAHMAQda3WBx8WCXu6phfn52YD0sLa0OZD+SNA6WllbL\nZUw3bxC6DeQbgT+NiD3N62/KzKe7vC9JkiZeV4GcmUeAi3tciyRJE8sTsSRJKsBAliSpAANZkqQC\nDGRJkgowkCVJKsBAliSpAANZkqQCDGRJkgowkCVJKsBAliSpAANZkqQCDGRJkgowkCVJKqDbP7/Y\nN48+up8jR44Mu4xnOXhwhu98ZzB/p/jRRx8dyH4kSXWUC+Tbrn4/u04+edhlPNsUsDaYXf3nygo/\ncvrcYHYmSSqhXCD/4MwsLzhtethlDNXJU8OuQJI0aB5DliSpAANZkqQCDGRJkgowkCVJKsBAliSp\nAANZkqQCDGRJkgowkCVJKsBAliSpAANZkqQCDGRJkgowkCVJKsBAliSpAANZkqQCDGRJkgowkCVJ\nKsBAliSpAANZkqQCDGRJkgowkCVJKmBbNzeKiJOAPwHOBp4EfjMz/72XhUmSNEm6/YT8OmA6M18M\nXApc07uSJEmaPN0G8s8CdwFk5n3AT/WsIkmSJlC3gTwHrKy7frT5NbYkSepCV8eQaYTx7LrrJ2Xm\n0z2oh/2rBzn65Cm9uKueOvnkkzh6tCcttrV4cJUfWhvMvqpbXF0ddgkl+Dg0+Dg0+Dh834GVFXYN\nu4ge6TaQ7wVeA9waET8DPNhm+6n5+dk2mzR86C8/2WVJkiSNrm4D+Q7g5yPi3ub1N/WoHkmSJtLU\n2trasGuQJGnieSKWJEkFGMiSJBVgIEuSVICBLElSAd2eZb2piDgd+HMav6c8Dbw7M/9hwzYfpjHb\n10FgDXhdZq5svK9h6rCP3wLeAhwBPpCZnxl4oR2IiNcDv5yZFx1nXfmxOKZNH+XHIiKeQ+M5NU/j\n8f71zPyfDduUHI9289dHxGuAP6Dx+N+UmR8bSqFtdNDHu4A3A4vNRb+dmQ8NvNAORMSLgKsz84IN\ny0diLKBlD6M0DqcANwFnAqfSeP25c936jsej54EMvAv4QmZeFxE/BtwCnLdhm3OBV2bmUh/23yst\n+4iIHwZ+t7nsOcBXIuILmXl4KNVuovkC/0rg/k02GYWxaNnHqIwF8DvAA5n5/oj4FeB9wDs3bFN1\nPL43f33zRfSa5rJjL0jX0phC97vAvRHxN5n530OrdnOb9tF0LnBxZm7281JCRLwXeCOwumH5yIzF\nZj00jcQ4NF0ELGbmxRHxPOBfgDth6+PRj6+sPwR8tHn5FOB/169svkPdBdwQEV+JiKq/w9yyD+Cn\ngXsz86nmJ5i9NN51V3MvjSCY2rhihMYCWvTB6IzF9+aAb/7/ivUri49Hq/nrfxzYm5lPZOZTwFeA\n8wdfYkfazcN/HrA7Iu6JiEsHXdwW7AUu5Nk/D6M0Fpv1AKMzDgC3Apc3L59E45PwMVsajxP6hBwR\nb+bZ7/B/IzP/qfmp5RPAJRvWPxe4jsa7hm3A3RHxtcz81xOp5UR02ccs8MS66weB0/tXZWstevhU\nRLxsk5uN0li06qPUWMCmffwX358D/ng1lhuPdY47f31zytw5ij3+LbTqAxrfhF1Po4c7IuLVFQ9/\nZObtEfGC46wambFo0QOMyDgAZOYhgIiYpRHOl61bvaXxOKFAzswbgRs3Lo+In6TxgL4nM+/ZsPq7\nwHWZ+X/Nbb8EvBAY2otOl31snM97FljuW5FtbNZDGyMzFm2UGgs4fh8R8Vd8v85Z4PENNys3Huu0\nmr/+CYo9/i20m4f/w8eO2UfEZ4BzgJJBsIlRGotWRmocImIBuB24PjPXz/+8pfHox0ldP0HjXcIb\nNnlnH8AtEXEucDLwEuDmXtdxojro4x+BqyLiVOA0Gl9NfH2AJfbCSIxFB0ZlLO4Ffgn4KvAqYM+G\n9ZXHo9X89f8G7GoePztE4yu5Dw6+xI5s2kfzRM4Hmz/73wV+jq2/ORy2URqL4xq1cYiI5wOfB96W\nmXdvWL2l8ejHSV1/SOOs5OsiAuDxzHx986y5vZl5Z0R8HPh74Cng5sz8Zh/qOFGd9HEdcA+N4wa7\nC55EdMxa8x/wvTMYR2ksjmnVxyiMxUeAP4uIe2ic4ftrMDLj8az56yPiV4GZzLwhIt4NfI7G439j\nZh4YVqFttOvjUuBuGuPzd5l512Z3VMQawIiOxTHH62GUxmE3ja+hL4+IY8eSbwB+YKvj4VzWkiQV\n4MQgkiQVYCBLklSAgSxJUgEGsiRJBRjIkiQVYCBLklSAgSxJUgEGsiRJBfw/TvgF/IwKXWgAAAAA\nSUVORK5CYII=\n", - "text": [ - "" - ] - } - ], - "prompt_number": 5 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `normed` argument can also be useful if you want to compare two distributions that do not have the same number of observations. Note also that `bins` can be a sequence of where each bin starts." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "data1 = stats.poisson(2).rvs(100)\n", - "data2 = stats.poisson(5).rvs(120)\n", - "max_data = np.r_[data1, data2].max()\n", - "bins = np.linspace(0, max_data, max_data + 1)\n", - "plt.hist(data1, bins, normed=True, color=\"#6495ED\", alpha=.5)\n", - "plt.hist(data2, bins, normed=True, color=\"#F08080\", alpha=.5);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\np3Hxd5pbWAAAAAAASUVORK5CYII=\n", - "text": [ - "" - ] - } - ], - "prompt_number": 6 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `hist` function has quite a few other options, which you can explore in its docstring. Here we'll just highlight one more that can be useful when plotting many observations (such as following a resampling procedure)." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "x = stats.gamma(3).rvs(5000)\n", - "plt.hist(x, 70, histtype=\"stepfilled\", alpha=.7);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also represent a joint distribution with the histogram method, using a hexbin plot. This is similar to a histogram, except instead of coding the number of observations in each bin with a position on one of the axes, it uses a color-mapping to give the plot three quantitative dimensions.\n", - "\n", - "In `seaborn`, you can draw a hexbin plot using the `jointplot` function and setting `kind` to `\"hex\"`. This will also plot the marginal distribution of each variable on the sides of the plot using a histrogram:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "y = stats.gamma(5).rvs(5000)\n", - "with sns.axes_style(\"white\"):\n", - " sns.jointplot(x, y, kind=\"hex\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nrEKHywD+9/G//w8AL9zb6hAEcRz4xje+gV/8xV/EnXfeiQ996EOl16Mowlve8hbcfffdeMMb3oAr\nV67kr2mt8da3vhVf/OIX87IPfOADuOOOO/ALv/ALuP/++/e9/h/60Ifwmte8BnfccQe++c1v1h73\nkY98BL/5m7+57/VZJGYNSv8PgP9h/O+fBvDtva0OQRDHgfe+97344Ac/iI9//OP45je/ie9+97uF\n1z/+8Y+j0+ngYx/7GF71qlfhwx/+MADgBz/4Ae6++258+9vfzqevvvzlL+Oxxx7DJz7xCfz1X/81\n7rvvPvT7/X2r+3e+8x388z//M+6//3780R/9Ee65557K4z7/+c/j85///MJOs+0Xu92nlJnc/CcA\nf97pdP5nAJsA7tqXWh0TlNZgYEfCd484eB544AF88YtfxMbGBjY2NvCWt7wFr3zlK/GVr3wFf/zH\nfwwhBG6++Wbcc889iKII7373uzEYDHDhwgXcdddduPPOO/FLv/RLWF9fR6/Xw3ve8x68613vguu6\nMMbggx/8IM6ePYt7770XX/va1wAAt912G1772tfine98JzzPw/nz53Hx4kXce++9ePazn41XvOIV\neOYzn4lbbrkF73rXu/K6vulNb8JwOMz//qEf+iG85z3vqWzXYDBAkiS4+eabAQAveclL8KUvfQnP\netaz8mO+9rWv4Vd/9VcBAC996Uvxp3/6pwCA0WiE3/u938Of//mfw9q023n+85+PZz/72fl7tdZw\nHAdf/OIX8d3vfhdveMMb8tcee+wxvOtd70Kj0cDFixfx8pe/HG9729sK9btaW7761a/iJS95CQDg\n3Llz0FpjY2MDa2tr+TGPPPIIPvWpT+Gtb33rgYzcFomrBqWx5PvF43//AMDP7HOdjjzGGCRSQev0\nR+UIDs9z6ImKKMAYgzEGf/mXf4mLFy/i9ttvx8tf/nL8zu/8Dj7+8Y/jxIkT+JM/+RN8+tOfxnOe\n8xzcdtttuPXWW/HUU0/hta99Le68804AaaB55StfiY997GN43vOeh7e//e34l3/5F/T7fXz3u9/F\n+fPn8alPfQpKKdx11134iZ/4CTDGcNNNN+Gee+7B/fffj09+8pN43/vehyeffBKf+cxnsLKyUqjr\nRz7ykV23azAYFJRjrVYLjz76aO0xrVYrH/n8yI/8SOl8nufB8zxIKfHOd74Tt99+OxqNBl760pfi\n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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 8 - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Estimating the density of the observations: `kdeplot` and `rugplot`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A superior, if more computationally intensive, approach to estimating a distribution is known as a kernel density estimate, or KDE. To motivate the KDE, let's first think about rug plots. A rug plot is a very simple, but also perfectly legitimate, way of representing a distribution. To create one, simply draw a vertical line at each observed data point. Here, the height is totally arbitrary." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.set_palette(\"hls\", 1)\n", - "data = randn(30)\n", - "sns.rugplot(data)\n", - "plt.ylim(0, 1);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nTkSuQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 9 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can see where the density of the distribution is by how dense the tick-marks are. Before talking about kernel density plots, let's connect the rug plot to the histogram. The connection here is very direct: a histogram just creates bins along the range of the data and then draws a bar with height equal to the number of ticks in each bin" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "plt.hist(data, alpha=.3)\n", - "sns.rugplot(data);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nYOuifdsAAAAASUVORK5CYII=\n", - "text": [ - "" - ] - } - ], - "prompt_number": 10 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A kernel density plot is also a transformation from the tick marks to a height-encoded measure of density. However, the transformaiton is a bit more complicated. Instead of binning each tick mark, we will instead represent each tick with a gaussian basis function." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Draw the rug and set up the x-axis space\n", - "sns.rugplot(data);\n", - "xx = np.linspace(-4, 4, 100)\n", - "\n", - "# Compute the bandwidth of the kernel using a rule-of-thumb\n", - "bandwidth = ((4 * data.std() ** 5) / (3 * len(data))) ** .2\n", - "bandwidth = len(data) ** (-1. / 5)\n", - "\n", - "# We'll save the basis functions for the next step\n", - "kernels = []\n", - "\n", - "# Plot each basis function\n", - "for d in data:\n", - " \n", - " # Make the basis function as a gaussian PDF\n", - " kernel = stats.norm(d, bandwidth).pdf(xx)\n", - " kernels.append(kernel)\n", - " \n", - " # Scale for plotting\n", - " kernel /= kernel.max()\n", - " kernel *= .4\n", - " plt.plot(xx, kernel, \"#888888\", alpha=.5)\n", - "plt.ylim(0, 1);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 11 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We then estimate the distribution that our samples came from from by summing these basis functions (and normalizing so, as a proper density, the function integrates to 1).\n", - "\n", - "There is also a function in the `scipy.stats` module that will perform a kernel density estimate (it actually returns an object that can be called on some values to return the density). We see that plotting the values from this object give us basically the same results as summing the gaussian basis functions." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Set up the plots\n", - "f, (ax1, ax2) = plt.subplots(2, 1, sharex=True)\n", - "c1, c2 = sns.color_palette(\"husl\", 3)[:2]\n", - "\n", - "# Plot the summed basis functions\n", - "summed_kde = np.sum(kernels, axis=0)\n", - "ax1.plot(xx, summed_kde, c=c1)\n", - "sns.rugplot(data, c=c1, ax=ax1)\n", - "ax1.set_yticks([])\n", - "ax1.set_title(\"summed basis functions\")\n", - "\n", - "# Use scipy to get the density estimate\n", - "scipy_kde = stats.gaussian_kde(data)(xx)\n", - "ax2.plot(xx, scipy_kde, c=c2)\n", - "sns.rugplot(data, c=c2, ax=ax2)\n", - "ax2.set_yticks([])\n", - "ax2.set_title(\"scipy gaussian_kde\")\n", - "f.tight_layout()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 12 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The seaborn package has a high-level function for plotting a kernel density estimate in one quick step, along with some additional nice features, such as shading in the density." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.kdeplot(data, shade=True);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 13 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Much like how the `bins` parameter controls the fit of the histogram to the data, you can adjust the bandwidth (`bw`) of the kernel to make the densisty estimate more or less sensitive to high-frequency structure." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "pal = sns.blend_palette([sns.desaturate(\"royalblue\", 0), \"royalblue\"], 5)\n", - "bws = [.1, .25, .5, 1, 2]\n", - "\n", - "for bw, c in zip(bws, pal):\n", - " sns.kdeplot(data, bw=bw, color=c, lw=1.8, label=bw)\n", - "\n", - "plt.legend(title=\"kernel bandwidth\")\n", - "sns.rugplot(data, color=\"#333333\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 14 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Although the gaussian kernel is the most common, and probably the most useful, there are a variety of kernels you can use to fit the density estimate. The kernel you choose generally has less influence on the resulting estimate thean the bandwidth size, but there may be situations where you want to experiment with different choices." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "kernels = [\"biw\", \"cos\", \"epa\", \"gau\", \"tri\", \"triw\"]\n", - "pal = sns.color_palette(\"hls\", len(kernels))\n", - "for k, c in zip(kernels, pal):\n", - " sns.kdeplot(data, kernel=k, color=c, label=k)\n", - "plt.legend();" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAeMAAAECCAYAAADNb78fAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XeAVOW5+PHvOWfmTNmdbewusLAsuODQe+8CFiwRjcYk\nRo0xaky4Kebmxphmbm7KTfFnrokaE0sSW2yAqKAgTUCa9DbA0mHZXmannvb7Y4BARNayhd19Pv+w\nO+fMOc+7w8wzb1ccx0EIIYQQrUdt7QCEEEKIjk6SsRBCCNHKJBkLIYQQrUySsRBCCNHKJBkLIYQQ\nrUySsRBCCNHKXOc7GAwGVeARYDCQAL4aCoVKzjg+Cvg9oADHgFtDoVCy+cIVQggh2p/GasazAD0U\nCo0H7iOVeAEIBoMK8Djw5VAoNAl4B+jVXIEKIYQQ7VVjyXgCsBAgFAqtBUaecexioAq4NxgMLgOy\nQqFQqDmCFEIIIdqzxpJxBlB/xu/WyaZrgFxgPPAwMAOYHgwGL2n6EIUQQoj2rbFkXA8Ezjw/FArZ\nJ3+uAvaFUkxSNeiR/34BIYQQQpzfeQdwAauAa4CXgsHgWGDrGcf2A+nBYLD45KCuScBfz3cxx3Ec\nRVE+TbxCCCFEW9No4lPOt1HEyUFap0ZTA9wOjADSQ6HQX042S//65I1WhUKh7zRyP6eiIvxRAm+X\n8vICSPml/B1RRy47SPml/IFGk/F5a8ahUMgB7vm3h/eccXwpMOYTRSeEEEIIQBb9EEIIIVqdJGMh\nhBCilUkyFkIIIVqZJGMhhBCilUkyFkIIIVqZJGMhhBCilUkyFkII0WG9+eZ8nn/+mbMe++lP78c0\nzRaNo7EVuIQQQoh261yrQv7sZ79s8TgkGQshhLggxF5/CWPr+016TffgEfiuvvG856xb9x5r1qwi\nGo3yla/cyYMP/oZHH32C733vWzz11HNs376N733vWyxYsITy8jJ+/ev/4cEHH27SOCUZCyGE6LAc\nxyE7O4ef/OTn1NRUc9ddX8ZxHDIyMsnIyKK8vIy1a1fTpUsXdu3awa5dO5kypek3KJRkLIQQ4oLg\nu/rGRmuxTU1RFIYMGQZAdnYOfn8ax48fA2Dy5KmsXr2S7du38qUvfZl169awY8c2fvCDnzZ5HDKA\nSwghRIflOA47dmwDoKKinEQiQVZWFoqiMHnyVBYvfou0tHRGjx7Hu+8uxzAMsrOzmzwOScZCCCE6\nLEVRqK+v41vfuocf/ej7fP/7P+TUjod5efkkk0lGjhxFIBDA5XIxbtzE5onjfFsoNgPZQlHK39ph\ntJqOXP6OXHaQ8kv5G99CUWrGQgghRCuTZCyEEEK0MknGQgghRCuTZCyEEEK0MknGQgghRCuTZCyE\naDWO42DsjGHujrV2KEK0KlmBSwjRKswDCeJza7D2J0CF9P/qitZNb+2whGgVUjMWQrQoq9wg8kQF\nkQdPYO1PoPX2gA2x56tw7BZd90CIC4bUjIUQLcY8lCDy0AkwQeup4702G1dvL9G/VWJsiJBcEcYz\nNaO1wxStJBx7ibjRtLs2ed0jCPg+fL3rRCLOL3/5M8rKyjAMg29+87vMm/cKpaXHsCybm266menT\nL+XVV19i4cI3UFWVvn378+1v/2eTxinJWAjRIhzHIfZCGZigT23Ae33/03vJeq/PxtwZIz6/Fvdg\nP2qOfDSJljF37isUFHTnZz/7FUePHuGdd94+vYtTakvFLzFy5CgWLJjPd7/7A/r27cfcuS9jWRaa\npjVZHPI/XgjRIhIL92Ef1UHdRXLNK+C5DO/M61E0DTWg4b0um9izVcReqsZ/V945N30X7VvAd+N5\na7HN4ciRw4wdOx6A7t0LqaysZNSoMQD4/X569erFsWNH+cEPfsoLLzzD8ePHGDhwME29lLT0GQsh\nml39ipUkFkQAC326CzWvM8nlbxN5/EHs+joA3GPS0Pp4MLfHMDdHWzdg0WEUFfVi166dABw7dpSl\nSxexZcsmAKLRCCUl++jatRvz58/lP//zB/zxj4+zZ0/o9E5PTUU2imhBsli6lL+jld9xHBJL3iTx\n+gkwZ+AaZpD2ld448RjRF5/G3LYRJZCJ/5a7cfXqg1Vu0PCr4yg+lcCPClD8TdcM2Jo64mt/pgu5\n/Mlkkl/96r+pqCjHcRz+4z++wyuvvMixY0dJJBLceOPnueKKq3j99bnMm/cqfn8aeXn5fP/7P8Lt\ndn+ke3yUjSIkGbegC/k/ZEuQ8rf/8sfjNh6PcrqJOb5gDol3lkHyG+D1kPFA99MJ1nEckisWEX/z\nFdA9ZPzwNyheL5UvVeNeEcaemUX2lZmtWJqm0xFe+/OR8suuTUKIFlJTbfGLH1fyyEM1NIRtrLLj\nJJYtBNdl4HjQLq+jQZlDbeQxDOswiqLgmXIZnulXQTxGcuMaTMPh5d0WAPUbpaladBznHcAVDAZV\n4BFgMJAAvhoKhUrOOP4d4A6g4uRDd4dCoT3NFKsQ4gK2cX2MeNzh4H6DX//2ML1nPk/O5/OY9HR/\norlVLB72BFbSRgXGR0rpE/gpiqKij51CYsmbJFcvYUX5CErKbapQyKhI4lgOiiYDuUT719ho6lmA\nHgqFxgeDwTHA708+dspw4JZQKLSpuQIUQlz4HMfhvfV1OC6wxq+FFRPY/srNfDb/GKqtMueK5YQ0\nlVONcXVOBYXGOnz6WNSMTNyDRlCysYzlhyLk5Lo4WqcxxDCxDiVwXeRt3cIJ0QIaa6aeACwECIVC\na4GR/3Z8BHB/MBh8NxgM3tcM8Qkh2oDjR03qylzYwb1cc9kixl3xMvlJhf5HCqjPhlun3sb96ffz\nw/Qf0ksrZB8ae2Ov4DgGAM6oabxmfBYH+PwtGcS7ppbFjGyRNatFx9BYMs4A6s/43TrZdH3K88Dd\nwDRgYjAYvKqJ4xNCtAFL15UBkDdkK2NCU5i+bDO3dKoE4O0aN4/c53BgVSadrM7M9FwDwLtEiCbf\nAWDhxjxqnRzGuVZSlB1G7+vFAeI7JRmLjqGxZup6IHDG72ooFLLP+P0PoVCoHiAYDL4BDAPeaNoQ\nhRAXMtt22L7RwPGazLg4jPnQPvDm4a/JgU4uOg1Io+S9BHNeCrNoYYRJUwvpnDWePXYFy9lDID6a\nNasMOmclmBJbTHKNSkGfqzj+tkLXMgMnYaN4ZKypaN8aS8argGuAl4LB4Fhg66kDwWAwE9gaDAb7\nA1FSteMnGrthXl6gsVPaNSm/lL+9WbH5CFa9H+/I9xm0K5NE/CC+vncS2wx51+Zx52WduOlmk7fe\nrOWdt+tYMD8CTEMHlgJgoGlwz3eLsP/gx1i/kiHX3MQrVNDNsUirUEkb1vb/bu3xtf84Onr5G9NY\nMp4DXBoMBled/P32YDD4BSA9FAr95WQ/8VJSI60Xh0KhhY3dsIPPNZPyS/lbO4wmN2fRXqAHIwft\nJ/7CFtS8HsT2ZKKkKyQGuKioCJOXF2DqDA9jxueyZVOcRNxhSfId6p16xmMxsu8VpGU5xEdNILF0\nIXVrllOdVQy1FpVrqol2b+1Sfjrt9bX/qKT8jX8ROW8yDoVCDnDPvz2854zjz5PqNxZCdEAViWqq\nt+ejZtYxJWqgOODqdh3GEQd9ZgaKfnbzss+vMnaCH4AMo5Ano09Sh0m2KwZ8LTXNadlbJFctwV3c\nH+P9OPbOGP5WKJtoeXNjc9lkNO3knGHuYczyzfrQ46Zp8tvf/pJjx47iOA533HE3jz/+CD16FHHk\nyGGysrJ54IH/wTRNfv3r/yESaaCysoLrr7+RWbNuaLI4ZaMIIcQn9uqmTSiJgRSN3oLy1j7IyMbc\nkwVuG33S+WsDQ1xD6Kx2ZptdxiRzI+lWOa6cfFz9h2Du2EzXonoOo1JcYWLXW6gZ7WNpTHFhmT9/\nLllZ2fzgBz+hrq6Wb3zjLgwjyfe+dz/Fxb354x8fYu7cVxg2bAQzZlzOlCmXUFlZwezZd0syFkK0\nvogdYe9GHQWY3rUeZX0SV+8vYG6w0CemowbOnzxVReVyz+X8PfZ3VqNRkFxKwHcT+pjJqWQc3s5W\nhlCMjRmKoY9Kb5mCiVYzyzfrvLXY5lBSso9t2zazc+d2AGzbQlFUiot7AzB48FDWrXuP6dMv48UX\nn2fFiiX4/elYltmkccgQRSHEJ7K0diXsKcbfuZyCdbtAdWEf7wkK6NMyPtI1hruHk6vksgUXVcmV\nOE4CV5++4PHS6chqDqupjygzFG/GkoiOrGfPnsyYcTkPP/xn/vd/H2T69MuwbYvS0uMAbNu2hV69\ninn++WcYOHAQP/7xz7nkkumyhaIQ4sKwaWMtiqUxom8Fzoly1C6zsI9buIb40fI+2m42mqIxxTMF\nC9iEQSy5BsXlxt1vEFpNGWq+Q5RUMm7hTW1EB3HttZ/l0KGDzJ59F7Nn30V+fmfcbjePPfZH7rnn\nDqqrq7j22uuZMGESr776EvfeO5tVq1bg9/sxzaarHUsztRDiY0s4CWq3FaECI+r2gN0Z+1BflDQV\n3/XZH+tao/XRzI/PZwMOExNL8OmTcQ0YhrF5Pd28lRwgmwG1FnaZidbloyV5IT4qt9vNj370s7Me\ne+ml5/nZz3551mPDh4/k73//Z7PFITVjIcTHtr1uF8rBIvzdS0nfvR2cz4MFvltyUbM/3nd8v+Jn\nlD6KehR2OScwrD24+w4ETaNLdCcHOdVULatxiZZxagvQliTJWAjxsa3fXIpiq/QuPADJK8EI4JmR\ngXuA7xNdb7I+GYANaEQTS1G8Ply9+9G1dgsHkH5j0bL+9rcXWvyekoyFEB/b4W1ZAEzZFwN7AFov\nHc/VWZ/4egVaAcVaMQfQOG5uwrKrcQ0YSielipjboV5VsPYnpN9YtFuSjIUQH0t1pAH3vh4Mzagm\no2IM6Cb+2/M+9b7DUzxTANiASiy5AveAISiKQ4FezmFbwYnY2BVNO51EiAuFJGMhxEdilsSJvVxN\n7Fdl3GubXF3vR0HF94Wsj91PfC6DXYPJVDLYiovaxHKUQBpaj4vomtjL0ZMfVdaBxKe+jxAXIknG\nQohGJdc0EPlDGcnlYTz1GiWoHC1aiz7zKPrITk1yD03RmKBPJAFsJUYsuRr3wKEUKEc4JslYtHOS\njIUQ55Vc20DsuSoUn4p+Rx6/0zSey62na9YevFdMaNJ7jdfHo6KyARfh+BzUgUEK1KOUoWAqYEoy\nFu2UJGMhxIdKrmsg9mwqEafN7syWeDWWqZMe3EnmNV9BUZv2IyRTzWSoeygVKOwjRiz9PbI6p5Ou\n1HNCUbFLDZyY3fiFhGhjZNEPIcQ5Jdc3EHvmVCLORyvUWfvKIaA3xcXH0ToXNMt9Z3hmsNnYzOt4\n6JJcTpcx4+h5tITD9iC6A9ahBK6+n2wKlbiwzQ2H2RRv2ilsw7xeZgU+fNOSRCLOz3/+U6qqKsnP\n78yWLZt44IFf8NRTf8G2bWKxGD/96f/gcrl44IEf8uc/PwXAXXd9mf/+71/TpUuXJolTasZCiA8w\nd8eI/aMKvCpp38hHK/QQ27uP44cLsbNrGDlgRLPdu1Ar5BrvNTTgMA8X0QEH6antPz2IyzwoTdWi\n6cybN4du3brz6KNPcMcdd1FTU83Bg/v58Y9/zsMP/5kpUy5h6dLFH1gIpKkXBpGasRDiAxLLw+BA\n2tfyUAs0ku+vYce8zdjGddB/I729VzXr/afp09hn7mOHuYPlrjJGjTRZ/J4M4mrvZgUC563FNofD\nhw8yZsw4AHr06ElWVha5uXk89NBv8fv9VFSUM3jw0A88TzaKEEI0KztiYe6MoRZomAeXEv7VD4i9\n8ARbnSIAOg88gltt3jWiVUXlFt8tZCkZrMBF+eW1+NUKagDzYBLHlsU/RNPo1auY7du3AXDs2FFq\na2v53//9Bfff/1Puv/+n5ObmYds2uq5TU1ONbduEw+HTuzo1FakZCyHOYmyKgg121TskFqwEjwdz\nzBXsWzsQJ6OegT1bpuaSpqZxu/8O/hB5iFddNv16lXC0ZATZURu7XDaNEE3j6quv5Ze/fIDZs++i\nc+cu6LqHyy+/ktmz7yQ3N48ePXpSVVVJTk4nRo0aw1e/eivdunWne/fCJo1DkrEQ4izG+xHAAbbi\nveoG3KMn8vdnDYx4AnPyu/TzjmuxWC5yXcTVnqt4LfE6R8YcQi8ZxSBsrIMJScaiSezdG+Lqq69l\n1KixHDlymB07tjF79rfPee73vnd/s8UhyVgIcZpdbWLtS4ByGK24C56pl/Peyig7tiZQex3ENXEt\nPbSbWjSm6Z5LKTF3sqPXEbwnHzMPJNDHprdoHKJ9KijoxgMP/JAnn/wLpmly773fb5U4JBkLIU4z\nNkZSP2jb0cdMoqzUZP6rYdy+GOEb5jHYXYymaC0ak6qo3J72dX5v/ZTSbmUkj3VC2S87OImmkZPT\nif/7v8daOwwZwCWE+Jfk+ghgQdoh6DucZ56uwzCA615DzWzgat+NrRKXR/HwtfTvol50kFIU7BOy\n+IdoXyQZCyEAsEqT2McNUPehjxrGgjfjnDhukj9qPeH+e5msT6ar1rXV4svR8pjZNYujqCgonCg5\n2GqxCNHUJBkLIQAwNvyrifpQ3mRWLo+RlVdJ6czFBBQ/V36KucWm41BnWVRaFslPMT9zwqgpHFNS\nz9+0dz3VdvUnvpYQFxLpMxZC4DgOyXVhIIHa02LxGi9gkHbjK5zQLT7nvQ6f8tGWoEw6DiXJJLuT\nSfYkk1RbFtGysrPO8SoKmapKkdtNP10n6PEQ+AjrXOteDS07CtVe8g4U8LvwL7kn/VsUak07zUSI\nliY1YyEE1oEETq0DaogTxdM4dMCgazBESUEFRVoRo92jG73GYcPg6bo6vl9eziO1tSyJRjlhmmSo\nKv3TfAzzeBjt9dJX18nRNOptm3XxOH+rr+eHFRU8WF3Npngcq5Gac9GwbKpR6Hm0Kw12nIcaHmSX\nsaup/hSig0kmk7z++tyzHquuruL3v//fFo1DasZCCJLrG1I/+Paw8sh4wKRu0nsowI3eG1GVD//e\nviuRYFEkwl7DAKCzpjHQ46GvrnORrqMrCnl5ASoqwmc9z3Ycjpkmu5JJdiUSlBgGT9bVkaOqXJKW\nxnifD/0c6//2GZrNoXdOMNhw88WKXF7oXMlj0Uf5gu+LjNXHNtWfRHQQVVWVzJ8/j6uvnnX6sZyc\nTnz3uy07xUmSsRAdnOM4GO+HgRh1/S9i53qTjMLDlBcdZZx7HEWuonM+r8ayeDkcZmsitVZ0X11n\nmt9PX13/SIvoq4pCodtNodvNZWlplJsmS6NR1sZivBIOszwa5YZAgAEez1nP697DxVpNYbAF/d4a\nwJduXcGL2Dwbe5Yj1hGu8FxBQG3Z9Y1F03h9bpitm5p22trgYV6unvXh/x/+/vcnOXhwP5Mnj2bk\nyNHEYlHuu+8n/OIXD/D44083aSznI8lYiA7OLjUgpoJawurESABqJ63Br/i4xnvNB863HIcV0Shv\nRCIkHIdit5sbAgG6uz/dilj5Lhc3ZWRwZXo6iyMRlkWjPFZby1CPh+sDAbK11PxmVVVQenshFCGx\nL5v+lVdyW+58/onKiuQK1iXXMc0zjUs8l+BVvI3cVXR0t912B/v3lzB27HjC4Xq++c3vUlp6vMl3\nZWqMJGMhOjhjZ6qJOpEVY9MOF+7cSsJ993C97/MfqGGGbZunamvZaxj4FYUvZmQwxutFbcIProCq\ncl0gwGivl3+Gw2xOJAglk9ySkcEgbyq59hrlJxyK4LZ7Yb70JMXfvou7E4+xCYWV2LyZeJMVyRVc\n6bmSifrEFv9gFZ/M1bMC563FNodTuy85jkNh4blbgVrCeQdwBYNBNRgMPhYMBlcHg8GlwWCw+EPO\nezwYDP6qeUIUQjQnY3MNAO9n9MS2VKIT19DT3ZNx7rPXoD5oGPymqoq9hsFgj4cf5eYyzudr0kR8\npm5uN9/OzuamQADTcXi8ro554TCW4zBslI9ynwsvLkrLc2F1KXlp9zJG8fN16pmudsZwDF6Mv8gO\nc0ezxCfaB1VVse3UAjKt+aWtsdHUswA9FAqNB+4Dfv/vJwSDwbuBgaRWlhdCtCGO5WAfVbCValaW\ndoX0MM6Q7Xze9/mzBm2tikZ5qLqaOtvmM+npfDUz8yNNRfq0VEVhot/PvTk55Gkai6NRHq6pocGx\n6TwuDYD91mXEF7+BK5JHTvqPSNeKGWcf4iuKFwWFefF5WI7V7LGKtik7OwfTNEgmk2cl45ZOzI29\nmyYACwFCodBaYOSZB4PB4HhgNPBnQNqBhGhjrEMJsDTKPHGSCR1j/Dqmpk2mm9YNSDXdvdnQwAvh\nMD5F4etZWVyaltbiH1Td3W6+l5PDUI+HEsPg/9XUoI9JDezKtDPYFu9H/I2X0dRsstP+E58+lU5O\nGUOAE/YJ1hprWzRe0Xbous5TTz3H1742m2uvvR6Arl0LeOyxJ1s0jsaScQZQf8bvVjAYVAGCwWBX\n4CfAbCQRC9EmmdtT043WOfk4ngTpo/dwpfdKIDX16NWGBhZEInTSNO7NyaHvv41sbkk+VeUrmZnM\nTEuj0rJ4UKvHSlcpwmaJNZPIxs2Y+/egKC4yfDeT7r2BycRwo/JG/A0STqLVYheiMY0N4KoHzuxN\nV0Oh0KnV2W8AcoE3gS6APxgM7gqFQn8/3wXz8jr2lAMpv5T/QnJo134cVPYm0rAmruHOnjfTPT0X\ny3H487FyVkSjdPfo3N+zgBz3pxvv2VRlv40MulXX8cTxCnYVKQzcAbrtY5U5mSsWvkKXn/wCRVHI\nda7DOLKGMckTrHTqWetaxWdzrm2SGD6JC+21b2kdvfyNaezdtQq4BngpGAyOBbaeOhAKhR4GHgYI\nBoO3AX0bS8TAByb+dyTnWvigI5HyX1jldxI2xjGFctUmptgUTjhEUWwWZdF6nqmvZ308TpHLxT0Z\nmVi1MSo+xb2auuxDULkjM5PtPaoYuAP6eB3WJiYxtOR9tKUrcA8aDoDPdR3jkg+zCQ/zqt9gqDGS\nDDWjyeL4qC60176lSfkb/yLSWDP1HCAeDAZXkRq89Z1gMPiFYDB45znOlQFcQrQh5v4EOColto41\nZBuX50/DcRxeDodZH4/T0+1mdnY2aS0wUOuTGOL1MnZwNgBdOlmYjsbb5lXEFs7FOTk6VncNIl0L\nMpk4SZIsSCxozZCF+FDnrRmHQiEHuOffHt5zjvP+1pRBCSGan7G1FoD9qAQmbaKf+1LeiER4Nxaj\nwOXinqwsvBdoIj7lom4+agIqPWtt7CKHvYf6srl0B2M3rkEfOR5FUQh4P8vQyC9Zh5fVydVM0afQ\nRevS2qELcZYL+50mhGg20S1RTOBQ331M7daPZdEYb0Ui5Goa38jKwn+BJ2JITT/x9vESiED2RAdH\nh4XWNZQvWIJjmgC4Xb3wu0cxjRg2NiuSK1o5aiE+6MJ/twkhmpwdsXCFNY6iYk1eRUC5hDkNDWSp\nKrOzs8k4ufRkW6D1Sa3KdUvci3UpGLbOy/WXk1j77ulzAt7r6I2CCygx97VSpOJCdKHs2iTJWIgO\nqPy9BhQUDmTV07dHAS/UxdAVhbuzsujUhhIxgOtkMs47YHLzlAysYjgW78HixVU4ydR0Jk3NI6Bf\nQgE2pXYpMSfWmiGLC8ipXZvOJLs2CSFaRNm79fQE9o7dRk3ycgzgzoyMT73ZQ2tQ810oGRrmvgQj\nvLlU3mSx+MEIy2sn0u+dNRTPnAKA3zOTwuQyDqNy0DxIP3e/Vo5c/LvY3BqMTZEmvaZ7WBq+Wdkf\nevx8uzbdeuvtrF+/lu9857/4xz+eZseOrfz61w/y9tsLKCs7wS233N5kcUrNWIgOprrSIqPaIa44\nlA/rS9hW+Ex6+ulNGNoaRVFw9fbg1FvY5SZXdE0neKUGpsJfN/UiGk5thKGpGRQpnQAoMT8wDlV0\nULfddgc9e17E7bffSc+evXj00SfRT24DOmrUWDZv3gTAli0bqaysxLIsVq1awZQp05o0DqkZC9HB\n7FgZYTAOu/OTxLWujPV6me73t3ZYn4rWx4uxMYq5N47W2c0dU3L4n81HCe/38Nsnqrl/th+3S6XY\nNQCM1ZSYO4EPLgDiOA720SRqNx1FlYUFW5pvVvZ5a7HN4Xy7Nnk8HgoLe7B7907cbjcDBgxk8+aN\nlJWV0aNH0+7wJDVjITqY8JbU4gsl/dLo4dL4XEZGm99i8FS/sRlKbUyvqirf+Uou7q5xGko8PPh4\nJabpkOkeQh42h+0T59w8Irk0TMNvThB7tur0h7Ro3xrbtWny5Ev44x8fYvjwUYwaNZY///lPjBo1\npunjaPIrCiEuWJEGm8zK1AfPoT4Gd2Rl427jiRhS/cZqvgtzRwwnlipfINPPN8ftQ+1uULnL4f8e\nr0Z1iukOJLE5Zh876xrWCYP466m518a6CMaapu27FBemxnZtGj9+Ijt3bmf06LEMHz6CvXtDTJ58\nSZPHIclYiA5k6/YYPbEJ6zDFfYicNjZy+sMoioJ7VBoYDsbW6OnHO0+YyO2eZ3F6WpTuMnnsLxF6\nmF0BKDF2nj7PsR1iz1aC4eC9Lht8KrGXqrGOJ1u8LKJlNbZrU3p6OkuWrKaoqCd+fxpLl75Hnz4X\nN3kckoyF6CAcx2H92lrSgSM9ahjbbWhrh9Sk9JGp/Y2N9f+q0Sq6h17Dh3Oj/hxWb4fDuw22P38D\nmCr7zO2nz0suqcc6mMQ93I9nWgb+mzuB4RB9ogInbn/gXkI0NUnGQnQQyxuidDmY+tnXeT1qRmar\nxtPU1Fw3Wi8P5p44dq15+nF93BT61ZxgcrelqTnIoWy8r1zLAaMUx3FSzdNv1KIEVLyfywHAPcSP\nfkkAu9wk9k/pPxbNT5KxEB1AqWny2q4GepmpWl5RoH1uZ+celQYOGO+fUTt263imzWTq2iVcPK0S\nuxDYNoDY/OlUJSuJPVMJJvhu6oSa9q9me+9nstF66hgbohirG1qhNKIjkWQsRDtnOA5P19WhhByK\nsKkNhAn0HdbaYTUL93A/qJBcf/bgK33MZNTMLK5/5a9kfR7szuBaP4Jdj9RgHUriHunHPeTs6V2K\nS8F/ex6A9/tlAAAgAElEQVT4VOKv1+LYUjsWzUeSsRDt3GsNDRw3TYpCSTyAmVeCVlTc2mE1CzVN\nwzXAh33MOGvwleJ245l2FZ5oA7e+vxi+aOLLsum734OhK3hvyDn39XJcuAf5cBps7BNGSxVDdECS\njIVox3YlEiyLRsmtUymqSzXB5uhRFFf7Xe9HH/XBgVwA+uiJaEXFZK1exudqtjOxu4UOLEpqvLsm\n/qHXOz2Hee+HnyPEpyXJWIh2KmrbPFdfjwoUHaqjFxYODr7+Ba0dWrNyDfCBVyG5IXJW07LicuH/\n8jdQc/Lo+9xSRu0wqchS2BbQeH1uA8sWn3tesau3BwBzb6JF4hcdkyRjIdqpV8Nham2bK9LSOLo9\nTHccYhnl6IP7t3ZozUrRVdzD0nBqLaySsxOomh7Af8c3wbkU1VF46/JyGm5R0DMV3pjXwDv/PEj8\n7dew62r+db1OLpRsDWtfXPqNRbORZCxEO1FtV/Na/DV+Hv45f6h/k7XxOIUuF+NVN50O5qEBPu0Y\naqe81g612Z1qqv73gVwATn0WGL2JZh9h94B/4s2pI3yzg0+PsnCljyUL6og8+fDp7RcVRcHVx4sT\nkX5j0XwkGQvRhjmOw25jN49HHueB8AMsSiyi0oqyL3YxYKK45/Pi9nfpdXLdCndPvVXjbSlasQcl\nS8PYFMHYFTtrM4D4vFStV59kghonx3kOLWAQu00j4I2z1LyMLUdyiL34t9PPk35j0dwkGQvRRjmO\nw99jf+dP0T+xzdxGd7Ubn9PH09/6LBCgk2s9h+332bVJoxc2tmLhHtm9tcNuEYqq4JmRAXGH6CPl\nRB48gbEjhrk5mlppa5ifTpdOJddWqPBWMitWQbyzB/ddabhcsJwriG/eSHLZQkD6jUXzk2QsRBu1\nNLmUDcYGCtXOfM3Vn9vsYyixo2w3CylQjvAVaxHBf15LYHs/uuCguI7gDgZbO+wW45mSQfp/dcE1\n2Id1MEn0sXKiT1eCBp5rsgDo676IpKIQKNjCWK+X4xkWOaM1ao0AW/QJxBfMwdi9TfqNRbOTZCzE\nBcCJ2cReriaxvP4jnb/X3Mu8+DzScXG9fYhccyMG6Sw0b0LD4RqzOy89cT+Htg9iRG6q31TNCaN4\nvM1ZjAuOVugh7c580u/rinuYHxzQp2ag5bkBGOm5AoCN5mZuCKRR4HJxeLSF5oZVzMBUPUSf/Qt2\nZZn0G4tmJclYiFZmHU3S8NtSksvDxF+uIbEyfN7za+wanow+Cdh8lgg5Wm+y/N9mlfJ96hwPI0t9\nvPQHlRPHXYyd6GNqVmrxC3f/9rkE5kehddPxfyWPjF93x3tt1unHe7kuJhOd3ZgY5ibuyMzEG1Aw\nR0JdWGX7wK9BPEbsub9Kv7FoVpKMhWgljuOQXB2m4cET2BUm+qR0lHSV+IvVZ20DeCbDMXgi+lca\nnAYuxaDYNYDstO9w1OnDylicTvtVtj0VJ5Fw+OznA1x/YwDnkArEcI++qGULeAFS/NpZe9aqispQ\n9xASKGxPLCTf5eKLGRkkxgJuWL4rH6fvMKyjh1CyUq0W0m8smoMkYyFagWM4xP5RRez5ahS3gv/u\nPHyf64T/7nxwK0SfrsQ88MEP/ZdjL3LIOsxALCZoA8jyfx0TN8/X16OWQfIVG1WDu2ZnM3aCn8TS\nHWB4Ie0wWreOMXjr4xqhTwJgq12KaR1nmNfLlFwf5igI19tsdE8FwDyyUfqNRbORZCxEK0iuDGOs\nj6AV6aR/vyvugalNClw9PfhvzwXLIfrncqzyf/VPbkm+z2pjDfnYXKcNICvtHhTFzaJIhLJai/QX\nFYwEfOGWTC7qrWPXVpNYsA8A79V9z6oRin/pqfUkS0ljDyr1iSUAzAoEKJjkwtFhyc5OJFUv5vaN\n0m8smo0kYyFagbEtCgr478pHzTl7nWj3QD++m3JwIjaRR8pxohYNdpgXYs+g4XCTK0he2tdQFBfl\npsmimgi+FyFZ5zDzmnQGD/PiWCaRf/wFEr3BY6GPK2ylkl74FEVhmHsUCRR2GmuwnRguReGOrlko\nYyARgWWdP4d9/Ahql1T/u/Qbi6YmyViIFuZEbaySBFqRjpqhnfMcfXwA19AYTpVJwyPr+GfFr2nA\nZCoZ9PF/HUXRcByHF+rqUeaBUwqjxnq55NJUDTu+cC72wSQQwD0iA0WTWvH5DHePAGAnNvHkewDk\naBpfuCwDxwvryvsQ0X04iV2A9BuLpifJWIgWZuyOgX1yQ4NzcIwksVeewdz1EFCHeSSfA3GHrqbN\npAcraPj9z4m/8wbrKso5MN9AC0FxHzfX35SBoigYu7aRXPYWnEww+rC0Fixd21SkFZGtZJ1uqj61\n8tbwLB/dhrggovD88C9hlKyRfmPRLM67j1owGFSBR4DBQAL4aigUKjnj+GeB7wMO8GwoFPq/ZoxV\niHbB3B4DwD3A/4Fj1oljRJ95HLvsOGrXbpgD46ivZzLjnQl073sYb48k5r7dVC15m1d2D8G1201u\nRpTP5a8l8VwN8UgD1rFDoLlRGADpKlqfjjW3+JNQFIXh7hG8k3yHvU4FWeYmvO7hAHxmbDqPra3l\naG0R73bpxtR0BXOzhX3CQCvoGMuLiubXWM14FqCHQqHxwH3A708dCAaDGvArYDowDvh6MBg89w7d\nQggAHNvB3BlDydRQu7vPOpZcv4qGP/wCu+w4+vhL8M2+hzmjn6O0SzlDNw+gV9EdpN3xLbz3/Y4n\nc76Hs9tNRkYttyUeRHv/HcxtG7H270Fx6egTv4oTAfcQvzRRf0TD3MMA2ImLuuhfSRjbAOh1kU4g\nU8W1y+Ht8ZdR6SsHwDrHaHchPqnGdhifACwECIVCa4PB4MhTB0KhkBUMBvuGQiE7GAx2BjQg2Xyh\nCtH2WYeSOBEb9/j0s0Y3mwf2Envpb5ieAPWXfp1yz0VsfmkdofB1OPkN3HAin4ZXa9DvyOOxJ+LU\nH3TjKoZ7b+mMp+4/UPxpKOkBFF8aiqYR+2cV0JBadUp8JD20HuQoOex1wpjEqY3+iUz/nXjdIxg2\n3MuKpVGUAwrzijO57T2wjsnHnWg6jSXjDODM9fmsYDCohkIhG+BkIr4e+CPwOnDulQqEEACY21Nv\nEffAVH+xbTsc2R9ly5O72Ru/h7J4V5w5ChAG+qEBu3E4gEGvPXGevv8Exy0Nqz/cfmsmaWle6JR1\n1j0cy8HYEkVJV9F6SxP1R5UaVT2Md5LvUKLPoG9yCXXRP+P4bmfoyJGsWBql23u1HPhSNraSwDom\n05tE02ksGdcDZ66hdzoRnxIKhV4NBoNzgKeBW0/++6Hy8jruknwg5e/o5Xd2J1HcCmmDcnhlTg2b\n349QX2cBo9FUm959fPQo0qlNn8PqzgcZklfAlcmvcGJDGBZXMl2x2DtGZfh16Uzpee59iaPbG6gP\n22TMyCG/S0bLFvA82sJrf2VyOiuPrOTl5DK+lDWTXvWLqI89RVGxi/zOxdSeyKJXyR4qcnuSeyxJ\nbqd0FPWjdQO0hfI3p45e/sY0loxXAdcALwWDwbHA1lMHgsFgBjAfuDQUCiWDwWAEsBq7YUXF+dfd\nbc/y8gJS/g5c/izFQ/JwHFd/LwveqmLFkgbS/TZDtE0Es8oY+N0v4gt4iCZW8If4KhxUZqZ/ma6a\nTdduaYSro3TeGGVQV5srvb4P/VvGllYBYPVzXzB/77by2usE+Kb/mzwafZR/1C5khnscY+11lFY+\nyYAhP2Pp2yqD39hBVY9COleorN5axsXdGh+t3lbK31yk/I1/EWlsANccIB4MBleRGrz1nWAw+IVg\nMHhnKBSqB54BVgSDwXcB++TvQohziG5qAMA1wM+BklR/410ZT3CNPpdhX56ML+DBduJsic/lOCqD\nXf0p0ApOP//daW4MDT6zwCSz9Nzfe083UQdUtJN78IqPp4erB/em3Uuumsti4z0Waj2wMOkzYBkA\nJeoICsreB2D1vnpqrEbrIEI06rw141Ao5AD3/NvDe844/hfgL80QlxDtTmRjqmag9fNyaH4NnfxR\n/LWH0Kdchqtnceqc+AJWkABUZnqvOf3cUtPkLW+C2us8XPVygsij5aR/t8tZq3c5SZvYM1U4DTb6\n5MBHbj4VH5Sn5XFv2r08Fn2MDdYBImRwfafFdO46mX1lhXxGWw6MIuuExVN1dXwrOxtNlhsVn4Is\n+iFEC3CSNrHtDahd3ZTFIR536J7YhZrXBe/l1wJg2VXsSC7iKCoDXQPorqU2dnAch5fq67GBwWOz\n8V6fjVNvEXmkDDuSqpXZYYvIw2UYm6JoxR48V2a2VlHbjYAa4Jtp36S31ptdJNmOQt9B2zFtlb1O\nNgB9KxQOGAavNTS0crSirZNkLEQLMPfEcQwH9wAfB/anRuH2UA7iu/6LKO7UwhEN8Tm8S6p2NdNz\n5ennbkok2GsYDNR1Bno8eC7JQL8kgF1mEn28AutIkobfn8A6mMQ9Mo20b3RGTTv3Mpvi4/EoHm72\n34wbN4vw0n3AIgC26kNBCdP1hE2+prEkGmVLXNarFp+cJGMhWsCpVbdcA30c2JP6uSi3Aa24b+q4\nXUHIWM9hVPq7+tPD1QOAhOMwJxzGBVwf+NcgEO+sbNzD/Vj7EzT8phSnysQzMxPfrZ1Q3NJc2pRy\n1Vyu8l5FFJv1nerp2r2CA7HumEol1Nnc4c7ADTxTX0+FabZ2uKKNkmQsRAsw98RR01TUIp39e+Ok\nU0/+qP6nF/6IJZbxLqna7EzPzNPPezsSoda2mZ6WRp7rX/3Diqrg+1IuWh8PaOC7pRPeK7Nkm8Rm\nMlWfSne1O1txkTd4PbajsMdOLTiYX2ZzU0YGccfhybo6DEfWrBYfnyRjIZqZk7CxK0w8vXxU19g0\nxFwUqofwjBybOu4kOJx8l4NoXKz1oaerJwDlpsmSSIRsVeWytA9On1HcCmmzOxP4RXf00ektWaQO\nR1M0vuD7AgoKe8ZsotfFB9lNLgDG/jCDvA5DPDZHTZO/1h1hi7GF3ebuVo5atCWSjIVoZtbxVB+x\n3sPL/q3VABTlx1BzUh/mMWMdm0idM9Ez6fTz5oTDmMB1gQD6h9R4FVWR/uEW0sPVg6n6VGo1h6zP\nP4tRUArApvdKua/uPrbwG1BOsDPh4a/hNfwp8ie2GFtaOWrRVkgyFqKZ2cdTc4o9PbyUbExtMlA8\nojOQGikdjr/DNjTSFD+DXIMA2JVIsD2ZpI/bzVCPzBe+UFzlvYpsJYv3dIftX34BA4dO1VlkL5rC\naC2T0b4duLDQjGtQ7HzmxedhOtKPLBonyViIZnZqDWO90MPB4y48xCmcPBAAw9rLTqeUKApj3GNx\nKS4sx+HVcBiF1KAt6Qe+cKRGV3+JzmpnRmYOIpFZQR428RUT6LSyB5fZS7lOn4+Fhtf8IhVWLSuT\nK1s7bNEGSDIWoplZx5OgQE3VEarNbAqz6tD8qd2UosmlbD45cGucPg6AVbEYJyyLcT4f3d3uD72u\naB1BV5AfBX7Erf5bycjW0FAo1E1WLr4SO3kpfbWdjNZWEbPTUY1reDO+gAYr0tphiwucJGMhmpHj\nOFjHk6j5LnYu2QXARX1Tg60su4YyYyP70bhIu4guWheits0bDQ14FYWr02VQ1oXO3S8fgIlphzGS\nChtWziQv8DtmBTrTTTmMbfcnZvZlbs38Vo5UXOgkGQvRjJxaC2IOalcXe3al5hcXj06tNx1LrmDL\nybfgqVrxgkiEqONweVoaAVXenhc6V7/UzllF4VKyshRWvxslXK8S8EzgC/49+IiAeRkLqrdTaVd+\n6vtZ5Qbx12uw66Qfur2Rd7sQzejUBvSKVsWhZAGaYlPY04PjGEQSy9iCCw8ehrmHccI0WRGNkqtp\nTDnZjC0ubFqBG3BQ7Vym9K/ANGDpolSTdHf/VcxyzwM0LGMWr8be/MT3cSyH+Nt1NPzqOIm36on8\nuQInaTf+RNFmSDIWohmdmtYUr9xFmdOV7t0U3G6FuPE+JUSpB0bqI/EoHuaEw9jAdenpuGXQVpug\neFSUHAXszgyKLCM7R+W9VTFqayw0NYshaf2YqC0DJ4tt0Z7sNw587HtYhxM0/K6UxPxaFL+Gq58X\n+0iS2PNVOLLASLshyViIZmSfrBkfLS3DQeWifmk4jkM08RabTm6aNs49jp2JBDuTSS7WdQbJVKY2\nxVXkB3yw+wDTJ1pYJrzzdqp27Nenc4m+nS5qCdhBnm0o+VjXTiypp+F3J7CPGrjHphH4YVf8d+aj\n9fJgbIiSXFLfDCUSrUGSsRDNyDpugO5wwEwtndirWCdpbqfOPsoeVArUArqphcw5NZUpPV2mMrUx\narfURh/Y+fSvWURunsb692JUV1koiptM30183v0iKg2UJwewJV7zka5rVxrEX6tBCWikzc7Hf3Mu\nil9DcSv478hFydSIz6vFODkWQbRtkoyFaCaO4WCXGyhpEQ47PVFw6NnLTSSxkI1o2KQGbq2Kxzlh\nWYz3+egmU5naHK1b6jVTfBdhb1zN9CkKlgXvvJXaVtHjHkTXtIsZpf8TgH/UR6i3rEavG3+zDizw\nXpeNK+g765ia6cL/1TzQIPpUJVaF0cSlEi1NkrEQzcQ+YYANjl1KqdONgm5uNP0ADdYe1uHBr/gZ\n5BrNmyenMl0lU5naJK17qmasZPYHy6Rf/VLyO2tsWBunsiI16rlL7q1MUg+jupaScNz8ra4O+zz9\nvdbxJMaGCE5nF/t9GobxwXNdPT34buoEMZvYM1XNUzjRYiQZC9FMrJPLYNZFyjHQKQ76iSYWsh6N\nGDbT9GksjZhEHYcrZCpTm6VkaigBFac+AIFMzLXLmTHdjW3D4oWpvmOP3pUs9yj6aitB3cMew2BB\n5MMXAom/XgsOvBHReOKxOh64r4J/PFnLpg0x4rF/jaLWx6bj6uvF2p/ArpbpTm2ZvPuFaCanRlKX\nOqmBWkW9YtSbm1mLjh8/QW0C78Zi5MlUpjZNURRcvb04dTb68CshEScYXUWXri42ro9TXpZKkn7P\n5QxVLHC/hkeJ8VYkwq5E4gPXMw8kMLfFiOW52dwAPXq6ychU2bopwXN/q+e/f1jB/n3J0+e7hqT+\n7xhboi1TYNEsJBkL0UxObRCx38kEICt3BRvQiOFwiecS3mwwUlOZAgFcMmirTdOKUyPg1cyh4PVh\nrlzMpZd7cBxYtCBVA3Zr3blY60eGEsN2v4gK/K2ujpp/6z+Ov14LwCJDQ1UVvnR7Jv/14058+2sO\nU4oPYxjw9nMHcWKp5Ose7AcFjK2SjNsyScZCNBPrWBJcYQ473dA9QNpbrEHHh498xrMrmSSo6wzU\n9dYOVXxKrt5eAKxDNp7xU3Eawlwc30C37i62bIxz9EiqBhzwzmQIFoZ6hBH+KiKOw1N1dVgn+4/N\n3TGsPXFi3XS21sLwkTppB9YReex3pD/9YyYde5weygFKKjLZ//PfEn9rHrjiaL08WCUJ7PrGB4aJ\nC5MkYyGagV1v4YRtLLuMSiePgsIa3lcdYjhM1qfyRkNSdmVqR9SubvCpWCUJ9IkzwOXCWPE2l17h\nw3Fg7supfazd2sUMV7sCUOm8zgivlwOGwbyGBhzHIT7/ZK3Y1FAUGH3kKWIvPIm1fw9a7374br6T\nSV8MArAhOYrE4tcJ//I+lMzj4IC5TWrHbZUkYyGawakm6nriAOQW7GQNbrx40e3xlFsWE30+Clyu\n1gxTNBFFVXAVe7ArTbD86KMmYldXcFHpIgp7uNiwNsKxowaKotDdezVFWOx3jnBpWoLOmsbSaJQ9\nW+qxDieJFXvZWubQ1xMip2YX7jGTCNz3S9Lvvhd96GgGjcoiM0tlKyPg8ptQNBfm7tS0Kek3brsk\nGQvRDE4N3jpxcpWthsJDRIHx7hksjiTxKQpXylSmdsV1st/YLEngvWIWSnYnkkveYPrQVK140Zsn\nR1a7hjJMSQPgfWMFd2RloQPh5anVtJY2pAZ8TbAX45l5Pb7P3oLaKe/0fTRNYdxEP8kEbHONx/uZ\nz4FTjZIextwTx4nJmtVtkSRjIZrBqWlN+5UMAPYVnsCr6ESNUcQch5lpaaTLVKZ2RTvVb7wvjuJP\nw3/L3aCqdF/1KMUXudixLcGRwwaKojLCMxMdh7XJ93CrtXwp6ad4n83xPIWNZS6K1T30vGE63mkz\nz9mNMWa8D5cLVq2Iog0ehZqbj5PYCBYYO2RFrrZIPg2EaAbWkRgOBiEK8GfV0JAeZajvUtbGDfI1\njckyland0Qp10BXMktRgLVdhL7xX3wiRMJNZCMCcF+sxTYdMfRIj0agnyS/C/41n9VZU4D011ZIy\nbWYmnnFTPvRe6QGVIcO9VFZY7Ntn4Zl+FSip/bKlqbptkmQsRBNzLAf7hIWl1BC1vCiFx8CBitgw\nHFJTmTQZtNXuKJqCq5cHu9TAjqRGNesTpuEaOIyCY8sY0q2CI4dM3pzXgKK4meW7jetwkZ7UyNmQ\nS4M3TqhMxS6E0nFV1EX/Qk3k/1ET+SMN8ddIGFuw7NrT95s4JfWFbtXyKO5hY1ByFVCqMHdEZXvF\nNkhGjwjRxOwKE2yFsJIavFXf/ThZzghKYtBP1xkgU5naLa3YgxmKY5UkUAf7URQF/+e+TLTsGJdX\nPMaxtO/y7jIoyqhi0JhiJuy5noHLduKJe1mhKFhuE2OGzcvxPmywl5GvlpOPw8XmFk61pahKFn7P\nDLoVTqNHTze7dyapqnbImHEVsWd3gzEBc3c8Nf9YtBlSMxaiiZ3qLy47uemD2b0Uw5yGSqpWLFOZ\n2q9T843NffHTjyk+P12+/i10t8P1xl9xkeSl1+Dwz/5/e/cdX0d1J/z/c6bcXtQtuRfZY4NtMGBj\nMBBK6CV0SCAEE5IAaZvdbDbZfZInv9+W7CabhPRCKKGHYkIJ1RSDbVxo7h53uchF/fY2c54/rkxs\nY1ty01U579frvqSrmdE9R7p3vnPad/6TzF8eRGydjgt8JEyGfH4OkVGvAyab8tezSAZ4AZO78fMX\nMYRl2giSMkMi8xTN8e8zbUYjUsL8d9KYJ01HVOwEILe4e3eGUnqPg7aMLcvSgN8Ck4EscLtt2+v3\n2P5Z4JtAAVgG3GXbtrrbtTKgOZuKXYmbPVFwHKgZStL1cmFFlDq1lKlf00d4QAdn/d5pLn2jxhD+\n3o8ING7hiveambVgMLOM2/jMoAaqNpaxCo2rby/n+Ek3ATArFuPNdBWW/Ecs3yo+KnzIWmczayVo\naJyijeJ0dwvDxv6aUPg7LJwvOe+CIN6LppF5KEZheQDpSISuLvz6iq5axlcCHtu2Twe+C/x09wbL\nsvzAvwNn27Z9BhAFLjtWBVWUvsJp6ABgfTKCW9uEEGcSEIJraypKXDLlWBMeDX2EF2dLDpnZe9xW\nC0cwreM57aYpTJ3uY3uqnJ0bjwOg7KIox0/yfbzvZ8JhRpkmds7A657OP4f+mR+Gf8iVviup0qpY\n5G7ndwRY7BnKSWe+RS4reOO1bXhOPhWCDVAwyC9Rd3LqS7oKxjOgOA3Qtu2FwCl7bMsAp9m2vbs/\nxgDUnHplwHObHSQOrVLHHazh4uHiUIiwoZe6aEoPMMZ4i9mwNn7yJhC7XXldhNE1GuNxyZbpWJeE\n99quC8HMaJSQEMyKx2nI56nUKjnPex7/GvpXrvNdhyk8zHZ3smDaOrzhGO++A+3xZsyTqwDILdx8\nTOupHF1dBeMIENvjudPZdY1t29K27SYAy7K+DgRt2559bIqpKH2DlBKZ8JI2M0gE7uAqBuk6Z/r9\nXR+s9Av6mL+vNz4Qj0dw8yQDnWKreH/zCMp1nVuiUVzgvvZ2km6xpa0LnbO8Z/GD8A+4wHsBaSND\n4ux55PMeXn35A8wz6wCJ06DyVPclXQ1gxYA9L9k027Y/7nvpDMw/BuqBa7rzgtXV4a536sdU/ft3\n/QttGWKul+ZAHvIgh2jMHFpDbbiYcam/1/9gBkrdnWkBNv5hF6KhsFed9/w+vytH7J04elSn7qJa\nNN/+20XVhNlpCp5uauOJTJJvD69D+zhwh7mNz3F+9lP8l/wZqbc7eH/hZE47+wnKfOcgk+WUm2CU\n9Y6/+0D5/x+uroLxPOBy4EnLsqYDS/fZ/geK3dVXdXfiVlNT/JAL2V9UV4dV/ft5/XMLGgDYlg8g\n/TCyxmVIxqUpEx8Q9T+QgVZ3baiHzPoUuxpjCFPsVX8pJanf70LmJJ7PltMST8JB/jRnCZPlHg8f\nxFM83rCT84PBvbb7iPAPkW9w97mvk37mLB58fSx3Hr8N4/1JND73Af6LTzqWVe2Wgfb/31d3LkS6\n6qZ+BshYljWP4uStb1mW9VnLsr5kWdYU4DZgIvCGZVlvWpZ15ZEWWlH6svy6ZgBash7cIZLPRau7\nOELpj4xxPihAZlYrUu7dTil8lKKwMoNh+TBP7notsCYEX4hGiWoaLyQSrMvlPrFPhVbBt8+8GL0i\nTvvik3hzSGPxtZapSVx9xUFbxp2t3Tv3+fGaPb5XM1IUZQ9uYzEVYSsaZcOS1Bq1JS6RUgq+CyIU\nVqXJzU0ggjrMLOYol2mX9FNtYIDvhopurzkPaxozo1F+2dbG/R0d/EtFBRF979NvxAzxmYsHMeuR\nFB9uHsH5gLPdRBYKCLWkrtdTST8U5ShKJ4ofqRYElx9XVeLSKKUiAjrBu2rQqgyyr3TQ/mKxxyTz\nQjsy5uC9MIpebR7S7xzj8XBFKETMdXmgowNXfnJkcNrUEJU1Grmlk2iqaAN3MIX1a49KnZRjSwVj\nRTlKZD5HUtSQEZDUHSaNiZS6SEoJaVGD4FdrEBGd5gd3kP5rG7l34mg1Bt7zoof1O88NBJjk9bI2\nn+fFZPIT23VdcOHFIXB1GjQQrpfswnVHWhWlB6hgrChHyZJtjYQ7PLRIQd3oAoapsh8NdFqVSfCr\nNWhBndzrMZDgv7EScZjvDSEEN0ciVOo6rySTrMh+ci3zCSf5qKnV2dJS7JnpaG//xLi10vuoYKwo\nR3tXkkMAACAASURBVEFOSmYnBaYDrQgmj1PZtpQifbCHwd8dgQhoeM4KY4z1dX3QQQQ0jduiUQzg\nwY4OWp291xNrmuCCS0JslsVx4jbqcHZsPaLXVI49FYwV5Sh4LZnE01EcA2xBo36ct8QlUnoT39gA\n4f8aiv+6o3ORNtw0uSYcJiUl93d0UNin5TvpBC+hwSZtQOXm4azZ9exReV3l2FHBWFGO0K5CgdnJ\nJNUtxWywHToMG3Fok3OU/u9o37Rhht/PKT4fm/J5nk0k9tpWbB0H2YxOIOvlXa1FdVX3cioYK8oR\nkFLyVDxOAajYtR2AwHAvurpbjnKMCSG4MRymVtd5K5Xiw8ze6TePn+wlVl7sqvatnsCHsedKUUyl\nm1QwVpQjsCSbZVUuRxnbqG4s5p+uPu7IxgQVpbu8msZtZWV4gEdjMZoKhY+3CSEYd2Ex89Ooj47n\nVeet0hRS6RYVjBXlMGWlZFY8jg5kzGepao8QA0Yfp8aLlZ5TZxhcH4mQ6Rw/zu/RHV1/mp+kBiNi\nIRp3VrK90FDCkioHo4KxohymVxIJ2lyX472tOE6MaM5Dm9AYMlRlO1J61ql+P9N8PrYUCjy3x/ix\npmloY3yEgEGvnMOCzIulK6RyUCoYK8ph2JrP83oqRYWmkdWepaJhKABOuY6mqfFipeddH4l8PH68\nZI/x4/IpxeGTketH8F7TDlzpHuhXKCWkgrGiHCJHSh6LxXCBa8MBNshGhq8aA4BvqKe0hVMGLK8Q\nzCwrwwQeicVo6Vx/bNQX5zCMQJJadCLrCytLWErlQFQwVpRD9FYqxeZCgak+H7q+njySQZuKLeMK\ntb5YKaHBhsF1kQhpKXmgc/2xVmtCUDASB33xFObH3yh1MZX9UMFYUQ5Bc6HA3xIJQkJwdTjM8ty7\nICHcUkzmUDleBWOltKb7fB+vP34+kUAIgTnOTxhBZdbL8g/C5GW+1MVU9qGCsaJ0k5SSx+Nx8sA1\n4TAhTWNVYT1mW5SoY+ACepVK9qGUlhCCG8JhanSdN1Iplmez6OOKXdUjRQF3wSksyS4ucSmVfalg\nrCjd9G46jZ3LcZzHw8k+H01OE82kqV43gkokubB+1LMsKcrh8HXe/9gAHuroIDWmeJF4vLcDbWcN\nc1bbpS2g8gkqGCtKN7Q4DrMSCfxCcGMkghCCFfkPAQivGIMfMOtUq1jpPYbukb/6AU8SEdEYmjUA\nSeP80cTdeKmLqOxBBWNF6YIrJY90dJCVkmvCYcp1HaAYjCWYm4cD4B+mgrHSu8zw+5ni9bKhUGD7\nGANd+hlRuROxehzv7Hiv1MVT9qCCsaJ04e10mrX5PJO8Xqb5imNveZlnvdtIRXM5wXxxHac+SAVj\npXcRQvDZSIQqXWf+kOL64jOMrQipsWBupoujlZ6kgrGiHMSuQoHn4nGCnUn5hSiOCa8rrCOPS8W6\nkVRQTD+oVatgrPQ+/s77HzeMLPboDIn50YIJUosn0JjeVeLSKbupYKwoB+BIyYMdHeSBGyIRIp3d\n0wDL8+8Xv7FHUbk7GNeoYKz0TsNMk3OGRWgtE0hnFEMnLENk/Myz15W6aEonFYwV5QBeSCRo6Ezu\nMcW3952YVhZW4nElLQ3DGYQDAQ0RVh8npfc6y++nfYyBN6dRZZQBsGZtqsSlUnZTZw9F2Y/V2Syz\nUymqdZ3rw+G9tjU5TTTLOIN3VZHLBykD9Frz4y5sRemNhBCMPa74Xta89aA7tK2vxJFOiUumgArG\nivIJcdfloVgMDbg1GsWn7f0xWdmZ2ze8fjSVSAQCfbDqolZ6v4BVnGw4eouLrAMaa7HjG0tbKAVQ\nwVhR9uJKycMdHcRclytCIYabnwyyu9cX5+0xVO8eL65VwVjp/bSogVZjMKYhjxyuIaTGQnt9qYul\noIKxouzltVSKlbkc4z0ezgkEPrE9L/OsczZR5brs2DKUYRTH3PQ6dbcmpW8wxvnQHYPJohGA1WsG\nlbhECqhgrCgfW5XN8rdEgnJN45ZoFG0/Y8DFJU0Og3fUkMn7GGoUE+5rKvuW0kcYnXmqL1qyGqlL\nChtr+SATK3GpFBWMFYViussHOjrQgS+WlRHW9v/RWFlYAYBvvQVAhTAQIQ0trO93f0XpbfSxxWDs\nSw0mOHQ72k7BIzsSNDtqIlcpqWCsDHg5Kbm3vZ2UlFwbDjNiP+PEu63KL8NAklhbj4HEzJuqVaz0\nKVpIRxtsgjuMccE1ABQadO5tbycnZYlLN3AZ3dnJsiwN+C0wGcgCt9v23qP+lmUFgNeA22zbVrcE\nUfoEKSWPx2JsKRQ4ze9nxn7GiXdrc9vYKVsZ7Ui2bq5lDG0IAui1arxY6VuM8X5yjXlO2ZXmI8Bs\naGbr+CqejMX4XOeNUJSe1d2W8ZWAx7bt04HvAj/dc6NlWacAbwOjAHVppfQZLyWTLM5kGGmaXLfP\neuJ9rS6sBqBmex25gslY0QKo8WKl7zFPLl501rQNQpg55CZJnQELMhnmp9MlLt3A1N1gPAN4GcC2\n7YXAKfts91AM2KpFrPQZC9NpXkomqdR1vlxWhtlFa2BlfjkAxoZJAAz1Fq87dRWMlT5GH+ZBq9bR\nnLHUDNqG1lTNhMIqAkLwVDxOQz5f6iIOON0NxhFgz+l2TmfXNQC2bc+3bXvrUS2ZohxDa3M5HovF\n8AvBHQeZsLWbK13sgk0ESev6egDKPUFAtYyVvkcIgTktBJicli2e2tet286t0SgOcG97OwnXLWkZ\nB5ruBuMYsGcfnmbbtvpPKX3StnyeP7W3A3B7WRm1RtdTJxqcBtJkGV2ALQ3lVImd6IUIIqKjBdVM\naqXv8ZxSvJisbyue2neuL2Ocx+CSYJA21+XPHR24akJXj+nWBC5gHnA58KRlWdOBpYf7gtXVBx+X\n6+9U/Utb/8Zsjt9taCYlJXcNqWFGeaRbx81p3QBJKN8ynHxBZ4y2CVIn4JvoO6Q6lbr+pTSQ6w69\nsP7VkB25E7mpjoiZoX3DCNpC27mpejyNm7fzYTzFbDfHTbVVR+flelv9e5nuBuNngPMty5rX+Xym\nZVmfBUK2bd9zKC/Y1BQ/lN37lerqsKp/Cevf4jjc3dpKh+tyXTjMhILodnnei7+PQJJbMxWACaJ4\nH1inUuv27yh1/UtpINcdem/9takRxKY2pgXamN1Sx5vr3qKqbig3+oJsS2V5vrmd8pzLVL//iF6n\nt9a/p3TnQqRbwdi2bQncuc+P1+xnv3O6VTJF6WEdjsOv29pod10+Ewpx1kGWMO0rJVM0uFsZgqTB\nHo1JjsFBE2Jq8pbSt5knB8k81cLEpMFsYOXaDNRBQNP4clkZP21t5dFYjGrDYORB1t8rR04l/VD6\nvRbH4e62Npodh4uCQT4dDB7S8XbBRiIZ3BKleZePUdp6dN8wQE3eUvo2LaijDy8QKoSpwqVj7WDi\nbrEFO8gwmNk5oeue9nbaVYauY0oFY6Vf21EocHdrK82Ow4XBIJccYiAGWJVfBYBhHw9AvWYDNQAq\n4YfS53nOrAbgBDONtqae5ZlFH2+b4PVyZShEzHX5Y3s7WTWh65hRwbgPc5MOuUUJnK055AD6kLit\nBbJvxXC2ZA+635Z8nl+0ttLuulwZCnFZKHTImYWklKwqLMePpHXNCQDU6zakQogyHRFQHyGlbzOn\nREDLM9l1ECk/763bstf2cwIBTvP52FIo8EB7u5phfYx0dwKX0svkl6VIP96KjBW7jkSFjjkpgDnR\njz7Wh9D7Xzo7Z2uO7Osd5D9IQefCOuPEAL5Ly9D3uZ/wymyW+zs6yErJjeHwQdNcHsxOdyftMs74\njMGWjVXUeZuI6C4yITAmqC5qpe8THg19eIrgpijDkGxeWYE7yUUTxQtNIQQ3RCK0uS7Lczmeise5\nLhxWKTOPMhWM+xiZckg/1UZ+cRIM8JwXQbYXyK9Mk5sTJzcnjj7SQ/DrgxCe/tFqc7bmyDzbRmF1\nBiiO03qmh8h/kKTwUYrEkhTmtCC+S8oQ5Tpz0mlmxePowBeiUU72+Q77tVcVil3U4fWjcBxBvViO\nNmgi7mbQalUwVvoH84wqnE15TtFyNKway8a8zRjPhI+360JwWzTK3W1tvJNOU6XrnHsYQz7Kgalg\n3IcUVqdJPdSCjDnowzz4P1+JrDIxTIF0JM66DNm34hSWF/cLzKxCaH376tXtKJD87U5k3EUf68V7\nXpSOaoO/Pp9k6sXljHFcMi+0k1+YJL88zZt3hXndmyWiaXyprOyIZ4C+l1sMSPJ2sYt6rLYaLXIu\nLqDXqfFipX/wTK0j8/RSjktHmd9axsKtbzFm9IS99vFrGnd0zrD+ayJBVNeP6EJX2Vv/aDoNAE5T\nnuQ9Tcikg/eyMoL/VMtbS3P827d38fiD7SSSLoblJ/DFavQxXgofpci+1FHqYh8R6UhSDzQj4y6+\nq8oJfaOWXRGD3/y8jaUfZXngng5WS43Qd+twLotA0mX0YzGGCZ1vV1QccSDe7Gxms7uFetdlsz2G\nkDdHnWgErRZQM6mV/kNoAt9lYQSCcylgL9//e7tc17mjrAyvEDzY0cHK7MHnbSjdp4JxHyBdSfqh\nFshJ/DdVYp6h8/I9y3j5hSTSdXh/cZb/+f423rr7dTJzX8N3lURUGmRf7iD3XrLUxT9s2RfacdZl\nMU4MYJyYY/Vz7/O7n+4kkXA5VZ+H5uR4+N42nnlxLT86IcuyCRojtkruWqhTrh95isq52bkAjN5W\nRyrpod7fgNBApkIAnxinVpS+zHvWaNxgE2Nwqf5wHDGnbb/7DTVNvlJWhg78qb2dDblczxa0n1LB\nuA/IvR7D2ZjFsCBnP8bz33+JN1YMoky08nXrBS6qnAuu5G/rJ/K7WVWs+809mFPXg0+QfqSZwqa+\nd/WaX5YiOzuGVqUjxTO89x8P8cBrteQdwdWR57l4yg6uHfQ86JL5r0ZxlzowcjmiQsN5LUZh9ZHd\nBi4lU7yXf48yIGcfB0B95gO02iG4u1xEhY7wqY+P0r8EriwD4LzmMt7bNe+A+9V7PNxWVoYD/L69\nnW3qLk9HTJ1NejlnW47M39rB75DffDevvlfOvPxZVAaz3Pkvgxn2tS9z3g+v4Tv/PpgpkyTb5RAe\nTM9k7RvvoA2eDw6k/rgLN9Z3Fuy7zXlSD7WAAVJ/gveWuTydvxHdEMz8nGD6f9zO+htuY9ZXriN9\ns47mkejPa8jZG8B4GgSkHmw+ojovyi0iT56TyLNpzYnoumSUtNGHTiiO2av1xUo/5J1ez/ZIC4OR\ntL188Pf4RK+XmyMR0lLy67Y2thcKPVTK/kkF415M5iWpB5vBAVl4ktnO2bzrnEl1jc6d3xtCxZDo\nx/tGykw+9+Vabrk9iqt7eCx/K+u3bAT/O8i4S+b5/Xc59TbSkaTua4a0C+ZLLG4J87fCVQSCOnd8\nq5pB0wZxX0cH93R0kHRdLrGC3PX1CnwBnefz1/Bhqxc8byHjLumHmpHuoa+JlFIyNzcXHRjTEWJH\nY4hRlXG8IofwWQDow1QXtdI/VVwQwAFOXjaGQu7gLd6pfj83hMMkpOSXra00qoB82FQw7sUyL7bj\nNuaR2ge8qk9iYW46NYNg5l2b0TzP0Jb4KU2xf6Y5/gNaE/9Le+qPjBz3V268dQdSM/iLcyvrCztA\n7CS/IIGzufd3V+feieNsyYG2hEWun5cLVxAKwefubGBRZAn/f/NOPspmGapt5cu+x5muPUzVoJe4\n9Y5t+IOCFwpX84GbA20dhdUZcnMPPTn9WmctO92djMehed2nARjrWQeATBYzbxnjjixxvqL0VtVn\nWXzkz1KeN9j64vtd7n9GIMCNnQH5VyogHza1tKmXKmxMk5vdgaSdV30RFqcmUjGomckz7+VtI0Vz\nTqMFQQcaHpnBRwt+R+IHqka9xaduspjz6A084dzM572zGZoZROqRRkLfHdlrF+vLlEPmuSagwHsB\nh1cTFxEMpxl0yyJ+7zuZfDaEToyg8Ra79KU8hiSad4myhGiFZOrMQSy6/zZeTF6BFniTE1PDyDzb\nhGdqCOHv/nXn3Fxx4tZJssC8xZMRAsbE3kVUVOFskmAK9FHeY/RXUJTSEkKwbeIuJi4ehv+dCuSF\nbpefn91JdR6Px/lVayt3lZczTN1Y4pColnEvJKUkdf86QPBudZLFqYmYddvZftsDvBDK8xYmy9Fp\nwkNIq0GIclrxsRGdlei8jcmL4zbAzY/hagX+nD+XmH8bbqNG9s1tpa7eAaUeXgt5gw2VO3g5cToi\nnKH1ZpcV5Z8ijweMt3C8vwNzPZV6HY6IsgmdJRi8jckrta2kbnsAPZTghdQ5bK7YDjmD1KNru12G\nDreDJfkl1OBSWDWDxq06kye4lOW2ow85HrcxjzHGizB75wWNohwN086awLsY+HJeUo+u7tYxMzpb\nyEkp+WVbG7aaZX1IVMu4F8o8txTZVsbGcJw3mupxBzcSv/VRhocqmOKZSq1WS61eS4Wo+DhlHUBe\n5knKJBsKG1hWWMbK+mVkP/845kM38GBmBHeJLJnn4piTQ+hVZSWs4SfllzWQX+Yh4c3ylxYLGZFk\nb/YhK7KM8GzlvGCYYcZ5RMXVmOLvV9x5mafNbaPFbWFNYQ2LaxcQ++JDeO67mUdbLb7u7SDwkUF+\n9VbM8UO7LMe7uXdxcTnJdZn/5jkIAWeP2ggbQHRmJDIslehA6d/GjajgwfA6ToqbBJd5MdZsxDtu\nVJfHzQgECGgaD3Z08Pu2Nm6JRpmiEoN0iwrGvUxuWwMdixy8SF6OV8LQbZxy+1rOKvsHhuhDDnqs\nKUzKRBkneU7iJM9JONJh46SNvDxzPpseOI13cyYznDBb//Iaw790BcLTO7paY+2tNL6yizpqeCXr\nIx8V8PksMwYLLgsMJqgPP+CxpjCp0Wuo0WuYYE7gct/lbAhs4J07PmDFH6bwWizMleTZ+ewK6oZG\n0UMHvsl3RmaYl5uLB/CtOIGd272cNNVHRcsK8uweL3ZVMFb6PSEE08/xM+c5weWOYOkbKxk/xEM4\nePBzEMAUn4+ApnFPezv3d3QQc12ukaEeKHXfprqpe5HG5DJe+3A2/lglH2JQGBHnB1+fyGcrb+gy\nEO+PLnTqjXq+NukGrr8D5nskcSCw9mQeW/8fpJ3E0a9EN3U4DgvSae5u2cqfVqeoa6hhC4LVZS4X\n32nw43HDuCE8jKB+aNeLmtCoN+qZOfx6vvqNKuxohkYE4a3jeXrVr2jONez3OEc63J+6n3bZwcmu\nw3tvXoimwfkXBylsWgeBME6DQAQ0tCFqWZPS/1326SEYp5nsRDB2xQQeWH8/23JrunWs5fHwzfJy\nQprGU/E4f2psoqDu9nRQKhj3AlJKXk4+zC/a/8yU2eeQBdYO1/n21+oJB47OiX/a2OF8+et1vGPo\neBydYc9dyk9i/8b2wv6D09GWdF0+jCf5azzOj1pa+D/NzTwSi7EhZ3DJM8XZl/OiGv/2j3V8ekgl\n+lGYZDZqUDnf/tYI5oWKb/MJf72C/0n8L8uz7+61n5SSpzNPs7KwkjGY1C49nuZdAU6e5qPC6EC2\nt6IPnoxsc9DH+fp8vm9F6Q4hBNffWM3aEV4EghmPXsPPE79iWXZ+t44fZpp8u6KCoYbB620xft3W\nRtx1j3Gp+y79hz/8YU++3g9TqYE7qB8Metm3/gVZ4KHkr5iTXcl5v7uV+qSflRUeLvt2Ld6jnOEp\nWqZTPsFH27tJxsRCLHN0Xhn2GOUiyBBj5FF5DSkl7a7Lhnyepdksc9NpnkskeDaRYF5Hgo35PGnX\npUJvJSXmccqjGlN3hVnr1Tn7e3VEy448jeWe/AGNUScF2Tg/wciUhx07BvHS+CcxyTDKGI8Qgjm5\nObycfZkadG5wUrz8xBfIZjzc8sUyjI3LKCz/EL32ItztITyfCmMMP7zu/f39/weKgVx36Lv1F0Iw\n6pQgDW/HGZE22LqzllfHP4mXPKPM8V0e79c0pvr9xHTBsnSGDzIZRprmUUlX25cEg97/r6t91Jhx\nCSXdJH9I/pSN2VYG//kGTm2JktIF075dg8d7bFpfQ0d42P7ZCni0hYvfPpX7onEeOvUpNjjruc7/\nBXRR/JC4UuJ0fi0AjpRkpCTpuiR3f3VdEq5LSkpaHYcWx6HNcdg3TYBPCMZ7PBwfDVKey/Ju/n5W\nFNZS9uzFnLehjgIw7hvVRKLH5gNaXqGj3VWNc/dOzl9dz8a/fZpnL5vNNnc7k8zTmJV5mhBwA0m2\nLL+B1uYQ02f4qajUSb20BACZ6FxfrMaLlQHG9AiGf6Wa/C92cv6qMTS8cSbPnDebJreZawO3fnzO\nOBCvEHxz2CAqN0leTCa5u62NS0Ihzg8E0HrpMstSUMG4RHY5u/ht8me05LKUP3wdZ2waiolL8Jpy\nvOGug5KUknRnUEx0BshEZ4BMui5pKcnvfgCFPZ4XxsGnR+uM3eAw6fmLWJI7j3lTXd6Nb0fHLAbh\nw6hTQAhqDYMqXafOMD5+VOs6mhB4yiX/t+Hf2eG0U/bcxZz+/mTCOIjzIkQOs7XZXdExPjpmhCif\nl+DUhaewWAjeu/Q13iusxkByPQ7u9i8y57Ux6IbLeRcGcRq3kF/6PmLwCJytOqJCoFWpj4wy8ATr\nfbRPCVDzYYoT3prBCqkx99NzaE7+nNuCX8UvDp4ERwjBRaEQYzweHuzo4IVEgjW5HJ+PRCgbYK3k\nA1FnlhLY6mzll4mfkc5B9UM3UL5xBJPJIYaY+Gb8fdahIyUtjkPzHo89n+cOY0KESfFG4bMvMRj1\nW4dzZR77FS9ZN0F+egyv8DBUr0FHYAiBBhhCoFO8wg1pGsHOR0CIj78v0zT82oG71ZNukv/e+mN2\nOB3UPnMp4sMTOJUslBmEL4ke8LijKfKZcmJLUpyRyLNswTT8rkbzZS9zQVslS16/nRVLPYDLBZcG\nKSvXSc56BqTEc/LVZJ90MU4I9dqEKYpyrEWuLie2Is15uTyr55xBlaOx+sI3uTv+P3wz/B0CItDl\n7xjr8fAvlZU8GouxLJvlP1ta+EwoxOl+/4BvJatg3MOanCZ+k7ibdBZqH7yRWMNwbhVJwKD9mghL\nMmm2FQpsyefZXih8ossXikGxStep0DRCnY/dQTHUGSD9moYpBCYUv3YG1I/f8DWQObcNY3aMs0jy\nxmthItpCWs5cQESfwMzgV7rsfuqulEzxq8T/ss1po/bpy2lfMplbRBxdmgSuLkd4emYeofBr+K8s\nJ/1wC5eS4NFFUxmxfRRzGqtwHBg+0uTyq0OMHOWhsGENhdXL0MdYkBkCtKsuamVA08oM/JeWIZ5p\n4xISPD13BnWOTuMls/ld4m6+FvonvKLrHq6QpvGlaJT56TTPJhL8JR5ncSbDZyMRao2BG5IGbs1L\noLXQxq+SPyWRllQ9dAvtm+s4OxInGjNZdJLOc5EkdKZS1oE6w2BwZzdvla5TqetUGQYhIY5KC817\nQZTcwgSnpTRWOh3seOXTVDoaS86ez32pP3Bb4MgDclqm+U3iZ2wrtFDz1GdoXzaRaexguCxDr/di\nnNj11fTRZE4NkpuXYPRGOIldfLClhvJywaVXRpg8xYsQAiklmRdnAeC7+Cqyz2cAMMapYKwMbJ5P\nhcktTDChESbSzPJ3p1MjNTZd+ip/Sv6GrwS/gSG6DitCCGYEAkz0enkqHuejbJb/bmnhDL+fi0Ih\nQgfpZeuvVDDuAY6UrM3FeWjD68QSl+J9ZAyJbTqhesm0BpNkANad7+fsgIehhsFQw2CQYWAc424b\n4dfwXVpG+vFWbilv5fdtktjsc6l0NZaeO5eHU/dzS+CLhx34szLL7xK/YHO+iaonriS24niG08D5\ngSikwX9tRY93+wpN4L+unMRPdnCx32BM7hHGhuJEht+JEMVJWoWVS3Aa1mNMnIJeN4rC+i1og020\nbozlK0p/JnSB/4YKkj/fyRUBQVNqBzsXTKPC0Vh9+cs8mLqXWwNf2isz4MFEdZ0vlpWxNJPhmUSC\nOek0izIZLggGOSsQwDOAuq7V0qZjIOO6rM3lWJTJ8HIiwZPxOAsyBbKJYXgeqURr1AgPa+UL2SSh\nDi+R6yuYflwZx3m9DDVNIp0TnnqCNtRDfmkKrTnAxOjbrM5VEt84gbALG0YswiM0Rhv1h/x7Xely\nf+qP2NkGKp64iuTK4xguNnJzTRNa+xA8Z4bwnHbgbFjHkhY1kB0O7gbJoLFBaJxL/oMF6MNGIsoq\nST30e2QqQeCWO3B3mOQXJDFPCWJOOLI7NfXV5S1Hw0CuO/Sv+mvlBm67g9wIkyPLWZczaN9mEYyF\n2DR2HnHZxvHGpL0utLuq/yDD4Ay/n5CmsS6XY3kux/xUCgcYbBiYfTwod2dpkwrGR0GH47Aql2Ne\nKsULiQRPJxIszmRYl8/T5rqYop1CdhWhR3zI7X4m6x/wBWMl/l0T0Ou9+K/p+RbibkII9BEe8gsT\neORIxuuPs0aOIr5xPJ6CzooRrzJcG06NXnNIv/eV7N+Ym36PyOPXkFk1npHaBj5XPhc9di7CqxH4\nUnWPjRXvjz7SS/7dBG57Gd6L6ymseZ/8+wtwdjbibFiDOXUG3lPPJPNKB+7WPN6LytBrjuwuNP3p\nhHyoBnLdof/VXx9d/Pxo2TqO9z3ORmcI7Y3j8LdFWTfuTbxC3+sivjv114RgpGkyw+9HF4JN+Twr\ncznmptMkXJcKXe+z3dcqGB8DjpRsKxRYms0yJ5Xir4kELySTfJjN0lAokHJdRpomp/h8XBAMUu1Z\nyOrULEJ/Pp3CjmqmGO9xeXAOwr0R8hD8Sg1auLSjBVrUQAvrFD7K4ItYWM69rNOPJ7VxLCJnsnTE\nLE4wpxDSupdfdkVuGY/Hn8b/2LU49jhGm5u43nwGr3YrMgX+myoxRpQ2L7bwaIiARuGjNLK5HN8N\nU3HWLsHdugl0g+Atd5Kblyf3ehytxsB3RRlCP7ILpv52Qj4UA7nu0P/qLzwaIqhTWJLGU3c8+93/\n/gAACpZJREFUE1J/YJM2lo7GesyWclaOfYGxRj0VeiVwaPU3hWCcx8MZfj9BTWNzoYCdy/F2Os2a\nXA4NqNL1Yz6MdzR1JxgL2bP5QmVT06Hf7L1UdmeT2pzPs6nzsTmfZ8+3lF8IRpsmoz0e6k2TYab5\ncZfKuvwafrXrPjz33QS7ajjF/yEXypfRw9/AbdLxXhjBd1l5aSq3Dykl6UdbyC9IIio3E0/M4hHj\nazQlQxSmLyJ62Xy+E/l+l8sXdjm7+Enrj3EfuRpt/RjqQ1u5NvcAZvCryDY/3kuj+C7qPXeMyrzQ\nTvaVDrRBBv4veMi++hiGdTxCm0b6sVZEmU7oW7VoFUd+wVRdHaYvvf+PpoFcd+if9ZeuJPnrnThr\ns2hD20jtupfH9S+zNV2DM3EFvute4ntlPyCshY+o/nkpWZbNMq8zGENxieYEr5cTvV6O93oJ9PIW\nc3V1uMsrh4MGY8uyNOC3wGQgC9xu2/b6PbZfDnwfKAD32bb9py5er9cG44KU7CgU2FYosLVQYFs+\nz7ZCgdQefx9BcYbzSNNklGky0jSpOcD4bsyJ8aPtPyF3341oTVVMiy7n/PRf0QJfQ7b78MwI4bu+\nolflOZY5l8TdO3G35BCVc0kkFvOIcRe7klEK095n5BVLuCPyjwdcvpCVWX7c8p+0PXgp+saRWGXb\nuCr1R8zgrci2OsJnlSGujfSqtbpSSjLPtpN7PYZWZxL8xiCctRlS9zcjghrBb9ai1x6dm6T3xxNy\ndw3kukP/rb9MuyR/sxOnIYeo2kom9hhPBL5KQ3sFznGrGH7jfL5R9l0G1USPSv2bCgUWZTJ8lMmw\nw3GA4g0Whpsm4z0exnk8jDDNXjfx62gE46uBy2zbvs2yrFOB79m2fWXnNhNYCZwCpIB5nfvuOsjr\nlTQYu50t3V2FAk2OU3x0ft/sODj77F+t6wzpnN080uNhhGHg68YVmCtd7t72M7bdewlacyXTK5dx\nXvxZdO9XcONhzNND+G/oXYF4N7c5T+InO5BZF23QWyRaPuBh8w52JSpwTlxK+WXzuavqq1TqVXsd\nF3NjPND6Bxr+/Cm0TSMYH17PVbkHMcJXIpuLY+Mj/+8YmtuTJarZgUkpyTzdRm5OsUvabSmAIQh+\nY9Bh56Hen/56Qu6OgVx36N/13zMgE1pLLvcMfwneQUNbDc54m7M+38CXxtx11Ou/o1Dgo0yGlbkc\nDfn8x1kDNYqTvkaaJsM6swDWGsZBkxIda0cjGP8UWGjb9hOdz7fatj208/vJwP/Ytn1x5/OfAfNt\n237qIK93TIJxTkrSe+RM7nAcOly3+HAc2l2XmOvS7jgU9nN8QAhqDIMhezwGdzPw7ist0zzV+AQf\n/XEaWmsFpwXf4dz8uwjjC5CKYp4Wwn9j7wzEu+VXpkn9fhdIEMFdpHOv8bDvcnbEa5CBJNr5c/ji\nGSdh+U7AlS6vx57n1cWtuPOnoTVVYYVXcnXmXYzohcjmKrRqg+A/1TJoZFmvPSFJKcn8pZXcvAQY\nELxz0FFfV9yfT8hdGch1h/5ff5nqDMibcxBeRz77PI/5bmRzbBTOuLXc8jXJRE7r1hrkw5F2Xdbl\ncqzJ52nI59mSz3/iXF+maVToOhW6TrmmUa7rxYemUabr+IU4ZqtYuhOMu/rLRIDYHs8dy7I027bd\nzm0de2yLAwfNa9iSzvD2uoaPW6Du7ocUuIDsfOz+eV4KCtCZW1ns9TUjBRkgKwUFDl5PgSQgJEOF\npFxIyoSkXHOJAuWai28/h7cd9DfuLZaJ8dGOLTRsh/iOKrTGsxmXNTndu5Hh6dHgToUcRM4th8+E\ne3UgBjCP8xP651oyz7dTWFWDj5u4zVnDytA2FmWOI/PsRTw+v4n6Gc+yeauX3LITiGZ96Lh8yree\n4xIRhLwJ2QzaMA+BmVVowd69RlcIge/6CrQ6E32IB6NeJfhQlO4SAY3gVwd1BuR6TPEP3FxoZIl/\nJ0vWjGHWvyaYNXg+FXUdjKkzmVQzAq9xdD9jVZ2P0wFHQrOr0Uzxa4ur0SoFO2WB7QeIFwKJF/AK\nia/zq1cUf2YIiUExGZMhZPEroAuJRrE17vUYnDJs8GF3kXcVjGPAnotBdwdiKAbiPbeF6SKGvfbd\nVZyyc3/jbwdqnffE5LIj77oIUMZF7DspKQ/ZOjDBGO/HnOyn+oo6mlsSR/x6PUEf5iV41yAKazNk\nnmuDTeOYmICJSCAHTVH464l7HJEtfskMAU1inhzEc1YYfaSnV40RH4zQBN5PRUpdDEXpk0RAI/i1\nQWTfilFYlYFNQ5iShinkIOGBNSNgTc+VJwyMAv7evDvWCrx08kd85tYph3V0d8aML7dte6ZlWdOB\n79u2fWnnNhNYAZwKJIH5nftuP6ySKIqiKMoA1VUwFvx9NjXATOBkIGTb9j2WZV0G/IBi8/Je27Z/\nd4zLqyiKoij9Tk+vM1YURVEUZR+9e6W0oiiKogwAKhgriqIoSompYKwoiqIoJaaCsaIoiqKUWElu\nF2RZ1nhgAVBj23b/uZVJFyzLCgKPAmVADviCbduNpS1Vz7EsKwo8THEJoAf4R9u2F5S2VD3Psqyr\ngGtt276p1GU51rrKbz9QdKYT/m/bts8pdVl6UucS2PuAEYAX+A/btp8vbal6hmVZOnAPMI5i0ow7\nbNtecaD9e7xlbFlWBPgpkOnp1+4FbgcW27b9KYpB6TslLk9P+xbwmm3bZwO3Ar8paWlKwLKsXwD/\nBV2kjes/rgQ8tm2fDnyX4md/QLEs6zsUT8qlvW9oadwENNm2fRZwEfDrEpenJ10GuLZtnwH8H+A/\nD7ZzjwbjznXLfwC+B6R78rV7A9u2d5+IoXileChZN/uDnwN/7PzeZAC+ByjeUOVOBk4wngG8DGDb\n9kKKN5YZaNYBVzNw/ud7epJiLgooxpv93R6gX7Jt+1ngK51PR9LF+f6YdVNblvVF4B/2+XED8Lht\n20sty4J+/OY8QP1vtW37fcuyXgcmAhf0fMl6Rhf1rwUeAr7Z8yXrGQep/xOWZZ1dgiKVysHy2w8I\ntm3PsixrZKnLUQq2bScBLMsKUwzM/1baEvUs27Ydy7IeAK4Crj3Yvj2a9MOyrLXA1s6n0yneEers\nHitAL2IVr0b+Ztt2fanL0pMsy5oEPAb8k23br5S6PKXQGYy/Ytv2Z0tdlmOt885vC2zbfrLz+Rbb\ntoeVuFg9rjMYP2bb9mmlLktPsyxrGDAL+I1t2w+UuDglYVnWIGAhMMG27f32CPboBC7btsfu/t6y\nrI3045bh/liW9T1gq23bD1HM5z1gumwALMs6juLV8XW2bS8rdXmUHjEPuBx4sjO//dISl0fpQZ1B\n6FXgLtu23yx1eXqSZVmfB4batv0jikNyB71jRUlmU3caiHk47wX+bFnWbRTvxjWzxOXpaf9FcRb1\nLzuHKdpt276qtEUqid13Cx0IngHOtyxrXufzgfae39NA+Z/v6V8p3lr3B5Zl7R47vti27YEwgfcp\n4AHLsuZQnCPzTdu2swfaWeWmVhRFUZQSU0k/FEVRFKXEVDBWFEVRlBJTwVhRFEVRSkwFY0VRFEUp\nMRWMFUVRFKXEVDBWFEVRlBJTwVhRFEVRSkwFY0VRFEUpsf8H8EfWx2z/5AMAAAAASUVORK5CYII=\n", - "text": [ - "" - ] - } - ], - "prompt_number": 15 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `cut` and `clip` parameters allows you to control how far outside the range of the data the estimate extends: `cut` influences only the range of the support, while `clip` affects the fitting of the KDE as well." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "with sns.color_palette(\"Set2\"):\n", - " f, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 8), sharex=True)\n", - " for cut in [4, 3, 2]:\n", - " sns.kdeplot(data, cut=cut, label=cut, lw=cut * 1.5, ax=ax1)\n", - "\n", - " for clip in [1, 2, 3]:\n", - " sns.kdeplot(data, clip=(-clip, clip), label=clip, ax=ax2);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nXXvH/fKaAAAAAElFTkSuQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 16 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As in the case of the histogram, plotting shaded density plots on top of each other can be a good way to ask whether two samples are from the same distribution. This also implements a simple classification operation. For a given `x` value with an unknown label, you should conclude it was drawn from the distribution with a greater density at that value.\n", - "\n", - "A legend can be helpful when overlaying several densities. This is done either by providing a `label` keyword to `kdeplot`, which explicitly assigns a value to the data, or by inferring the name if you pass an object (like a Pandas Series) with a `name` attribute." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "f, (ax1, ax2) = plt.subplots(2, 1, sharex=True, figsize=(8, 6))\n", - "c1, c2, c3 = sns.color_palette(\"Set1\", 3)\n", - "\n", - "dist1, dist2, dist3 = stats.norm(0, 1).rvs((3, 100))\n", - "dist3 = pd.Series(dist3 + 2, name=\"dist3\")\n", - "\n", - "sns.kdeplot(dist1, shade=True, color=c1, ax=ax1)\n", - "sns.kdeplot(dist2, shade=True, color=c2, label=\"dist2\", ax=ax1)\n", - "\n", - "sns.kdeplot(dist1, shade=True, color=c2, ax=ax2)\n", - "sns.kdeplot(dist3, shade=True, color=c3, ax=ax2);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nAABJRU5ErkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 17 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you want to plot the density along the y axis, use the `vertical` keyword." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "plt.figure(figsize=(4, 7))\n", - "data = stats.norm(0, 1).rvs((3, 100)) + np.arange(3)[:, None]\n", - "\n", - "with sns.color_palette(\"Set2\"):\n", - " for d, label in zip(data, list(\"ABC\")):\n", - " sns.kdeplot(d, vertical=True, shade=True, label=label)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 18 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also use `kdeplot` to estimate the cumulative distribution function (CDF) of the population from your data. This plot will tell you what proportion of the distribution falls at smaller values than a given point on the `x` axis:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "with sns.color_palette(\"Set1\"):\n", - " for d, label in zip(data, list(\"ABC\")):\n", - " sns.kdeplot(d, cumulative=True, label=label)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 19 - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Multivariate density estimation with `kdeplot`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also use the kernel density method with multidimensional data. For visualization, we are mostly concerned with plotting joint bivariate distributions. As with the hexbin plot, we will color-encode the density estimate over a 2D space. The `kdeplot` function tries to infer whether it should draw a univariate or bivariate plot based on the type and shape of the `data` argument. If using a 2d array or a DataFrame, the array is assumed to be shaped (`n_units`, `n_variables`)." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "data = np.random.multivariate_normal([0, 0], [[1, 2], [2, 20]], size=1000)\n", - "data = pd.DataFrame(data, columns=[\"X\", \"Y\"])\n", - "mpl.rc(\"figure\", figsize=(6, 6))" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 20 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.kdeplot(data);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 21 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also pass in two vectors as the first positional arguments, which will also draw a bivariate density." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.kdeplot(data.X, data.Y, shade=True);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nJAAAAABJRU5ErkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 22 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As in the univariate case, you can also set the kernel bandwidth and choose different points at which to clip the data or cut the estimate. However, only the gaussian kernel is availible for the multidimensional kde.\n", - "\n", - "When specifying the colormap, the multivariate `kdeplot` accepts a special token where colormaps that end with `_d` are plotted in a way that maintains the overall color palette but that uses darker colors at one extreme so the contour lines are fully visible." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.kdeplot(data, bw=\"silverman\", gridsize=50, cut=2, clip=(-11, 11), cmap=\"BuGn_d\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 23 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.kdeplot(data, bw=1, clip=[(-4, 4), (-11, 11)], cmap=\"PuRd_d\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAXYAAAFtCAYAAAATT0E9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzsnXdcVuX7x9/PYu+9BJwoouDeK/csMytnaY7S1Bypuc09\n0tS0zJGamWmpuc3cExRQQJShKCAgG2Q8POv8/qD6lqWsBwF/5/169UrlOff94ZznfM597vu6r0si\nCAIiIiIiIq8O0ooWICIiIiKiX0RjFxEREXnFEI1dRERE5BVDNHYRERGRVwzR2EVEREReMURjFxER\nEXnFkFdUxxqNVsjIyKuo7ouNtbUJok79IerUH1VBI4g69Y29vbmkqM9U2IhdLpdVVNclQtSpX0Sd\n+qMqaARRZ0UgTsWIiIiIvGKIxi4iIiLyiiEau4iIiMgrhmjsIiIiIq8YorGLiIiIvGKIxi4iIiLy\niiEau4iIiMgrhmjsIiIiIq8YorGLiIiIvGKUKaWAl5dXC2B5REREJy8vr1rADkAHhAHjIyIixPJM\nIiIiIi+ZUo/Yvby8pgNbAMM//mkNMCsiIqI9IAFeL7s8EREREZGSUpapmGjgTQpNHKBxRETExT/+\nfALoUhZhIiIiIiKlo9TGHhERcQDQ/O2f/p5xLAewLG3bIiIiIiKlR59pe3V/+7M5kFnUAfb25nrs\nvvwQdeoXUaf+qAoaQdT5stGnsQd7eXl1iIiIuAD0BM4UdUBKylM9dl8+2Nubizr1iKhTf1QFjSDq\n1DfFefjow9j/jHyZCmzx8vIyAMKBn/XQtojI/yu0Wi2ZmRlkZWWSnZ3N06fZ5OTkoFQqUSrzKShQ\notFo0Wg0GBhIUSo1yGQypFIpCoUcIyNjjIyMMDY2xtzcEgsLSywtLbG2tsHU1BSJpMgaDSKvAGUy\n9oiIiIdA6z/+HAV0LLskEZFXF0EQSE9PIzb2EYmJj0lMTCQpKZEnT5JIS0slMzMDnU5XdEOlwNjY\nGDs7BxwdHXFxccXNrRpubu54elbH2tqmXPoUqRgqrDSeiMirTk5ODjEx93n0KIaYmBhiYu4TG/uQ\n3Nzcf31WoVBga2tPvXr1sbGxwcrKGnNzCywsLDA1NcPY2ARj48LRuFyuQCaTYWdnTnp6LlqtFq1W\ni0aj/mtkn5eXR07OU7KyMsnKyiI9PZ2UlCekpKQQF/foX/3b2NhSs2YtatWqQ9269fDy8sbSUox/\nqKqIxi4iogdUKhX370cRHn6HyMh7REdHkpDw+B+fkclkuLi44efXGHd3T1xcXHF2dsHZ2QVra5sS\nT5OUdk44NzeXx4/jiY+PJS4ulgcP7nP/fhQ3bvhz44b/X59zcXGlYUM/fH0b4+fXCCsr6xL3JVIx\niMYuIlIKVCoV9+6Fc+tWECEht4iMvIdarf7r52Zm5vj5NaZWrdp4etbAw6M67u4eGBgYVKDqQkxN\nTalTx4s6dbz+8e+ZmRlERUUSEXGXe/fCuXcvnJMnj3Hy5DEAatWqTatWbWnduh0eHp7ifH0lRjR2\nEZFi8uRJEjduXMff/zqhobcoKCgAQCqVUr16Tby96+Pt7YOXVz2cnJyrnPFZWVnTrFkLmjVrARQu\n5EZHR3H7dhBBQTcJCwshOjqK77//DhcXVzp27Mxrr3XF1dWtgpWLPItEECosnYtQVUKLRJ36oyrp\nTErKJDw8jOvXr+Lvf43Hj+P++rmHhyd+fo3x82tMgwa+mJqaVYjGl3kuc3JyuHHjOteuXSYgwJ+C\nAiUAXl716NatJ506dcHY2LjCdZaWKqSzyBGDaOxFUIUutqhTD2i1WkJCbnH9+kXOnTvP06fZABgZ\nGeHn15gmTZrRvHkrHBwcK1hpxZ7L/Px8rl69xLlzvxMcHIhOp8PExJTOnbvSp88buLt7VAqdJaEK\n6RSNvaxUoYst6iwD9+9Hc/r0CS5cOEdmZgZQGCnSsmUbWrZsja9vo0oxP/53Ksu5TE1N4dSp45w4\ncZS0tFQAWrduxzvvDKFOHa9Ko7MoqpBO0djLShW62KLOEvL0aTZnz57m9OlT3L8fBYCFhSVt23bg\njTf64OpaE6m08pYsqEznEgrfdq5fv8L+/XuJiLgLQJMmzfjkk4nY2VX+efjKdj6fR3GMXVw8Ffl/\nhSAI3Lt3l+PHD3Px4jlUKhVSqZSWLVvTvXsvmjZtgVwurzI3eWVCJpPRpk17Wrdux+3bwezdu5vA\nwBsMGzaMTp268N57H+Do6FTRMv9fIBq7yP8LtFotV69eYv/+vURFRQCFcdq9evWjc+euYoy2HpFI\nJH8tLN+6FcTOnVs4d+53Ll26wIABbzNo0DAMDQ2Lbkik1IjGLvJKo1KpOH36JL/88hOJiQlIJBJa\ntWpL375v4OvbqFJPtbwK+Pk1pnPnXezff4gdO7by008/cPHiOSZMmEKjRk0qWt4ri2jsIq8kKpWK\nkyePsW/fHtLSUlEoFPTs2YcBA94R465fMlKplNde60rr1m35/vsdHDr0M7NmTaNv3/6MGvVhpVuU\nfhUQjV3klUKr1XLq1DF++GEX6elpGBoaMWDAO7z55tvY2IiJrioSIyNjRo/+iI4dX2P16mUcOXKQ\ne/fuMHv2QnHuXc+Ixi7yynDzZgBbtmwiNvYRRkZGDBw4iDffHCjOn1cyatf2Yv36zWzc+CWnT5/k\n44/H8Omns2jevGVFS3tlEI1dpMrz+HE833yzgZs3A5BKpfTo0Zthw0aKI/RKjKGhIVOmzKB+fR82\nblzH/PmfMXbseN54462KlvZKIBq7SJWloKCAffv2sG/fj2g0avz8GjNmzDiqV69Z0dJEikn37r2p\nWbM28+fPYvPmjahUat5+e1BFy6ryiMYuUiW5fTuYL79cTVJSAra2dowdO562bTtUucRbIlCrVh1W\nrVrHzJlT+O67b1GrVQwePFy8lmVANHaRKkV+fj7fffctR44cQiqV8uabAxky5H1MTEwqWppIGXBx\ncf3L3Hfv3oFWq2X48JEVLavKIgbxilQZ7twJZdy4URw5cohq1TxYs2Yjo0ePe6VNXRAEKjDtx0vF\n0dGJlSvX4eLiyo8/fs+ZM79VtKQqizhiF6n06HQ69u//kV27tgMwcOAghg59v8rFPytz80hNTCI1\nMYm0xCdkpqTxNDOTpxmZ5GRlU5CXT0F+PgX5SnRa7V+GLpFIUBgaYmBU+J+ZpSVmVpY4ONthZG6J\nnbMTdi7O2Lu5YGZpUcG/Zdmwt7dn4cJlTJr0EevWraZaNY9/FQQRKRrR2EUqNRkZ6axatYzg4JvY\n2toxY8YcGjTwrWhZL0QQBDKSU3h0L5K4yPskxDwi8WEsmSmpzz3GxNwMIxNjLG1tMDAyQiaXIZFI\nkUgKH2zqAhWqggIK8vNJePAQjVpN+H+0Y2Vvi1utmrjVrkGN+vWoXr8uBlVs+76bWzVmzJjNggWz\nWbRoLuvXfyMW2y4horGLVFrCwkJYunQhGRnpNG/eiilTZlTKAss6nY6kR7FEBocSHRLGw/AInmZk\n/uMzlrY21G3ih72bC7bOTtg5O2Jlb4eFtTVmVhbI5MW/FQVBoCAvH7lETfTdGFITkkhLTCLpURzx\n0Q8IuxZA2LUAAOQKBdXr18W7eRN827XC1qni88gXh+bNWzF8+Eh27tzGmjUr+fzzZeJiagkQjV2k\n0iEIAocO/cLWrV8DMGrUR7z55sBKdWMrc/O4ezOYO9dvcPdmMDmZWX/9zMreFt92rfDwqo173dq4\nVPfE1MJcb31LJBKMTE2wtzdHbmIJz6RcyU7PIDYymujbYUQGhxB1K5SoW6H8+u0OPOrWxq99G5p3\n7YSZVeV7SP6dd94ZQkjILW7e9OfChbN07Ni5oiVVGcR87EVQVdK3vio6lcp81q5dxcWL57C2tmbW\nrAX4+DR8iQoL+S+dytw8Qq5cJ+j8ZSKDQ9BqNABY2Fjj1diX2n4NqO3XABtHhwrT+F88zcgk7NoN\ngi9cJupWKDqdDrlCQaOObWn3ei88vGpXCp3/RWJiAh9+OAJTU1O+/XYXZmblV4KwCt1DYj52kapD\ncvITFi6cw4MH0Xh7+zBr1nxsbe0qVJNWoyE8IIiA384SHhCIRq0GwK12DXxaNsOnZTPcatesVG8T\nz2JubUWrXl1p1asrOZlZ3Dx7kStHTnDj9DlunD5HzQb16TtqGNW961a01H/h7OzCoEHD2LlzG7t2\nbWfcuIkVLalKII7Yi6AKPcWrtM6wsBAWL55PVlYmvXr15cMPJ6BQKCpAYSGavEyO7T5EwOlzf02z\nOLq70eS19jTu2A57V+cK0/YnZbnmOp2OyOAQLhw4QnhAIAAN27akz8hhOFZz1afMMn831Wo1H344\nguTkJ2zf/gP29uXzRlSF7iFxxC5S+Tl58hhffbUWgPHjP6FPn9crRIdOpyMi8DYXDh7h7o0gAEzM\nzWn/Rm9adO+MW60aFaKrPJBKpdRt4kfdJn7cDwvn8JadhFy+zp3rN+k6+C26vjsAeQU+WP+OQqHg\n3XeHsmbNCvbv3yuO2ouBOGIvgir0FK9yOnU6HTt3bmPfvj1YWFgwe/ZCGjb0e+maNGo1Qecu8ftP\nB3gSGw9AHT8fWvXuQcPWLZAbVA6DexZ9XnNBEAi5fJ0DX28lMyUNZ093hnw6kWp1apW5bX3o1Gg0\njBo1jPT0NL77bk+5TNFVoXtIHLGLVE6USiVffLGcy5cv4OrqxuefL8fFRb9TAEVRkK/k6rFTnD9w\nmMyUNKQyGc26dKRD/z40btOoStzk+kIikeDbrhVejX05vHUXV46eZO2kmQwYP5o2fbpXtDzkcjlv\nvz2YDRvWcOTIId5/f1RFS6rUiCP2IqhCT/EqozMy8hELFswmMvIePj4NmTv3cywsXl7onaqggCtH\nT/H73l/IyczCwMiIVr260nFAP2wc7P/SWdnPZ3lqvBd4i13L1pCblU2HN/vyxpj3kcpkpWpLXzoL\nCgoYOnQgCoWcnTt/0vsaTFW45iCO2EUqIbGxsUyZMoGkpAS6dOnOxIlTX9oiqVar5fqJ3zm1+yey\n0tIxNDGm+9B36NC/j17jzF8F6jbxY8qGlXw7ZzEXDhwh5XEi7302BSPTisvLY2hoSJcu3Tl06Geu\nX79Cu3YdK0xLZUdMAiby0rh7N5yRI0eSlJTA4MHDmTJlxksxdUEQCL3qz4oxk9i37mvyc3Pp8u4A\n5n+/mV7vDRJN/TnYOTsxef0K6jbxI9z/Jl99Opf83NwK1dSrV18Ajh8/UqE6KjviiF3kpXD16mVW\nrFiEVqtlwoQpf92g5U189AMObNrK/dBwpFIprXt3o+fwQVjYiOXyioOxqSljlszlp7Wb8D91hs2z\nF/PRsvkYGhtViJ5q1dzx9q7P7dvBpKamYGdnXyE6KjuisYuUO6dOHWP9+jUYGBiwatUq6tQp/52k\neU9zOL5jD5ePnkTQ6WjQujl9Rg7DyaNauff9qiGTyXh38jjUBSqCzl9i64JljFk0G0UFZdfs1KkL\n4eF3uHDhHAMGvF0hGio74lSMSLkhCAI//fQDX365GjMzM5YvX0ubNm3Kvc+A386yeMQ4Lh0+jr2r\nMx8tX8CohbNEUy8DUpmMoTMm4dOqOZFBt9m5ZDU6rbZCtLRr1wmZTMa5c79XSP9VAdHYRcoFnU7H\n5s0b2bFjK/b2DqxevQEvr/Ldsp4c/5iN0+fxw6r1qAsK6PvBcGZs/pK6TV5+bPyriEwu5/0506jT\nqCGhVwM4un13heiwtLSkceOm3L8fxePH8RWiobIjTsWI6B2tVsu6das5ffok7u4eLF68Cnv78psL\n1Wo0/P7TAU79sA+tWoNPy2YMmDDmr9DFikDQ6XianE7W42RyUtLJTU0nJyUTVW4eqnwl6jwlOq0O\niUSCRCZFJpdhaG6GkaUpxpbmmDvaYVXNCSs3R4wszCpNLhqFgQEj5k1nzcfTObPvINXq1KRRh7Yv\nXUe7dh25ccOfS5fO8+67Q196/5Ud0dhF9IparWbFisVcuXKROnXqsnjxCszNy6+qT9KjOHavXEdc\nZDQWNtYM+Hg0vm1bvVQj1Gm1pMc8JunuA57cvU/ag3gyHiWgVha88DiJVIKgK3ofibG1BU7eNXGq\nXwsXnzo4etcodUy5PjAxM2PUws9YM+FT9qzegEM1N1xreL5UDa1atUUu/4KLF8+Jxv4fiMYuojcK\nCgpYvHg+N2/606CBLwsWLC23eqQ6nY4LB45wdPtuNGo1zbp24s1xH2BSjmld/0QQBNJi4om7eYe4\nwDskhkT+w8SlCjnW1Zyw8XTFys0JMwcbzOytMbW1xtDcBAMTYxTGRkhl0sKapjoBrVpNwdNclFk5\n5Gc9JSshhcz4JDLjk0iNjiXmSjAxV4IBMLQwxbOFL749W2NdtzYK45dfIcnJoxpDpn/C9oXL2b5w\nOdM2fYGxqelL69/MzIzGjZsREHCN2NhHuLt7vLS+qwKisYvoBaVSycKFs7l1K4imTVswZ85CDMup\nJFt2egbfr/iSyKDbmFlZ8vakj/Bt27Jc+voTnVZHYmgk9y8F8uByIDnJ6X/9zNrdGaf6tXCsVxMn\n75rYeLoUe0RdOBUjQSozRGFkiJl9YQm4as8Uz8hJSScxLJrHt+4Sc/UWEaevEnH6Kgamxnh1bU39\nvh2xq/FyF4d927aky7sD+H3vL+xds5H353z6Ut+UOnXqQkDANc6d+5333vvgpfVbFRBTChRBFdpm\nXGE6lcp85s+fRUjILVq1asPMmfOeW2i6rDrv3ghi98p15GRmUb9lUwZPnVAulYD+1Jn6II67Jy4T\neeYa+RnZABiameDRoiHVmvpQrUl9zOxfbky8IAgkRz4kKTCUoINnyU0tLMPn7FObpsP64t6swUsz\nWK1Wy4apc4i5c5fB0ybQovu/qxyV13dTqVQyePAALCws2L79B6TSssWCVKF7vciLKxp7EVShi10h\nOvPz85k3byZhYSG0adOemTPnIn9B/c7S6tRptRzbsYff9/6CTC6n3+j36NC/T7kYmDq/gMfXAvHf\ne4qUqEcAGFmYUbNDU2q2a4pro7olqlFaXtjbm/MkKZOH124RdvgcsTfCAHCsV4MWI96kWtP6L8Xg\n05KesHLsZARBx/Rv1mLn8s9c9eX53VyzZgWnT59k5covy1zkvArd62KuGJHyQ6lUsmDBLMLCQmjX\nrgPTp895oamXlpysbHYu/YLIoNvYuTjx/uxpekkn+yzZiSmEHDrD3eMXKcjJQyKV4tnaj3o92uHZ\n0heZovLdLlKZjBptm1CjbRNSo2MJ2PUrDy4Fcnj6atyb+dBh0nAsXcu3VJ+tkyMDJ43l+2Vr2bVs\nLZPWLn1pD74uXbpz+vRJTp06XmZjf5UQR+xFUIWe4i9Vp0qlYuHC2QQF3aRNm3bMnDmvWKZeUp1x\nkdFsW7iCjOQU6rdsytAZn+h9gTQjNoGbu48SeeY6gk6HibUFzd/tQfXOrTG1tdJrX/rkeecyJeoR\nVzfvIy7wDjKFnCZD+tJkUC9k5ZxXfteyNQSevUj3oe/Q671BRerUB4IgMGrUMNLSUtm9++cy1USt\nQve6OGIX0T8ajYZlyxYSFHST5s1bMmPGi6dfSkvQuUvsWb0BjVpNz+GD6DZkYJnnUf9ORmwC/t8d\nIvrCDRAEbKq70WRQL2p1bI6Ti3WVuMn/C/vaHvRbNY3ocwFc2vQjATsOEnX2Ol1nj8Whjme59fvW\nhDE8CLvL6T37qd+yabkXyYbCxefu3Xvx3XdbOH/+d/r0eaPc+6wKiDtPRUqETqdj7dqVXL9+lUaN\nmjB79kK9Z2gUBIFTP+xj59IvkMnljF40mx7D3tGbqeemZXLuix3sGTGH6PMB2Nf2oNeiCQza+jle\nXVtXyimXkiKRSKj9WguG7FhKgzc6kxGbyM/jFxH00wkEna5c+jQxM2PIpxPR6XTsXvElqiLi+PVF\nly7dkclkHD58iAqcgahUyBYsWFBRfS/Iy1NVVN/FxtTUEFFnIYIgsHXr15w4cZS6db1ZtGg5hoYl\ny/JXlE6NWs3eL77i/IEj2DjaM37VImr41Cur9MK2VSpu7j7Kqc838ST8PlZujrz26QjajhuEjYfL\nPxYaq8J1L45GuYECz5a+OHrXIO5GGA8uB5F0Jxr3Zg3KJf7d1smR/Nxcwv1voszLw7t5k3I/l8bG\nJjx+HM/t20HUretd6kpcVeGaA5iaGi4s6jPiiF2k2Ozfv5eDB3+mWjUPFi5cipGRsV7bV+bl8+2c\nxQScPoe7V20mr1+Js6e7Xtp+5B/CjyPmELDjIAbGRnSc8j6Dv1tCjbZNKs12/fLEo3lDBm1fjGdr\nP+IC77DvwwUkRz4sl776jByKk0c1Lv16nLs3gsulj2fp338gAAcO7Hsp/VV2RGMXKRZnz57mu+++\nxd7egSVLVuq9lF1udjYbp88jIug2Pi2bMWH1Yr3kTM9Lz+LEgq84MnMN2Ump+L7VnaHfr8Cnb8cK\n3ZZfERhbWdB78SRajnqLnJQMfpmwhMgz1/Xej4GhIcNmTkYml7Nn9Xqy0zP13sez1KpVG1/fRgQH\nBxIZGVHu/VV2RGMXKZKQkFusXbsSU1NTFi1agb29fsPnslLTWT9lNrERUTTv2omRC2ZiYFT2aYLo\n8wHsGTGb+xdu4lS/Fu9sWUi78YMwMNXvm0ZVQiKR0HRIH3ovmYRULuO3xd9wbevPep+bdqtVg97v\nDyY7PYMtn3/xUua+33lnCAA//LCj3Puq7IjGLvJC4uJiWbRoLgBz5y7Cw8NTr+2nJ6ewfuoskh7F\n0f6NPgyaNgFZGUfS+Vk5nPp8EycXbkJToKLdx0MYsH7WS99yX5mp3sqPt7+eh6WrI4E/HOXMiq1o\nNRq99tFp4BvU9mtA4PmrXDtxWq9t/xd+fo3x8WlIQMB1IiLulnt/lRnR2EWeS1ZWFvPmzSQnJ4dJ\nk6bh69tIr+2nP0lmw9TZpCYk0W3IQN4c90GZI18SQiLYO2ouUecC/hilf47vgK5I9BgmWRSCTkdB\nWjbZEXGk+t8l+WIIib8HknAigKTfg0i5EkZ6YCRPox+jzsl/abqexdrdhbe+mo1D3ercO3WFY7PX\nocpX6q19qVTKkE8nYmJuxsGvt5Ec/1hvbf8XEomEYcNGAPD999+Va1+Vnaof1yVSLmg0GpYsmU9S\nUiKDBg2jS5fuem0/IzmFr6bNJT0pmV7vDaL70HfK1J6g0xG45xj+3x0AJLT84E0aD+qDVFa+hq5M\nzSIr9AFP7yeQcz+RnAcJKJ9kIGiLH1IoNzPG2NkW8zquWHhVw8LLHfM6bkjl5b8GYGxlQf81Mzm5\ncCOP/EM4+Mly+q2cirGlfgp8WzvYM2rOZNbPWMSOxauZsn4l8nLcKNWwoR9+fo0JDLxBcHAgjRo1\nKfqgVxBx52kRVKHdaHrVuWHDWo4fP0ybNu2ZNWu+3mLI7e3Nib77iPVTZ5GakESP4e/Sc9i7ZWpT\nmZ3Db0s2ExsQiqmdNd3nfYRLgzpl1vlf51OTV0Ca/13SbkaQERxNXlzyP36usDLDxM0OQzsrjOwt\nMbAyQ2pkgNRAjlQhR6dSo81XoVWqUKU/Rfkkg/wn6eQ/TkOnUv/VjszEENumXti2qIddK2+M7P+9\nA1af11yr0XD+i53cPXkJG09XXv/iU0xt9LPr1t7enA2fLefaidO0f6MPA8aP0ku7zyM6OoqJE8fi\n6VmdDRu+LfbUXhW618WdpyIl5+jRXzl+/DDVq9dg6tSZet3tmZ2eycYZ8/6afimrqafFxHN8znqy\nEpLxaNGQLp+N1tto80+0+QUkXwzhyYXbpPnf+8uAZSaG2LXyxtq3FuZebpjVcMbQpnRFRXQaLXmx\nT8i+F0fW3Uek3Ygg+WIIyRdDQCLBpnFtnLs3w6FDQ+QmJds7UBxkcjmvTR+JwsSIkAOnOTBpGW98\nMR1zB1u9tP/muFE8uHOXi4eOUq9ZI7ybl99Iulat2nTu3I3ffz/F77+fpHv33uXWV2VFHLEXQRV6\niutF5507ocyYMRlTUzPWr/8GR0cnPagrRJmbx9efzefh3Sg6DujHG2NHlCmG/MHlQE4v+Ra1soCm\nQ/vS/P3+ept6sbMz4/6FMB4fu07SmSC0eYW7KE09HHHo6It9a59yny7Ji08h9Xo4T84GkxkaA4DU\nyACXHs1wH9gRzyY19f7dFASBa1t/JmjPMcwdbXnjixllTiL253czPvoBayZOx9jUlOnfrMXS1kZP\nqv9NamoKo0YNx8TEmC1bdmFqWnQOmSp0rxd504g7T4ugCu1GK7PO9PQ0Pvts6h9ZG5dSs6b+cn2o\nVSq+nbuYB2H3aNmjC29NGFNqUxcEgeCfTnB21XdI5TK6zR5Lwze7IpGWfaORTqPlyblbBC/YSfR3\nJ3kaGY+htTnV3upAvWlvU3NkT2wa18HI3qrcF2QVFqZYenvi2rslzt2bobAwIS8uhfSbkcQduERa\n2EMUdhYYO+rPICUSCW6NvZHJZTy4FMSDS4FUb90II4vSV0f687tpYWONkYkJty9d4/H9GJq+1r7c\nzqGJiSmCAP7+V1Gr1TRp0rzYOis7xdl5Ko7Yi6AKPcXLpFOj0TBz5hTu3All1KiPGDDgbb1p02m1\nbF+0ktAr/jTv0o5B0z4p9eYgnVbLxfW7CTt8DlM7a/os+wT7WmUvi6ZTa3h89DqPfjxLfmIaSCQ4\ntGuAa7/W2Db1QlLOi7DFRafRknIplEf7zpEV9hAAu1be1BrTB/NapdtK/zyCfjzO1W/3YeZgQ/+1\nM7F0Kd3I/e/fTUEQ2DpvKWHXb9D7/SF0GzJQn5L/gUqlYuzYEaSkPGHjxq1FhupWoXtdHLGXlSr0\nFC+Tzm+/3cilSxdo374jo0eP09s2e0EQ2L9+M4FnLlDbrwHTvvycAlXpklCp8pWcmPcVUWf9satZ\njf5rZ2JdrWxTRTqNloQTAYTM+46k04HoVGpc+7Si/ZcfYdejBSZu9np5E9AXEqkUs+pOuPZpRc2u\njci4n0T6zQjiD18lL/YJlvU8kJvpZwOWc4PayAwVPLgYSMyVIGq0aYyheclH7n//bkokEuo29SPw\n3EXCrt+vwm3DAAAgAElEQVSgdqMG2DjY60Xvs8hkMpydXTh37nfi4mLp3LnbC7/XVeheL3LELhp7\nEVShi11qnefPn2Xbts24u3uwYMEyvWZrPL3nZ87uP4hrzep8tGw+1raWpdKZn/WUw9NWk3A7Avdm\nPvRdMRVjq9IvkgqCQMqVMG7P2kbCcX+0BSqqvdkO38UjcercGBtXm0p/3e1rOWPZ3hfL+p7kPkwi\n/UahwUsAy3oeennLcGlQB6lCzoNLgTy4HETN9k0xNC1ZgfJnv5sGhoa416lFwG/niAgMpnnXThiU\nU31cN7dqREVFEBR0Eze3anh61ii2zsqKaOx6oApd7FLpfPToIQsXzkKhULBs2RfY2trpTVPAb2f5\nZdNWbBzt+XjVIswsLUql82lyGgcnLyftQTz1erSj29wPUZTBCHIfPSFs0ffE7PoNTW4+bv1a47uo\n0NDlf2Q8rArX/U+NJm72uPZthbGzLRm3okm9eoeks0GYVXfG2KXsUS0uDesgkUl5cDmIR/4h1OrQ\nDIVx8SNz/utc2jg6IJFKCL0awJO4xzTu2LbckrHVrevNiRNHCQ29TY8evZ9bj7cqXHMQjV0vVKGL\nXWKdeXl5zJo1lfT0dKZPn6PX0mIRQbfZsXg1xmZmfLxqEbbOjqXSmRGbwMHJK8hOTKHROz1pP2lo\nqefntUoV97ceI2zxbvLiU7Bp5oXfstG49mqB3PSfRlUVrvuzUxzmtd1w7dsKbYGatIB7JJ4IQJ2V\ni5VvTaRlzDHv0rAO2gI1MVeDib15h9qdmiM3/G+DfJHOv1Ojfl1i7tzj7o0gjM1M8aznVSaNz8Pc\n3ByQ4O9/lfx8Jc2atSiRzsqGmLZX5LkIgsC6dauJj4+jf/+3aNeug97aToh5xPaFK5BIJYxaOBNH\nd7dStZMc+ZBfJiwlJzmdVmMG0ubDd0o9qkv1v8u14ct5+MMZDB2s8F3yAY2/+AgzT/2Fc1YGFOYm\n1J00gObfTMbU04m4A5e4/v4K0oOjytSuRCKh1ZiBNHj9NdIexHFkxpoypx+QymQMmzkZMytLDm/Z\nRXz0gzK19yIGDHgbN7dqHDv2KzEx98utn8qCaOz/Tzl8+AAXL57D29uHkSPH6q3drLR0Ns9ehDIv\njyHTJlKzQf1StZN4J5pDU1agfJrLa9NG0GRQ6TaZqLNzCf18F8HTvkGZnIHn4M603jkTh/YNX+k8\n7Jb1PGixdRqeQ7uQ/ySdwEkbid56HJ1GW+o2JRIJ7ScOpW73Njy594AT875Cqy5b4jALG2uGTp+E\nVqNh59IvKNBjrpq/Y2BgwNixH6PT6fj66w2vfKUlcSqmCKrQ61mxdYaHh7FixWIsLa1YtuyLMhUA\n/jsF+Uo2zZxPctxjeo8YQrt+vUqlMz74LkdnrEFToKLb7LHU7d62VHrSAu4RNPUbssJisKjnQaMV\nY3Du3qxY0xJV4boXpVEql2Hb1Au75nVJC4wg9XIYGbeisW3q9a+pp+IikUjwbOVHStQjYgNCyU5I\npkbbxmWKNrF3dUaZl8ed6zfJycrCp1XRMeelwcXFlfv3owgKukm1au54elYvkc7KQnGmYsSUAv/P\nyMzMYOnShQiCwMyZc/W2WKrTatm1bA3xUQ9o2aMLXQe9Vap2HvmHcHzuegSg58Lx1Ghb8q3nmjwl\nkZt+5fGvV5HIpNQa0wfPwZ3LNRZdUGvQJGaieZyBNj0X3dN8dE/zEZRqkEhAWvif1MwImbUpUitT\n5I4WyN1skRqVX1IsAMv6nrTc9inhK38i+fwtro9cSYP5w7FtVrdU7UllMrrPG8ev01YReeY6JrZW\ntP2obKkh+o4cRlRwCNeOn6Z+i6Y0aP3f8+BlZfTocQQG3mDbts20bNkGw3KKxqloRGP/f4RWq2Xl\nyqWkpaXy/vuj9JqG99ctOwm7FkCdRg15e9KHpZrmiLkazIkFG5FKpfT6fAIezRuUuI3siDhCFuwg\nPz4VsxrO1J81BAsv/eZh1+UWUBD+GNXdx4X/j0xE+yQLdKV4vZdIkDtbofC0x7BBNQwbumPg7ap3\ns1eYm9Dw8/eJP3SFiA0HCJr2DbU/7IfHu51Kda0URob0WfoJv0xcyq19JzG3t8H3rW6l1ic3UDDs\nsymsHjeVvWs3Ud27LmZW+q3SBYWj9jfeeIv9+3/k4MH9vPvuUL33URkQd54WQRXajVakzl27tvPj\nj9/TvHlL5s9forfkXpcOH+fnDd/i6O7GJ+uWY/KCqZ3n6XxwOZCTCzchlcvos3Qybo1KVsBaEATi\nfr5A5KbDCBotHoNeo9bo3qWOBnl2t6T6fjL516PIvx5Nwe1Y+NtctdTaFIWHHXJXGxRu1khtzJBZ\nGCM1N0byp0ELAoJWhy47H21GLrrMPDSJmagfpaJ+lIouPed/nctlGPl5YNy+Libt6yJ3/G+DK+13\nM/POQ27P3oYqLRunrk3wnv4uMqPiRbg8S3ZSKj+PX0ReRja9F0+keut/DxZKovPs/kP8+u0OGrZt\nych5M8plHSQ3N4eRI4eiVqvYtm031tY2JdZZkRRn56lejd3LyysIyPrjrw8iIiI+eMHHRWPXI0Xp\nDAi4zvz5n+Ho6MSGDd/+EQJWdu7432TLvKWYWpgzZf3Kv8IaS6Iz+sINflv0DTKFnD7Lp+DqW7Kw\nN3V2LmFL95B6JQyFlRk+c4Zi16JkD4ZnsbMzI+F6NHlnw8k9ewfNo9S/fmZQzwWjJtUxrO+Ggbcr\nMnuLMhuQNj2HgpA4lCGPKAh+hOpewt/6c8Wsly8mXRsgs/zf5qCyfDcLUrO4PXc7WWEPsajnjt/y\n0aXOTPkkIoaDk5YhkUgY8NUc7Gr+8w2pJDp1Wi1ffTqX+6HhvDd7Go07lm59pSiOHj3Exo3r6N27\nHx9/PLnEOiuSl2rsXl5eRsDViIiIxsU8RDR2PfIinU+eJPHxx2MoKFCyZs1X1KpVtlzlfxIf/YB1\nk2ch6HR8vHoxnvWKbvdZndHnAzi16BvkRgb0Wz4V5wYlSzyWFf6IkHnfoXySgU2TOvjMGYqhXelf\n4XVP88k9FUL+sWDy7yUCIDGQY9ymDsbt62LcvCYyG/0sNr8ITXI2+ZfukXfhLsqgh6DVgVyGSTsv\nzN9qgWEjDxwcLMr03dSpNNxd/RMJJwIwcrah8aoPMfV48YP5edy/eJMT87/C3NGWgV/Px8T6fw+J\nkt5DqQmJLB8zCUNjY2Zt+wpTC/2mYYbCackxY94jOfkJ27f/gL29Q1W611+qsbcAdgKPKJy7nxUR\nEeH/gkNEY9cjz9OpUqmYNm0CUVGRTJo0jR499JObOjM1jTUTppOdls77cz/Fr13rEuv8u6m/vmoa\nTt61it2/IAjE/XKJyI2HELQ6aozoQY3h3Uq9QKp+mEL2nqvk/haKUKBGIpdi1MYL0y4+GLeujdSk\n4hbZtGlPyT0VQs6xW6gfFBb2MKjnituHndE09kRShtTBgiDwYMdJHmw/idzcBL+lH2DtV/zr8Hdu\n7PoV/+8O4tKwDq+vno7sj2mw0txDZ/Yd5PCWnbTo0ZnBUyeUSk9RnD59kjVrVtC37xuMGzepKt3r\nRRq7PsMEcoFVERER3YEPgR+8vLzEOPkK5uuv1xMVFUnXrj3o3v3f4YeloSA/ny1zl5CVmkbfUcOL\nbep/pyymri1QcWfxbiLW/YLC3JjGaz6i5ogepTJ15e1Ykmf8SMKgr8g5EoTMzgyrj7rQ8NI8HJa/\ni2kXnwo1dQCZrTkWg9vgvHscjps/wLhDPVT3EngwaRcJQzaSe+YOgq50idUkEgk1R/Sk/meD0eYp\nCZr6NSlXwkrVVtNh/ajZoSkJIZFc/vrHUrXxJx0H9MO1ZnX8T54h6lZomdp6Hp06dcHJyZmTJ4+R\nmppSLn1UFPocsRsA0oiICOUff/cH3oyIiHheBdtXe4dAJeDQoUMsXryYOnXqsH37doyMyl55R6vR\n8sUnc7l12Z9Ob/Zm1NzJJZ5fDjtxhZ9nfImBsSHDvp1HNd/iTw3lxCVzaeImMu7GYutbg3brx2Pi\naF3SX4Ps69EkrD9FTkDhLkRTX3ecxr6GVRefl1r4urQoY5JJ2nKO1F9ugFaHibcrLlN6YtmhXqnn\n+xMvh3Fxwlfo1FparxqNR8+Sx5MX5OWzdfBnJEfHMXDVFHx6timVFoAHdyKYO3Q8LtWrseynLcjL\nmBbhv/jzHhk2bBiTJk3Se/vlxEudihkLNIyIiBjv5eXlApwB6kdERDxvKCFOxeiRZ3VGRkYwbdoE\nDA2N2LBhM05OzmXuQxAE9m/YzJUjJ6nbtBFjFs1GJi/ZzZYcFMr+T9eiMDak38ppOHnXLPaxqf53\nCV24C83TPFz7tcZr4pvIDEsWFqi8HUvm5jMUBD8EwLh1bSyGtsXQz+MfhlgVrru9vTkJQQ/J3HqO\nvNNhIAgYt66D9ZSeKFxLV3wjI+Q+t6Z/iya/gPozB+NSCnPPiE1g39iFIJXwzuYF1G5Uu9Tn8qcv\nN3H12G+8OW4UHfr3KVUbL0KlUvHee++i0ag5fvw4ubml35n7snjZUzHbAAsvL6+LwF5gxAtMXaQc\nycrKYsmS+X8Uz5ijF1MHOH/gCFeOnMS5ugcj5nxaYlO/f/EmP09fW7hQunJqsU1dEARif75I8PTN\n6ApUeM8chPen75TI1DWJGaTM2ceTD7dREPwQ49a1cdo6GocvhmLUyLPKphdQVLPFfuFbOO/6CKMm\n1cm/Gkni4I1kbjuPUKAuuoFnsG5Yk8ZfjkdhZsydpT8Qf/hqydtwd6HjlPdQ5yk5uXAT6oLS7+bs\n/f4QjE1NOLHrR3Iys4o+oIQYGBjQp8/r5OTkcOzYMb23X1GIcexFUBVGbvA/nRqNhjlzpnP7djDD\nh49k0KBhemn/9uXrfPf5CsytrZiyYSXWJSyO8OByICcXbEJuqKDviqk4+xQv+kWn1nBv7c88PnIN\nAxtzfJeOwqq+Z7H71eUoydp+geyf/UGtxaC+G9YTu2PU0P2Fx1WF6/6sRkEQyPs9jIz1p9CmPkVR\n3R7bOf0x9C55ZaWn9xMInPQV6uw86s8agkuPZiVu4+zq7wg/doEWQ3rRbFTpK3KdP3CEg19vo12/\nXrw1YUyp23keGRnpDB/+Lm5urmzatL3SP+SLM2IXd56+Ymzd+jW3bwfTqlUb3nlniF7afHg3ku+X\nrUFhaMiYRbNLbOoxV4M5uXATMgMFQ7+Zg0m14mV7VGXkcHvONjJDHmBe2xXfpaMwdireFIMgCOT+\nFkrmhlNo03KQOVliNbYzpt0a6HUOXSjQoHucgZCWi5Ceiy4tF/JVCCoNFGgKd6MaysFAjsRQjsTa\nBKmdGRJbM6QulkjMyr7u8ScSiQTTrg0wbl2bzK/P8PSXAJJGb8FiSBusRnVCYlD82928pgtNvhzP\nzQkbuLPsB2RGChw7+pVIT/sJQ0gMi8L/h+M4+nnj3tSnpL8SAO369eTSr8e4evw3Og18HVun0oVk\nPg9raxvatGnHhQtniYyMwMurdKkWKhPiiL0IqsLIDQp17t79E19+uQoPD0/WrNmIiUnJKt38F6mJ\nSaydOIPc7KeM/nwW9Vs0LdHxjwJCODZnPVKZlH4rpuLbuWmxzmdubDLB0zeT/zgVx9caUf+zwcXe\nHal+mELayqMUBD9EYiDH4v32WA5ujaQEUzf/dd2FfBXae0lowxLQRSejfZSGkJhVpjAAib050hp2\nyGrYI2vgiqy+CxLj4uks6rupDIwhbemvaBIyUNR0xH7J2yg8SpYbKOvOQwInb0Kn1uC3bBR2Lb1L\ndHxy5EN+Hr8IYytzBm1bjJFF6fYA3Dxzge+Xr6V5t9cY8unEUrXxIv7cwNe3b3/GjdN/+/rkpe88\nLSGiseuRxMQYxowZi5GREevWfY2LS9kLG+dmP2XdJ5/xJC6egRPH0rZvzxIdH3szjGOzvgSJhL7L\np+DWqF6xzmd6cDQhc7ahzs6j+nvdqPlBr2K9HgsaLdk/XCFz+wVQaTBuXxfrST1QuJQ8asbe3pzk\npCx095LQ3HyE5uZDdFHJ/8gHI7EwQuphi9TdptCgbU2R2JgiMTMEAxko5EhkEoSCwtG7oNT8MarP\nQUjNQRefge5BKkJ67v86lkmRejkib+aJvHVNpJ62z/3di3MudXkFZHz1GzkHbyIxNsB2Rl9Muzcs\n0blID44m+NNvAGi67mMsSzAVBnD34G+cWb+HWh2b02P+uBId+yc6rZaVH04mKTaez7asL3WO/+eh\n1WoZPvxtNBoNu3f/rNfykPpGNHY9UBWMPSUlmcmTx5GRkcHixStp1KjkGRGfRaNSs2nmAu6H3qHT\nW6/zxtgRJTo+PiicI5+tBUGg95JPcG9W+Bpe1PlMOHmD8BU/giBQ79N3cO3dslj9FYQ/Jm3Zr6ij\nnyC1McP20z6YdCx5WgFBq0MbHIvs2gNyzkfA04LCH/xpuD4uyOq7Iq3riNS65IWd/wtdVj66iCS0\nIfFobsf/4wEicbFC0a4Wih71kbr+8wFVku9m7u9hpC07jJBXgNnrTbCZ3LNEbzApV8K4NWsrCgtT\nmm+ejIlL8Uf+tjYmbB48i6Q70fSYP45aHUuXlvf25etsX7icpp07MGzm5FK18SJ27tzM3r17WbRo\nBU2blk/qYH0gGrseqOzGrlTmM23aJO7fj+LDDz/m9dcHlLlNnU7H98vWEnT+En7tW/Pe7GklShiW\nEBrJ4emr0Wl19F40EY8W/xshPu98CoLAw+9PE73lGHJzE3yXjMSmUdELrIJGS9aOi2TtuAhaHWb9\nGmM1vhsyC+Ni6wXQxqajPhqC5lwEQlY+ABI7M+QtqiNr6oHcrxoS05ezUUnIUaIJeIjm6n00Nx5C\nfmF0i8zXDUVPH+TtaiNRyEr83VTHpZEyex/qqCQMfNxwWP4uMtvib9ePO3iZe2v2Y+LuQPOvP0Fh\nUbwHm729OZFBUewdNRcDEyMG71iGsWXJp2R0Ol3hqP1RHLO3b8TeVT/RXn/y+PF9Ro0aRbduPZk8\nebpe29YnxTF2sdBGEVTm5Ps6nY4VK5Zw+3Yw/fv3Z/Dg9/Wyon9k2/dcPXaKGj71+GDBZ8hLENaY\nFH6fwzO+QKfS0HPBx3i2+ueC23+dT51GS8Tan3m45wxGjtY0WfcxlvU8iuxLHZdG8qd7yDsVgszB\nAvvl72LxTiukxRyJChotmsvRFHx1DtWWS+juJSExUqDoXh+nmT0R3muFolUNZO42JVp4LCsSAzmy\n6nYo2tfBYEBjpB42CE+VaG/Ho7kcjfr0XZBKMPN2Jl9V/LhrmaUJZr380CRmorwWRe6ZOxg19iy2\nuVvWc0erVJF6JYzMOw9x7tKkWLt9TU0N0ckNkCnkxFwJJi89i5rtSv5WKZFIMLUw59aFKxQolXrP\n2V69ejUOHDjIo0cP6d9/oN6yn+obsZi1HqjMxr579w6OHz9Mgwa+LF++jIKCsm+uuHzkBEe378bB\nzYVxKxZiVIIF2OTIh/w6bRUaZQHd5330nzfvs+dTq1QRMm8HSacDMavlSpP1Hxf5mi8IAjlHgkiZ\n/iPaxExMuzfEYdVgDKo7FEunkK9CfTQE5bITaE7cQXiSjcyvGoaj22H0SWcULWtgWdOevPyKv+4S\nmbTQ5Lt5o3jNC6RStGGP0V6PIfuXIASNDllth2Lni5HIZRh3qIdELiP/wl1yT4WgqOFY7EVVmyZ1\nyI1NJu16OAVpWdi38SlyMPHnNXesV4OH124TeyMUF18vLJxLFl0F4FjNlVsXrhJ1O4yW3TtjZFr2\nAIE/MTMzIibmEaGht/HxaYizs4ve2tYnorHrgcpq7BcvnuPrr9fj5OTM0qWrsbe3LrPOsOs32L1y\nHWaWFny8ahFWdsWfR02LiefXqSspyM2n66wx1Hmt6Erw6qd5BH/6Dek3I7Fp5kXj1R9iYPXiV3Rd\njpK0xYfI3nUJqYkBtnP7YzWyY7Hmi4XcAlR7b5C/7ATaK/dBo0PRywej6d0xfKsJMg/bv0IhK+N1\nl1gYI2/miaJ3AySGcnQRT9Bcj0H9WziYGiKtYYdEWvQbm0QiwcjPA4NaTuSdDyf3t1DkjpYY1Cl6\nakMikWDX2ptU/3ukXQtHYWmKpfeL367+PJcSqRT7Op6EH7vIk7v3qd+nI9IS5veRSKXIDRSEXvVH\nJpPh1aRkIZhF6Swo0HHmzG+Ym1tU2nn24hh75XzXEHkhERH3+OKL5RgbmzB//hIsLcteaSY2Ipqd\nS1YjV8gZs2g2di7Fn7/MiEvi16krUWbn8Nq0EXh1aVXkMcrULG5+vJ7M0BgcOzem0YoxRdbhLLj7\nmMT3viHv9zAMG1TDeedHmHYpOjZaKNCg2h9IzvDvUO32RyKVYjC8Jaa7P8BowmvI3Eu3/b6ikFoa\nYzi8Fe5HJ2AwqDlCTgEFa38n78Mf0IQ+LzXTvzHpWA/HDe8hNTUsfFjuuVKs42SGBvgt+QADazMi\nNxwkPTiq2H06elWnQb9OZMQmcmv/yWIf93eadu6AmZUlV4+dQpmXX6o2noePT0OMjY0JCLhWpQte\niyP2IqhsI7fU1BQ++2wqeXl5zJmzkPr1C8vHlUVnWtITNk6fhzIvnxFzP6VOI99iH5udlMKhySvI\nTcuk/cSh+PTr9MLPm5oakhr5mJsTN5AXl0K1N9vhPeNdpC+YShAEgZzDgaTM2ocuOx+L99tjN7f/\nP4pO/OdxOgHNmXvkLziC5nI0KKQYDG2B8Wc9kTfx+F91o+fofNH5FAQBIacAbXwGmshk1PeeoA55\njDowFlVQLOrgeNTBcahvx6OJSEbzIBVtfAa6rHwEjQ6JgRyJovTpdgHMrE0oqOOIops35BagDXqE\n5lQ4QspTZPVdkRgWvS4gd7DEuG0d8i9GkHcuHEGlwahpjSKnV+Rmxlh6e5J4MoCUK2E4dm6Ewuy/\nF6yfPZdO9Wtx9+Rl4m7ewatrawzNSjadIpPJ0KhU3L0RjLm1FZ71SlaY5XmYmhqiVGqIiookPDyM\njh1fw8JC/+X5yopYzPoVQ6lU8vnnc0hPT2P06I9o3rx4oYAvIi8nh82zF/E0I5MB40eXaEEqNy2T\nX6euIiclnVZjBtKwf5cij8mMjOfG+PWo0rOp+UEvqr/X7YUmolOqSV99jNxjwUgtjLFbMADjVkVH\ny2jvJKD85gK6iCdgIMPgnaYYvN0UiXnJd3oKgoAu+WmhOcekoXmYhu5xJkJ+yXOx/B2JtQlyTxtk\nnrbIq9shr+1QLDN+FqmdGUZTuqLo6YPyyzOoT95Bcz0Gw4mvoWhbdDpkg+oOOG3+gCef7CL7+8ug\nE7Aa37VIc7f2rYnXpAHcW7Of0Hk7aPrVxGKVIjQyN6X1mLc5s2IrV775qVSx7W369uS3PT9z6ddj\ntH+9F1JZ2R6Sf6dZs+ZcuXKRwMCbuLm9OPVEZUU09iqCIAisXbuSqKhIunXrSf/+A8vcpkatZvvC\nFTyJjafjgH60f6P4RTjys3L4ddoqshKSaTqsL00GFX1s1r1Ybk37BlVWLl6TBuD+VvsX60vKJGXG\nXlSRiRjUdcF+6dvInV+82UiXlU/BtxfRnL4LgLxjHQw/aIvUsWRl37S5BagCHqK+k4j6TgJCet7/\nfiiVIHWyQF7XHKmDBVJ7M6TmRkjMDJCYGhZG0EgAiQR0AkK+CiFPjZBbgC4tF21KDrqUp2jjM/8Y\n2ccXtiuXIq/jgMLHBUVjd2QOJascJKvnjMnGQagOBKPadQ3l50fR9vTB8MMORe5mlTtb4bhxBE/G\n7yD7hysglWD1UZcizd3tjTZkhcWQ+NtNor45gteE/sXSWrdba8KOnCP6fADx/TqVuMatmaUFTTq3\nx//kGcIDAvFppb/58EaNCndXBwXd5PXX39Rbuy8T0dirCHv27OLixXN4e/swfvwnZQ5rFASBvWs3\nEXUrlIZtW/L6mPeLfawqL5+jM9eQ/vAxDft3ocWIor/8GSH3Cf50MzplYXbGojYeFYTGkTxzL7r0\nHMz6NcZmSq8XLpAKQuG0S8HmiwhZ+Uhr2WM0vhOy+sWPbBDy1ahvx6O68ZDM0AQETWFyUompAYqm\nHshr2yOvbofM3Vpv4Y+6jDw0j9LQRKWgCUtAE56EJjyJ/H1ByGrYYdCyOgbNPZFaFO9NQyKXYfh2\nU+Qta6BcdgL1iTA0oY8x/qwnstovjhqS25nj+NV7heb+/WUkMilWYzu/uD+JhLpT3yY7Io7Yfeex\n9q2JQ/uid7ZKpFLaTxjC/nGLuLjhB97dsrDEo+4O/fvif/IMFw4e1auxOzg44uZWjZCQYNRqdaXe\nhfo8xDn2IqgMc+yXL19g48YvcXBwZNmyNZia/ntjSEl1/vbDPi4cOIJH3dqMXjgbeTG/vFqVmmOz\n15EYGolXtzZ0mvJekUm10oOjuTV9MzqVhjZrPsSmw4sjGXJO3Cbls70ISjXWk3tiNea1F4bz6dJy\nUC4+jnp/IACGH7TBaErXYo3SBUFAG5NG/sFb5G6/ijrgEbqkbAzcrVF0rIPxwMYYv9sUw+aeyGva\nI7UxLXX5vf9CYqxA5mSJor4zhp3qYNixDjIXSwS1Fm1UCprQxxT8fg9d8lOktmZIrf43j/2iay61\nNEbRzRtBpUF7PQb16XCkjhbIarw4xFBqaohJh3rkX44g/+I9JEaKIjNhShVyrP1qknA8gDT/cJy6\nNv3HQvjzdJrZWZOTnE7sjVBM7Wxw8PJ8YT/PYmFtxf3QO0TdCqVRhzaYWZVtPvzvOuPj4wgPD6Nx\n46Y4OOg36VhZEcMd9UBFG3tMzH3mz5+FQiFn2bIvnhtbWxKdgWcv8vNXW7BxtGf8qkWYmBdvF6BO\nq+O3Jd/w6PptqrdpRLc5Y4scZaUHRxE8/VsEjZaGi0ZSu2/L5+oUBIGsrefI+PIkEhNDHFYOwqxb\ngxe+nagvRJI/5xDCwzRkTdwxWfwG8hbViwz7EzQ6VNdjyNt+FeWRULSxGUitTTDsUg+TYS1wG9Ea\nVUW6wQYAACAASURBVDVrpNYmLzWNq8RIgdzDBsPWNTDsWBuptTG65Bw0d5NQXYhCczcJqbUpUnuz\nIq+5RCZF3sQDmZcjmmv30ZyPRMhXIfOr9sLzIzU1xKR9XfLOhpN//i7yarYY1HqxuRlYm6OwNCH5\n/G2eRj/GuXvTv87bi3Q61K3OncPnSAyLwqdvR2QlHB0bGhsTfOEKEiR4tyhbKo1/hOKq1Vy8eA4H\nB0caNtRfSKU+EI1dD1SksWdlZTFz5hSysjKZOXMuDRs2eu5ni6sz5s49ti1cjoGREeNXLcLOuXij\nEUEQuLjueyJ+u4pLQy96L5lY5E2YHhhZaOo6Hb6LR+LQtsFzdQoaLWnLfuXpPn/krtY4ffU+hvWf\nn+hJyC1A+X/svWd8FOfV/v+d2SppVxUJ1CiiiN7B9GIbGwM2YNztuMSJE8fJ48ROHKfnSeLE8ZM4\n3Ymd4sQlrmAwxTSb3qsAARJCBfW2krbv7JT/iwV+jqOZ3UUy4M9f1zs+3Pe9s6vda86cc53rPL8Z\n6ZU9IAjYHpuD7dHZiFGKo1pIJrS1FP+LO5B2nUXzhLCM70vi3RNJuHsSlmF9EJ32K35Dh/MkPzAT\n29xCzAW9UL2hCMHvKUcubcSen4aUGN3xUsxNwzJjEMrhcyh7K1BO12OeMsAwnSQ67NgnD8S38Tj+\nj05iG5kXdSpTcmE+njO1tO47hdmRQOrIAYDxd9OaaEdVVKr2FiGYRPLGxecemZmXw74NH1J1upSZ\ntyzAbL30tMnHrzMtLZ3ly98iHA5326zg7kKPjv0zDFmW+fnPf0xjYwP33vsA06cbFxpjQWtDI3/7\n8S9QFYWHfvgU2f1jr/gffG01J97fQq+B+Sx85nHMVmNCaSs6y5FvXyD1h8mcrq83V/0hmr75b3xr\nj2Idlkufl76Apb9+ykA524zvsTeQPzqNWNibpD/fi3XRaMPIWpNVQltL6Xh6JYF/H0D1BLFdP5Tk\nZ5fgeGw2lpE5MTX3XAkIooBldC7OJ67D+aOFmEfnIp9upPqp9/C+sA21zR/1DDEvjcTf34XpmgEo\nh87h/+a7qG0+wz3Wgb3J+uXdIEDzd95COttofJ2CwPCn7sKa5qDspTX4zhmvv4Bxd8wnMSOVo+9s\nwOdqj2nPBZhMJqYtvJFQIMjBj7bFtdcIDoeDoUOHUVJyCq/X223nXi70ROxRcKUit5deeoHt27cw\nbdpMHnvs8ZjbtvUQ9Ad44akf4mpo4ravfYnxc2bEfC2n1u9kxx9fx9k7g6W/eZqEVGO1Rsfpcxx+\n8s9oYfm/SP2/LAU6/DR947WL4+qyfn2voYFXeH0xgZ+shvYA1jsnYn/6JsRUfR20pmmED53D96ft\nSLvLQdOwzx9B0qOzsE7sh6gT8cbyd9c0Dc0nITd5UZo8hOvchM+1Ide0I9e7kRvcKC0+VF8ITVIi\nKhmL2KXUjpiagG3KACzDsxGbvUjH6whtL0OwWzD1Tzc8W7CaMc8egtbhR9lXibynHPO0AkNzM3N2\nKpa8dPwbjxPYc4ak+WMQDfT/pgQribm9aNh0CE9ZLTk3TSbJYTf8LE0WMxa7jfKdh1HCYfpfE3sf\nBUBmbjbbVqymvbmVaQtvvOTP95N/85aWZoqKjlBYOIy+faN7F10u9OjYP6PYtGk9q1Ytp2/ffjz5\n5NNdNiNSFYVXfvFr6ivPMWvJQmbcPD/mvdWHitnyq5exOZO45bknScpINVzvOVvHkSf/jBKUGP3j\nBwwjdcXlpfHxVwiXNZI0fwwZ31usWyTVwgqhP24h/MEJcNhI+P5CzFMKDK9Fqe/A//p+5JMNIArY\n5g7BfstoxJT4nB8hksIJ17uRa9uR6zqQm70obX6Iw4QLzhdLMx2YsxyYs1Ow9E/HlJEUNxmZB2fR\n57lbqV1xhMA7hwm8vh9p91mSvjAdU7Z+EVEwidi+di2Cw4705gH833iHxGeXIubrp1mS5o0iXNFM\nx8vbaPnhu2Q9f59hATlr1hiy5oylaetRqt/bSdaXo0thhy2YyeG31lG8eivj7phPcp/YfWRSMtIZ\nMXUSx3fto7r0LH0Lo2v3Y8G4cRN47bV/cvjwQaZPn9ktZ14u9BD7VYYzZ0r4wx+ex+Fw8KMfPdMt\nU5DW/OM1ivceZOiEsSz58udj3tdaUcMHP/ojiAILf/Y/pPU1lg76a5o5/MQLF+dk9p6rXxOQm9w0\nfu2fyOdacdw6ifQnF+iqa9SOAMH/XY1yog5xUCYJP1iEaEBeWlghuOY4wXXFoKiYR+WQePckTH1i\n17Jrmobc5EEqbUI600z4XNt/DtmwmjClJ2JKS8SUmoiQaEG0mSPdrKIAmhbRsEsKqi+E6pVQPUGU\nFh9ydRvyuTagGgDRYcNSkIFtRDbWQZkI5thu5IIoYJs9GMvYPPxvHiS8rxL3/64l8XPXYJuuPyhc\nEARsn58OSVakv+/C/63lJP7mDsPPNOULc5BK6wnsKqX9pY9Ie9S4GW3o15fhOlRK2YurGXrLFLAY\np+5MZjPXPLiUTT9/if3/WsX13/6C8Zv/BKYtuIHju/axe92GbiP2wsJhJCYmceTIwW4573Kih9iv\nInR0dPCzn/0IWZb5wQ9+2i1TkA5v3cmHb79HVl4OD3z/m5hi1AoH2t2s+e5vkXwBbvj+l8kZbdy2\nLbV5OPzNvyC5PAz9xm3k3KSvK5ab3TQ+9jJyjYvk+6aT+hX9Lke12oX/B6vQ6jowzx6C/cl5hlYA\ncnUb/r/uRKlpR0hLJPGeSVjG58ccESvtAULHajl7oh6p8bzXuQDmnBQsfdMx56VgyUlFTEu45Ed+\nLaygtPgI17QRrnQhVbYSOlZH6FgdQoIF27A+2CfkY8kzfjq6ADElAceXZiKNy8f/r734/74buayZ\nxHsmGdoW2O6M/H/oL9vxf/e9CLnrpLUEUaTXj5dR/+CLuF/diX3CABIm6988bBnJDHlsMSeffYMj\nz71F4feiD1UffO0UDr2+hpKNu5n8wOK4ovahE8aSlpXJ4a07Wfrlh7EldH2WrMlkYvToMezdu5vG\nxgZ69+7T5TMvF3py7FFwuXLsiqLw05/+kLNnz3DffQ8yf37sXaDQ+XXWnq3grz98BovNymO//EnM\nQ6gVKcya7/6W1rPVTH5wSVSrANkf4vCTL+CraGDAAzcw4F799daARMXnX0KubiX5/pmG3Y3KyTr8\n314OrT6s90zG9tW5ukSlqSqhDSfxvbgDrT2Adc5gnF+bi7mvcd45sldDKm3Cu7YY3/qThMtbUUMy\n1qG9SZxRgHPRSBKnDsA6KBNzlhMxwdKlPLlgEhGdNiy5qdhGZJMwbQDWIVkIVlOE8KtcBA9XI51t\nQbSZI6maTgq7n/ybm3JTsUzqh1zSiHyslvDpBixj8wxtCkzDsiGsoOwpRzlWg2Vuoe5nLFjN2Ebl\n4117lOC+MpJuGouYoB+JOwfl0HqghKY9J0kbO4iE7Azjz0UUsCYlcHb7QZSwQv+psefaBVHE7/FS\ncriIrPxccgcOiHnvBXT2G2pra+PQoQMUFAxi4MDueRLoKnpUMZ8hvPrqyxw5cpDJk6dy993Ro5to\n8Lnd/O3HvyAckvjct79On375Me3TNI2tv32FumOlDJozmUn3LzZcr8oKx370Mu7T1eTcNJmBD+tL\nw5QOP6X3/xm5qoXke6aR+uXrdAlS3l+B/9srwCdhf3Ietgen6apW1I4A3l9tJvD2YYREK47H55J0\n/5SobfSarBDYX0XbH7fh/vchwuWtmPPTcNwyiiHPLiblzvHYx+YhOj7dyUmCIGDJTcUxfzjpT1xL\nyv2TsA7JRK5uw/32EVx/2E7wRF1MboOmLCfO783HMrk/SlkznmfWozS6DfdYH5qG+YbhqCWNBH66\nFk1RddfahuWS+uXrUFq9tD77vuE1CaJI4deXgSBQ8rvlqHL0esTga68hOSeTUx9sx9can0Jm8o3X\nArBv/ea49hlhzJiIhr2o6Ei3nXk50EPsVwEOHNjHW2+9TnZ2Dt/61ne7XixVVV599re4Gpq48d47\n4jL2OrHqI059sIPMIf257tsPR41MS36/gta9p8i4ZhjDnrpLP6XiD9H05OsEShtw3n4NqV/VN/8K\n7ywj8OPVACT8+GYsN47QfX25shX3T9Yhn27EMjaP5J/cjGWM8aBjTdUIHqvF9YfteNcWo7QHsY/L\nI+3RGaQ9PJWECfmYYtCHfxoQRAHrwExS7p1E2ldnYZ+Yj+oO4HnnKB3/3IfcGH0UnmA1k/TIDOyL\nRqI2efA8uxGlvkN/vSBg//p1mCb3RzlYhfTqXsPzk++eim18fwLbT+PfespwbcrQvgy8bSbe8nrq\nNxyIeu2iycT4uxaihGWOr/ww6vqPo1d2HwaNGUnZsWJcTc1x7dVD//4FOJ3JHD9e1C3nXS70EPsV\nRnNzM7/61S8wmy1897s/xuGIfxbkJ/Hh2+9x6sBhhk4Yy/z774p5X31xGTv+9G/sKU4W/PRrWOzG\nkWr1ezuoeW8njoJsRv/kQV3rXU1WaP7+O0jFNWQsnUja1+frk/r2UoI/WwsWEwnPLDFUvkh7K/D8\nYgNau5+EZeNI+tqcqJ4qUqWL9r/uwrO8CNUTImFqfzKemItzyWjMcRRXLwfMmQ6cN48i/auzsRZm\nEa500faXnfg+KjWMqiFyg0i4dRwJd09E6wjgeW4TSpP+TUEwm0h4ej5Cn2SkN/YjHz5ncLZIxtO3\ngMVE228+QPWFDK9l1GO3IFrNlP9rA2pYNn7TRAzCbMlJFK/ZiizFlwYdPyeiXinavjuufXoQRZFh\nw0bQ1NRIa2tLt5x5OdBD7FcQsizz7LM/we3u4JFHHmXQoOh2tNFwpug4a19+nZReGXzu6W/EHP37\nXR2s//Ef0VSV+T98FGeWcT7UdaiUkt+twJKSxNhnv4g5sXNC1TSN1l+uJrjnDPapg+n38zt11S/h\nnWUEf/4B2C0k/mIp5tGdR96aphFYWYTvpZ0IZhHH49diX2g8ok31SXhWHqPj5b3IdW5so3JI/9os\nHPOHf+qplq7ClJ5Iyj0TSb53IqLThn9bGe1/30PIgKgvwD5v2EVy9/5qs2Ezk+Cwk/C9BWASCf5y\nPapLv4HJkp9Byn0zUJrddPxjq+E1JPZJJ/eWaQTrXdSt2xf1ms02KyMWzCbQ7qFsS/Qo/+MYM2MK\ngihytJuIHWDEiIhk9+TJ4m4789NGD7FfQbzyyj84efIEs2dfy6JFS7p8nqetnVd+/jyCIPDQ978V\nsymSqqhs+Nlf8LW0M+ULt5E33ritO1DfyrEfvgyCwJhnHjYsirn/tR3fmiMR292f3Y6oU5iTD1YS\nfGYd2MwkPrME0/DOpZWaquL/116C7x9DzHTg/P5NWEYbq4dCpxtx/Wk7wSM1mPskk/rFaSTfNhZT\nWtekpGpQRmrw4D/Tgq+4Ae+RWtz7z+E5UI33aB2+k40EylsJu/xRI+xYYBuSRdpXZmIbnYNc20HF\nLzYSKm2Kus8+bxj2JWNQW7x4f/sRWkg/ajYV9sH28Ay0Nj/B32w2zKEn3z8Tc04a7rf2Ej5nHM0O\nuG8eotVCxaubYsq1j1x8LYIoxJ2OcaSmMHjsKCpPldDWTemYYcMiqcDTpz87xN4jd7xCOHLkEO++\n+yY5Obn8z/882WWjKVVVef3/fo/b1cbiRx5kwIihMe898Ooqao+comDGeMbfZeyLoYTCHPvhy4Td\nfoZ9607SxuhL3vzbT9P+4keYeqeQ9et7ERM7j4yV0w0EfrIWRIGEny7WtdrVZBXf33cR3leJqV86\njm9ci2jQparJKr6NpwnsqwSzSNINQ0mY0v+S3Bk1VSXc7EOqcxOq9yC3+FCD0dMKFyGAKdmOtY8T\nW14Kttxkww5OPYh2C8nLxhIcnIn3/RO43ziEc9kY7CONewzsN49CbfMjbTuD//X9JH1+mu5ay63j\nkPdVoOyrQN59Fsv0ztUgot1C6mPzaPne23S8vI1eP1qme6YtI5mcBZOpWbmLlt3FUa19k/v0In/C\nCM4dOEF7bSOpubE7LI6aOonSw0WcPniUqQvmxbxPD4MGDUEQBMrKYh8BeKXRQ+xXAB0dHfzqV79A\nFEWeeur73dKE9MHryyN59YnjmLPslpj31Rw+yYFX3sfZO4Nrn4qhWPqHFRcVMLk36882lcqbaPnf\n5Qh2C1nP3Y0pvfPagVrXTuAHq0CSsf9wkX76RVbxvbiD8KFzmAZl4vz6tQgGBU6lPYD7nSPINe2Y\nMh0k3zEOc5yDKzRVJVTjJni2lWBlG1r4/0WapmQbtiwH5lQ7pmR7pDnJakK0mNBUDS2sRPTq/jBy\newClPUjY5SdQ0kygJBJJWrOdJBRmklCQbmhL3Bnso3PJ6J9B1R+343n3KIRV7OP0i8aCIJB4zySU\nylaknWcxF/bWbWISBAHbV+fi/9JrhF7cjnlSf13DsMQ5w7AUZOHbeJyUz8/Bkq//9JZ/60xqVu6i\nesWOmDzbB187hXMHTlC2ZR8T74v9O104IdIYd/rgkW4h9oSEBHJy8igvL0PTtMvq9nmp6CH2ywxN\n03j++V/icrXy0EOPUFgYe2Sth+rSMt783d9wpKZw71OPx55Xb3Oz8ZkXEUWRG3/4FezO//Z5/zjq\n1h+gdtVuHINyGfrk7fr6c3eA5qfeQPNL9PrZHViHdD4YW3MH8X9vJVpHIDLGbVrnRKOpKr6XIqRu\nHtobx+NzDYduSBWtuN86jBYIYxudg/PmkXENxlACYeo/OkPL4dqLaQuTw4ptcAbWnGSs2cmYokgp\nO38fGuFWH1JNB6HqDqR6D1K9B/fuKhKHZuIYn4sYx3UmDswk5YHJdLx6AM/KY2iqRsIEfVmrYDGR\n9JVZuH+0Fv9r+zEPycKU2fnNztQ3HcuSsYSXH0Zafhjb3Z03nAmiSMpDs2n5wTt0vLKDXt/TTyk6\nBmSTNn4wrkOleCsbcPQ3bvgpmDGerc+bKf0wPmLPysshvXcmJYePoihKzE15Rhg4cBDbt2+hoaFe\n1zr7akJPjv0yY/Xqlezfv4dx4yZw2213dvm8UCDAv37+PIos87lvf53ktNi6FTVN48Pn/o7f1cGU\nL95Gn+H6KRUA37kmTj//NuYkO2N++hAmW+fRsqZptD6zErnWRfL9M0m6rnOpoqaoBH6+Dq22PTKP\ndFHnEZymafhf3U/44DnMhdFJPXSqgY5XD6BJCo6bR+K8dUzMpK4Ewrj3naP5jaM076lCECFxZG8y\nFg8n8+6xpMwYQEJBxiWROpyXMmY6cIzLJeOW4WTeNQbHuBwEs4jvWAPNbx8jWOGK60xLbiqpD01B\nSLTiXXOCcI2x9tuU6STx3kkQkgksP2q41nbfNeC0EV5xBE3STzslzh2O+bxRmOoLGp6Zd0skBdT4\n4WHDdQA2RyL5k0biqqzF3RB7vlwQBAonjCXg81NXXhnzPiNcaE6qqqrolvM+bfQQ+2VERUU5f/vb\nn0lOTuGb3/xOl/XqACte+DvNtXUsvP92hk7U92b5JI69t5mqvUXkTxzBuNtvNFyrhmWO/+QVlIDE\n0G/eQWKefger5809BLafxjZhAKmPXKu7LvT3nSiHz2G6ZgDWh6brrguuOoa07Qymvmk4/meOIakH\nj1TjfuswmARS7p1IwsS+MT02a6qK91g9zW8exVdUj2A1kXNDIVl3jyNlWn+svZ2fyuO3OdmOc1I+\nWXePxTExDzUo07bpDG0bS1Hi6HY293aSfPtY0DTc7x5FDRoP2bZOLcDUL53w/krkylbddUKSDeuC\nUWgdAeSPSvTXmUSSFoxFk2T8W4x17b2mDkewmGjaecL4TZ1H/oRIYFBz6GRM6y+g/7CIBUbV6dK4\n9ukhNzfyJFRTU90t533a6CH2y4RQKMRzz/2McDjMN77xFOnpxnLCWHB0+272rt9M3qAC7vhq7OZe\nLWer2f2Xt7CnOLn+6S9GHW1X9tJaPCXV5Cy4huzr9afUhE5U0/anTYjpDjL/d5lukTK85TThdw8j\n5qVFtNM6HaWhHWUR9UsvB45vXIdg0L7u312OZ+VxBLuF1AeuwTqwl+F7uvgadW5alp/As/cciALJ\n0/qRdddYek3Kj9mMq6sQTCLO8blk3jYKax8nwco2Wlac+H9eNTHAWtCLhBkDUdv8eNcaqzcEUSDh\njvEABN45bKh8sdw8BkQBaeURw3WO+ZEnLu8Hxk8B5kQ76ROG4C2rJVCvf1O5gAvEXn04PmLvN3QI\nAJWnuofY8/J6iL0HneDll1+isrKCRYsWM2WKviIhVrS3tPLWb1/AYrNy/3eewBJl8MUFyJLExmde\nRAnLXPfth6Pa8LYeLKHqzY9IzM+i8HF91YPqDdL8w3dB0+j1v8swZXSeu5XKmwk+vxkSrdh/fLOu\nF7hc2oT/lb0ISVYcT1xnaLUbOFCFb8NpxGQ7qZ+fEpN5lqZquPdX41pzCrktQOKwLLLuHEPSyD6X\njdA/CXNqAuk3D8N5TV/UQBjX2tNIzbEPeUiaOxhzXiqhY3VI5cbyQ8uwbMwjc5BPNaBU6BOsmOXE\nPHMQankLaon+4Axzdhq2sf0IHa5EcRlf8wUr55Z9xtE9QFrfbBIzUqkt0n9i6Ay9++ZhS7BTfeZs\nXPv0kJ2dgyAI1NXVdst5nzZ6iP0yYP/+vaxatYL8/H48/PCXu3zeBWmj3+NlyZceondf4xb6j2Pv\n31fgqqhh5C3XMmCq8SzHsNtH8c9fRzCJjPrR/Zh15IoArl+vRalvJ/n+mSRM7LxbVAvJND29AkIy\n9ifmYerbuQe42ubH++ftoEHSV2YZ2u2Giuvxri1GSLKS8uA1MSlf1GAY1/oSfEfrMDltZCwZQcrM\nAZckP+xuCIKAY0w2qdcPRlNU2taXILuN89YX95pEHAsiPQj+bWVR19uvjxTupT3lhuvMMyPRr3yw\n0vi8826PoePGUe2FkXme0pqo1ygIApmD+uJvbSfojv0mJ4oivXKycTU0xuSxEw1WqxWn00l7e3z+\nNVcKPcT+KcPlcvH887/EbLbw9NPfx27vup3othWrKT1cxIgpE5m+KPahGTWHT3L0nQ2k5PVm+peN\nC7eapnHq1+8Qau6g4PM3kVyor7bwbTyOb/0xrMNzSX14ju660Ivbkc40YVk0CsuszrtstbCC94Xt\naB0BEu4Yj2VY54oaOK9+WV6EYDGRct8kzBnGqh6AcIsvkuao6cDWN5Vet47EmtV1G4fuRsKAdJKn\n90cNyLjWnUY1KF5+HJbcVCyDehGudBGuMi7EmodnIzhsSAfPoan6DVTm8fkgCsgHqwzPs4+OjFoM\nHdO3IwBI6t8H0WrGHQOxA6T3j6hQXFV1Ma2/gIw+vZGCITzt+j458SAlJY2OjrZuOevTRg+xf4qI\nSBufpaOjnYcffoSCgq7bftaerWD1P17FmZbK3U98NeaiXsjrY/Ozf0MQBG747pewJBi30TdsPkTj\nR0dIGTmA/vdcp7tObmin9f/WICRY6fXjZbp67PDOMsJrjmEdnIXtS7N1zwu8fQjlbDPWKf2xzRum\n/7pNHtxvHAI0ku+agCUnepdtqLaD1vdPonglHBNySbtxCKKBpe2VRtLw3iSNyUZxh3DvNibV/9g3\nO/I98+82VnAIZhHLxL6R4ugZfdWJ4LBjGpaNeroBzcAXxjo8F0wioWPGEbtoNuEoyMZbXhdTF2pa\nv0hnsasyTmI/P6i9tb4hrn16SElJwePxoCjxTc26Eugh9k8Rq1ev5NChA0yYMInFi/Xz07EiLEm8\n+uxvUMIydz/5VZwxShsBtv/+NbzNLibdv5jew4xHygWb2jn9/LuYEqyM/P59+uZeqkrrz1aieYOk\nf+Mm3eYUtdVL8DebwWoi69lbdf3BpSPVhD4sQcxNIfGBqfpOkcEw7jcPoYVknEvHxFQoDdV24Fpf\niqZqpM0bjHNC3mei0cQ5KR9zeiKBMy3IHmOzrQuw9E3H1CuJcHlLVCsDy/kuXyVKTl4c0htUDbVG\nP2IVE6yYc9II10aXbCbk9EILK0jt0dMrzqxIyi7QFl/knZQSSeEFvMZDu2OF3Z4QmaEbNlYdXQ3o\nIfZPCefOVfH3v/+F5ORknnji291CImv+8Rr1leeYfvN8RlwzMeZ9ZdsOULJpD72HFjDxvkWGazVV\npfgXryN7Awz56lISc/VJ0/POPoKHKkiYUUjSos6llpqmEfz1JvAEsT0yC2tB51JJ1eXD/4/dYDHh\n+NJMXfLXVA3PiiKUVj8J0wuwj4reLBKq6cC1vgTQSLthCPYB+vM9rzYIYiTnjga+4/Ux77P0T0eT\nFOR6Yy920/knHaXOOHcsnq9zqA1RzstwoLb70aJE4paUSNos3BGddK2JkcK55A9EXfsf+2yRp9Jw\nqHsG5Yjn1VvdkbP/tNFD7J8CJEnil7/8KZIk8T//82S3SBtLDhexdfn7ZOXlsPiLD8a8z+dqZ+vz\n/8Jss3L9d76IGKULr3rFTlwHS+k1dbihZUC4spn2FzYjpiWR8Z1b9G143y9COViFaWI/LDfrNCGp\nKr6XdqL5JBLvmogpL033df3by5BKmrAUZJB03RDD9wIQqnfj2hBRVKTfMAR739ifcq4W2AemIyZZ\nCZxujtmfxtI/8p0LVxpHz2KWE8wiSpTGpovEHmVohynDAZqGYuAMCWCNh9iTIsQe8sZH7JbzTXRS\nKLYnnWgQhAhdqmpPKub/l3j11ZcpLz/L/PkLmT59VpfP87ndvP7c7xBNJj739BMxz3PUNI2PnvsH\nQbeXqY/cTlpf/UIkgO9cI2f+/D6WlCSGf/tuXbLWZIWWn6xAk2QynrpZ3wempo3Q33aC0479Sf3B\nGsEPipFLm7BM6It1jr51sVTegn/rGcQUO8m3jYtq5iW3B2jbUAoapN0wBFv+Z4/UIdK2nzQ8C01W\nCUUh4Auw5EYicTkKEQsmETHDYWjRCyCcn4WqtRuTq+iMkHC0DlTTea9/JRCddE3nO4cVKb4UESXv\nogAAIABJREFUyIUGQKPCcDy4QOgXCP5qxtV/hZ8xHD9exPLlb5GTk8sjjzzW5fM0TeOt3/6ZjlYX\nCx64O64J7MdXfkjVvmP0nTSS0Uv0C6AQGXFX/MzrqFKYYU/egS1DX2LY8coOpFN1JN00hsQ5nRc4\nNUUl8NyGiLTx8WsRdRQrcpWL4MoihJQEEh+Yop9X94XwLC8CQSD5jvGISca6fSUQxvVBCZqkkDJr\nAPZuJnVN05CDMsFWP/56D6G2AIqkfGqP6ZbeERlnuFXfT/3juNDMZWTRexFmAaJdthpZIJiizI89\nr96JZuOgnE+PmKIU8QHC58nfquP5r4eAL/JZ2brBZA/A6/ViMplISNDvqbhacPVKAj6D8Pl8/OpX\nv0AQBL75ze92yxfgwKYtFO3YQ8HIYVx3x9KY97mq6tj1l7ewJzsiro1RuksrX9tMx8kq+sybQO+5\n+vp2qaSejn9sw5SZTPo3btJf9/ZB1NMNmOcWYpnVecpEk2R8f90JikbSw9N0B15omobnvWOo3hBJ\n84ZGbUDSZDXSlu8J4RiXQ+KQ2KfdG0ENK/gbvPhrPUjtQTT1v9lQMIskZCaSPCgdi7P7BnhYMiLk\nFDOxn69RxETsCBDlhnSxCBvFhfIisRtYPwAogfPEbo/eWCf5Ik8JlqT4fk9BX+QpJNERXQYbCzwe\nN05n8mei6N5D7N2IF1/8I01Njdx99+cYNsx4WEUsaK1v5N0//RVbYgL3PfX1qPnxC1DCMpueeRFF\nCnPD976Eo5d+zhrAXVJN+T/XY8tMYeg3btNdp4XCtPxkBSgqGd9bfPGx+79e/2wz0qt7ETKSsD82\nV/e8wIqjqHUd2K4txGLgJx7YV4V0phnLwF4kTIs+fb5jZyXhRi/2gRk4JsbevKUHRVJwl7nwVXeg\nKRECtCTbMCdaMCdaMNlMKEEZ2R9G8kj46734G7ykj+5NUm73jNsTbWZMDiuyK0ZiFwWwiIbmXf8P\nMTxlSOfzylHSX9p5j5tozV7yebKOJWIPeSPv+UIRNVb4PRHFTUI3EXtHRwepqZ+NdF4PsXcTdu3a\nzqZN6xk8eAj33HN/l89TFIVXf/lbQv4A9z71+EVNbiw48Ooqms9UMWz+TAbOMlbPKCGJEz99FU1R\nGfHde7E49R9b2/+6hXB5E45bJ5FwTecpIU2SCf7fBpBV7E/MQ9CZQRo+1UBo4ynE3skk3D5e9zXl\nRje+TacRkqwkLx2j6ytzAf7TTQRKm7H0SiJ1dkGXoitN0/DXemg/1YwaVjHZzSQNTCEp14lZx+FR\n0zQCjT5cxxpxFTWiBGWSB3aTCsckQowaai2sQFg19Ne5uLYtgJBqTJpqQ0RqKPY27uwN17oQnXZd\nq4gL8Ne2gCCQkB39s+moiVgZpGTH9+TVXBtREaX3yYprX2fweNx4PG6GDtXvrbia0EPs3YDW1hZ+\n97tfY7PZ+Na3vofZ3PWPdfMb71JRfIpxs6cz6fo5Me+rP36GQ6+vwdmnFzO/ek/U9WUvrcVX1Uj+\nsllkTCzUXRcsqsL9792Yc9NJ++oNuuuk1/ehlrdgWTAS86T+na7RAlJE2igKJH1xur60Mazgfvco\nyCrOO0YhRklthFt8dOyqRLCZSJ03uEueLyFPiOb9tYRaAwgmgdShvXD0T416YxEEgcQ+DixJFpoP\n1NFR0oo1xY69VzfkeVUNorz+xaXnNe/RPjPVG0LzS5gHG5OmWhsp2ooGiiVNVpBrXFiH5US9ofqq\nGrH3SYspFeM6F2lMSusXnw96Y3UNyelpJHbDgPgL5l/5+X27fNblQE/xtIvQNI3f/vb/8HjcPPzw\nl7rlD19Vcob1r75FamYGdzz+aMxRp+QPsOkXLwEw7ztfvCgT04PryBnOvb2VxPwsBn/5Zt11qj9E\n60/fAyDjB0sRdaJA5VQ90lsHEfokY3tEXw3kf+MgaqsP+8KRmAv0dfK+D0tRmrzYJ/XFVmj8xKJK\nMm2bz4CikTpnIOYu5LeDLX5OrzlNqDWAPSuJPrP64SxIi0rqH4fFaaPXhIgKqa24qVuKqpqiRi1e\nXoDqjahSog3qVs+bjIk6AzcurjvfmCTm6hO7XNsGioqln3HDWNgbQGp1k9Q3tqfQtqp6BFEgNT/2\np1YpGKKtsZmsfON5uLHiArFfsO+92tETsXcRmzdv4ODB/YwfP7FbBlJLoRCv//J3qKrKfU99nURn\n7NHGzhfexF3fzIR7FpEzWj/6BpB9QYp//m8QBUZ+717DyKntT5uQa9tIvm869jGd37i0kEzw/zaC\nqmH/5g26Y+u8+yuRdp7F1DcN+82jdF9TKm8hsKcCU0YSjhuMH381TaNjazmKO0TS2Gzs/YxrCkbw\nVrXTdrIZQRBIG5VFUt6lF8usKXYSsx34672EPRLW5Eu/2WiqihqQsWTGFvnLjRHCNqUb55eV837s\nJgNLBk3TUE83IPRy6KbWAEInIt4v1sHGk5E6iisBcA6KHoErYZmm0grS+uZgjtHBFOBcaWSMXe7A\n6DWZWHDmTKQXYsAA44E0Vwt6IvYuoLW1hZde+hMJCQk8/vg3u6Vavu6f/6axuoZZSxYxeKw+8X0S\nZ3Yc5uTabWQU5DP5gcVR15f8fgXBBhcDPjePlBH9ddcF9pXhXXEAS0EWqV80GJzx8i7UmjYsS8fq\nzi1VvSEa/7g1Mlj6C9N1fWXUQBjPymMgCjiXjUGwGheN/aeaCFa2Yc124px4aRGVpmm0nWymrbgZ\n0WJi8I2DceSndPlvajufggm1xddc80nIHUHQNMxpsRF7+HyEbYki8wyXRvLXZoMnIrWyFa0jgGmM\ncSE6eCjiEmmfYGxZ0XY4MhQ6fXz0BrPGU2eRgxJ54+LLbZcVRQZ5DB4zMq59ejh5shiLxcKgQfp9\nFlcTeoj9EqFpGr///a/xer08/PCXycqK/TFRD2VFJ9i6/H0yc3NY9Pn7Yt4XdHtZ9aM/I5pNzPvO\nFzFZjRUJTTuOU7duH84heRQ8oD89SfUEaH1mFZhEev3wVl1tsnyshvB7RxDy0rAZTEPyv74fpc1P\nwpIxht2l3nXFqB1BEmcNwpIbhZhcftx7qhBsZlLnDowrXXIBmqbRdqIJb2U7ZoeV3tPycXST46Mt\n7Xw7vKuLxH7+xmBOi00ZIte0I9jMmHrpvw9N05BLmxCS7Rc7SzuDcjSShjCP1b9pappG8FAFYloS\nFh3biAtwHS5FMJtIHRU9mq45EvFszxsfJ7EfO4EgCBSM7Lo6LRgMUlFxlsGDh2CxXHlr51jQQ+yX\niG3btrB//17GjZvAggX6+elYIQVDvPH8H0EQuO+px2PuLgXY8cfX8TS5mPzAEnoNMs7xh90+Tv3q\nLUSrOWLwZdHPxrl+twGl2U3KQ7OxFuoMpA6EI14wgkDCt25A0JG5SQerCO+rxF7YG9t8/R9b6GQ9\noWN1mHNTSJxl/NiryQrtH5ZF8uqzB2CKkk/u9AxNo+14E75qN5ZkG1lT8jAndt+P15xkQTCLSJ6u\n+ZWEWyKSP0t6dGJXPSGUFh/mPONir1rXgdbmxzykt+GTiXygEgCTAbGHzzahNLmxTxhg2DMRcrlx\nl9aQMqJ/TFLHcwdOIIhC1NTixxH0B6goPk1OQX+SkqP780fDqVPFqKrK0KGdz++9GtFD7JcAn8/L\nSy/9CavVyte+9kS3pGDWv/omLXUNzF12C/2Hx/4lrthzlJJNe8gdOYjxdy+Iur7kD+8huTwUfP4m\nHAP0LQYCe8vwrT2CdUg2KQ/M1F0X+udutPoOLMvGY9LxTlc9Qfyv7gOLiT5fv1b3h696Q3hWF4NZ\njAyhjqKZdu85F5l+NLw39v7xSwo1TcN1rBFfTYTUMyfnYoqS9okXgiAgmsVOm5nigVTvBgEsMQwS\nCZU2AWAdZBw5S4civunW8QaRuDuIcqQacVAWYm/9qN73YST1kTjHOEJu2loEqkbv2Z37Bn0cnqZW\nGorLyBkzFHty7E9Qpw4cRg6HGTl1csx7jHDgwF4AJkyY1C3nXQ70EPsl4JVX/kFbm4u77rqP7Oz4\nJFidofrMWba8u4qM7N7cdP/dMe8LeX1sff6fiGYTi3/6WNQGpuY9xdSvP4CzMJ9+d+o3Dqm+IK3P\nvg8mkYzvL9HNhcvFdYRXnk/B3K9vGOb/9wE0T4iEpWOx6qRgNE3Ds7YYzS+RdF0hZoMUAkCwwoX/\nVBPm9ESSp8SvRLqQfvHXerCm2sm6pvtJ/SIELrbkXwrUsEK4yYclMwkxhmuUTkfy5rahxvrt8MFz\nYBaxjNFXjsi7z4KiYp6tn1vWNA3/phMICVYSphvnoBs+OgKCQNbc6IPXy7cfAmDQ7PgI9fjufQCM\nnn5NXPv0cODAPux2OyNHRr8ZXS3oIfY4cfZsGWvWrCI3N59ly4ynEMUCVVF46zcvoKoqd379K1jt\nsacTdr/0Dr6WdiZ97hZ6DzYmN9kf5NSv3kYwmxjxnXt0PdYB2l7YjNLYQcoDM3UVDpokR1IwgP3J\neYYe6+F9lZgG9sJ2w1Dd1wwVNyCdbMDcN42EKf0N34vil2jfXgEmgdTrBl6SXj3SSXo+Up+Ug2j5\nlEidSNTeFbmj1OABTcOaE72LVQvJSOUtmLIchooYpcGNUtOGZUS2YRNTeGtEDaJnCwEgnapDrnWR\nMKMQ0UBdFWxso/1YOamjC7D3ij4Y5czW/SAIFMzUb2D7r+uVJE7uO0h678xuUcTU1tZQU1PNuHET\nsMahyrnS6CH2OKBpGn/5yx9QVZVHH/1at/yhd6/bRPWZs0y8bjaF48fEvK/xdDnFa7aR3j+X8fcs\njLq+/OX1hJra6X/v9TgH6j9lhIpr8L53EEv/TFIe1NeiS28dRKtpw3LLGMwjOj9PC4Txv7YfTCJJ\nD07VT8H4JLzrzqdglow2zAtrmkbH9gq0kEzyNX2xxKgS+Tg8Ve24z7gwJZjJnPjpkjqAKquIUdJK\nRghVR7o+bbkxTIkqaQRZxTbMWHIo7Y4MebZM7q+7Rm10oxw5hzg8GzFb/7W970cia8d844i2du1e\n0DRy5kePwF2VtTQUl5E/YThJ6bG38R/fvZ+Az8+42TO6JUW6deuHAEydOqPLZ11O9BB7HNi+fQsn\nThxjypRp3ZJv87Z3sPbl17AnJrL4kQdj3qcqKtt++ypoGrMevw9TlE5Xb3kd597ZRkJOBgM+d73u\nOk1WcD23BjSN9KcWIegUVtXaNqQ3DyBkJGF7cJrueYH3jqK1+bEvHInJQN3iXX8SzSeRdO2QqHNL\nAyXNhM61Y81NJnFE/Eokf72H9uJmRKspklO3f7qtHJqmoYYVRNul3zxC1e0IFhFrnxjy6ycibfQ2\ngwEkmqoh7akAuwXreP0nvfCmk6CB5Ub9oqHqD+HbdBxTnxTsOjYTEHEPrV2zB3OSnT7XRY/Ai9du\nA2DEQv0xip1h3/rNAFwzX/97His0TeOjjzZhs9m6xX77cqKH2GNEKBTi739/EbPZwhe/+JVuOXPN\nP17D7/Fy0wN3kZwee1PNybVbaSqpYMj1U8kbG71559Tz76IpKoVfX4bJpv+U4XnvAFJpPUkLxmIf\n11/3vODvt0BYwfboHF1PELmildCHpxF7J2NfqK8llsqaIyqYnOSoKRjZE4pIGy2mS/KBkTqCuIoa\nEcwimZNysESx/u0OqGEVNC75qUDuCKJ0BLHlpkQtJquBMFJZM6Y+TsyZ+jUKuaQBtdWHdWJfw0lV\n4Q0nwW7BMls/DePbfALNL+FYNN7w+lr3nSLU3EGfGyZGVcPIksTpjbtISHUyYHrsaRhXUzMlh4sY\nMHwovbuh47Sk5BR1dbVMnTqdxG6y/r1c6CH2GLFu3fs0NzexZMkycnK6/qVpqKpm74YP6dMvn5mL\no6dSLkDyB9j38ntYEu1MfzR6jr9p+zHai86SOWMUmVP1Iy/FHaDjr1sRnXZDLxh5TznKkXOYJvXH\nPFPHCEzT8P/7AGiQeP9kBB1S02Q1koIRBZy3jDYkBk3TcO+sQAurJE/vF7e0UQnJtByqR1M1Msb2\nwZoSn7f3pUI9b5trusSIPXQu0mhki2HyU6i4HhQNu4FTJoC0M5KGsU7Xl5Mqh6rQGt1Y5gzR7SLW\nNA3v8gMgCjh0RiNeQPV7OwHIM5jKdQFnPtpHyO1j2PyZmAzkuJ/ErtXr0TSNKTd1PVoHWL9+LQDX\nXaff63G1oofYY0AwGOSdd94gISGRO+6IXbVihA9eeQNNVVn40L2YYrTjBTj6zgYC7R7G33lT1Nyj\nKiuU/WU1gklk8KO3GK51/3M7qidA8v0zMaV1ng7RZCUyEUkUsH1plv44vP2VKGebsUzoi0VHAgkQ\n2FsRmV06qS/mbOPCYLDCRai6A2tuMgmDow+v/o/rVjVaDtejBGVSCjNIyOoeG9dYIAcixK7nBhkN\nwaqI+VZMxH48YpZllIZR/RLSwXOIvZ2Yh+irZsJrjgFgWaSfN5dO1CCV1pMwcyjm3vo5eHdFA637\nTpE6ugDnYOPuVU3TKFq+CUEUGRVlOMx/XEsoxJ51G0lKdjJ+rr48N1b4fF62bv2QPn2yGT8+9vnC\nVwt6iD0GrF37Pm1tbSxZsgyns+v+2jVl5Rzdvpu+hYMZNS12SVag3c2Rt9aTkJbMmNujRxG1q3fj\nr2kmb/F0kvoa/Ijr2nC/uw9Tn1SSb9e/nvCa45GC6cJRmPp2rhvXJBn/O4fBLBra8SruIP5tZQiJ\nVhLnGreWq5KMe3cVmARSZgyIOwXTVtyE1BYkIduBs+DSfWQuBUogMs7NlBB/Ll8Nykj1biyZSZh0\nouaLr9MRIFzlwtIvDZOBBa+0tyKSRpsxSH9aVbMHeV8F4uAsTEP06xie5fsBcC4z1ouXvh4pQOYv\ni56nrj9eSkvZOQpmjsfZO/ZZwYc+2o7P7WHawhsvDrHuCrZs+ZBQKMT8+Qsvjtj7LOGzd8WXGRei\n9cTEJJYu1R9CEQ/Wv/omAAsfvCcukjry1nrCgSAT77sZa5TOVCUUpvyfGzAl2Ch40Pgm0PGPrRBW\nSPvydbqTb7RgGOn1fZBoxfq5KbpnhT4qQXP5sc0bhsmgmca/pRRNUki6bghilGjWe7gO1R/GMTYH\nc5wpFH+9JyJrdFpJH2XcYflpoCsRe7C6HTSw949+MwqdqAcNbKOM04TSjjIQBazT9f1cwh+cAFXD\nslDfq0hxefF9WIy5Xy/sE/VlhWFvgPL3dmHLTCFrVnQdeNGKiIR29NJ5UddegKZp7Fi1DkEUmXHz\n/Jj3GZ33wQerEUWRefO6ft6VQA+xR8Hq1avp6Gjn5psXd0u03lBVzfHd++k/rJDCCfoj6D4JyRfg\nxOotJKQlM2JRdKVA/YYDSC4P+UtnYE3TJ1i5sQPf+mOY+/UicZ5+kTP8wQm0jgDWpeMQUzsvJGkh\nmeD6SMHNvsAgn9/qI3i0FlOmA7tB1yNECqa+Ew2YHFYcY+JrBlNCMm3FzQiiQMa4bMQu+LNfKuQL\nEfsl2BSEqs7n12Nwqwwdr4ukyEboyxzlcy6UKheW0bn6f0NFjRB7ohXLXP0OaM97B0FWSL5tsuHN\nsm7NXmRfkPylMw17JwDc9c2U7zhE5uB+5IyObhB2AWVFJ6g9W8GYGVNIzYwvTdcZiouPU15+lmnT\nZpKeHvtTw9WEHmI3gKIovPbaa1gsFhYvXtYtZ3707koArr1jaVzR44nVW5B8AcbcOi+qfammqFS9\nuQXBbCL/duObgPuN3aCopHxuhq7OXAsrSO8cApsZyxL9m1Fo+xk0dxD7dYWIBhN0fNvLQNVInDM4\nqmmXZ381qBrOyflxNyK1nWxGlRRSCjOwOK5Mc4nsC4MoxF081RSVUHUHJqctqvGX3OxFrndjHZSJ\naJCyuVg0naFfNJX3lqO1+rBcP0y3cUkLy3hXHkRIspF0k/73QZUVzi3fjinBSt5ifVnsBRSt2ISm\naoy9/ca4fhtbV6wGYM6txnWkWPH++ysAWLz41m4570qgh9gNsGvXdmpra5k3bz5paV0fb9bR6uLg\n5m1k5eUwalrsPhaKLHP03Y1YEu2MWqJvnXsBzbuL8Vc3kX3jRMMOP8UdwLvqEKasZJJu0H/slj86\njdbixbJwFGJK5ySjyQrBD4rBZsZm4J8utXgJHavDlOXANty4iSbc4iN4thVLZhL2gfFFTv4GL4F6\nL9Y0O47+V25OpewPY060xC/NbPCghRVsfVOj7r1YNB1toF2XFaS9FQhOGxYdW2WA8NrjAIZpGP/W\nUygtHhwLxxnewJt3HCfY4KJgyXQsycYF65DXz8l120nqlcqgObH/Nppq6ijee4B+QwfH5bGkh+bm\nJnbt2kFBwUBGjIjdNvtqQw+xG2Ddukgk0F259QObtqLIMrOWLoqrIFO1twh/azvD5s/EFsNg3trV\nuwHoe/scw3X+jcfQgmGct1+j24wEIK2JeKNbb9WXtIWP1qC1B7DNHITo1M+Du7adj9ZnRrfY9R2L\nNNs4JubFRYyqotJ+shkErkhe/QIUSUGT1Utyi7zYbRqrzNEiYjNSuRyvQ/OGsE4ZoPvkoza6UQ5V\nIQ7PxjRAP6XhWXEAAOdtxgR8bvl2AIYYNMVdwMl12wj7g4xeen1cEsety99H0zTmLFvcLX/nVatW\noKoqixcvu2Lfm+5AD7HroL6+jqKiI4wbN468vK6Pu9M0jf0bP8JssTBhbnxdbKc+iGiAh98UXcYV\nbOmgZd8pkofmG1oHAHjXHgWTiOMmfSsDpaIFtaQR08R+iFn6NYbQtsjwBJuRWVRYoX1PBUKSFdtw\nfRkkgOKTCJx1YU5LwJYXvZX+4/BUtKMEZZwD0q5YCgZA9kWsei+J2Gs6wCRgyzbuNpWbPCgtPqyD\nMnWbjQCkPZEhGNZpBkXTDcWggfUmg4ayiiZCR6uwTyrAkq//FOUpq6W96CwZk4aSUhDlby3LFC3f\nhNluZcTN+uZ0//Uabe3s3/gR6X2yGDMzuj4+GrxeL+vWrSYtLZ05c2KXWl6N6CF2HWzatB6AxYuj\nTyOKBVWnSmmsrmH09Clxjbvzuzqo3FtE5uB+Ub3WAerX7wdVI2ehvnIFQDrTgHS6joRpgzFl6JNH\neH3EjtUyX//HrjR7kIvrMQ/OMrQOCJ2oQ/VLJEyIni/3FTeCppE0sk9ckZMiKXjK2xCtJpIHXl5p\n4ych+yOFU0tSfMSu+CVklx9rH6eus+YFhE42AGAboU+eql8ifLQGMTtFX6aqaoQ3noQEC+ZZ+jdn\n78qDADiWGltqVK/YAUD+sujByNntB/E2uRh200zszth7DHa8/wFhSWLussVx9YLoYeXKlQQCfhYv\nvvUzZfjVGXqIvRNomsaWLZtJSEjg+uu7p4vt8NbIF33yDbFHJABndxxCU1WG3qA/mejjaPjwCILF\nFNWPw7cpkkt1LNBPr2iqhrytFCElAfMUfUmbtK8SAKtOJ+oFBI/WAmCfYHyD0lSNQEkzgs0UdzOS\nr7oDTVZxFqR96uZe0SB7z0fscVoXhGrdANjyoqdhpFONYBKxDtb3Xg8frgZZxTpVvwdAKapBa/Jg\nmT1Et2iqBsP4PijClOEgcaZ+Pjvs8VO/8SD27HR6TYk+wejYis0gCIxZpt/x/F+vIUnsXP0BiU4H\n19zY9ehaURTefvttbDZbtwzOudLoVgekwsJCEXgBGA2EgC+UlJSc7c7XuBw4c6aUhoZ65sy5Frvd\njscT7tJ5mqZxfM9+7ImJcc0xBajccxSAATOie2YEGl14y2rJuGYYFqext0VgVymC1Yx9ir5CQj3T\niObyY75huGHkGD5aA6KAZax+UU71hQhXuUgoyDBsoIHIUAk1ECZxWFZcShhN0/Ce60AwCTjyuy5N\n7SrC54k93nSQdIHYc43fQ9jlQ25wYxnUC1FnchWAdH4CktXAyTH8YWQEnfl6/cJ3YPtpVE+Q5M/N\nMPw+1G84iBoKk7d4elR/m+YzVTQUl9HvmtGk5sZu6nZk6058HW6uu/PWuKaN6WH//j3U1dWxYMHN\n3SJrvtLo7oh9CWAtKSmZBjwN/Lqbz78s2LYt0ik3e3Z0BUosqCkrx9XQxIgpEzHHMTMxHAhRc/gk\nGQV5JPeJHrm27D4JQOY04xFecn0b4fIm7BMLDP2z5X0VAJin6Odl1Y4ASnkL5iFZiAb+LaHTTaBB\nsgH5X0DgbCtA3EqYYLMfJSCTmO284tE6gOSREK2muJ0dpXo3gs2EOd345uy5oIYxGkTtDSGfrMfU\nN123YUwLhpF3liH0dmIaqd/g5F19GMDQF0bTNGpW7UIwm8hdEL2r+vjKyG8tHvsATdPYtnJNtzUk\nQaRoCnDLLUu75bwrje4m9unAeoCSkpJ9wGfOZEHTNHbs2EZSUhLjx3fPKKzju85PdJlhnPf+JGqO\nnkIJy/SbEptPe8veCLH3ikLsgT2RQme0aTfy/kowi5iNrF2PRdIrRtE6gFQSmerjjLJOUzWCFW2I\niZaYbGo/Dt95JYmjX3zF1k8DalhB8YexOK1x1QhkdxDFE8KanRxd43+e2K1Gapgj1aBoWCf103/N\nveXgl7BcO1T3NeX6doIHy7GN6Yulr36Q0X68HF9lA1mzxxg2xgEEPT5KP9xLck4m/SbH/iRbebKE\nmjPljJo2mfTexlOiYkFFRTlFRUeYPHky/fp1fTjH1YDuJvZkwP2xfyvn0zOfGVRVVdLc3MSkSdd0\nWwHlTNFxBFFk6PjYO00B6ooi02vyx0cfoqtpGh0nKkjIziChj7HmPnQ8MnXePl7/S6yFZNSzzYhD\neuu6+wHIZc0AmIfqa9I1TSNc3YaYmoA1ysg72eVHC8nYogxi/q/XUDWCLX7MiZbL5txoBKk9CIA1\nNb5riTUNo0ky/tImTL2dhqmt8OHIXFPLRP2bs7ytFADzXP0JV74NRQAkLTD+DteujswHjcXFsWTj\nbuSQxMhFcw0HYH8S21dFXBdnxeGKaoRVq5YDcOedXZ+IdrWgu6cMuIGP36bFkpISVW838WecAAAg\nAElEQVRxZmbXJ4h3NzZsiLjazZkz6+L1deU6Q4EgVafPMGDoYPL7xzcYoqWkHNEkMmLWaGyJxnlp\nd2UjYbefnJmjol5vw+l6TE472RP66/6ggker8SoqjrH59DI4z3fOhWA302dsvm4+NdTkocUfxnle\n4mh0fS2VkTb6jCGZpMfxuXvqPWiKRlq/1G77XnXlnPo6b+SMvmmkxnFO1Y5I+it7VA42g6EjnmO1\naLJK6pg83etUA2HaTzZg7ZdOH50Ui+oLUXWgEktBL/pM6t/pGk3TaNx8AsFmpu8d12B2dv5dDHsD\nNG07SlJeL4bcMO4/vlufvEZN0zi9bhsmi5kZ980nKT22z6ituZWiHbvJG9ifqddP7bLWvK2tjS1b\nNpOXl8fMmTM/k4ZfnaG7iX0XcDPwTmFh4RTgmNHi5mZPN79817FtW0QzPnjwSJqbPWRmOrt0nSWH\ni1Bkmf4jhsV1jixJ1Bafpdfgfrh9MviM93qLIjVq+6Bcw9dROvyEKpuxTx5IS6tPd90F3bPUL133\nPC0gIZ1zYR7SmxaX/lnBoprIa58f/mB0fW1lLQCEkixxfV7tZyJPDpojvn166Orfva02khYKilrM\n52iahrvchZhkpUNREAz2eQ5WAaDkpeieLx2sQgsriKP0vxPhj06jSQrC9EG6a0Kn6wiebSLxuhG0\nBWUIdr6udu1elIBE7xsm/cd3q7PPsraohObyGgZfOwW/IuKP8TNa/+p7KLLC1IXzaWnxxrTHCG+8\n8SaSJLFo0VJEUbwqOemTiCXg6G5ifw+YV1hYuOv8vx/q5vM/VSiKwqlTJ+jbt1+3mf+cKy0DYMAI\n/cfczuCqqEMNy/Qeql+4/DjaSyLkmTzE2FQrXN4EgHWIcTu/UhUhWNMg/RymUtcBGpj6GuvF5cbI\nj8USxXMdQG4LIFhMmJLjs16V3CEAbOnGTzaXA5qqEWoLYk60YDJoGvokwi0+tJCMvX+mYSSqaRpS\naTNiohWzgSQyfCSScrMYGK3J2yP1FrPBlCT/xog0NukGY3fG+o0RjXvOjdFrUycvjL67OfbRd4qi\nsHvdRmyJCUy6Pr6ReXrnrV27ioSExM+si6MeupXYS0pKNODR7jzz/2PvvMPjKK++fc9sb6pWsSzL\nTbbcCzYuFGOKuwm9hh5qypvwEkLoJLQQSEIChJK8CaGD6biBCxgbGxv3XmRbsvqqS9tndub7Y3aN\nwqednZUECaD7uviHa55nR7L2N2fOc87vfJNUVBwlFApRUqI/bi4Vqg+XAdBvcGqHMk3l2qFk9iBj\n05rajmiNKk4d33UAqUKrODHrdA0CqFWtIAoI+TpeM15NsMU8fcGONgcAMGXpN5+oqorcFsKc6Uj5\nFVsOSJjs5m4Nje4pIi0hVFnBVpBaKueYjUCSTtuotx2lLUTapKKE6S9VVpB2VCFkOjENSNCUFIwg\nbypDHJCl07ik4F+1G8FlwzE1cZ9CyNtC89ZSMsYOxlGg/7cVCQQ5tGYTaQU59BtnPODZu3EzrQ2N\nnHTmXGyO7j/Av/jicxobG1iw4Kxv3ei7ZPznvwX/RRw4sA+gZ4X9SDk2p4PMvMQNJJ3RVK5VPGQO\nMCbs7WW1WNKcWDOSHE5WNgHotoMDKNUtCDmehGPtAJSYsOv5rgNEmwJgNSEkqedWghJEVcw6XjOd\noSoq0ZDcpWEWXwehBu1BZu+TmliEK2PCnuTgNHJASzt5RifuNpVLvaj+CNbxiX125C/KIBLFfFLi\n6qjwrkqida04pw9HsCb+/dau2AyqSt9ZyQvhDn26CTkUYfisk1J6gH8eG1Q9bZ5xr3Y94l5Qc+cu\n6JH9/pvoFfYOlJZq1QFDh3bfJQ60Vz1vRRV9BxSlfCjTUqFF4JlF+j4boEVVvsp6HP2SPzzkau1w\n0twvceWMGpFRm/yIBfqRoxLLo4o6g5NBq3U3ZSSPwqOxhh6TJ7VqJEWKaimhFNIeXydBrx8EsGcb\njyqVsIxU144lx6XbbAQQPuAFAVw6fjvH0jA65aXyGi1NaNax8Q2s1CwlXGcktpQALQ0jmE3knZq8\n8mvfh1qmdvis5Fa+cdqamtn9+SYKiwdTWGwsPalHXV0tmzZtpKRkOIMH63dMfxvpFfYOVFZqeerC\nQv08tVHaGptQolGy8lOvtfXXNyNazDgykr/OS+1BFCmKrU/yHHa0WRNjvbSI2hoEQEgwjOHYdT4t\nry3oRNiqoqKGZMQEber/dm0kqu2nExn+tyO1h5HawthzXCk1SYXKmw1NS1J8YeSKZixFmZgTNISp\nqqrZCDgsmEd0fpaihmXkjUcQCtIRB3ceEKhRhcCqPYgeB/bjE4tp+6FqfIeq6TN1ZFJ73rbaBqq2\n7aNgbAlpfY2/xX6x4hMURWHqnJ6x+PjwwyWoqsq8eT3j4f7fRq+wd6C6upLMzKwey7c1e7UDyMwu\nTHXxN7bgyko39KoaadZSIskaQgCUlgCix67bEq62aTXYQpp+SkT1R0AAQSfCVMPaaDjBnlysVTkm\n7CkO1DhW766qKa37OvBXa/8WrhTz66FYmadtYJIehP1aB691eOLS2ejRJpRGP9Zx/RL+O8ubyyEo\nYdGZfRrecZRoQzvOGSN0bZ1rl2uHpn1nTdS9d4ADKzRL6eGzjXkfgfag2rBsJWaLpUcGVcuyzIcf\nLsHlcjF9emreTd8WeoU9hiRJ1Nd7KSgwltM2Qku9JuypjutSFYVAUyvObGMDIlIR9mhLADE9SSQe\nF/YkuW4lEEZwWnUbiY4Ju4E0iSprLQ+pCjuxz1ej/1lhVxWVQFU7gknAnmfcpVCRokQqWzGl27Ek\nmZYU2Rtzc9QRdmlTrClJp2NYXhOrhjk5cX7dv0JLwzh10jCqolC7fDNmlz1px7Oqquz7aB0mq4Xi\nU4x3dZft3X/MGdWV1v0ehQ0b1tHU1Mjpp8/Cbv/PN7N9HfQKe4yWlmYURaFPn9QOOfXwt2mC68lM\nrcVdCkdQolHDFqayXxNiszv5H6kaiiA6k5QShjXTM71IXLtRBZLYyqLExNZIF2n8GiU1gRZEAdEi\nIgW6Z9bWXXzlmg+8qzAtpeqc0OEmVFnBkcQbR/GHiRxqwNw3LWEqTVVVzW3TZsYypnM/fjUkIa87\nhJCfhjis8weEKkcJrNyNmOnCftzAhPfUvP0QIW8LuaeMw2TTT7fV7iqlpaKWISdPxOoyfv6wfok2\n4Hrq3J5Jwyxe/D7Ad8LFMRG9wh6jtbUFgIyMnhujFgrEqiNSTO3IQS13bbEbq+VWYkJssiU3GFOl\nKFj1xViNxpqFjYhTMr1OQazjOWlViib/3I63IAhYM+xEAxLR2BvCN000LNN6sAnRIpI2NMUxfns0\nHx1HiX5QEd5ZA4qKbWzit8rooXqUBh/W4/ojJPh7kNcf1tIwpw5PmIYJri9FaQ3gmjkmqZMjQN85\nySPwvcs06+oRBgbGHLsPv58tn6whOz8vZWfUzqiqqmTr1s2MHj32O+ML0xm9wh4jLuzp6T0p7Noh\nZKrCLoU0YTc7UhN20aov7KqqghRNOryBeEojmbCrKiQ7A0gh/y3EHjhKJDVhB7DGUhjh5lDKa3uC\n1gONqLJC2tBsTEkenB2RGvxI9X5sRRmYPfr/3qGYPbJdZ7ZpZL1mSWCdpjMp6WPNg8h8WuLqL/9H\nWtO4a3bipqRoWMK7eju23AwyxyWurAHtb/rgxxvx5GVTOMF4OfHmVWuQwhGmzZvZI+3+H364BPhu\nR+vQK+zH8Pm09mS32/h0o2REQprIWJK8on6VqKRFnUZnPyqxQ0fRaFokaSRuMBUiChBNaAUEfJkv\nVw2IdbzMTwmmnlKJlxYGqtqSXNnzBGra8Ve0YXZbcRelOMZvi9aI5hyl7yMkVTQj17RhHZqT0B5Z\nDUvawOp0R8JqGKXRR3TjEcTiHEwDOn+ziLYGCHy6D3NRNtYRiR8i3jU7kH1B+p4xMamJ16HVm5CC\nIUpmnZCS4dfnS5cjiiKTZ3bfQluSJJYvX4bb7eHEE1MbT/lto1fYY0hSbIyZpedGYqmxKDWVP2Tg\ny9djg/oa7z5Uk6Q7BJOoiXGSVMexcsOIflpDsFtQQ/oiLNgtIAoogYjudRCrXxcF5JbUo25rhh1r\nhp1gnf+YvcA3geSL0LTTi2ASyJ6Qn5IjZcTrI1zWjCXPnbTbNPCZ5t3jOEFnktX6I6hBCdspQxN2\npEpLd4OiYpmXOK3h/3AHRGTcP5ioW5VV9cF6gKRjGAF2xywERs41LqgVBw9RcfAQI6dMIr2PfrWQ\nEdatW0NLSzOzZs351o++S0avsMeQZU3ELCkMwkhKTGdTNqCLLVBV/Wj42OWxeY9qNHlULJhNx8oK\nExKrYFGTCbvDApHolzn5zq4RBUSXFcWXXNgFUcScZkNuCR57KBpFEIRjM07bDzWltLaryEGJ+o2a\ny2Lm6FysSVIpX6X9C62JyHN8f10BlRt8RPbVYe6XjiWRPYCqEl61XxuAnWCguBpVkJbuAocFy2md\nt/Krqorvvc1gNuHWsegNVNbTvOUgmeOLcSWxsag/XEnNzgP0nzgqpdr1+KHpCfONj8zT47vcafpV\neoU9hixrkafZ/J9vjonnEo2W78XTHYqRQ0erOblgx0sTk0XjDu0hqCZJnYhuG4ovZEiszRkO1EgU\nxZ/8QfBV7LkuLB4rgRrfsbb+r4tIawjv+kqiIZn0kmxcSWwAvkqovJlIVRvWwnRsBfprA2sOgQrO\nEwcntgfYU0u0sgXLcUWImZ2f6cjrD6PWt2sDNRJ47Ie3H0U67MU5fTimzMRVWZXvad2j/Qz4rm9a\nqAn0yHnGo/WQP8CmVavJyMlmxKTEE5uMUl5+hB07tjFu3AQKC5MPhf+20yvsMY6JaQ82uZhjh5ly\nEiH9/9bFxtXJYWMphfghazSUXAxFlw3Fr7+vkKblq+P17Amvi9XDqy36ImrKdoGkoCTZD8ASm5oU\n7kKuXBAEMkfnggCN22qPzRztSVRVxV/V9m+i7hms3y36VaJBidZPj4AokDZVX2Sk6lbC26ow5Xmw\nJuoiVVWC72izce3zOq8lV1WVyCsbQADruYmFsu1Fzbbac1Hi9IrkC1L5/jqs2WnkzdC3EAj7Amx9\neyWuPhkMPjl5A1Oczz9cQTgQ5MQFcxBN3R9z+PbbCwE466xzu73Xt4FeYY9hMmlRajzX3hNYbZrg\nRgwKdBxLTKgjQYPC7tLq1+P17HqIHjuqL5lgx4Q9Zi2QcK/YTE6lKYmwx7xkot7k/tn2Iq0qKXy0\nOem1nWHLdJA5KhclEsW7voJwF/L1iZBDMg2ba2jaXgeiQJ+JfUkbkpWSkZWqqrR+chglKOE5vj8W\nnbmmqqriW6qNO3TPGZEwfy9tqyR6uAHLpCLMiQ5EN5ahlNZjnj4MsX/n6ZxIaR3BdQewjSvCPjbx\nA6fqg/VEA2GKzp+OmMT+Yc+STwn7g4w95wzjxQDRKKvfWYTFZuXEBbMNrdGjqamJVatW0K9fIVOm\nGPen+Tbzn887/JcQT8FEDeSpjRKvQ49Xx6S6TgoYW/elsOsLMYDotiMd8qIqSuJDXbcNTCJKMmHP\njAt74iEbAOaYsMve5EMMTOl2TGk2wpWtqLKSehcqaJUpAjTv9FK/oZKMETm4+qd1edpONCzjr2ij\n7XCzZseb5SBzTC4WV+oHcP7tNYQrWrAWpuMaq++JH95ehXy0GevwPKyDO+9eVmWF4NtbQRBwnN15\n9KyqKuGXtJF11ksS15u3vqjVmadddlLCaxRJpuLN1ZgcVgrP0rcFiMoyO95ejsVhY9SCGbrXdmTH\nZxtoqvVy4oI5uNJSS3F1xgcfvIMsS5xzzgXfmQlJyegV9hhWqyam4RSjaz3sMc/ocDC54HZEEEWs\nLgdhn7E8sdmjCazclvx6U4YTVFVzXEyQQxUEASHTiVqvH2GbYpNcokkE25yvfTnl2FQhPQRBwD44\nG/+2aoKlDTiHd21Ysbt/OiaricbtdTTv8tJ6sBHPgAyc/TyYHckPyKNSVCtjrGonVO8HVTvLyByd\n2+WHRLC0gfaNFYhOCxkzEufLAaKNfnyLdyNYTbjnJK77Dn+0B6WqFevJxZgSuHHKH+9H2V+H+aRi\nTAkMvyIHaggs34VlaD6OExLbDFQtWk/I20LRBadg8ej3Z+xbtpb2ukam/HAe9jRjZcSqqvLRy28g\niCIzzut+rXkgEGDRovdIS0vn9NN75hD220CvsMeIG38Fgz136OaIWQL421If4WVPcxMyuC7uwR5p\nSX69mBVLizS26x6OiXlpRPfWoMqJG5ritr7Ran3BFrOciB4bUnmToTMM16g8/Ntr8O+sxVGiP01I\nD0eem77T7bQdacZf3krrgUZaDzRizbRjcVsxu6yYnRbtQScr2nAKX4RIS4iK9i/z85Y0G67CNFz9\nPCk5NnYkdLSFlk8OI1hNZM0bjklnQLgqR2lbuBU1EsVz3jhMCQ5DI9WtBN/bgZBmx3HBcZ3vFYwQ\n/vtasJiwXZe447P5r8tBVcn8ycyEb3LRUITD//oIk8PKwMv02/ujksymlz7AZLVw8rXnYvSddfeG\nTVQdLuO4U08mt7D7vk3Lli3C52vn8suv/s76wnRGr7DHiAt7INBzwu7yaBFt0Nc1YW88UomqqkmF\nzWS3YrJbDQm7qU8sym70gY4NtZCXBrurURt8CacoiW4bQroDpbpF9zMFQcAyIIvwrhoiXl/Skx2T\ny4p9SBah0kYiVW1Ja7x197KbyRyRQ3pxFoEaH/7KNiLNISI6HaqCKODOcyO4LTgLPCmXMX6V0NEW\nmj86AIJA5uxhunl1AP/y/cg1bdgnFGJPYB+gKip1T30CUhTnj05I2LQUeWUjaoMP66WTEft2/nsM\nbigltOEQ9ilDcExJ/EdR8fYaIo1tDLp8JrYs/RTJvo8+o72ukbHnzsSTk0nIwCxRVVVZ/sqbAMy8\n5Pyk1ydDkiTefnshdrudM888u9v7fZvoFfYYXwq7fr44pT09WnQcNwNLBXu6m2hEQgqFsTqSRxr2\nLI8hYTfHhT3JFy0+7k6paUPUGY9nKkhH3luLEogg6kShloHZhHfV4NtdA2OSDw9xjcknVNpI+6YK\nrP26nh+PI1pMuIvScRelo0QV5ICE7JeIBiQQBUSziGASMDstWDw2cvPSemSwsX9PHW2flYEokDVn\nGLYkc1+DG8sJfl6GqY8L97yRCa8LLd5JaEcVlvGFWI4f0Ok10X21RBZuRsj1YL2489y6EpJo+sMS\n7aHz48STicKNbRx5cTlmt4MBl+h3gUrBMBuffxeTxcxxl8zTvbYjuzdsomzvfsacOIWCQZ3/TKmw\nbNkiGhsbOOec8/F4up+r/zbx/ThJMIDbrQmerwvRdSJc6dofk6819dI9R4a2NthsbK09J51IUzuq\not/UZMrTRFquS5I+iQ1JVqr0q1PMg7QqjOiRBt3rbMPzQIC2LUd1r4tjzXFjH5KN5PUT3F9vaI1R\nRJOI1WPDme/GMzgTz8AMXIVpOPt6sKbbU+oeTYQaVWhde4S2tWWINjPZ80dg66f/5hHaUYVvyW4E\nt5W0SyclHDgi7agi9O52zDlunFdP6/ShpwYjBH+3DFQV+y9nJXTqbP2/j5ErGvFcOAXrsMQP3ANP\nvYvsC1J8/fykufXNryzC39DMhIvn4u5jrBQ0Ksu899zziKLIgqsvM7RGj0AgwMsvv4DD4eDCCy/t\n9n7fNnqFPUZc2Nvbe85rxJ2ufZF9LckPDb+KMzMm7C3G7seRk44qR5GSHKCaY8IeTSrs2hdSqdAX\ndtMQ7TBOPqwv7KLHhmVgNsHDjUSTVNvESZtahGARafv8KHL7N2cT0F3k9jCN7+8hsMeLOdNB9tmj\nsObr+4iH99fR/s4OBJuZjMsnY87u/PwjWteG/7m1YBIpuGMOYgLP/PDTq1GrW7CcPxHz+M4ngoX3\nVNH2yjrM/TLJuOH0hPfWtPkAtcs3kza8P4U/0K+Eaa3ysuX1pbhzsph4ifEOz3WLP8JbUcW0ebPI\nH9D9CWZvv/0Gra0tnHfeRWRkpNZn8F2gV9hj2Gw2rFYr7e3df/0+tqfDjtli6VrEHhP2QJPBiL2P\nJtjhRv3rTbEUi1yrnxcX+8eFXb893xwrw5MP6Qs7gG2UFhGGd1QnvRa0XHvatAGokSgtKw8a66z9\nD6KqKoH99TS8tROp3o9jaB+yzx6FOckkqvD+Otre2AomgfQfTjpWRfRVFF8Y3xOfoAYiOK+Yir24\n84ohacVepGW7EYtzsF3ZeWeoEgjTeP87oKhk335WwtGF0WCYvY+9AYLAiF9elNCDJv7zf/rESyiS\nzIk3XnSsHyMZ/rY2lr7wKjang7lXXGxojR5NTU289dbrZGZmcu65F3Z7v28jvcLeAbfbg8/Xc8Iu\nCALujDT8bakLe8oRe66WOokkEXbRbkXMdCHXJDnwdNsRsl0oZY36+6U7EPPSkA94dT1jAGyj+yJY\nTAQ3HU1qWBbHUZKDY2gfJK+flpWlSVNN/ymkBj+N7++hdfVhUFXSpw8ifcbgpFU0wQ1ltL26GQSB\n9IsnYilK4AUTiOD740qU6lZss0ZgSzCAWt5dTehPK8BlxXH73E7TOaqq0vT7RUhl9XgunIJ9YmJj\nsf1Pvkugsp6iC08hrUQ/kj6wYj3lG3ZQeNxIik+drHttR97/2wv429qZc9lFeDK7b5v9/PN/IxQK\ncdllV+FwGB/o8V2iV9g74PF4ejTHDuBKS8PfhYjdGZu6ZFTYnTFhTxaxA5gLMpFrW5MKsTioD2qD\nD7Vdv1jNPCIfQhLRI0keAg4L6ZMHoLQEiRzwJr1P0B6O6dMHYS1MJ3y0hdZPj/So7UN3iQYlWtcc\noeHtXUh1PuwDM8m5YCzO4bm6B76qouL7cC++JXsQXFYyrp6CtTjBUOmQRPvjq4iWNWI9aQiOCztv\nzVdqWgnd9wFEFRx3zU/YYep75wv8H+7AOrqQzJ8mru32frqdqvfX4S7ux9Dr9WvK/U0tfPrEy1js\nNk679WrDh92Hd+3l82Ur6DtoAKec031zrgMH9rN8+TIGDRrM7Nnzu73ft5VeYe9APGJXejAqdKen\nEQ6GkCKp+ZY4YvNLjaZi4hF7uCF5Pt/cNwPkKNHGJJUxg7Q0SzRJ1G4ZqXVQSntqkn521gyt+SX4\neVnSa+MIJpHMmUOx5LgIHmigeflBlP/QpKQ40UCEtg1HqX91G4G9XswZdrLmDSdz1jBMCUoP4ygh\nibY3thBcdwRTjpvMa0/A0q/zSFUNSfj+8jHR0nosUwbivGpqp4e7anuI4D3vo7YGsf30VMwTO68q\nCe+upOnxZYgZTnIeuDDhkOqQt4U9j7yGaLUw5p4rdK0DVFVl9eMvEm73M+36C0jLN+bgKEsSb/z5\naQAu+vlNmLppwKcoCs8++wQAN974M0w94DHzbaVX2Dvgdns0Q6UebFKKt0SnWvJ4rCrG8OFpCsJe\noOXP5SQVL6aYsCuH9atSzMPzQRCQdibPndv7ZWAZlI10pBGpUj8d1BHRojX2WAvSCJc10/DWTiK1\nPZc2M4rU4Kfl08N4X92Gf3sNgtVE2gkD6HP+GEP19lJFM81PryWytw7LwCwyfjQtYQOS0hai/dHl\nyPvqsBzXH9ePTuy0eUj1hwnc8Q5KeSOWc8ZjXdD51COpsgnvra9AVKHPfecdO0j/KtGwxPY7/47U\nFmDYz87GPUjf+mDX+x9zeM1mCsaVMOYs4wMxlvzrVWrKjnLC/FkMGtW5jXAqLFnyPnv27Oakk05h\n7Fh9c7LvOr117B3o2KTkcvXMJKV492mg3UdGH+OzMB2xFuxQm7G6+vjhaaQ5udiZC7SHgFzdAjqO\nqGLscC5aqp82Ed02zENzkA96UdqCiGn6eU3njGJajzTiX76P9KumGH5tF21msuYNx7elCt/WKho/\n2INzRC7u8QVJo+TuoIRlQkeaCOz1ItVr/x4mjw3XuL44h+UY8rNRFZXgZ4fxrzoAqopzejHOGcUJ\nDyOj3nYtp+5txzptsFbW2MnnHBP1/XWYZ47AdsMpne/X7Md784sozX6ybl2QsBFJVVX2PvY6bfsq\nKJg7OakfTP3BctY89Qr2dA+z7rzB8FCZ0u27WPXGO/QpyOfsG642tEaPurpa/u//nsXt9nDTTf/T\n7f2+7fQKewdcrpgI92CTkiO2Z9Cf2p4mqwWL3WbYVsCW5QFRMJhj13KvcrV+xYvYPxNsZpTS5HXk\nlvGFyAe8SDuqEx7sxbEOzMY6LJfIAS+RvXXYRupHhB0RRAHPpEJs/dJoWX2YwB4vgX31OEfk4hqd\njzm9Z9rGJV+YwP56QoebCFe1amMFBbAVZeAcmYutMMNwvXu0yU/7uzuQypsRPTY8543HOijxQ14+\n3IDvz6tQ28PY54/Gfu74Th9+SiBC8K73UPbWYj59OPb/ndnpPSmhCN5fvYJc2UTaFSfjOTexEdjR\nhaupWfYFaSMGMPyWC3UfuhF/kGX3PYUiycy8/TrcOcamHAX9fl76/Z8RBIHLb7sZWzcPOFVV5Ykn\n/kgoFOKWW35BVlb3py192+kV9g44nTFvlxRFWHdPd0zY27tgK5Bu3C9GNIlYMzyEDeTkzf1iqZhq\n/VSMYBIRB/dBOeBFjcgJG2YALOP7E3xjC9KWo0mFHcA1eziR0np8S/dgGZx9bN6pUax908i5cBzB\ngw34tlQR2F1HYHcd5iwHtqIMbIUZWLKciPbkf+JKREZuDiI3B4l4fURq2qlp/fLA2JztxDE4C8fQ\nPim9GahRheDnZfg/PgCSgnVEHp4zRyO6Eu8R/uwQgRc2gKzgvHwKtlOHdb53W5CaWxYS3V2NecYw\nrQmpk+hfDUvU3/46kV2VuGaPJePGxPXq9Z/t4sBT72LN8jDuwWsw2RL/myhRhY8efIbWai8TL13A\ngCmJh17/2/2oKq/94SmavfXMvuwiBo5MPFDbKMuWLWbz5i+YOPH475XRlx69wryly7YAACAASURB\nVN4Bm63nHR5tTi0aiYRS39PqctJel7w+/Nj1mW5CtcnHwplz08AkaqmYJJiG5qHsrUU5VI9pROLO\nRFN+GmK/DKRd1aiBSMIJPcfuoY8b5/QhBD4pxb9sL56zjQlDRwRRwFmSg6M4m1BZM8ED9YSr25C3\n1eDfph3kCjYz5gw7os2sRbNmERQVJSyjhGSUoIQS+HcPfsEi4hmSDdlO7IOyuvQWEClvwrdkN9Ha\ndgSXFfdZI7VyzwQRsCpFCb62ifDHBxAcFlw/OQVLAp8YxdtG8PZ3UCqaMZ8xAvstMzsX9YhM/R2v\nE/q8FMcJQ8m+86yEn9+y8wg77nke0WJm/EPXYs/RLztc/7c3KFu/nf6TRjPlmnOS/Da+5OM332Pb\nmnUMGTOS2T+8wPC6RBw9Ws6zzz6J2+3h5z//ZbetJ74r9Ap7B74O615bzFEuHEx94IPN7aTxSEjf\nO70D1gw3vkPVKBFZt4pBMJsw5aYljdgBTCV5SED0QJ2usANYJw8g9M52IlsrsJ2YPGp3Ti8mst9L\naGsl1hF52Erykq7pDMEk4hiSjWNINooUJVLdRqS6Dbk1hNwaQvL6oZMSScEiItotWAvTsWQ4MGc6\nsPRxYc52dtkrJtoWwv/RXsI7tQeLbXw/3LNH6ProKE1+fH/9lOjhBkyFmbh+Mv1YI9n/t/+RBoJ3\nvIPa6Cf9imlEL53ceZVMRKb+9tcJrjuIfdpQch66KGEFjO9IDVtvew41GmXcw9eSPmqg7s+4Z+ka\ntr6+jIz++cy59ybDE45Kt+/ig7+/QFpWJlfddWu3q2AikQiPPHI/4XCYW2+9g5ycrlk8fxfpFfYO\n2Gzaly8c7rmpO9a4sIdS82QHsLodoKpEAkFs7sQWu8euz4zZ9za3Y8/Tb6O2FGQS2nwEJSTppkHE\nEi3/Hd1fl/zzpwzShP2zQ4aEXTCJeM4ZR/Ozn9H+3k7M17oxZSX/OfUQLSbsAzKxD/jy51cVFVVW\nQNGseREFLYLX6aJMFSUoEVx3mMD6MpCimAvScc8biaW//r9DZPNRAs+vR/VHsE4dhPPKqV/OnP0K\n8pajBO9fBP4Ituunk33TKZ0+fJSQRMOdbxBcdwD7lGJyH74IIUFaJVjdyJZbnkZuDzDqzh+SM63z\n0XpxKrfs4ZM/Po/N42LBQ78w9HcJ0Oyt5/kHHwNB4Kq7byUtq/tt/v/4x7McPnyIuXMXcOKJxuep\nfh/oFfYOWCyasPfoFKXYw0KKpD5yzxpL40jBsKEvkCXuy97qTyrs5n6ZsPkI0doWxIGJ647Fwkxw\nWYnuq036+aZcD+bhecj76ojWtGJKYBP7b/eR58E9ZwS+xbtp+ddGrfwvSQt+qgiigGA1AT1f16xG\nZIIbywmsPYwalBA9NpxzR2KfUKh7uKqGJQKvbiLyaSlYTDgvn4J1xtDODb1UFen97YSfXg0mEfuv\n52A5rfPyQMUXwnvrK4S3lWui/sjFiUW9rolNv3iScH0rQ39yFgVz9LtF6w+Ws/juv4AgMPe3PyWj\n0NihdygY5G/3PER7cwvn/vhahoxO7FpplE8//Zj33nuboqIBXH/9j7u933eNXmHvQHxsVk8Ke/x1\nMyql3lBzbESewTSONV0TdqnVgH1vrJZdqmrGoiPsgihgGtGX6KZylNYgYrp+BYPt1BLkfXWEPz6A\n89LE1RcdcUwegBKIEPj4IK0vbCTj6qmIXRg7902ihCTNYnd9mXam4LDgmlmCY/LA2EMkMfKhevx/\nX4dS14apfyauG05OOP1IlaOEn/oEafFOhAwnjnsXYBpV0Om10SYfdb94EelgLc7TR9HnnnMTHngH\n65rY/LMnCdU0MeTaeQy8WL/+vLXKywe3/QEpGGbOPTdROD7xVKeOKIrC03f+jqpDRzhh/iymn939\nbtDKyqM8/vij2O127rzzN9jt30/bAD16hb0D8U61nhR2s1WLluQuDMmOmyhJQWNdq5Z0LaqPtCav\n6jH3i5U8ViU/bDWNjAn7nmrEafopFsuE/ggZDsKfHcJx7viEdrFfxXlKMWpYJrjuCK0vbST9ssn/\nleIebQ0S/OIooS/KUUMygt2M85RiHNMGISYZuadKUYLvbie8bA+gYps1Asd5ExAS+MkoTX5CDy4h\nurMKcUgOjt+ciZjbee5dqm7G+/MXkCubcJ8ziaxb5idMNYXqmtn8P08SrGlk8DVzGHyl/sBof1ML\n7//qMQLNbZzy88spnmHcB2bxP1/mi1VrKR43mvN/en23DzdDoSAPPngfwWCQ2267m6Ki7vu2fxfp\nFfYOxCN2RenBiN0Ui9jlbyBiz9CEXTIycKMwJuyVBoR9tFadIW+vxJxE2AWziG3GMELvbif86UHs\ns4y9dguCgGvWcNSQTGhLBc1Pr8Fz/nisA403dX1dqKqKXNlC8PMywntqQVERXFZcZ5RgP77IUKmm\nfKge/z/Xo1S3IuZ6cF4zDcuwxIfF0d3VBB9YjNrox3xSMfZbZyMkeHCE91Th/eXLKM1+0q+aTvr1\npyUU0GBNI5t//hTBmkYGXTmbIVfP1b3vYGs7793yKK3VXiZdfiZjzk5cLvlV1i3+kBWvvUVe/35c\nc8+vesQy4A9/eISysiMsWHAWM2YY73L9vtEr7B340lyq50qmvnxYpO4/Y7ZrEats0GfmWI69JXnE\nbokZREkV+j4woEXsWE1Et1UYug/baSWElu4mtHQPthnDdOvfOyIIAu4zR2PKcuJfdYDW5zfgPKUY\n5ylDe2T4Raoo/jCh7VWEtlYS9WoPS1OeB8eUgdjHFiSMtDuiBiME39pG+OP9oGq/G8cFExLmvVVV\nRfpgB+FnVoOiYrv2JCwXTEwo1IE1+2i4+01USSbrl/PxnJc4mvYfrWPzzX8l7G3RIvWr5ujee9jn\n5/1bH6OprIqx585kytXnJv154+xav5E3/vIsrvQ0fvXkQ5id3Z9g9OqrL7J27WpGjx7L9df/pNv7\nfZfpFfZO6MlaWDH2OtwVu1lzrK5eDhkTdmssFWMkxy667Jiy3cgGhF2wmjGN7kd0y1GUZj+izhBs\n0CwG7GcMJ7R4F+FPS7GfYdwHRBAFnCcPwTIgi7Y3txL4pJTIfi+uM0qwDOnztdcpK0GJyL46jpbW\n44tF55gEbKPysU8qwjIo29A9qKqKtKWCwCtfoDYHEPum4bxyqm6UrvrDhB5fgbz6IEK6A/sd8zBP\nSGyV631hDfUPvItgNZPzu0twnpy42ae9tIot//tXIs0+ht70AwZeqh95RwJB3r/tj9QfLGfUghmc\n/NNLDf/uy/bs5/kHH8NitXDDA3fRd0Bht8cMrl27mpdeep68vHzuuuu3WCypNbR93+gV9g7EI/ae\nFA+hOxF7LKqTDDY3pRKxA5j7ZxPecRQ1LCWMIOOYjisiuuUo0c1HEc9IfnBmmzWC0Ip9hBbtxDpt\nkG63ZWdYijLJvOlkfEt2E95RTeuLX2Dul45j6kBsI/INRctGUFWVaIOfSGk90qEGIocbIKr9HZjy\nPdjHawOlU8n3R73tBF7aiLyrWqtiOWss9nmjde85eshL8P7FqNWtmEYVYL99LmJu51OXVDlK818+\npH3hBsQsN7mPXYptROfNTACtu8vYcuuzyO0Bhv/vBfQ/5yTd+48Egnzw6z9Rt+cQJTNPYMbNVxj+\nTtSWV/Dc3Q8QlWSu/e0dDBjeeedsKhw4sJ/HHvsddrude+99kPT0rg83/77QK+wdiItvT9p9HvtC\ndMFC3GxPNWJ3gygQMWj1axmUQ3hbOVJ5g+68SwDzlEFE/r4Wed0hLAaEXfTYcSwYQ/CtrQRf34zr\nmhMM3dO/7eGwkHbeeOQTBuNffZDI3jra39pOu3UX1uIcbCPysBRmIGY4DadqlKCE7G1HrmxBrm5F\nqmhG6WAfYMr3YB/Vl/yTi2lL8fmuRmRCy/YQWrxLq2UfmY/zh5N1yz5VVUVasovw059AJIr1oklY\nrzoh4cGn4g/TcM9CrfFoaD7Zj1yMuW/i0tbGTfvZfsffUSIyo+68jII5+pVKkWCIRbf/iZqdBxh6\n2hROv+1Hho29mrz1PH37ffjb2rnklp8yasokQ+v0qKur5b77bicSCXPPPfczaNDgbu/5faBX2DsQ\nr4YRDf4hG+FYjl1NPWI/dnhqMGIX4n4xBozAAKxDtLRA5JA3qbCLRVmIhZnIX5ShhiRD1S622SOJ\nfFFOZO0hrFMGYRml/xmJMPdNI/3iiUQb/QS3VhLZU0NkTy2RPbHaeosJc44bMcOBYDUh2sxgNqFK\nUdSIjBqWUVpDRJsDqMGv2Ac4LdhG5WMZkoO1uA+mWDmnLccDBtMHqqoifVFOcOEWlEY/QroD58UT\nsUweqD9soz1E6E8rkNeWgseG4675mKcmFi65tgXvL19BOlSHfUoxw5+5muZQ4kP5uo+3sfO3LyCI\nAmN/ezW50/VtG6RgmEW//hPVOw5QPGMyM++43nBXaXtzC0/fdi8t9Y384LormTrnDEPr9PD7fdx7\n7+00Nzdz440/ZepUfafJXr6kV9g7EK+G+ToM+o2OgutIvNxRTsFnxpbtwV9Rj6qqSV+fLcWasEsH\na2HuON1rBUHAfFIxkde+QN5UjuWkzm1f/22NWcR59TTa719C4F/rSbuvexNyTNku3GeUoJ4+jGi9\nj8gBL3JduxaBe9uhWseL3ixiynBg6p+BKduNuV86ln4ZiJmObqXe5PJGgq9sQj7oBbOIbe4oHPNH\nJ/XKie6pJvjQUlRvO6Yx/bDfNidh6gUgtOMo9b9+DaXZj/vc48m6eS5mjwNCnT98Kt9fx97H3sDk\nsDL+4evIOm6o7v1IwTAf3P5HqnfsZ8gpk5h11w2GRT3o9/PMnb/FW1nNGRefx+kXGveOSXg/ksQD\nD9xLeXkZZ511HmeddV639/w+0SvsHZDlnhf2+Gus2pWI/VgdewrCnpNB+8EqZF8Qi6fzAQ5xrMV5\nIAiE9xkbLm0+Zagm7Mv3GBJ2APOALOzzRhFatAv/P9ej3tv9BhVBEDDnejB3EEI1qqCGJNRwFDUs\no8pRBKsJwWrW/nNYerSyRmn0E3x7K5H1RwCwHNcfx4UTMemIc/w+I69vIvLCegCsV0zFeslkXXsD\n/0c7aXjwXYgqZN0yD/d5k3UfRmWvrOTg0+9jSXdx3B9uSjqrVAqGWXTHn6jevp8h0ycx664bDYt6\nJBzmb3c/ROXBw0ybN5MF11xmaJ0eqqry5z8/yrZtW5g69QSuu+6mbu/5faNX2Dsgy9prurmb9bYd\n6U65o9WlCXMkYNxnxpGvlTGGapuSCrvosmMZnEtkT5UmhGb9L7NpSC5icS7yhiMojT7EbGPDSOxn\njdO82jcfpeGFz2HeaGM/TAoIJhHBZYPuWc0kRWkPEVq6m/CKfSArmIqycFx0HJYkBmkASn07od9/\nSHR7JUIfN/bbZmMel1h0VUWh9R+raf2/TxBcNnIevRTH5MR9BKqqUvrMB5S9shJbbgbH/eEm3AP1\n2/6lUJhFdz5O1bZ9mqjffaPhevOoLPPP+x/l0M7djJ9+Ahf+z409Unjwwgv/YOXK5ZSUjOC22+7+\nXo+46yq9wt6BLyP2nhf2rqRi4l4xYZ9xYbf31YQ9WNOEZ2hh0uttowuRDtURKa3DNrzzVvWOWOaN\nJvyXVUgf7cF2ibEORMEk4vrxKbQ//CHNb27FIQjY5+qbTf23oQQihJfuJrRiH4RlhEwnjvMmYJ06\nyNCbgLTmIKHHV0J7CPMJQ7D/7xkIOpOmlGCExt++TeCTvZj6ZpD7hx9iHZTYvVCNKux59HWqF3+O\nsyiX4/54E448/YETUijM4jsep2rrXgafPDElUVcUhZd+/2f2bNjE8EkTuPy2mw1H+XosWfIBr732\nEgUF/bjvvoew23vWN+j7Qq+wdyAa1Q6iejJij79iK12wKbC6Y17ufuMzWOMRe6DamI+7bUwRvvc2\nE95aZkzYTy0h/NwapPe2Yz33uIROhF9FTLPjvuV0Ao98RHDhFtSogn3+6P96/2wlECG8Yh/h5XtR\n/RGENDv2c8drjVdGGpRCEuGnVyMt3QU2M7b/OQ3L/DG6P3e0sR3vL18hsq8a24SB5Dx0IaaMxK8i\niiSz64GXqFu1FU9Jf4579MZjTp+JkCMRltz1Fypjoj77HuPDpFVV5e2//p0tH69h0KgRXHPPbces\nM7rDxo2f89RTj5OWls799z9CRoa+J3wviekV9g5IUs+nYuJ7dcV/xmK3YbKYCbYab+5wDdAORAPl\n+nNK49gna1UYwQ2lpF2SvCRRcNmwnjWOyOubkBbtwHrecYbvzdTHTeFDZ3H0jvcIvb0NpcmP85Lj\ne6wmvSeRW4ME395KaOV+CEoILiuO8ydgO3244YdZ9HA9oYeXopQ3IQ7ug/2OeZiK9KPoSGkd3ltf\nIVrbgmvBBLJvO1M3RRYNR9hx9z9pWL+HjLGDmfD7GzC79KPcaERi6T1PUrF5N4NOmJCSqAMsf+VN\n1ry3hL4Di7j+/juxObofVZeWHuThh3+D2WzmvvsepKAgcV1+L8npFfYOxMW3J4W9O+6OgiBgT/cQ\nMtBJGsdZmINgEvGVJbfZBTDnpGEpziO0tRwlFEG0J2/EsV4wkcgHO4i8vgnLvDEJPUw6XVuQgef2\n2fgeX0Xkk4NEDzXgumk6pvzut5z3BPLRJsIr9tGyoQxVimoR+oIxWoRu8OdUowqRNzcTeeFzkKJY\nzh6P7dqTklorBNbsp+G+N1EDEdKvP430q6brRvaSL8jWXz1H85aDZE8ezrgHf4Qpyb9fVJb58P6n\nKd+wg6LJY5hz749TEvV1Sz5i8fMvk5mbw40P34vT0/2h7/X1Xu6993bC4TB33HEvI0Z8u9J0/430\nCnsH5JhRV0/m2M2xqTVdcXcEcGR4aK1MPuQijmgx4yzMwV9Wa6jkEcAxtRiptI7QpiM4T0o+g1JI\nc2A9ZwKRlzcQeWcrtkuNu/0BiFkuPHfN1fzIVx+k7TeLcZw3AduMoUkPcL8OVCmKtPko4U8PIu/T\nfteWvumYTx2G7eRiwxE6gNLgI/TwUqI7qxCynNhvnol5yqCk69oWbqD5T0sRrGb6PHghrtP0xU32\nh/j4Z8/RvO0QuaeMZcw9V+pOzQLtMHbl7/7O4bVbKJwwgnm//RmmFFIouz7/gjf+/Ayu9DRu+t19\nZPTpvkFbMBjk3ntvp6mpkeuuu4mTTjql23v20ivs/8bXkWM3W+ODNox1j34VV3YGDaVHifiDWF3G\nfKfdQwrwl9cRqmnCUZD8y+ecPoK2lz4jsHK3IWEHsJ5/HNKSnURe3Yjl1BJEA0M1OiJYzbiunIpl\nRD6BFzcQfOULwh/twf6DcVinDerR6Uadoaoq0bJGIhvKiKw7jOrTSkrNI/KxzxpB39OG09CY2gBy\naW0p4cdXoLaFMJ84BNsvzkjqX68qCi3PrKTtxbWG7AFAE/Utv3ya1l1l5M+cyKg7foiY5IGoqipr\nnnyFAys/J39UMfMf/AVmm3GbhMrSw/zrwT9gtpi54YG7yOvf/VSJoig8+uhDHDlymPnzf8A553R/\nBmovGr3C3oEvUzE9FzWaTCZMFjNSuIvC3kdrF/c1NJNlUNjThhdRt2orrfuOGhJ26+hCTH0zCHy6\nN+movDiCy4bt+umEHllG6K+f4PjtD7p0EGqdPBBzSZ5mGPbJAQL/WEdo8U5s04dinTIQsZuj8jqi\nSlHkww1IWyuQNh9FadQ8dQSPDduckdimDz2WEkql5l31hwk99Qnyir1gNWH76alYzhyb9PehhiUa\n7n+HwMrdmAuzyH38ciz9klSy+IJs/eUztO4uY+CZUym+5SJDD8FNL33AjndWkDWokAUP33ysR8II\nLQ2NPHf3g0RCIa6557Ye8X8BeOml51m/fi3jxk3gxht/9l9/kP5tolfYO/BlKqZn0wE2u51wqGtz\nVN052hfdV99E1oDkVSsAacO12ui2fUfJP21C0usFQcB1xmjaXlxL8LMDuE43luM0n1aCadkuohuO\nIC/fi8Wg9/pXEdMdOC89HvuckQQX7SKyppTgwi0E39yCuTgX8+gCzCW5mAdkp5YWaQsSPdqMXNaI\nvK8W+WA9SLFDbIcF67RBWCYNwDKmoMspIHl7BaFHP0L1tiMOzcV+25ykB6QA0dYA9be+QnhnBbZx\nReQ8cgmmdP2+AzkQOibqfWdNYurvrqWxKbnh264PPmHDP97Gk5fND35/C3aP8YdlJBTmb3c/SGtD\nIz+49grGnTzN8Fo91qz5hFdffZH8/ALuuOPeHn1L7qVX2P+NeBORKPassFvtNsLB1IdZA7hzNZFo\nrzVWvghonYaiQMvOI4bXuOaMo+3Ftfje32xY2AVBwP6/M/Hf9DKhJ1YhFudgGpx4zF4yxCwXrium\n4Dh3PNKmciKfH0E+3KC16wOYBMT8dMRMJ2KGQ0tzmETNWhcVNSihtAS1/xp8qK3//js3FWZgHpGP\nZWRfzCP7dqsaR43IhJ9fj/TWZhAErJdNwXrpZEMPCLm+De/NLyEdqsM5cwx97jwrqbtmNBxh26//\npon67OMZdfulxyyh9ajYvJvVj7+APd3DDx69FXcf40OkVVXl9cf/SmXpYabNnclpPWAVAFBTU82f\n/qSNtrvvvgdIS+t1a+xpeoW9A18Ke8/md+0uF22NyScVdUZGP60ppbXaWPkigNlpJ62kP237jhIN\nhjEZeO22Ds7FNn4AoY2HkMrqdeegdkTsm4791lmEfrOI4G8W4XriEoRuDqMW3TZsM4ZhmzEMpTWI\nvL9OE/hDDUSrWlCqWvQ3MAmIGU7M4wsxFWVh6p+JuTgnab7bKNEjDYQeWYZyuAGhIAPHbbMxGeg8\nBZDKG6j7xYtEa1vwnD+ZzJvnJnVPVOQoO+79F81bS8mdPpaRv77EUPql+WgNy+57CsEkMu/+n5HZ\n39jw6ThrP1jKppWrGTB8aI+MtQPtrfiRR+4nGAxwyy2/ZsCA5AfLvaROr7B3IO7n0tPC7nC58B6t\nNFyl0pH0Qq0uvbXKuLADZI4vpm3vUVp2l5E9ydiBqOeCKYS3ldP+5kayfmnc08VyYjHKJZOJvLqR\n4O+W4rj/rB47/BTTHVgnD8Q6eeCx/6eGJJTWIEpr8Es7ZAEEm0Uz9XLbv5aJS2pEJvLqF0Re/wJk\nBcv8Mdiun264DDK8uxLvLS+jtAbIuOF00q48OXkeXlHY/dDLNHy2i6zjSxhz75VJD0oBQm0+Ft35\nOGFfgDN+fR0FY1LLi5ftPcA7T/8DV3oaV9/9qx5pQAJ46aV/sn//Pk499QxOP31Wj+zZy//P11t6\n8C1DUXp+0AaAw+1CURTCKXi+xHFmpmOx22hJoeQRIHOCZtLVtOmA8c+aPhxTbhq+JduIthrvdgXN\nzMo0aQDRTeWEn1jVJQsFowh2C6a8NCzD8rCUxP4blod5QBZimuNrEfXo4XoCP3uNyMsbEDJdOB44\nC/vPTzcs6qEtZdT9z79QfCGy7zgraY16nIPPfEDt8s2kjxrIuAd+lLSkEbSHwfKHnqO1so7jLp3P\n8Nmp2d2GAkH+9dBjKIrClXfcQmZu19NrHdm7dzdvvPEq+fkF/OQnv+g9LP0a6RX2bwBXmub452sz\n5pPeEUEQyBxYQHNFTUq2BFnjixGtZho+32P8s8wm0i45ATUYof219andp0nEcftcxOIcbXDEn1d+\nreL+TaFGZMIvrCfws9dQjjRgmT8G198uxzzZeAohuO4A3ptfRI1EyXngAtxnGuvWrXx/HeWvrsJZ\nmMOE31+P2WmskmXrwg8p37CD/pNGM+1Hqdvdvvfc8zTVejn9wnMoOU7fztkosizzl7/8AVVVueWW\nX+Nyfc1ubd9zeoW9A/EI4suh1j2DO10rofO1pC7sAFkD+6FIckrpGJPDRuaEofgOVROqaza8zn32\nRMRMF20LNxBtS+0NQ/DYcf7uPMTiXKSlu7Sa7m+xuMvbKvDf+DKRlzYgpDtw3B+L0pN4rXfEv3I3\n3l+9CoJA7u8vwTnDWOVQw4a97PvjQizpLiY8egOWNGNCWLPrIOufW4gzK52Zt19nePpRnH2btrJu\n8Yf0HVjE3MsvTmmtHu+8s5CysiPMnbuA0aPH9Ni+vXROr7B34OsSdk+Gdurva9UZBKFD9iCtGaTx\nSGVK6/pM00Skft1uw2tEu5W0H56I6g/T/npqUTuAkGbH+ci5iENzkZbtJvTIMtRw6nYK/0mUJj/e\nu94l+Ku3UKtbsJwzHtffrzDUQdoR39LtNNyzEMFmIffxy3FM0x92cWzdkVp23P1PBJPI+Ievw1lo\nLBUS9gX48P6nAZVZd9+IMyu1apNwMMSrf3wKURT54a9+3mN5da/Xy8svv0B6egZXX319j+zZiz69\nwt6BeP16Vwy79PBkaSVmbU3GI+eO9BlSBED9wfKU1uWcqPmeez/dntI6zzmTtKj9tfXI9V1IH3ns\nOH93LuKIfOSP9xO4+XWU2q491L5J1KhC5J2t+K/5F77FOxGLc3H+5WLsN81IKUoH8C3aSuP97yC6\nbOQ9eSX28QMMrZMDIXbc/Q+iwTCj7vghGWOMP0w+e+Y1fN4mJl3+AwrHJ59L+1VWvvEOLfUNnHbh\nOfQfmtj3PVX++c9/Eg6HuPrqa/F49AeR9NIz9Ap7B8xmLUKRpK51iSYi7qnRUt/YpfU5wwYC4N1f\nltI6R34WaSOKaN5aSqTFeHu86LSRccNpqIEILU+vSOkz4wgeO85Hz8cydzRKaT3+n7yKvCm1B9M3\nhaqqyOsPEfjxK4SfXg2iQJ/b5+J84mJMw/JS3q/93U00PvguosdO3hNXJbUI6Hgfux96GX95HUUX\nnEL+6cadMys272HP4k/JHtyfSZedmfI9t9Q3sGrhO6RlZTLr0vNTXp+I+nov7777Ln37FnDGGXN6\nbN9e9OkV9g5YLHFh79nUQUZOTNgbjDcZdcTucZFekIv3wJGU00R5p05AfePY1QAAIABJREFUjSp4\nP92R0jr3guOwDuuLf+l2wrsqUlobR7Casd98BrZfnA4hieCd79Dw8FJUv/FRf1838vYKAj9/neC9\nH6CUNWCePRLX/11J2oWTulSy2f72Rpoe+QAxw0nek1dhLTE+wLvspRV4V+8gc3wxQ398luF1kWCI\nVY/9A0EUOf1XP0rJrTHOon++jBSOMP/qy7A5eqbeH+D1119GkiQuueTy3klI3yC9wt4Bm02rOohE\nelZ4Mvr0AaC5rr7Le+QOH0S4zU9rVWplj3mnjQeg5sMvUlonmEQyb54LQOPvF6HKXU9PWeeNwfnH\nCxD7Z9H2xib8P/oX0sf7e/wswyiqqiJvKidw65sEb30LZV8t5pOH4nzuchy3zELM1G/tT0T7Wxtp\nenQxYqZLE/WhxhuCWnYcpvTvi7HlZjD2t1cZqlWPs/mlRbTXNnDcxfPILRmY8n3XVVSxacUn9Bs8\nkMkzZ6S8PhE+n4/ly5fRr18/TjttZo/t20tyeoW9A/ESLL8/uf9GKljtNtKzs2iorunyHn1Hawdv\n1TuM16UDOPKyyJo4jJYdh/EfTa3JyT5+AK75E5AO1tKWYvnjVzGV5OP866Vk/vRUVF+Y0MNLCfzP\na0iflX5jlTNqSEJasZfAT18leMc7RLdXYppYhPOJi3HcPR/TgK7b0La/uYGmxzqI+hDjKRzJF2Tn\nAy8CMOaeK7BmGs9Dt9XUs23hMty5WUy6PPUUDMDHC99FVVVm/fDCHhlvF2f16lVEIhHOOeec3mj9\nG6ZX2DvgcmlDA/z+1OxajZDTry/N3gbkSNd82QvGad2j1Tv2p752/hRt7ZINKa/N/NksxEwXrX/7\nGKmia2cEcQSrmcwfnYTrb1dgPnEIyv46Qr9ZROC6F4gs3ona3jWjND3UqIK8s4rQn1bgu/hvhH7/\nIUqpF/P0oTifuhTnw+diKkmt1f6rtC3cQNMfliBmucl76iqsgxPPJv3/7k9V2fvYG4Rqmhh0+Uwy\nx6V2aPnZs68TlWROuP5CLHbjjo1xWhub2LjiY3L6FTD2xCkpr9dj5cqPEEWR+fONdzH30jP0Wgp0\nIC7sPl/PC3t2QT6lO3bTUFNL/oDEk+kTrh/YD5vHRdW2fSlbE+ROH4vZ7aB66UaG/GguosX4P7sp\n3UnWLfNpuOsNGu55k/xnrklqWJUMsW86jnvPJHq0icgbm5BX7iP855WEn/oY08QBmE8YgnniAMTc\nrlVQKM0BoruqkD8/THRj2TEzMKGPG8tZ47HMHolY0DPzNNsWbqD5j0swZbvJe/Iqwx47caqXbKBu\n5RbSRw9i8FWpHS5WbNvPodWbyBs5hKGndU2U17y3hKgkc9qFZ/dotF5XV8vevbuZMGESOTk51Ncb\nH+/YS/fpFfYOZGVpTopNTd2LTDuj7wCtZLH6SHmXhF0QRQonjODQp5toqagls8j4oZzJZqVg7mSO\nLlxN3aqt9J19fEqf7Tp9FMH14/Ev3kbTXz4k+9YFqd5+5/dVlIXjl7NQrpiGtGof8uoDRDccIbrh\nCGE0ITYNz0cckI2Q7ULM8WgGY/GHmqKgtARRG3yojX6Uo41ED3hRO4iIkOXCMnc05ulDMY3v36MD\nPNpe+YzmJz7qsqgHa5vY/5e3MbvsjLn3ipTy6gAfP/U6ACfeeFGX2vOj0SgbP1qFw+Vk0uk9O7lo\ny5ZNAEyblnyObi89T6+wd6BPH+2L2dDQ9UPORPQbotUjVx06wnEzTurSHgOmjOXQp5so37gjJWEH\nKDr/FI6+9Snlb3xC/qxJKQtB1i/nE9lfg+/tL7CPLcI1e2xK6/UQcz3YLj4e28XHo1Q0IW8qJ7qj\nkujeGuS1pbC21PBeQqYT05RBmEryME8ehFic+7V4x7T8czWtz63ClJNG3pNXYinqk9J6VVHY/fAr\nRANhRt1+KY785B7uHaneeYBD67fTf+KolA2+4uzfvI3WxiZOXDAHqy31NI4e27ZtAWD8+Ik9um8v\nxugxYS8pKRGASiB+urd+//79d/TU/t8EcWGvr0/tkNEI/YYMBDRh7ypFk7VW7PINOxh//uyU1joK\nssk9eQze1Tto3naIrJhJmFFEu5Wchy6i5qpnafzd+1iK81I6IDT8Of2zsPbPgnMmoKoqqrcdpaYV\ntcGH0uhDbfsyDy8IIKQ7Efq4ELLdiH3TEfq4v1ZzKVVVaXl2JW3/WoMpP0MT9SRTjzqj4u21NG85\nSM5Jo+k7N7WZsQAb/vE2AFOu6bpH+saPVml7zD6ty3t0hqqqbN++lezsPhQWpv522kv36cmIfQiw\nef/+/T/owT2/UaxWK9nZfaiururxvV1paWTl51K+7wCKonTJGtjdJ5OcYQOp2rqPYGs7jvTUctAD\nLjoN7+odHHl+GVkTfpry51v6Z9Pn7rOpv/11vDe/RN4z12ApMD64IVUEQUDIS0PMS/vaPiMVVDlK\n06OL8L2/BXO/LPKevBJzfuq5en+Fl4PPfoAlzcmIW1NPo9TsPEjVtn0UnzSB/JGpPaDjhIMhdn3+\nBbn9+1FUYszqwCi1tTW0trYwffqpvQ6O/yF6sipmItCvpKRkVUlJyeKS/9feeYc3VbZ//JOm6d50\nL9oCPUWgQCl7TwHZQ1RQUUQRfX3RV8H9qq9774U4EMEJsvcotKVAoawCB+igtKV77zbJ748CInYm\nJ03S3/lcV6+LtMmTL8+d8805z7mf+xYEaRojtjFBQcHk5GQb5AZq5/DuVJSWcSVF9x2YoaP7o1Gr\nubivdXnpAC49gunQL4yCYxfIj299dg2A3YhbcH3sVtS5JeQ89oNOJQfMEU1VDbnP/ELZhmNYCT54\nf71AJ1PX1KlJfP0nNFU1hP3ndqzdWv+lFb96EwDDHmx95cZrnI0/Rm11Db2GDpLcfJOTkwDo1Em3\nLx0Z/dHJ2AVBWCAIwqkbf4BM4HVRFEcBrwOrpBTaVgQHhwCQmpos+dhdetbXbrlw4rTuY4wcAAoF\n53fH6fT6zg/V5zpf+HIj2qsdo1qL052DcF4wgrqMQnIeW4m6UNq8f1NDXVBGzr9WUhktYtOvE16f\n3YfSzUGnsS6t2UPx6VS8x0S0qB/tzeQlXeZS3Al8eoTSMaL19WCuceJA/b4EqXqY3si1Yyc4WLp6\nMzKtQ6elGFEUVwArbvydIAi2QN3Vv8cIgtBs52UPD9MrCBQe3o3ff4crVy4xcmR9gwKpdPYbOYCf\n3vmYlFOnmf3QXTqN4eHhSHDfbqQcPo2irAT3YL+//a3513cla2I/Lm05TGn0STrNHKqTDvenJ5Ou\n0ZL9XRR5//qBLt88gHVAyzb4mGLcG8LDw5GyhFSSHv2B2uxi3KZEEPTmHS1qdtEQBYmXSP5uK7Ye\nzgx+dT7WLq3/coh6ZzsAox6edV1ja6mrreXskWN4+HrTa0C45GfsV67Ul6CIjAy/rs+cYt4ekHKN\n/UWgAHhHEISeQFpzLzDF3NaOHetXkGJj4xg7djIeHo7S6VTZ4RcSxKm4o1xKuYKdg25nfcLE4aQc\nPs2+b/5k+JJ7AFqlM/D+iaTvPcGxt37BpkdnrFx102G9cCSOVTWUrjlI4rQP8Hh9DjYRQU2+RtL5\nNCDu7g6kfLWHgg+3gUaDy8NjcLh7CPnFujUlV1fVcOiJL9HUqun69F2U1GqhlfNQnJnD6W0xdAgJ\nwCWsfl1cl7k8F59AZVk5/caNIi9P+iXH5OQUbGxsABtyc0vNJubmpLM5pFxjfxMYJgjCXuBdYL6E\nY7cZnp5e+Pr6cfLkCcnL9wL0GjYYdV0dpw+2fo38GiFDInD06sDZ7dFUlbT+wLTxcqXzwonUllRw\n/tN1OutQKBS4PTYet2WT0ZRVkf3YD5Su0/3/ZSqoi8pJ/veP9SUCHKzx/PAenO9pvj9pU5z78A/K\nL2UTMGsYHfqF6TTG0dWb0Wq09LnrNr20nIo9DECPQa3PxmkOrVZLZmYmvr5+8o1TIyKZsYuiWCyK\n4mRRFEeKojhWFMXWFTUxIXr27E1FRTnnz5+TfOxew+s3bCTsi9Z5DAulkh7Tx1BXVcPpDXt0GiNg\nxjCcwgK4siO+1fXab8ZxWiReH9+LhYMNBW9vIvfF31EXSH8maGi0Wi3luxPJvOszCrccx7pHAD7f\nPYRt3xC9xs3YHEfm5jgcu/jTZZFuSWMlWXmc2x6Ns78XnUfobsgajYbTcYexc3QkpHvLujm1hoKC\nfKqrq/D19Zd8bJmWI9eKaYB+/epvKEVH75d8bE//+vSys/EJFGTrni/f7bbhWDvZc+znrVQWt95E\nFUoLuj1zFxbWKk6/9hPlaa2rGnkzNhFBeH/7IFbd/KnYeYrMOz+lbPNxo1VwbC21l/LIXbqGvOd/\nRVtejf+ySXh9cb9OmS83UpSYytn3fsXSwZbw/92HUsdyDEd+WI+mTk3feVOw0GP3bJp4kaLcfLoN\niDRIYa6MjPouX35+LatBL2MYZGNvgIiISOzt7YmOjjKIMQ2ZPB6tRkPs5h06j2HtYEffeVOoKa8k\nftVGncZwCPHllmV3oK6o5sSzK6ir0K8Il8rXFe+vFuD6+AS0tWryX11H9uLvqD6lWz33tqAur5T8\ntzaSOfczKqNFrCOC8Fm1GO+Fo/QuP1CVV8yJ51agVWsIf3k+dn6t2516jcK0TM7tiMYtyI/QMfpl\nsZw4EAtALwNkwwDX94DIZ+zGRTb2BrCysmLAgEHk5GSTmNjyfqEtpfeIIdg5OnBw606dqz0C9Jg6\nCkdvd06t301hum5n3D5jIwmcPZzyS9kkvv6TzimQ11AoLXC6fQC+qx/BdqhA9fFLZD34DTlPrab6\njPQbv3Sl9nI+BR9sIXP2R5T9GY+lnxseb95RX/Olhdk9TVFXXsXxp5dTk19C6MNTdV5XB4j7di1a\njZYBC2bodbau1Wo5fiAWaztbwvq0PtWyJWRk1H+J+/nJxm5MZGNvhKFDRwKwefNmyce2srZmwPgx\nlBUVc2iHbmvkAEorFQMWzERTW8emV5frfHXRZfFUXHt1JifqJOfe/12SqxRLbxc8374Lry/vx7pX\nRyqjRbIWfM2Zae9TuvYImjLpS/Q2h7a6lop9Z8l+YhWZt39M6a+HsHC0xW3pJHx/Wozd8K6S3PCr\nq6ji2JNfUipexve2AQTOGaHzWOkJZ+srOHYNIXhwy1vlNUTy6TMUZOXQY2A/yRpV30xaWv3mO3//\nQIOML9My5CJgjRAZ2Q93dw82b97MHXfMx85Ot646jTFy1lQObNjCjtW/0n/cKJ0PtNDRAzi3PYaL\n0QkE7Ygl7NbBrR7DwlJJz9fuJ/7fn5G+PgYUEPbEbElMzqZnR7w+v4+q+BRKf42jIvY8FafTKfx4\nO7aDumA7WMB2UBeUrvZ6v1dDaEorqTqWSsXeM1QcENFW1HfHsg4PxHFWP+xGdEXRijLGzaGurCZh\n6dcUn07Be0wEt+hQMuD6WDW1RH24EhQKhv37br3jcXDLTgAGTBij1zhNkZR0kQ4d3HF2djbYe8g0\nj2zsjaBUKpkwYRI//vgde/fu4rbbpC2B4+TmytApE9nz25/EbtnBsGm6NSNQKBSM/M98fl7wPPs/\nWYV/RFccPFpflErlZE+fDxZzdMlnpP8ZAwoFYY/PksTcFQoFtn1DsO0bgrNaTdqPMZRtOV5vtnvP\ngEKBVZgv1t39se7mj5Xgg6W/G4pWlrHV1tZRe7mA2uQcas5mUHUslZrzV+Bqhyaljwv2M/tiP7ZH\nq9rWtZS6ivrll6ITSXiN7EW35+bptU4fv3oThWlX6DF9NF5CsF7aKsrKOL4/FndfHzqHd9drrMYo\nLi4mPz+Pvn2lbdgh03pkY2+C8eNvY/Xqlaxfv5YJEybpVLirKUbfPp3ojdvYsfo3+o0diY29blcF\nTt7u3PrUvWx8+St2vPYV095bqlPTBCsXB/p8+Ei9ua+LRl1RTden5uicydHge3i74Dx/GE73DqXu\nUh4V0SKV0SLViRnUnM2g9LerXZ4sFCg9nLD0cUHpao/CzhoLeysU1qr6/qt1GrQ1daiLKtDkl6Eu\nLKMuqxjUN9wjsFRiHR6ITUQQtoNCsbrFcLnVlVfyOf7MN5QlZeI5vCfdX2x9ffUbyT6XzNGfNuHg\n4cbAB2bpre/Qtt3U1tQwcMJYg83BxYv1Gc4hIXIpAWMjG3sTuLl1YOLEiWzcuJGYmAMMHSptMwIH\nF2fGzJnOlh/WsPXHn5m+6H6dx+ozayyJe+JJPnCUmC9/Zegjd+o0zjVzT1j6NVe2H6H8UhY9X12A\njZe0VRwVCgWqIA+cgzxwnjcEbXUtNeezqE5MpzYlh9q0fOquFFF9Ig2aW/NXWqB0tceqqy9WwZ6o\ngj1QdfHGups/FrZWkupuiMITSZx4bgW1xeX4Tx2MsGSmXqZeVVrOtpc/R6PWMGrp/VjZ2eqlr6aq\nmt2/rsPazpaBEw23DHP8+FEAevToabD3kGkZsrE3w3333cfmzZtZs2YlgwcPlfysfdTt0zm8cy/7\n122i/62j8A0O0mkchULBmGUP8NulTE78vh2vsGBCRw/QaSwrFwciP3mUs+/+xpVthzm08F3CX7kP\n116Gq9ansFZh3SMA6x5/r9+tra1DU1aNprwabUU12uo6sLRAYalEoVJi4WyHhbMtConj0hK0Wi2X\n/9jP+U//BOrvSwRM162Jyo1j7n7rG0qz8uh7z1QCI/VfNonZvJ3SwiLG3TUbeyfDlUA+diwelUpF\n9+6ysRsbOSumGQIDAxk+fBQpKckcOhQr+fgqKytmLH4AjUbDbx9/hUaPdEMre1sm/u8xVHY27Hnn\nW7LOJOk8ltLaim7P3oXw75nUllRwdMlnJH+/HU2d9GUWmkKhskTpao/K3w2rUJ968+/qh1UXb1RB\nHvXLNEYw9aq8Yo4/vRzxo7VYOtoR8f5ivU0d4MjKDaTEJODfuyt975mqv86KSnb/shZrWxtGzJys\n93iNUVCQT3JyEt2798Ra4m5MMq1HNvYWMGfOXCwsLFi58luD1I/p1j+S8CEDSD59lqi1um02uoZr\noA/jnl+EuraWjU+/T16S7puDFAoFgbOGEfHBI1i5OpK0YguHH3yPkvPpemk0Z7QaDRmbDnLw7jfI\ni03ErU8oA79biluE/s0qTq7bxeHv1+Ho1YFxLzysV876NTZ/t4rSwiJGzZ5u0LP13bvrN9v172+Y\njU8yrUM29hbQsWMQo0ePIzU1hX37dhvkPW5/bBEOLs5s/PZHMvVoxAEQPLAXo5c+QHVpORueeoei\n9Cy9xnPr3ZmBK5/G97YBlF7I4PCD73H+s/XUlulW6dBcKU5M5fCiDzjz1s9o1RrC/jObiPcfxtpd\n/9Q+cWcs+z9ehZ2rE1PfW4qdq/4mnHr2PAfWb8EzwI/Rc3RvodccGo2GrVs3YWVlxahRYw32PjIt\nRzb2FjJ37r1YWqpYufJbampqJB/f0dWFO594BHVtHave+lCvHakAYbcOZthj86goLGH9k+9QkpWn\n13gqRzu6PX1nvZF5unDp5z3E3vUq6Rti23x5pq2pzMzn9GurOLzoA0rOpuE1OoJBPz5DwLQhkiwD\nJR04yq43v8HawY4p7zyFi5/+vWTVdXX8/MFnaLVa5ixZjMrKcDeRT5xI4MqVTIYPH4WjY/uoZ27u\nKF966SVjvfdLFRXSG6TU2NtbU1FRg4ODA2VlJRw9egQ7Ozu6desh+Xt5BvhRnJfPmcNHqSwr55b+\nLe/wfk3njXh1DcHCUknygaNcjDpCYN8eep8J2vm54z9lEEprKwqOXSAn6iTZu4+htFHhEOTTbN52\nQzpNEXt7a3IS0zj/2Z+cfecXSi9k4NDZj/BX7iPojpFYOuiXqXKNU+v3sPutb7C0smLy20+2Kl+9\nqbn886vvOH3wMAMnjNV5j0RL0Gq1fPTRu2RnZ/Hoo4/j4eHRKp2mhBnpfLm558jG3gw3Bjs0tCvb\nt2/mxIkExo4dj62ttLtRAbr0Cuf0wcMkHorHw88H35CgVuu8Ed9wAUtba5L3x3Nhdxy+PUJx9NKv\nFoqFpRLXnp3wndCfuqpqChMuknvgFJlbDqHVarEP8mo0993UDx6tWkNe3BkSP/idsx+tpexiBvZB\nXgiPTSfs3zOx9dG/jgzUr9XHfvUrcSv+wNbFiSnvPIn3La3L/25sLo/u2c+G5T/gFejP/f9dhqXK\nMOUDAI4ePcKaNT8SGdmP229vuCuYqcf8GmakUzZ2fbkx2NbW1tjb2xMTc4DS0hIGDtQ/C+JmLFWW\nhEb05MjOvZyKPUT3gX1xdG2+dGxTH0qf7l1w8vHg4r4jiLsO4h4SgGugj/5a7WzwGNQd34n9UVgo\nKDqVQt7BRNJ+20/F5RxULvbYeLr+bUOMqR48lVfySV8Xzek3fiJ9XTSll7JxvqUjYUtmITw2A8fO\nfigspNnYU1tZzc7Xv+bM5ihcAryZ8cHTdAhufdGshuYy/WIy3/z3dVTWVjzy1is4u0vzRdQQGo2G\n1177L8XFRTz//Mu4uja849lUY34zZqRTNnZ9uTnYISGdOXgwmqNHj1yvJyP5ezo54hXoR/zuKMSj\nx4kcPQyrZlLImvtQuncOxCssmItRRxB3HsRCaYFP9y6S7EK0tLehQ78w/KcNRuVoT0VGLoUJF8nc\ncogr249QlVOE0s4aa3dn7B1sTOLg0Wq1VKTlkLntMOc/WceFzzdQcOwCmjo1vuP7MfiNBfjeMQr7\njl6S7tTMT8lgw9J3yThxDt/wUKa88xQO7rpt/ro55gU5uXy+7CUqSkq57/mlhHTXvdl1S9i6dRPb\nt29h9OhxTZbcMCPDNBedzRq7woiNELTm0l/wZp2nTp1g6dIldOrUhY8++sIgDQsANn27ip1rfqdT\nj24sfvOlJguFtbRfY46YypYXP6Ysp4CgQb0Y+8xCrB2kLcCl1WgoPJ5ExqaD5EafRl1ZX3jLys0J\n38G3YNctGLeILpIta7SUquxCik6nUJBwgfxD56jKKqj/g4UCt95d8B4Tgefwnqgc7STvf6nVaknc\nFMWBT39CXVNLj2mjGbL4TpR6FCC7UWNJQSEfPf4seZlXmLzgHsbcMUMq6Q2SkZHOI48sxNJSyRdf\nfNfg2npDOk0ZM9LZ7JmGbOzN0Fiw3333DXbv3sGiRY8ydepMg7y3RqPhh9fe5fj+WCJGDOXuZx5v\ndOdraz6UlUUlbP/fl6QfO4OzryfjXliEV5h+7d8aQ11dS0G8SM7+k+QdTKSm8K9uT1ZuTjgJ/jgJ\nATh28cPO3wNbP3eU1vplcKira6jMLKA89QplKVmUJWdSfOYS1bnF159j6WCLW6SAe/8wPAZ3x8r1\n79kcUh7kZXmF7H3vey7FncDa0Z7RS+8nZEjLb4w3xjWNpYVFfPrUC2RdusyYO2YyecHdEqhunLq6\nOp588l+I4jmWLXuBESNGtUinqWNGOmVj15fGgl1UVMjChfeiVqtZvvwHOnTQrTtOc9TW1PDZ0v+S\nkniWUbdPY+rC+a3S2RgatYZD367l6OpNWCiV9L13Kn3uuk2n4mEtRavVYlVcStKu4xQmXKD4XBrV\nOUX/eJ61hzPWHZywcnVE5WSPytkepbUKC2sVFpaWaDUatGoNmro66sqrqCutpLa0gpqCEqqyC//2\n5XENKzcnXLoH4dwtCJfwEJzCApus5yLFQa7VaDi7LZqYL36muqyCgD7dGLX0fhw9pblS8fBwJElM\n4/Nl/+VKahrDp09i+sMLDN5EeuXKb1mz5kdGjRrLU0892yKd5nysmxqysUtAU8HesmUjn3zyPoMG\nDeWFF14xmIbykhI+XPIMOZczmLTgbsbe8c8rBF0/lJePnmHXm8spzyvEUwhm1FP3494poPkX6sjN\nOqsLSigRL1Oemk1Fei4V6blUZuZTU1CCpqYOLBTXy+42h0KlxMbLFVsvN2y8XXEI8sEhxAf7YG+s\n3Z1bZXj6HuTZ55I58OlqshIvorK1YfCiOXSbPEJS062rKOL1h5aRn5XNsGm3MWPxAwY39aioPbz5\n5v/w8vLms8+WY2/v0OxrzMgwzUWnbOz60lSwNRoNy5Y9zunTJ3n++VcYPHiowXQU5OTy0ZJnKMrN\nY9pD9zFy1t/riOjzoawqLefAp6sRd8SgsLAgfMYY+s+fjpW9NLnaN9JSnVqtlrryKmqLyqgrr0Jd\nXYOmuhZNrRqF0uLqjxJLextUjrZYOtphaWctWd0YXeezLLeQg9/8jrgjBoBOwyIZ+uhdOtXIb4rk\n02f59pU3KS0sZvw9dzB+nu4NPVrK2bOJLFv2OJaWKt5//1OCglqWc29GhmkuOmVj15fmgn35chqL\nFz+Ak5MTX331PQ4OzZ/B6EpuxhU+efJ5ivPymf7w/YyY8VcmghQfykuHT7L/o1UUZ+Zg5+bM4EVz\nCB09QNIiW2Z08LRKZ2VxGQk/b+Hkul3UVdfg3jmQIY/ciX8v6TNT4vdEsfrdT9BqNMx+bBGDJo6T\n/D1uJjs7iyVLFlNSUszLL79BZGS/Fr+2vcbcWLTE2OV0x2ZoLgXK2dkZhQLi4mIpKio0SG77dS1O\njnQb0JeT0Qc5vj8WlZXV9ZQ2KVK1XPy86DZ5OEqVJZePJnJx3xGSo4/h4OmGi780aX9mlFLWIp0V\nBcUc+XEDO1/9kozj57B1dWLI4jsZseRenH09JdWkVqvZsPwH1n/9PTa2Njz50at07d9yg9WVvLxc\nnn32SXJzc1i8+DFGjBjdqte3t5gbGzmPXQJaEuyuXbtx5Egc8fGH6dSpMwEBhmvke83cT8Ue4mT0\nQaoqKhAieuIgUX64hVKJX88whLEDqSop5/KxM1zYHcfl+ETsXJ1w9vPUy+DN6OBpUmdBagYHl//O\n7rdXkHlCxMbZkQH3z2DMswvx7tpJss1M198vO4flz79GQlQMngF+LH7zZXr062nwuSwoyGfZsifI\nyEhnzpy5je4ubYr2EnNTQc5jl4CWXp5dupTKv/71IHZ29nzxxYqHdQTMAAAaiElEQVRGd+FJRUFO\nLl8+8zLZaen0HjGEJW8/R1FxteTvk5+SzqFv15IcfQwAt46+9JozAWH0AJQ6NOA2o8vdf+hU19WR\nEpNA4qYoLsefBsDZ34tes26l6/ghWOqZptkYJ2PiWP3uJ1SWlRMxYihzljyMjb30ufY3U1BQwLJl\nS0hPv8zs2Xdy330LdfpSN+eYmyLyGrsEtCbY69b9ztdff0ZkZH9efvl1ybst3Ux5SSkrXnqDpFNn\nCIvowd3PPoWDs2FqbuddTCPh161c2HMYjVqNrYsjYbcO5paJw1tVnsCMDh5yc0vRarXkJ13m/O44\nzm2PpqKwBKivwdNr9q0ED+plsEYf1ZWVrP/6B2I2bUNlbcXMxQsZMGHMdXM15Fzm5ubw3HNPcfly\nGjNnzmHBgod0vlIzt5ibOrKxS0Brgq3RaHjhhWUcOxbPffct1OmytbXU1tSw6q0POb4/FhcPd+5/\ncSkdw0IN9n6lOfmcXLuTs1ujqSqpzxf36RFKl5H96Dy8L3ZuTdcmN4eDR6vVQmkx8X9GcX7PIQov\nZQJg7WhP2LhBdJs0ArcgP4NqOJ9wkjXvf0pBVg4+QYHc8+x/8A3u+LfnGGouU1NTeP75peTn5+lt\n6mAeMQez0ikbu760NthFRUU8+uhCCgsLeOON9wgP72VAdfVoNBpiN2zk98+/x0KpZPqi+xkyZYJB\n09/UNbUkRx8jcXMU6QlnQatFYaHAt2cYwQN70bF/OC4B3v/QYKoHT111DVdOXyA17gSpsccpzswB\nQKmyJGhgL7qM6k/QwJ5YGrCuOUBleTkbV/xIzMZtWFhYMHrODMbPm9NgOQlDzOXJk8d55ZXnKS8v\nZ8GCRcycebvenyNTjfnNmJFO2dj1RZdgJyaeYunSJTg7u/Dpp1/j5mb4migeHo4c2HaAlW+8T3lx\nCREjhjL73w9hZ8D0y2uU5RaSFHWEC/sOk5V48frvnXw88I+4Bd/wUHzDQ3H0csfT08kkDp7qsgqy\nzyWTlXiR9ISzZJ1JQlNbB4DKzoYuQ3rj26c7IYMjDJLPfzNarZaEqBjWfbGCkoJCvIMCmfvkYwQK\njTcQl9qIDhzYx9tvvw5oefzxpZJ1QzIjwzQXnbKx64uuwV679leWL/8CQQjjzTc/wMbGxgDq/uKa\nzsKcXL5/7V1Sz4i4eHTgziceJSyyt0Hf+0bK8gpJO3yKS4dPcjk+kZryv9rn2bo6ERDeBedAP9y7\ndMStow9OPp56FcJqDo1aQ1luAQWpGeSnpJOfnE7uhUsUpl2Ba599hQKPzoH4R3QlILI7fj3D8PZ1\nbbOD/EpqGn98tpwLx09hqVIxbu5sRs+e3mTRN5DOiDQaDWvW/MiqVd9ja2vLCy/8j9699a9lcw0z\nMkxz0Skbu77oGmytVsv777/Frl3bGTx4KM8++5JBb6beqFNdV8eun/9g26pf0ajVDLrtVqYuvBcb\ne+kbgzSFRq0m92IaV06dJ/PkeXLPp1Kanf+35ygsFDh6u+Ps44m9uwv2HVyxd3fB2tEeawc7rB3s\nsLSxQmlpiYXKEgsLCzRqNRq1Bk2dmtqKKmoqKqkpr6SyuJSKwmIq8ospyyuk5EouJVl518/Er6Gy\ntcEzLBjvrp3w6hqCb3goNk5/v7Jpi4O8pKCQrSvXcHDrLrQaDbf068PMRx7A3bdlN6Ol0FhVVcl7\n771FdHQUXl7e/Pe/rxEcLG1BODMyTHPRKRu7vugT7NraWp5/fiknTx5n5sw5PPDAIonV/UVDOtMv\nJrPqrQ+5kpqGk5sr0xfdT+8RQwy+9bwpbBVqzh06Q15SGkVpWRRlZFOUnkXl1WwTKbFxcsDZ1xMn\nXw9cA33pEOJPhxB/nLw9sGimhZ8hD/Kqikqi1m5g96/rqK6swivQn6kPzueWfn3atp5Ndhb/+9+L\nJCVdoHv3cJ577mVcXJpv6tJazMgwzUWnbOz6om+wS0tLeeKJR0hPv8wDDzzMzJm3S6juLxrTWVdT\ny65f1rJzze/U1dbSObwbMxY/gF+nlvfWlJLGdNZWVV8/067IL6K6rILq8gqqS8upq65FU6dGU1eH\nRq3GwlKJhbL+R2Vrg5W9DVb2dlg72mHv5oJdBxfs3Zz1Whs3xEFeXVlF9IYt7P51HeUlpTi4ODPh\nnjsZOHGsTjX99dEYH3+Yt99+jdLSEsaPv43Fi/+NykAt9MzIMM1Fp2zs+iJFsLOyrvDkk4+Rn5/H\ngw8uZvr02RKp+4vmdOZlXmHtFytIjItHoVAQMXIoE++9s8WX/VJhRgePZDory8uJ3riNqLUbKS0s\nwtbejpGzpjJ8xhRs7Nr2y0etVvPTTz/w88+rUCotefjhfzFx4mSdNbSE/48xNySysUuAVMFOT7/M\n008/QX5+HgsWLGLWrDkSqPuLluo8eySBTd/+SPrFZCyUSgZOGMuYO2bg5iVtXZPGMKODR2+dxXkF\nRK3bSMym7VRVVGBtZ8vw6ZMYOXMqdo76Zyu1VmNeXi7vvPM6J08ex8vLm+eee4kuXQS9dTTH/6eY\ntwWysUuAlMHOyEhn2bLHyc/P4957FzBnzlzJ1rtbu5Hq+P5Ytny/mtyMTCyUSvqMHMqo26f/YxOM\n1JjRwaPzTfPUMyL7/9zM8QOxaNRqHF1dGDFjCoMn34qtvXRtCFujMS4uhg8+eJuSkvom7I8/vhRH\nR8fmXygB7T3mbY1s7BIgdbAzMzN4+uknyM3NYeLEyTz88GNYWuqf7qfTZXldHcf2RbPrl7VkpaYB\nIET0ZOjUiXTrH2mQbkpmdPC0SmdFWRnH9h7g4NadpF9IBsAnKJBh0yfRd8wIVAbY2NQSjZWVlXzz\nzZds2bIBlUrFgw8u5rbbprbpDfT2GnNjIRu7BBgi2Lm5ubz00rMkJ1+kV68Inn32Jb3PnvTRqdFo\nOHMonj2/rSfpVCIArp4eDJw4lr5jRki6TGNGB0+zOtVqNReOnyJ+1z6OH4iltroGCwsLug3sy/Bp\nk+jcs7tBDbQ5jadOneD9998mKyuToKBgli17ocXNMaSkPcXcFJCNXQIMFezKykrefvtV4uJi8fML\n4KWXXsPfX/eWdFLpzExJJXrDNo7s2kdNVRUAnXp0o++YEYQPGYC9k/G+gNqSxnRq1GqSE8+RsC+a\n4wdiKSuqb5Dt7uvNgPFj6Dd2FM7uhq3s2ZzGqqoqfvhhBevX/4FCoWDmzDnMmzcfKwOXQ2gMc4+5\nqSEbuwQYMthqtZrvv1/O77//gq2tHUuWPMmwYSN1GktqnVXlFRw/EMuRXfu4eKK+RK2FUklo73B6\nDRtEj4H9cHBpuuBXW+g0FDfqrKmu5uKJ05yMieNU7OHrZu7g4kyvoYOIGDmEkO63tPn+gIbm8sSJ\nBD788F2ysjLx8wvgP/9ZRteu3dpU182YY8xNGdnYJaAtgr1nz04++eR9qqqqmDhxMg8++AjW1tat\nGsOQOguyczi69wDH98dcXz9WKBQEhHbmln4RhEX2JjC0M8oW3Cswh4NHo9FQXZxH3J44zsUnkHQy\nkdqa+gYMDi7OhA/uT88hA+nSO1yn/HOpuHEuy8rKWLHiS7Zt24yFhQUzZsxm7tz5Bi9l0RLMIeZg\nVjplY9eXtgp2enoar7/+MikpyQQHh7B06fOtWg9tK515V7I4ceAgZw7Fk5x4Do1aDYCVjQ3Btwh0\nDu9Ox66hBAqdG8wAMcWDp6q8gssXkkgTL5B8+ixJp89QWVZ+/e8+wR3pGtmb7oP6EdxVMMhNZV3w\n8HAkJ6eEPXt2smLFlxQWFhIUFMLjjy8lNNTwaYwtxRRj3hBmpFM2dn1py2BXV1fz9defs2XLBiwt\nVdx993xmzpzTorNCY3woK8vLEY+d4HzCSZJOJpJ16fL1vykUCjwD/PDv0omAziH4hgThG9yR4C7+\n5OWVtanOa2i1WkoKCsm6dJmMpBQyklJIT0oh+9JlbjwOOnh70a1fL/y6hBIW2QsXd8NX59SF4uJs\nXn31dU6fPom1tTV33DGPmTPnGGwHqa6YkWGai07Z2PXFGMGOi4vl44/fo7CwgC5dBJYseZKQkMbL\nt4JpfCjLiopJOn2WNPECl86dJ/1CEpXlFX97jp2jAx5+Prj7+dLByxNn9w64errj3MENeydHHFyc\ndUoN1Gq11FRVUVZUQmlRMaWFRRTm5FKUm0dBTi65GZnkpmdSXVn1t9dZ2dgQGNqJwLAuBIZ2Iahr\nKK6eHiYxn41RVFTEqlXfsXXrJjQaDQMHDuGhhx7By8vb2NIaxJTn8kbMSKds7PpirGCXlpbw1Vef\nsnv3TiwsLJgyZQbz5s3HvpENLqb4odSo1eRmZpGZnEJGUipXUtMozM4iKy0TdV1do6+zsrHGysYG\na1sbrGxssLS0xEJpgYWFEhQKNOo61HVq1HV1VFdVUV1RSVVFZZNjqqyscPfzwdPfF68Af3xDgvDr\nFIy7r3eDVTdNcT5ramrYtOlPVq9eSXl5OYGBgSxY8DD9+g0wtrQmMcW5bAgz0ikbu74YO9hHjx7m\n888/JjMzAze3DixcuJjhw0eaTWeim/HwcCQrq/5sujAnl6KcPIry8ikpKKS8pJTy4hLKS0rrDbuy\nipqqKtR1dfVletVqtIBSqURpqURpaYmVjQ029nbY2Npi62CPg4szDs5OOLo44+rpgaunBy4eHXB2\n79CqssmmNJ9qtZp9+3azcuW35ORk4+DgyLx59zJ//jwKCyubH8DImNJcNoUZ6ZSNXV9MIdg1NTX8\n9tsafvnlJ2pra+nWrQeLFj1K585/9TY1BZ0tQdbZcrRaLUeOHOL775eTkpKMpaWKyZOncuedd+Po\n6GQSGluCrFNaWmLshmtdIyMZVlZWzJ17L6NGjWX58i84eDCaxx5bxK23TmTevPl06OBubIkyEqLV\naomPP8xPP32PKJ5DoVAwevRY7r77fpNdR5cxLWRjNyN8fHx58cX/kZBwlK+++pRt2zazZ89OpkyZ\nzqJFCwHTSMOT0Q2NRsPhwwf5+eefEMWzAAwePJS5c+8lOLiTkdXJmBPyUkwzmOrlmVqtZteubaxa\n9QN5ebk4ODgwe/adTJkyHRsbwzdf1hVTnc+baUudGo2GmJj9rFnzIykp9RvABg0ayty59zSZDSXP\npbSYkU55jV1fTD3Y1zIlfvnlJ0pKSnBxceX22+9k4sQprd692haY+nxeoy10VlVVsXv3Dtat+42M\njHQsLCwYPnwUc+bcRceOzW9Ok+dSWsxIp2zs+mIuwba1VbB8+XesXfsblZUVuLl1YPr0WUyYMLnR\nFEljYC7zaUideXm5bNmykc2bN1BSUoylpYpRo8Zw++134efnbxIapUTWKS2ysUuAGQWb3NxSSkqK\n+eOPX9m4cR2VlZU4ODgwbdospk6diYOD/l17pNJp6hhCpyieY+3aX4mOjkKj0eDg4MikSVOZPHk6\nbm6trwj5/3kuDYEZ6ZSNXV/MKNh/01laWsqmTev588/fKCkpwc7OnkmTpjJt2kxcXdumrGxLdJoq\nUumsqalh//69bNr0J6J4DoDg4BAmT57OyJGj9bof8v9tLg2NGemUjV1fzCjYDeqsqKhg8+b1rFv3\nG4WFhVhZWTFmzK1MmTKDjh2DTEanqaGvzkuXUtm5cxs7d26jpKQYhUJB374DmDZtJr16RUhS4vf/\ny1y2FWakUzZ2fTGjYDeps7q6mp07t/H77z+TnZ0FQO/ekcyYMZs+ffq2WS3x9jKfDVFRUUFU1B62\nb99yPV3RycmJceMmctttU/D29jG6RmMg65QWeYOSzHWsra2ZNGkqEyZMIi4uhvXr15KQEE9CQjxB\nQcFMnz6bESNGG63Ljrmi1Wo5f15k27ZNREXtobKyEgsLCyIj+zNu3AT69x8oz6lMmyOfsTeDGX2L\nt1rnxYsX+OOPX9i/fy8ajQZXVzcmTJjE+PG34eEhXZ9TfXUag+Z05uRks3fvLvbs2Ula2iUAPD29\nGDduAuPGTcTDw8PoGk0FWae0yEsxEmBGwdZZZ25uDuvXr2Xr1k1UVJRfP+McP34iffsOwLIFnZHa\nQmdb0pDO/Pw8oqP3Ex0dxenTJwFQqVT06zeQ8eNvo3fvPm3aUcmc59IUMSOd8lKMTPN4eHjywAOL\nmDfvXqKi9rJ160YOHz7I4cMHcXPrwMSJk5kwYRJubqbZcMKQlJeXERsbzb59uzl+/BgajQaFQkH3\n7uGMHj2OIUOGm0QaqYzMjchn7M1gRt/ikupMSrrIjh1b2L17B+Xlf53Fjx17K/366b5ubA7zWVZW\nhiieYMuW7Rw5EkdtbS0AXbt2Y8SI0QwePNQkCq+Zw1yCrFNq5KUYCTCjYBtEZ1VVJXv27GLbtk1c\nuHAeAAcHR4YMGcbgwcPo2bN3q1qxmeJ8arVaMjLSOXr0MHFxsZw6dQL11V6uHTsGMXz4aIYPH4mv\nr5+Rlf4dU5zLhpB1Sots7BJgRsE2uM7U1BR27drOnj07KSwsAMDBwYEBAwYzePAwIiIimz2TN5X5\nrK6u5uTJ4xw5cojDhw9eTwEF6NJFYMyYUfTs2bdFNVuMhanMZXPIOqVFNnYJMKNgt5lOtVrN2bOJ\nREfvJzb2ALm5OQDY2toSGdmPyMj+9O4d2WBmiLHmU61Wk5R0kdOnT5CQcJSTJ49TU1MDgL29Pb16\n9aFPn75ERvbHw8O0e55ewxw0gqxTauSbpzIGQalU0r17ON27h/Pgg4s5f/4cMTEHiInZz4EDURw4\nEAVAQEAgPXr0IiysK4LQFX//gDbRp9Vqyc/P48IFkfPnRc6fP8fZs2eorPyrsXZQUAiRkfVG3q1b\nD0kzf2RkjI18xt4MZvQtbnSdWq2WtLRUjh07SkJCPKdOnaCqqur63+3s7OnUKQQfH38CAjri4+OL\nt7cPXl7erc4s0Wg0lJaWkpV1haysTHJyssnISOfy5UukpV2irKzsb8/39w+gR4+edO8eTo8evZrN\nMzeF+WwOc9AIsk6pkc/YZdoUhUJBx47BdOwYzPTps6itrSUlJRlRPIMonuXChfOcOXOGU6dO/eO1\nlpaWuLl1wNHRCQcHB1QqFUqlJUqlErVaTU1NDTU11VRWVlBUVERxcdH1G5w3olQq8fX1o2fP3nTp\nIlz9CcXR0aktpkBGxiTQ2dgFQZgOzBJFce7VxwOAD4E6YIcoiq9II1HGXFGpVISGCoSGCkyePB0A\nV1dbTpw4R1paKllZWeTkZJGVdeW6WWdkXP7bWf6NKBQKbGxscXFxITQ0DBcXFzw9vfDx8cXLywcf\nH198ff1alaUjI9Me0cnYBUH4CBgHJNzw6y+AGaIopgiCsFkQhF6iKB6XQqRM+8HS0pLAwI4EBnZs\n9Dl1dXXU1dWhVqtRq+tQKpWoVFaoVKo2K1YmI2PO6HrGHgOsAx4CEATBCbAWRTHl6t+3A2MA2dhl\nWo2lpaV8M1NGRg+aPHoEQVgALLnp1/NFUfxVEIQRN/zOCSi54XEpECKJQhkZGRmZVtGksYuiuAJY\n0YJxSgDHGx47AUV66JKRkZGR0RFJrndFUSwRBKFGEIQQIIX69feXmnudh4djc08xCWSd0iLrlA5z\n0AiyzrZGH2PXXv25xiLgJ0AJbBdF8UhzA5hJzqisU0JkndJhDhpB1ik1Lfny0dnYRVGMAqJueHwI\nGKjreDIyMjIy0mBhbAEyMjIyMtIiG7uMjIxMO0M2dhkZGZl2hmzsMjIyMu0M2dhlZGRk2hmyscvI\nyMi0M2Rjl5GRkWlnyMYuIyMj086QjV1GRkamnSEbu4yMjEw7QzZ2GRkZmXaGbOwyMjIy7QyFVqtt\n/lkyMjIyMmaDfMYuIyMj086QjV1GRkamnSEbu4yMjEw7QzZ2GRkZmXaGbOwyMjIy7QzZ2GVkZGTa\nGfo0s5YEQRDCgDjAUxTFGmPruRlBEOyB1YALUAPcK4pipnFV/RNBEJyBVYAjYAU8IYpinHFVNY4g\nCNOBWaIozjW2lmsIgmABfA6EA9XAA6IoJhlXVeMIgtAfeFMUxZHG1tIQgiCogG+BjoA18KooihuN\nq+qfCIKgBJYDoYAWWCSKYqJxVTWMIAiewFFgtCiK5xt7nlHP2AVBcALeA6qMqaMZHgCOiKI4nHrj\nXGpkPY3xOLBTFMURwHzgM6OqaQJBED4CXgcUxtZyE9MAK1EUBwFPU//ZNEkEQVhKvRlZG1tLE8wF\nckVRHAaMBz41sp7GmARoRFEcAjwPvGZkPQ1y9YvyK6C8uecazdgFQVBQL/IZoNJYOppDFMVrJgT1\nZx6FRpTTFB8AX1/9twoTnlMgBngY0zP2wcA2AFEUDwGRxpXTJBeBGZjeHN7Ib8CLV/9tAdQZUUuj\niKK4Hnjo6sMgTPcYfwf4ArjS3BPbZClGEIQFwJKbfn0J+FkUxZOCIIAJfEAb0TlfFMWjgiDsBroD\n49pe2d9pRqc38CPw77ZX9nea0PmrIAgjjCCpOZyAkhseqwVBsBBFUWMsQY0hiuJaQRCCjK2jKURR\nLAcQBMGRepN/zriKGkcURbUgCN8D04FZRpbzDwRBmE/91c8OQRCeoRm/NFpJAUEQLgDpVx8OAA5d\nXUYwWYT6b6DNoih2NraWhhAEoQewBviPKIrbja2nKa4a+0OiKN5pbC3XEAThPSBOFMXfrj6+LIpi\ngJFlNcpVY18jiuJAY2tpDEEQAoC1wGeiKH5vZDnNIgiCF3AI6CqKoslc9QqCEEX9+r8W6AWIwFRR\nFLMber7Rbp6Kotjl2r8FQUjBBM6EG+Lqt2O6KIo/Ur+2ZZKXk4Ig3EL9WdFsURRPGVuPmRIDTAZ+\nEwRhAHDSyHrMmqsmuQNYLIriXmPraQxBEO4G/EVRfIP6JUzN1R+T4eo9PgAEQdhL/UlRg6YOJpAV\ncxVTrkS2AvhBEIT7ASVwn5H1NMbr1GfDfHx1aatIFMXpxpXUJNfOPkyJdcBYQRBirj421VjfiKnN\n4Y08CzgDLwqCcG2tfYIoiqaWLPE78P3Vs2IV8G9RFKuNrEkv5OqOMjIyMu0MeYOSjIyMTDtDNnYZ\nGRmZdoZs7DIyMjLtDNnYZWRkZNoZsrHLyMjItDNkY5eRkZFpZ8jGLiMjI9POkI1dRkZGpp3xfxZF\nU9K9ibS4AAAAAElFTkSuQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 24 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.kdeplot(data.values, shade=True, bw=(.5, 1), cmap=\"Purples\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nZvZ8SZ+SdIFOXy7799z9s2WPqk5m9mJJD0h6o7s/OOR7hZzQzWxT0k2S/rf0sVTqfZK+4O6vl3SF\nuFXgbm+XtM/dXyPpAzr9/yU817skPe7ur5P0s5L+uPDxVGn2g+8Tkr6T4/3CBX1296RPSPqgpBOF\nD6dWfyDpT2dfP1/8e9rttZI+J0nufr+kV5c9nCrdIelDs6/PknSq4LHU7EZJN0t6LMebjXrJZY+b\nWD8i6a/d/atmJkl7Xgx+Clbc6PsBM/tBSX8p6TfzH1nVNiVtL3z/PTM7y92fKXVAtXH370iSmR3U\n6bj/Ttkjqo+ZXaHT/xXzeTP7oDK0KNxH/83sIUmPzr69RNL9s6UFLDCzH5f0V5Le7+53lz6empjZ\nTZLuc/c7Zt9/091fUviwqmNmL5F0p6SPufunCx9OdczsS/r/W3W+QpJLepu7/89Q7znqCX0Zd79w\n/rWZfUPSZQUPp0pm9mM6PVX9krv/e+njqdC9kn5e0h1mdomkrxY+nuqY2Q9I+ryk97r7F0sfT43c\n/dL512b2RUm/PmTMpYBB3yXWf36kc71O7275o9my1JPufnnZQ6rKXZLebGb3zr6/suTBVOo6SS+Q\n9CEzm6+lv8Xd2YhQULglFwCYqnC7XABgqgg6AARB0AEgCIIOAEEQdAAIgqADQBAEHQCCIOgAEMT/\nAZbBqp4gMBnzAAAAAElFTkSuQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 25 - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Bivariate and univariate plots using `jointplot`:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `jointplot` function allows you to simultaneously visualize the joint distribution of two variables and the marginal distribution of each. Above, we showed how to use it to draw a hexbin plot. You can also use it to draw a kernel density estimate:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "with sns.axes_style(\"white\"):\n", - " sns.jointplot(\"X\", \"Y\", data, kind=\"kde\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 26 - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Combining plot styles: `distplot`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Each of these styles has advantages and disadvantages. Fortunately, it is easy to combine multiple styles using the `distplot` function in seaborn. `distplot` provides one interface for plotting histograms, kernel density plots, rug plots, and plotting fitted probability distributions.\n", - "\n", - "By default, you'll get a kernel density over a histogram. Unlike the default matplotlib `hist` function, `distplot` tries to use a good number of bins for the dataset you have, although all of the options for specifying bins in `hist` can be used." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.set_palette(\"hls\")\n", - "mpl.rc(\"figure\", figsize=(8, 4))\n", - "data = randn(200)\n", - "sns.distplot(data);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 27 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`hist`, `kde`, and `rug` are boolean arguments to turn those features on and off." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.distplot(data, rug=True, hist=False);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nBh1HpOi4I0dT/fXvEJ01D++9d2j9t38g9epLQceSHBVpKVq+59H5x19COk3lVV/Ara4JOpJIUXIi\nESqvvo7Ka74EqRQdv/wp8cX3azx1AVCRlqKVWPE4me1vEpk2k8jk6UHHESl60TPmUvON7+GObiCx\nfDHtP/8xXltL/oYyZFSkpSil39hCYsmDOCPqqbhcp7lFBkto/FHUfPOvCU+aRmbrZtr+9e9Jv7k1\n6FhlS0Vaio7X1krHXbcDUPX5P8OtLo9JDUSGi1NZRdUXv07FxVfjtzbT/p//l8QzT+L7ftDRyo6K\ntBQV3/Po/P0d+C17iV1wOeHjTgo6kkhJchyH2PwLqP7aX+JUVRN/6A90/PpWPE0nOqxUpKWoJJY+\nSHrLJsJmMrH5FwQdR6TkhU8w1HzrbwidOIH0phdp+9e/I/2WTn8PFxVpKRrJZ1eRWPYY7ugGKq/9\nsla4Ehkmbl091V/9FrHzF+E376H9Zz8ivvwxfE+TSQ41/ZWTopCym+i877c4VdVUfeV/6HNokWHm\nuC4VCy+h+sa/wqmpI7H4ATru+De8Vl39PZRUpKXgZXa8Tcdv/xNcl6ov3UyoYWzQkUTKVvj4k6n5\n1t8SnnAq6S2v0vYv/5vUyxuCjlWyVKSloKXf2kbbbbdAMkHlZ7+sC8VECoBbU0vV9X9OxaXX4Mc7\n6fjNz+j43c91UdkQyLdUpZSYzkfuAaDyks8EnCS/9DZL+y/+HdIpKj93A9GpZ/Sp/cH62v2+rq8P\nfM7wCWbf9q7bXbr2deB+0tvsfvvp3ubA/Xt790Aijnv4/uv4env34NaP3Pf87/zv75P46COip8/e\nr33X/tPbLN5770AoDLEK6GiDUBj38HH7nr/r+br20dXee+8d8Dzo+lw/FCY6Z/4n+t3V3+b/85eQ\niBOdMz+7zw92Zp+zSyIOgHv4uGz/uu5LJWmJRvGjucd2TYzhurjjj9r3vQA+3l+329HTZ5Ncv/YT\nfUuuX5vdTf3I/b6fB+t35SWfofn7N0MqCTV1uPUj932/u9odmJmaOkb8P7cA0Ppv/4C3dw/OWfPg\nvEV0PnLPJzJ1vSaVl3xmv+9V95+z7j8j3bP15GCvRfdt8Mnf5UO16XzkHj6qjMJ5i3p8zq4+A9R+\n868P+TyO6xKbt5DwhMl03v1LUi+sI711M5VXX0fklKl5n0N6R0W6zKQ2rgcKv0gnX3qOzj/8CrwM\n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\nH//NPTEfH5VGAAAAAElFTkSuQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 28 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also pass a distribution family from `scipy.stats`, and `distplot` will fit the parameters using maximum likelihood and plot the resulting function." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.distplot(data, kde=False, fit=stats.norm);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 29 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To control any of the underlying plots, pass keyword arguments to the `[plot]_kws` argument." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.distplot(data,\n", - " kde_kws={\"color\": \"seagreen\", \"lw\": 3, \"label\": \"KDE\"},\n", - " hist_kws={\"histtype\": \"stepfilled\", \"color\": \"slategray\"});" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\ns3Uincv/MwAAAABJRU5ErkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 30 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also draw the distribution vertically, if for example you wanted to plot marginal distributions on a scatterplot (as in the `jointplot` function):" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "plt.figure(figsize=(4, 7))\n", - "sns.distplot(data, color=\"dodgerblue\", vertical=True);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 31 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If the data has a `name` attribute (e.g. it is a pandas `Series`), the name will become the label for the dimension on which the distributio is plotted, unless you use `axlabel=False`. You can also provide a string, which will override this behavior and label nameless data." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.distplot(pd.Series(data, name=\"score\"), color=\"mediumpurple\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 32 - } - ], - "metadata": {} - } - ] -} diff --git a/examples/timeseries_plots.ipynb b/examples/timeseries_plots.ipynb deleted file mode 100644 index 279b64b327..0000000000 --- a/examples/timeseries_plots.ipynb +++ /dev/null @@ -1,889 +0,0 @@ -{ - "metadata": { - "name": "", - "signature": "sha256:70fb0a281cfd361668b53ed3350722c25b2fa3ed97091ca6c1d8e6d00c61c390" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ - { - "cells": [ - { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "Plotting statistical timeseries data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This notebook focuses on the `tsplot` function, which can be used to plot statistical timeseries data. Timeseries data consists of values for some dependent variable that are observed at several timepoints. In general, these data are thought of as realizations from some continuous process, so the `tsplot` function makess the visuzalization of that process simple (though not strictly enforced). Importantly, the function is concerned with plotting *statistical* timecourses: the expected dataset is additionally structured into sets of observations for several sampling *units*, such as subjects or neurons. In the presentation of these data, the unit dimension is frequently collapsed across to plot some measure of central tendency along with a representation of the variability introduced by sampling and measurement error. `tsplot` is capable of representing that variabilty with several sophisticated methods that are appropriate in different circumstances." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%matplotlib inline" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 1 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import numpy as np\n", - "np.random.seed(9221999)\n", - "import pandas as pd\n", - "from scipy import stats, optimize\n", - "import matplotlib.pyplot as plt\n", - "import seaborn as sns\n", - "sns.set(palette=\"Set2\")" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 2 - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Specifying input data with multidimensional arrays" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll use several sets of fake data to demonstrate the `tsplot` functionality. The simplest dataset will be noisy observations from an underlying sine wave model." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def sine_wave(n_x, obs_err_sd=1.5, tp_err_sd=.3):\n", - " x = np.linspace(0, (n_x - 1) / 2, n_x)\n", - " y = np.sin(x) + np.random.normal(0, obs_err_sd) + np.random.normal(0, tp_err_sd, n_x)\n", - " return y" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sines = np.array([sine_wave(31) for _ in range(20)])" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 4 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`tsplot` can accept input data from one of two structures. In the first, as is the case here, a rectangular array-type object with timepoints in the columns and sampling units in the rows can be passed." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.tsplot(sines);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n3P8HRopxuzbbggIAAAAASUVORK5CYII=\n", - "text": [ - "" - ] - } - ], - "prompt_number": 5 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's create the second example dataset. Here we'll add an additional dimension: `condition`. This dataset will consist of three random walks with different probabilities of increasing position at each timepoint." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def random_walk(n, start=0, p_inc=.2):\n", - " return start + np.cumsum(np.random.uniform(size=n) < p_inc)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 6 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "starts = np.random.choice(range(4), 10)\n", - "probs = [.1, .3, .5]\n", - "walks = np.dstack([[random_walk(15, s, p) for s in starts] for p in probs])" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 7 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If the input data are a three dimensional array, the third dimension is assumed to correspond with condition and the traces are separated out and separately colored." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.tsplot(walks);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAeIAAAFVCAYAAAAzJuxuAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3VtsZNme5/Xv2nvHPXwJO23nxVmZzqo6rsxT58yFGeai\nmZ4zw2gkJEYjEE+8wAtCggfUIBo18IqENFLzggSjGQnmHSEEQkgjQatnugX0dA9Nn1NV6brkpfLu\nW9jhiNixL2v9edgRYadP3ux0XHb4/5Gsyiyn0zucdvxi7fVf/78REZRSSik1Gd6kL0AppZS6zDSI\nlVJKqQnSIFZKKaUmSINYKaWUmiANYqWUUmqCNIiVUkqpCQre9wc2Nzf/EvBfbW1t/c3Nzc3PgP8B\ncMCvgP9ga2tLzz8ppZRS5/TOFfHm5uZvAf8QKPX/1+8A/9nW1tZvAAb4e6O9PKWUUmq2ve/W9PfA\nv0EWugB/fmtr65/2f/2/A397VBemlFJKXQbvDOKtra3/CUhP/C9z4tdtYGEUF6WUUkpdFu/dIz7F\nnfj1HHDwvg8QETHGvO+PKaWUUrPiTKF31iD+fzc3N//G1tbW7wH/KvB/vPdqjGFn5+iMn2Z2rKzM\nXdrHf5kfO+jj18d/eR//ZX7skD3+s/jQIB5URv/HwD/c3NwsAl8D/+OZPptSSimlXvPeIN7a2noE\n/NX+r78DfjHaS1JKKaUuD23ooZRSSk2QBrFSSik1QRrESiml1ARpECullFITpEGslFJKTZAGsVJK\nKTVBGsRKKaXUBGkQK6WUUhOkQayUUkpNkAaxUkopNUEaxEoppdQEaRArpZRSE6RBrJRSSk2QBrFS\nSik1QRrESiml1ARpECullFITpEGslFJKTZAGsVJKKTVBGsRKKaXUBGkQK6WUUhOkQayUUkpNkAax\nUkopNUEaxEoppdQEaRArpZRSF0BE6IQJv/OP/6h6lo8LRnVBSiml1GUQx5YwTkkSBwaACtD90I/X\nIFZKKaXOyDmh20uIEotz4HkG4xlE5Mx/lwaxUkop9QFEhChO6cWWJHV4ngcYvI/c5NUgVkoppd4h\nTS1hZIkSBwjGmH4IXwwNYqWUUuoUEaHbS4kSi3WCZwzGwGAT+CJpECullFJ9pwuvjDF45uLD9yQN\nYqWUUpfa2wqvxkWDWCml1KUzqsKr89AgVkopdWmMuvDqPDSIlVJKzbRxFl6dhwaxUkqpmTQovIoT\nhxlT4dV5aBArpZSaGYN+zycLr7wxFl6dhwaxUkqpmRBGCZ3QDm87T3jr94NpECullMq1OLG0wwTn\nsuKrvNEgVkoplUvWOtphQpy6fgFW/kIYNIiVUkrlzGAfuBfbqS3AOgsNYqWUUrnRi1I6vQQRcrsC\nPk2DWCml1NSLU0unm5C+dg54NmgQK6WUmlrOCe1uTJRYPM/L/W3oN9EgVkopNXUG3bDCXorxJt+G\ncpQ0iJVSSk2V1/aBp7wZx0XQIFZKKTUV0jQ7D5ymgvFmax/4XTSIlVJKTZQTodNNiOIU43mXYhV8\nkgaxUkqpiemECWGUYozB5HgfWERotiKebLfP/LEaxEoppcYuji1HYZz788C9KOXZTocnr9p0e+m5\n/g4NYqWUUmOTWke7m5CmLrf7wM4J282Qp6/a7DRDhGzK0/WVGuurNf7wq+0z/X0axEoppUZORGiH\nCb3I4nkml/vA7W7Ck1dtnu20iRMHwEK9yPpqnesrNQqBh4ic+e/VIFZKKTVSYZTQDS0Ypn428Glp\n6nix2+HJdoeDowiAQuBx+9oc62t15mvFj/4cGsRKKaVGIq/jCUWE5lHEk1dtXu52sS5b5V5ZLHNz\nrc7qUhX/Al9QaBArpZS6UKl1HLaj3I0n7MUpz7Y7PH3VptMvvKqUfNbX6qyv1qmURhOZZ/5bNzc3\nPeAfAT8BHPDvbm1tbV30hSmllMqXOLaEcUqKIbWSi77Qbyy8MnD9SpX1tTrLC+WRv5A4T7z/HaC2\ntbX11zY3N/828F8C/+bFXpZSSqk8cE7o9hKixOJctgech33gNxVezdeK3Fyrc32lSiHwx3Yt5wni\nEFjY3Nw0wAIQX+wlKaWUmmYiQhRbenFKkrr+QAbDtPfjSFPHi70OT191aI6o8Oo8zhPEfwCUgfvA\nMvB33/cBKytz5/g0s+MyP/7L/NhBH78+/tl6/El/JnAvthTKHsXK24Or0aiO8creTkTYaYb88OSA\nH5+3SG1WeHVtpcanNxdZX6vj+xf3CmJcx5d+C/iDra2t/3xzc3Md+D83Nze/3NraeuvKeGfn6Byf\nZjasrMxd2sd/mR876OPXxz8bj38wjjBKLNZ92L5vo1Gl2eyO4ereLootT7fbPN1u0wmPC682rtdZ\nXzsuvGq1ehf6eccVxDWg1f91EygA47uZrpRSauQGhVdx4jAma0OZh+Kr/VaPB89a7OwfF15du1Ll\n5pgKr87jPEH894H/fnNz85+RhfBvb21thRd7WUoppcYtr4VXAGGUcv9Rkxe72Up8vlZkfa3OjTEX\nXp3HmYN4a2vrAPjXR3AtSimlxiyvhVcDqXU8eNbiwbMWzgkL9SL3Nho05suTvrQPpg09lFLqEkpT\nSxhZ4sQhZJ2vvLykL9kLiBe7Xe4/atKLLaWCz+ani9xYqU3l7ed30SBWSqlL4o2FVwYM+Qquw3bE\n1w+aNI8iPAOfrs/z6foCwQVWP4+TBrFSSs24vBZenRbFlq3HTZ5udwBYW65y9/Yi1XJhwlf2cTSI\nlVJqBuW58Oo064RHz1v88PSQ1Apz1QL3NpZYXszPPvC7aBArpdSMcE4Io5QktSSp9IM3P4VXp4kI\n2/sh3zxq0u2lFAKPn95pcPNqPZcr+rfRIFZKqRyLY0uUWOLU4pwMC67yuvodOOrGfPOwye5BDwPc\nvjbH558sTP1RpPPQIFZKqRxxTuj193tT6/pNKwYr33yHL2QzjL97csiPL44QshnAdzcazFUn0wd6\nHDSIlVJqysVJtupNEod1brjqNSZv9c5v50T48WWb7348IEkd1XLAvY0GK41K7o4jnZUGsVJKTRkn\nQi/KGmwkqSAiw9Vuns76fqjdg5CvHzZpdxMC3/DF7Qa3r83NxAr/Q2gQK6XUFEhSSxRb4tRhrQyP\nGQ3+O4s6YcL9R01e7Wddkm+u1fnJJ4uUirO3D/wuGsRKKTUBToQ4TokSR5o6nHBi1TubwTuQpo7v\nnx7y6HkLJ9CYL3FvY4mFev73gfvDl5KzfIwGsVJKjUmaWnqxJUkd6YlVL8Yw49kLZMeRnm132Hp8\nQJRYyiWfu7cbXF2u5nrV75zge4ZS0adSDviP/u2/0Hr/Rx3TIFZKqRHJBir0V73WDRtrwOyvek9r\ntiK+frjPYTvG8wyf31zgzo15/Jy2pRzMHS4WPMqlAsWPOFalQayUUhfEiZAkjiS1mGbI3kEv6+Vs\n8t1Y42OEUcrW4wOe72RtKa9fqbJ5u0GllM/4cc4R+B7lckC5GFzISj6fXwmllJow54Q4taSpwzoh\ntYJzrl9gZag6h7lkq96TrHU8fN7ih6ctrBPma0Xu3WmwlKPxhANOBM9AseBTKRUvfLiEBrFSSr2H\ntY44saROsDZ7c3IcugOzeLTorESEH5+3+KOvXhJGlmLB496dJdZX8zee0DnJbj0XCyOt5NYgVkqp\nE7I+zVnjDGuzwfMip/Z0DXhGQ/ekKLY83W7zdLtNJ0zxDNy5kY0nLAT5+VqdLrwaR09rDWKl1KUk\nIiTWDbtVDUMX8E+E7uAsr/p1zgk7zZAn22129sN+u024fWOeW2t1apV8jCccFl4FHuXaxxVenYcG\nsVJq5p0sonKS7eemVjC8vtK9bJXM59XuJsPVb5w4AOZrRW6u1bm+UmV1ZY5mszvhq3y/URRenYcG\nsVJq5sSpJY7tG4uoBnwN3TNJrePFbpenr9o0jyIACoHHrWtz3FytM5+TZhyjLrw6Dw1ipdRM6YQJ\nYS/BnCic0iKq8xERDo4inrxq82K3i3XZLdwri2XWV+usLVdz84LGOaEQeNRLoy28Og8NYqXUTBAR\nDjsxaepeC2F1dlFsebbd5km/8AqgUvJZX62zvlqnUs5HdGTzmaFcDMZWeHUe+fhqKqXUOySppdWJ\nAZO7IzLTwklWePX0VZvtE4VX165UublWZ3mhnJuvrYhMrPDqPDSIlVK5FkYJ7TCd2tXOtBsUXj3b\n7hAlFsgKr9bXaly/UqNYmP4gG3AiVIo+tUohNy8aQINYKZVTIsJRNyaOnVY7n9Gw8Gq7TbOVFV4F\nvsetq3Osr9VzNwXJ9VfAtcp0FF+dlQaxUip3UutodWKck0vdRvIsssKruF941RkWXi0vlLm5lq/C\nqwERwTOGhVoxVyv30zSIlVK50otSOmECRveDP0QUW57ttHn6qkM7zMbklks+G/3Cq2pOCq9OExEq\npSA3TUPeJZ//AkqpS6ndjQljq/vB7/Fa4VUzzFp09guv1tfqXMlR4dVp4hylYkCtWpiZ7wMNYqXU\n1HNOOOxEWCsz8+R70VLr2Dvosd0M2d4Ph4VXc7UCN1frXF/JV+HVaeKEIDDUayWCHFRCn4UGsVJq\nqsWJ5aiTnJjrqyC7NdsJU3aaITsHIfuHPfrbvhQCj0+u1rm5Vme+Vsz1101EMAbqtSLlKWvEcVE0\niJVSU+tNXbIuM2sde60oC99mSLeXDt83Xyuw0qiw0qiwOFeaiTsHzgnVckC1PLk+0OOgQayUmjqD\nLllJ6i59e8puL2G72WOnGbJ32MP1l72Bb1hbrrLaKLOyWKFcmp2nc+ccpYJPfa50KY6mzc6/nFJq\nJiSppdWOwZiZWNWdlXVCs5UF73YzHLaYBKhXC6z2V72NGQwpJ0LgGebnShRmbB/4XTSIlVJTI4xS\n2mFy6QI4jNLh7ebdg97wjK/vmWHwrjYquenxfFaDfeC5SmGmVvYf6vI9YqXU1Mm6ZCXEsZ25Vd6b\nOJdNNdruh+9RNxm+r1YOhnu9Swvl3DXZOCsRoZzDtpQXSYNYKTVR1joOL0GXrCi2w9vNuwchqc1W\nvZ6BlcXyMHxnoUHFhxi0paxXivg5bEt5kTSIlVITE/VvRc9ilywnQvMoYmc/C99sOlSmUvK5vpLd\nbl5eKF+qIBIRPM+wUM13W8qLpEGslJqIWeuSlaRZ/+tWJ+bgKGL/MBo21TAm6+k82OutVWb7OM7b\niECtElApXY5V/4fSIFZKjdUsdMmKYsthJ6bVjofhe/JML0ClHHBzqc5Ko8KVhTJBcHlWvac5J5RL\nPvVLvA/8LhrESqmxyVuXLBEhjFJanYTDdkSrk9Bqx8OV7kAh8FheKLNQLzJfy97Wr89zcBBO6Mqn\nQ9aW0mOhns/xhOOiQayUGotp75LlROiEyXCVe9iOaXUSUute+3Plos9qo8J8vchCrch8PWu9ePqF\nRR5eaIyKiADCfK1IcUbbUl4kDWKl1EhNY5cs64SjzvFt5VY7ptVNhl2rBrKjROVsldtf7Za0wOit\nnBM8j6wSWuT9H6AADWKl1Aglqe1XC0+uS1aSOo468Wt7uu1uwsmYMCbrWrXQv608Xy8yXy1e6n3d\nDyX9wC0GHuVagWLgU68VCbvRhK8sPzSIlVIj0QkTDtvx2G/RWif8+PKIZit6YxGV7xkW5krM1wrD\nW8v1anHmG2dcNOccge9RLgeUi5ezCvyiaBArpS6Uc8JRNybFG/uT88FRxJ9+t5edTea4iGqwyl2o\nFS/t0aGL4ETwDBQLPpWSFmBdFA1ipdSFkH6xUy+yGM8wzu1g64Tvnxzw4GkLAW5dm2Pj+jyV0q8X\nUamzc04oBB61UmFmZwJPkgaxUuqj9aKUTi9BhLG3qTxsx/zpd7scdRMqJZ+ffbbMlcXKWK9hFokT\njAflYkClHOT2zHceaBArpc4tSS3tbkJqs7aF43yudk744ekh3z89RARurtW5e7uhBVYf4U2FV2r0\nNIiVUmfmRGh3Y+LYYjxv7BOTWp2YP/1uj1YnplzMVsErDV0Fn5cWXk2WBrFS6kw6YUIYpRhjxt6c\nw4nw4FmL7348QATWV2vc3ViioKvgMxvMANbCq8nTIFZKfZAotrTDONsHnsCKqd1N+P++2+WwHVMq\n+Hz52RJrS9WxX0feOREKvkdZC6+mhgaxUuqdUuuyfeDUYca8DwzZyu3h8xbfPj7ACVxfqXFvo6Ej\n9M5AC6+mmwaxUuqNRIR2N6EX26wQawINLzphwp9+t0fzKKJY8Pjy02WuLusq+ENo4VV+nCuINzc3\nfxv4u0AB+G+2trb+8YVelVJqosIooRNajGHshViQhcijF0dsPT7AOeHacpWffrqkq+D3EBFEIPAN\npVJApaSFV3lw5iDe3Nz8BfBXtra2/urm5mYN+K0Lvyql1ETEiaUdZsMPJvUE3u1lq+D9VkQh8Pgz\nny9z7UptIteSB4PbzoHvUSr4lLTqOXfOsyL+O8AvNzc3/2dgHvhPLvaSlFLjZq2jHSbEqcMzZiJP\n5CLCjy/b3H/UxDphbanCl58uU9KCotecXPUWAo9S0aegt51z7TxBvALcBP414A7wvwBfXORFKaXG\nY9iWMrYYM7kJSWEv5U+/32PvsEch8Pjy0yWur9R0ZdfnnOAZQxAYiv1VrxZcTakkIvmvf7NU+M1/\n9MHjp84TxLvAN1tbWynw7ebmZm9zc/PK1tbW7ts+YGVl7hyfZnZc5sd/mR87TPfj7/YSWu2YSs2j\nWh/Nk3qj8e7CKhHhhycH/PHX26Sp4/pqnb/086tUy4WRXM+4ve/xv42I4Fw2tKJYyI4a5e3OwDR/\n74+COIe0diDtYKEOjDSIfx/4D4Hf2dzcvA7UgL13fcDOztE5Ps1sWFmZu7SP/zI/dpjexx+nlk43\nIe2vskal0ajSbHbf+v4wSvnV93vsHPQIfMPPP1vmxmqNKEyI+tOT8ux9j/+0wb584JssfIsBWCG2\nlriXr6/HtH7vj0znEC88zO7giHDWlyBnDuKtra3/bXNz8zc2Nzf/EPCAf39ra0ve93FKqclyLmtL\nGSUWz/MmdmtTRHi20+HrB/ukVriyWOZnny1TKV2+05TWOXzPUAx8SkVfq8LzJuritfcwWZebc/81\n5/rO39ra+k/P/RmVUmMlInR7KWEv7Y8nnFwrwyi2/PL7PbabIb5n+PLTJW6u1S/NXrATwZBVOBcC\nj0qpNJHjYeojpTGmvY+XRmC8jwph0IYeSs20SY4nPElEeLHb5asH+ySpY3mhxM8+u0K1PPtPQc45\n/H7wFgOfYsG7NC88Zo44THsfE3UwxstC+ALM/k+BUpdQ1pYyJk1lIm0pT4piy1cP9nm518X3DPfu\nLHHr6uyugq0TPJOtestFX1e9syJs4XUOsu/bCwrgAQ1ipWZMJ0zoRml2HnjCAfBit8NXP+wTp47G\nfImff7ZMrTIbFdEi0m+m4RH4Bt83/f1ejyDwWV6s4JJ00pepPlYc4rX3MS698AAe0CBWakak1nHU\nifsrsskGcJxYfv9fPOPx8xaeZ7i70eD2tbncroJFBCeC3w9dzzMUfEOhEODranc22RTT3sOLe+Bd\n3G3oN9EgVmoGvDYjeIJhJyI83W6z9fiAOHEszhX5+WdXqFfzswrOVrpkK9z+KjcIfIqBp7eYLwMR\nTKeJ6R1l+8BjKG7UIFYqx6x1tPqr4EmvNvdbPb5+0KTVifE9w5+7u8rVRnni1/UuzmUnL30/O7/r\nGUNBC6our95Rtg+cVTeO7dNqECuVU2GU0Omm/WKsyYVGGKXcf9TkxW7WvOLGSo3NW4tcuzp/poYW\nozYoovI9D9/P/lsseAS+hu6lF0eYzh5emvRvQ4/3+0GDWKmcsdZx1E1IrJvorVJrHT88a/HgWQvn\nhIV6kXt3lmjMlSZ2TQPiBPqVy8PQ7RdRKTXkbHYcKe6O7Tb0m2gQK5UjJ1fBk+yM9WK3y/1HTXqx\npVTw2fx0kRtTMKRBJCtUm6sVKeasN7MaIxFM9wATti70PPB5aRArlQPTsgo+bEd8/aBJ8yjCM/Dp\n+jyf3lggCCb7RAYgAtVyMDMDI9SI9Dp43SbGuYkH8IAGsVJTLoxSOmEy0TGFUWzZetzk6XYHgLXl\nKndvL05F6DknlIs+9Wph4ityNcXSODuOlMYX0pbyTUx4ROnbPzjzx2kQKzWlnBOOujFJ4ibWmMM6\n4fHzFt8/PSS1wly1wN2NBlcWKxO5npPECUHgsVAr6N6vejvnMJ2Lb0v5GptQfPjHFB/8IcaevYmL\nBrFSU6gXpbT7q+BJhLCIsN0M+eZhk24vpRB4/PROg5tX6xNvFiIiGANztWLuZvSqMese4nUPR9KW\nEgARgpffUbr/e3i9I1yxSu/u36Lyq39ypr9Gg1ipKeJEOOpMdhV81I355mGT3YMeBrh9bY7Pbi5M\nxYg+EaFSCmamTaYakbjbb0vpRnYUyTvcpvTN7xI0nyHGJ7rzF4nv/MsQFEGDWKl8ylbBKdmL9/GH\ncJxYvntyyI8vjhDgymKZuxsN5qrFsV/Lac45SkWferU08RW5mhBx4BykMdgEnGCw4LI34+zwzwxX\nwKPYB466FL/7AwpPfokBktVPib74DaTW6F+nnPnv1CBWasImvQp2Ijx52ebbHw9IUke1HHB3o8Fq\nozLx4icnQuAZ5udKFHQfeDY5Czbth6vNVrHSD1hxWcA6mx05EnlP32cD3oi+T5yl8PhPKH3/f2HS\nGFtfJrr7C+yVWx/9V2sQKzVBUWxpdxOY0Cp49yDk64dN2t2EwDd8cXuR29fmJ95TebgPXClQLunT\nVN6IyHG42iRbpYrNVrTSD1vX/z0Ow3tWsMaDCX5L+tsPKN//PbxOEymU6N37WyQ3f35hDUD0O1yp\nCZD+KjhO3URWnd1ewjcPm7zaDwFYX62zeWtxKoqfxAnlkk+toseRciWJMFEXkhBJAvyDDvCeblWe\nl/2ZKeW19yh983sEu48QY4hv/Vmiz/4KFC/21IAGsVJjFsWWdhgD4+8RnaaO758e8uh5CyfQmC9x\nb6PBQn3ybSmdCMXAo14v4vvT++Ss+pyDuIOJQkzay1a5/dvCxi+Bl+N4SXqUvv+/KTz+E4w40uVP\niO7+Ajd3ZSSfLsdfKaXyRSQ7FxwlbuwFRyLCs50OW48OiBJLuejzxe0G165UJ77qFBF8zzBXLU5F\nZbZ6hzTC9DrZ6tdGGAa3k0e4NztO4ig8+RXFb/8ALwlx1QXCL36BXb0z0kEQGsRKjUGcWI662Sp4\n3CHcbEV8/XCfw3aM5xk+v7nAnRvz07HqFKhVAiolPY40lcRB1MUkISbuZYVTg8A1MxC8J/h7Tyh9\n87v4R7uIXyDa/OvEt/4c+KOPSQ1ipUYoWwUnRIkdewD3opT7jw94vpO1pbx2pcoXtxtUpqD4yUm/\nLaXuA0+fNMH02pD0Tq16mY1V7ymme0jp/j+l8Oo7AJIbPyXa/GtIqTa2a5j8T6RSM6oXp+wf9mDM\nPaKtdTx83uKHpy2sE+ZrRe7dabA0Xx7bNbyNc0Ih8FioFgmmYUWusnOvUQcTh5g0ylo0zuiq9zVp\nTPHBP6f48I8wzmIXr9O79wvcwtWxX4oGsVIXzInQ7sZYzFgHjIsIL/ey8YRhZCkWPO7dWWJ9dTrG\nExoDCzqecDqkCSZqQxxl4WvMTK96XyNC8PwbSlv/DC/q4Mp1epu/QXptc6w/rydpECt1QUSEbi8l\njNKx94hutWO+frjPfivCGLhzY55P1xcoTMV4Qm1LOXEiEIfZ4IPTq94LOgubB97BC8pf/y7+4UvE\n84k++8vEG38Rgsl+b2oQK3UBerGlE8aIMNbVZ5RYvn18wJNXbQBWlyrcvd2YitBzNjuOVKsWtC3l\nJDgLYTsrtEpjDHLckWrWV72nmF6b0re/T+HZ1wAkV3+StaWszE/4yjIaxEp9hDS1tMOUNM3aU44r\nb5wTHr844rsnB6RWqFey8YQrjWkZT2i4slThwJy97676CM5iui1M3AWXYAZ7vIMjRpeNTSk++hcU\nf/h/MDbBzq9mbSmX1kfz+USQ7E5Y5ywfpkGs1DmICO1uQi+2eN54b0Nn4wn36YQpge9xb2ORT67O\nTUdbSqBezdpSam/oMeq1Mb02JulhLkOh1fuIELz6ntL9f4oXHuKKFaK7vyBZ/+loxiECIhYpzyO1\nBoXf/Ee9s3ysBrFSZ9TtJXR7FmMYa/i1uwnfPGqy08zaUn5ytc5PPlmciiYY4oRKOaBaDiZeGHZp\npAkmPMyqncVlAXPJbjkDWR/ruJvtf0cdvKhD8Pw+wf4TxHjEG/8S0ad/GQoj6h7nHK5UQWpL5z5z\nrEGs1AeKk2xAgxMZa9gkqeO7Jwc8fnGECCwvZOMJ52tTMp6w4FOfK018RX4piEDYwkTd7Izv8Nbz\nDBZc9Su7vaiTPd6ojYm6/d+3+/+vk4Xwmz58ZYPeF38DqS+N5vrE4fwCMrcCxY87GqhBrNR7pNbR\nCRPiNGtNOa4QFhGevGrz7eMD4v54wi9uN1hb0vGEl07cw/SOstUvZHu+ebz1LJI9hriD6R2vYM2J\nt+HvbfLuv8ovIuUattZAynWkWEVKNVy5jqs1cIvXRvcwEFytARdU7KVBrNRbiAidMCGMs65Y46z8\n3Tvs8fXDfY46Cb5n2Ly1yO3r8/jTsA+s4wnHwzlM9zBb8bl0ZIPuL5rpHuJ2v6HYbL4erL3+6lXc\nWz9WAClWcdVFpFTLgrX/39O/n8SRIxGHlOpIvXGhdyH0J0mpNwijhG6YIjDWAO72Uu4/avJyrwvA\njdUam58sTkXoSb8tpY4nHLFeJ1v9phFm8GQ/7beebULw6nsKT39FsPcEB5zckRXPz0J0Ye2twSql\nGlKsTue5ZmdxhTJSXx7JC4DJ/3QrNUXi1NLpJqROstvQY/q8qXU8eNriwfMWzgmLc0XubSyxODdF\n4wkrOp5wZNIEE7aOV4zGm/7wFcFrbVN4+isKz+9j0giAtHGD4mc/p22Og5aglIvV/K8RQTwPt7AK\nxerIPo0GsVJk53KPuvFwH3hcq2AR4flOh63HB/RiS6no88WtRa6vTEdbSs8zLOh4wtEQgd5Rdss2\nT4VXcUjeK9DaAAAgAElEQVTh+X0KT3+Ff7QDgCvViD/5Ocn6l0itQblRxTa7E77QjyPicNVFqC6M\n/HNpEKtLbbAP3IssxhvvPvDBUcTXD5scHEV4Bj5bX+DO+vxUDEMQHU84OnGE6bXyVXglgr/7mMKz\nrwhefo8RixiPZO0zkvUvsVduT+ct5XPI9oFr2XGkMT0mDWJ1afWilE4vydpSjrEIqhenbD0+4Nl2\n1nzn6nI2nrBanvyPo3NCuaTjCS+cc9mZ3yh/hVeFZ19RePoVXu8IAFtbIrn5Jen1u2MdFThy4nBB\nsb8PPN6jgZP/yVdqzNLUctRNsFbG2pbSOuHR8xbfPznEOmGuVuDexhLLC5MfT5i1pfRYqOt4wguV\ny8KrdFh45e/9iAHELxCvf0my/mV2LCgHLyI+mAhiDK5+BcqTeWGhQawujcF4wji2GM8b2ypYRHi1\nH3L/UZNuL6UYeNzdaHBzrT7xVefwOFKtSEnHE56PSDZgwcaQJtlZWZfi3D7+0VF2e3PawxfwDrcp\nPP3lrxVeJetfkl79ycQnFI2COIdU55Hq4kRfXGgQq0uhEyYnxhOO70nxqBPz9cMme4c9jIHb1+f4\n/OaijifMA+fApZDGWdA6hxGb/Vocxg1+LRiRXwtcU5vSozgnxSGFF/cpPHl74dUsErFIsZZ13ZqC\ntqAaxGqmRbGlPYHxhHFi+ee/fMm3j5sArDTK3L29RL06+dAT5ygWferV0uUbTygC4sAm2ep1EK79\ngDXODUMXHAbz7v1c4+VvqJEI/t6P2ZnfV99j3KDw6lOSG19iVzam/wXEeTmHCwpIfXV0vafPQYNY\nzSRrHUfdZCLjCX98ecR3Tw5JUketEnD39hKrS5MfT+icEPiGubkSway2pRQHSQRJdLx6dba/enXZ\nSjZr0/LusPG87M/MEBO2KDz9isKzr/DCFtAvvFr/kvTGjBVenTbYB55bgvLcpK/m12gQq5kTRgmd\nbpoF8BiroXcOQr550KQdJgS+4c/fW2V1oTzxYQjDfeDqjLWldA6SEJIYY+OsN7FNs1Xsm0I2D8eE\nLtqw8Oor/L3Hs1949QYiDinPZbfZp/SxztBPpbrsBqvgxLqxhl8nzMYTbu9n4wlvrmXjCa+uzdGc\ncFMD6R9Hyn1bSmchDjFpDDbGpAm4NGuCcfJxTcF+38SJ4B3t9DtefYNJBoVX108UXk1+ctfIOYcr\nVrJ94HOOJxyX6b46pT7QoDc0Y+yKlaSOH54e8vB5CxFYmi9xb2OJ+frkn+SccxQLPnN5HE+YJjAY\ndJBmoXvc9vFk6OrT15BN8PeeEOw8JNh5hBceAuCKVeKNv5AVXo1qHOA06O/9S3/bQYICMncFipPf\nEvoQ+p2scs05odWJs1XwGNtSPt3usPW4SZw4KiWfL243uLpcnfiqcxDAtVoO9oFFsorkpJfdVk6z\n28tZBfKJa7+Mt5Q/gOkc9IP3If7+k2wfHJCgRHL1JyTX72JXbuf7LoE4cA4xJvs+8HzEeNlj8nww\nPuL1f+8XspVvDh+vBrHKrV6U0g4TzBhXwfutHl8/aNLqxPie4SefLLBxfX7iwxCcCKXAo1ovT2dD\njpNFVDbB9M/cGjgVujmsQh4Xm+I3nw3D1+s0j981d4V0ZQO7soFdvDb9YTR40QDZfv6pgBV88EwW\nrkExN2exz0uDWOWOE+GoE5MkbmzFWGGUjSd8sZvt+V5fqbF5a5HKhIufxAml/mjCqboFHXdxrRhz\n2Hx7EdW0h8UUMGGLYOcR/s5Dgr0fs68lWcFVsvopdmWDdGUDqUxRJbAIYi1iQIw/DFqMn/3e97OV\nq1/ITavPUdMgVrlychU8jhC21vHgWYsfnmXjCRfq2XjCxvxkzyCKnAjgaXgicxZ67WyQQRphBExQ\nx0vj7P0auh/GWfzm8yx4dx7it/eG77K1pWHw2sb16StAGhwRqixgrt3AFdqTvqLcmLJ/SaXebNyr\nYBHhxW6X+4+a2XjCgs/mp4vcmOB4QhEBoFyckiroOMyGGCRRVsnsnRjjNwWvDfLC9Nr4O4+yW857\nj7PKcEA8n7QfvOnK7awN4zQSh3g+rjIPlXkwZvLfmzmjQaymXrYKTrOanTGE8GE7G0/YbGXjCe/c\nmOez9QWCCbWlFBEMUCkFVMvB5J7knMvm5yY9TBJllcyD8NUV74cTh3fw8rjQqrU9fJerLJDcuJet\nepfWs9u308o5xPdxtelskpEnGsRqao17FRzFlq3HBzzdzm6prS1V+OJ2Y2K9mAeNOCYawEmEiToQ\n97KmGYM9Pa1kPhMTdfF3+6ve3ceYpAeAGI90+ZPhyneam04MOYvzC8hcA8r1SV/NTNAgVlMpii3t\nbgJjWAU7Jzx6kY0nTK1Qrxa4t9HgyuJkziAOArhWCaiUxvwiwDmIO5goxKS9rPeyrnrPTgTZe07x\nh2+yCufDl8O79a5cJ7n6s2y/d/mT/DTXEJcFcH0ZStVJX81MOXcQb25urgJ/DPwrW1tb317cJanL\nTPqr4Cgd/blgEWG7GfLNw2w8YSHw+OmdBjev1idSAOVE8I2hVhlzK8o0wvQ62ep3MDfXGMBo+L6J\nTTBRBxN18aL28NcmauNFXUzUwQtb2KRHCRBjsEvrw0IrV1+e/lXvSc7hCsVsjzonDTLy5lw/7Zub\nmwXgHwCdi70cdZkNJiXB6M8Ft7sJXz/cZ/eghwFuXZvj85sLFAvjDx5xgu8bapUi5XHMBBYHURcT\nZ4VWxlld9YpA0sOLOv1gzd7e+PtBJfjb/io/QEp1vJubdOdvkl75BArlMT2QiyNikaCc3YIuTs+k\noll03pfdfx/4b4HfvsBrUZeUiHDUTYgSO/IATlLLdz8e8vjFEQJcWSxzd6PBXHX8twcH05BqtSLF\nUQdwmmB67X4XqwjDifObsxy+zvZXq+8I1sGbuHf/VYUKrjKfzbEt13Cl2vDXUqziyvVsgpFfAGNo\nNKqkE+41fh4iDilUkNoCBBrA43DmIN7c3Px3gJ2tra1/srm5+dvoQQX1EeLEctQd/SrYifDkZZtv\nfzwgSR3VcsDdjQarjcrYi6CcCAXfY65aGN0KXASiTv9cbw9jT6x6Z7XIyjn8g+fHzS+6h3j9oqi3\nEeMjpSpufjUL1hNvr/++OtsvWABxNnus1UUIprhaewaZwdnED7W5ufl7ZJ3JBPizwBbw97a2tl69\n5UPO9gnUpSAiHLYjer105MVYL3c7/PFXrzg4iggCj599foXN242xt6V0ViiVfOrV4sgC2MU96LQg\nynaNZv08p4Rt5MUPyPMfkBcPYBC8ng/1RUy5DpXs7fjXNUx5Lvt1sTzzX6P3Eeeyr0W9gacBfFHO\n9E115iA+aXNz83eBf+89xVqys3N07s+Rdysrc1zWx/+2xx4nlqNOjDDaoOj2Er552ORVfzzh+mqN\nzVsNSuPYhwUajSrNZvd4EEM5GM0gBucw4WFW6WzjqVm5DR7/hRKHd/jq+Azu4fHrf1eZHx4Dsks3\nJ76qG8njv0DiHFKuI7XFC/+euczPewArK3NnemLT40tqbESEdpjQi7O94FFFcJo6fnh2yMNnLZxA\nY67EvTsNFurj3e9yTigEhmp5RIMYoi4mbGW3ns0MF1vFIcHuo37P5Ud4SfbCKjuDe3M47MDVlvJV\njTwJIgiClOeyW9De7A5SyJOPCuKtra2/eVEXombbYC9YhJHtBYsIz3Y6bD06IEos5WI2nvDalfGO\nJ3TOUSr4rC3X2LvonZk0ycI36WKs60+lmbHwFcFrbR9PGTp4iel/HV2pRrz+5fEZ3IIWE30QkWwI\nQ3kuK8Ka4UlGeaQrYjVSIkInTOhFKcbzRrZgaR5FfP1gn8N2jOcZPru5wKc3xjue0IkQeIa5uRLF\nwL+4aUgiWWvJXhdjT65+Z+jJNIkIdh9nt5t3H+H197jFGGzj+vEZ3Lkruuo9CxHEM7jyAlTn9Ws3\npTSI1cjEiWW/1UMEzIhCoxel3H98wPOd7In72pUqX9xujHU84aAT1txFN+KII0yvlZ33ZcbaSorg\ntfey4N15iN98hunXq7hiddhvOb1yK5dncCduMIihupD1gdYAnmoaxOpCiQhRbOnFlpSsO9MongOs\ndTx8fsQPTw+xTpivFbm30WBpYbxP2k6EykVOQ3Iuu/UcdY8Lr2blNmIaE+z9OBzx5/Wynt4CuIWr\nWfCubuDm1zQ4zkIEnEV8H/GLEBSQoKR9oHNEg1hdiDS1hJElThyCYIwZyaB6EeHlXjaeMIwsxYLH\nvTsN1lfr490HFqEYeNQrxYu5/R11Mb0jTBLOTOGViGDa+8cVzvvPMGKz9xXKJNc2s0KrK7ezc7rq\n/cRl4zAHoesXkaCQtZ7M+ffLZaZBrM5NROj2UuLEkjrJirAMjKoeutWJ+frBPvutCNMfT/jp+gKF\nMY4nFBE8z7BwEWeB0yQL37gzO4VXNsHff0qw8xC794h6++D4XfOrw+NFbvHq7Kz0R8VlL2rxAyQo\nIl4RCsXsVv0s1QcoDWJ1dnFsCeOUOHH9iXij7YoVJZZvHx/w5FV2K3O1UeHuxvjHE4pcwEQkEYja\nmLAzM4VXpntwvOrde5L1rgYolEiufn686tVbpW/nLILp31Yu9EO3lL3pbfqZp0GsPohzQreX9YN2\nDjxvNLeeT3/Oxy+O+O7JQTaesFLg7kaDlcZ4J8A4J5SLPvXqR+wDJ1G29zsLhVc2xW8+Oy606jSP\n31VfHp7rnbvzGUeH0QQvdEo5ixiDBEXwC4hfyFa5QVFD95LSIFbv1ItSerElSV0/eM1YFm/ZeMJ9\nOmFK4Hvc21jkk6tzIw//k8QJQeCxUC+eqyGHOAedw2ywQM4Lr0x4NAzeYO9HjE0AEL9Asvrp8HiR\nVOaOP+aS71nKoIjK846LqLwASjXw9alXHdPvBvVrxlV49SbtbsI3j5rsNLPuSZ9crfOTTxbHOp5w\neBypVjx7O0xnIWxjkhBJffzeid7HeeIs/sGLYYWzf7Q7fJetNYbBaxs3LmeoiMv2cE3/7obnIcbP\n/p09P7vbMb+INY38/dursbuEP0HqTUSEMEqJYktqHZ7njbTw6rQkdXz/5IBHL44QgeWFEnc3lpiv\njXc8oYhQKQUfvv8sAnHYn+3bA5cO932Nl68G+qbXxt99lO337v6ISbPbyuL5pCu3++d6N7LexLPK\nWchefmZ3LzwP8Y4DVvDBM9mow6DYL7B7810OrzoHncvbb1l9OA3iSy5OLGF0qvBqjIVDIsKTV22+\nfXxAnDoqpWw84drSeMcTinMUiz71aun9hWfOQq/dHzEYYYTjYqs87fuKwzt4eVxo1doevstVFkiu\nf5GtepdvZsGTV4NbxAYgC1c8H+n/F+Nnq1nfz1b3fiELV92vVWOiQXwJnSy8EgdmDIVXb7J32OPr\nh/scdRJ8z7B5a5Hb1+fxx3gtzgmBb6jPlSi8azJSHGbD45MYbITx+j86xsvVRG4Th/g7g1Xvo2wV\nz2CAwifD40VSa+QziJxFPB8plLP9WM87DtfB75WaMhrEl0gvtvSi9LXCq0nUDnV7KfcfNXm5l42I\nu7FaY/OTxYttD/kew33g6lvaUg5WvUkPk0QYccd7fV6OfmxE8FqvCLaz4PUOXgxfN7hyneTqz44H\nKATj3Qa4ECKIOKRQQoIylKsQ6CAIlS85ekZR5zHJwqtfuxbrePC0xYPnLZwTFueK3NtYYnFuvE+c\n4oRy6Q1tKeMeJupkR41sjBncnszbUaOkdzxAYecRXpy94MkGKNzIgnd1A1fP6QCFQSVyoYIUSlCq\n60pX5ZoG8QyadOHVm67n+U6HrccH9GJLqejzxa1Frq/UJtKWcm6ulL0YcS6bahT3spm+7uSqN0fB\nK4J3tHt8rvfg+akBCj/tF1p9ks8BCoNVr581uZBSDYo5fBxKvYUG8QyJU0vYm1zh1ZscHEV8/bDJ\nwVGEZ+Cz9QXurM+f61zueYkIvmeYqxYpkmK6zWzVm0bHq15MvsL35ACF7Yd40YkBCovXjltJzq/m\nd9VrPKRYRgplXfWqmaZBnHPTUnh1WreXcP9PnvPg6SEAV5erfHF7kWp5fNW3IoJBqJmEKhGmFWXt\nF/O66u3s98/1PsLff5rtWwOuUD6ucL5yGymOt/PYRRGXQlDK9nuLuupVl4cGcU5NS+HVSdY6Xu6H\nPH3VZu8wq8adqxW4t7HE8jjHE4rD9TpUJaYexHjGP14V5il8bYK/92R4vMgLW8fvml8bnu3N7QAF\n5xADEpSyFw/ler7+fZS6IBrEOXJceGURmGjh1UmH7Ygnr9o83+mS2myV1pgv8cWdZRarwdj2gSUO\nMVFIRSJqZa9/Wz5f3+Kmc2KAwv7xAAUJSiRXf9Lv43w72yfNIRELXiFb9ZaqUNTxh0rl61nqEnpz\n4dWkyq6OxYnl+U6Hp9ttWp2s73Cp4PPJ1XnW1+rUKwUajSrNZne0F+IshEeYNKLqWWrlAGNy9G19\nYoBCsPMQ7+QAhbkrwwEKdvFaPleL4hAMUigiQQUquupV6rQcPWNdLoPCqyTNVpjTUHglIuwe9nj6\nqs2rvS5Osju+a0sV1tfqrDQqIx2HeOJCIA4h6mJsRLUYUK0ajMlH9ycTtoZHi4K9xxibAv0BCmv9\nAQpXXh+gkCvOIn6AlKrYuUo2tD6PBWNKnYEToecSYpvyX/yz/7X8D/76v9X70I/VIJ4ibyq8Gufx\nnrfp9lKebrd5ut2mF2W3SmuVgJtrdW6s1M8+GOG8bIzpdSEJMQjVgk/l9FngaeQsfvP58fGi9t7w\nXba2dGKAwvV8DlAQQUT6e73lbLpQUMBbnINEey2r2WPFEdqExFlSZ0lwpK5/WiX7IzVAgzhPprLw\nygmv9ro8OVF45XuG9dU6N9fqLM4VxxOA4rKVbxSCTfB8j0rJo1qY7uIk02vjfviW8qOtbNWbxgCI\nFwyPFqUrG0h1YcJXek7OIr7fb6rRD99pf0Gk1Dmk4gjTmEQsqXMkWKxzeOb1hdKgNa/0z/CfhQbx\nhKTWZWd+U4vINBVexTzdbvN8pzO8Ld6YL7G+Wufaler4zv8mPUzUxSQRQvZNXq34lKc1gN8wQMEB\nBfoDFG7cy1a9S+v5HKAgguCyVW9QgXK26lVqlsQ2pef6K11xJGJxIm8I3Yt9HtIgHqM3Fl5hJr6Q\nSFLLs50OT191aHWylVup4HPnxjzrq3Xq1TE94TqL6fd3xlkED98zVIpmKgPYRN0TYwMfnxig4JMu\n36J46ye06utIdTGfq8XBAIViGSlUoFTN5zEppU4RESJniU6FLgjeie9xYwz+GH52NYjHIE4tvV5K\nPGWFV3uHPZ6cLLyiX3i12i+8GtcKPeoORwri+TgnFHyPWtFQDKboiV8E7/DVcYXz4csTAxTmjo8X\nLd+EoEi5UUVGXTV+kQatJINS1kqyXNMBCir3ThZRpWJJxJFKVutyMnS9QYe9CdAgHqFOmCB7XVpH\n8dQUXoUnCq/Ck4VXq3VurI6z8CrB9DqYtJdVQRsPwaNgoFo1FMfYAvOdXhug8BAvDoFsbKBdWh8e\nL3L15RyvevsDFAaFVrrqzR0nghNHLNn+pTvHPuVFCno+h/2flUlwyIkiKpstfk78fHpT9j2uQTwC\nToTDdoy1jkpNMBPe+x0UXj3dbrN7cLLwqtYvvCqNt/Aq7mFs3D9PanBAyYNa2Yy1B/Wbr1Hwjnay\nNpI7D/GbzzH0ByiUasTrP+2PDbwFhRyuFoer3iIUKllTjTw+jkvCisM6R4LDumy/0ko2Sc2KYMkC\neBC8pwNnUgpJQNtFk74M4OL3c0dBg/iCJant77NOfgXc6sT9jlcnCq/mSqyvjbvwKsLEHUwcHY8V\n9HzECaXAUC1OOICT6HiAws5DvKgDgGBODVBYye+qVwcoTA3pB2jqHIlLcf0wtf1jYBbBkoXrYG62\necfzybj2MdXoaBBfoDBKaHfTiVU/W+vYb0VsN0N2miHdXtYooljwxl94ZVNc+xDvcD/rfuX5wyd/\n54RyYKiVDf4kAlgEr70/DF6/+ezEAIUKyfW7/bGBt7JmFDkkzoJf7LeSHP8AhdDGHEZdWsnkbk9O\nmt/z2Y87/ZAFRz9scccnJd4RoN7gRauaeRrEF0BEaHViksSNPYS7vYSdZo/tZsjeYQ/nsltUgW+4\nulzlxkptPIVXItmRo6SXnZl1FmP6/ZD7LQ3FCeWCoVb1xn/7LE3w998yQGFh7XjVu7CWzz3SwQCF\nQn/VO4EBClYcR0mPjo1xOCQ2HNnpuD05CUESELrktf+XrW69SdUEqSmlQfyRUus4bEf9eqPR/3Q5\nJ+y3euw0e+w0Q9rh8Q96vVJgpVFhtVGhMV8affhaC/1qZ5PG2ZPLIMQG4dvfu6oEhuqYA9h0micG\nKDx9fYDCtc3+2MBb+R6g4Bey8C1WJjJAQUTopBFdGxM5m33PGfDI4YsZpSZEg/gj9KKUdjfpV0SP\n7vOEUcpO/3bz7kEP21/1+p5htVEZhm+lPOJ/TpFsvzfpZUeNXpvt6536o8cBXCuNab/cpvj7T49X\nvd2D43fNrZCubmCvDAYo5DAoxCFMdtU7ENmUjo3o2gSQqWlIo1QeaRCfU6sbE8V2JE8+ToSDE3u9\nR93jVW+1HAzDd2mhPGyrNjKuv+pNoqzSWTgOsTeEgBOhYKBUMqzO+xy40Qae6R4S7GRNNfy9HzFu\nMEChSLL2Wb+P822knPMBCsVK1lRjggMUnAjtNLv1nLpsGya7FA1gpT6GBvEZWes47MQ4Jxd6mzWK\nLTsHITv7ITsHveFcX8/AlcXyMHxrlTEUWyW97IhRGoNLwOt/m5g3720NKjtLvqFaOC7AGskq2Nnh\n2EB/59HrAxTqy9iV2/0BCjfyOW5PskIeKZaQ4HiAwiSFNqaTxoQuyXYfdPWr1IXSID6DOLG0Okn/\nBM7HPRGJCAftuB+8IYftePi+Ssnn+krW3Wp5oTz6oz3vXPW+/VvEOaHoZy0oSyNsQWl6R8NzvcHu\nY4zN7hBkAxTu9Autbud8gEJwfMt5CgYonC688swECuyUmjKDqUsdG9MdvKUnfm3j/nbN2WgQf6BO\nmNDtJR/VmjJOLDsHveF+7/GsYVheKA/3emuVYPR7qkn0+qrXeP2SzndXdDon+B6UA0OlOKInZ+fw\nD14cHy862jl+V3WRZDA2cGk9x2MDXRa8QXlqBiiICF0b00kjLbxSl4aIkIilY+Ph3Z+uTU6Fa/bW\n6299vY2HoeoXz3wNOXwWG6+TXbLOGsIiwv5hyA9PDthu9jg4Oj7KUSr63Fw7XvUWRt1T2TmIu1mF\ns02y3w8Lrd59C3dQeFXyDdURdb96bYDCzqOsGAwQzye9cut4bGCtceGfeyyGYwOnb4CCFl6pWTTo\nMd1N4+OQPbFqPRmwab+PwNsUvYCqX2CpWKPmF6m+9lag6hep+UWK/TuI/93j3z/TtWoQv8N5u2SJ\nCM+2O2z9eEAU2+H/b8yXhqveueoYBtqnMSYOIYkxJ1e9mA/aPz1ZeFUJLrjyWdxrAxT8w1fHn7cy\nT3L9i/6q9+ZUrBbPTARxDucH/VVvdaoGKGjhVX4Nbo+eXq1lYZPQSWN6LmGS3ab952Z4umMSsq9R\n/M6vgQEqfpHFQvW1MK2+IWiDM9Sb6DziCxRGCe0wPfOt116U8qsf9tluhvieYWN9gYVqgZVGmUIw\nhuIh5zBRGxP3wKUfvOodeFvh1YWIwxMDFB7hJccDFNLlm6RXBgMUlia+R3ougwEKQRkpVTBXryK7\nnUlf1Wu08Gp6xS799Vui6etB27UJPffuPUgPQ9kv4E3wRZWT8wXSRQmMx1pp/tdWrJVB2AZFyl5h\nauoeNIhPERGOOjHxGbtkiQjPdzp8/bBJkjqWF8r8/LNlrl+bpzmOUXhRiIm7WbGVefvxorcZReGV\niOAdbh+f6z14cWqAwpf9AQqf5HPwwHsGKJgpufWshVeTIyKELjkRqskbVrHZf997e9T4VP0iS4Uq\n1eA4YE6+1fwiJW8MNSbv0Vis0TyYrheh00yD+ITUOlrtGCdnm5gUxZZf/bDHq/1sFfzTO0t8crU+\n+h8Gmx6PEnQuq3Q+w5P/SAqvkohg7zH+zkPs3mNqYRvIBijYxvX+ud4N3NyV/K56czBAYVKFV6mz\nPOju8W1nm+SVHR7Du2xEhOhZSieJPuD2aIHFQuUNt0SzsB2s5Ap5PI6nPogGcd/rXbI+PCBe7Hb4\n1Q/7JKljab7Ezz9fploe4Z6mSFZ0FYUYF4N5c2ert3/4ceFVpWwofOytZxG89t5wXq/ffD4coECp\nSnLj3vEAhcJ4Bw9ciP6qd5IDFN7HiRC7lMimJL82+Hz0t55FhJ24zTftl3zf3iHuf+6i5zPhsbgT\nVSsU+7dHf33lOli9lv3puT2qJkeDGDjqxvTO2CUrTixf/bDPi70unme4t9Hg1rW50a2CkzgbJZj0\nK6+NOQ7hD+CcUPAuqPAqjfH3TgxQ6B0B2fFjt3A1C97VDeZvbXB0kMPpO4MBCkEp6+E8wVaSpw0q\nQWObkoollrcNPh/9k3toE77rbPPN0Uv2k2z7peYX+bJ+nS/qa9xeuXKpb0/q7Vn1oS51EJ+3S9bL\nvS6/+mGPOHE05kr87PNl6qPoeHWy8ErsiarnD3Oy8KryMceORDDdA4LtEwMU+qseKZwcoHA72yft\nm/Q+1VmISyEoTnSAwmlWHGEak4gldY6Y7L++ef2F1DgHnzsRnoZN7rdf8bC7h0PwMNypLvNF/So3\nKw1d4Sl1Rpc2iM/TJStOLF8/bPJ8p4Nn4Ivbi2xcn7/4wIl7mKiDsdHxqvc9e78iggj4XjYMwveg\n6BtK5z2fbJNTAxQOj981v3pibODVqdwjfa8pGBt4UmJTeq5/a9k5ErE4cXinQjeY0Ne6lYTcb7/i\nfvsVHZt1gWsUqtytX+Un9RUq52hioJTKXMog7oQJYS/BnOFJbXu/yy+/3ydKLAv1In/m8yvUqxe4\nClyXxE0AABsBSURBVH5j4dWbg2EQuoEHvm/wDRR8Q9H/uFvO2QCFhycGKAzGBhZJ1j7PVr0rt5Fy\n/dyfY6JOtpIsVicyQEFEiJ0lcgmJy/ZzE7H9F1HH12IM+BOuuh4UXt1vv+RZL3shVjA+9+pX+WLu\nKqvFMRQkKnUJXKogFhEOOzFp6j44hJPU8c3DfZ5udzAGfnJrkTs35i/m9tsHFF45l91eDjzwPENg\nspVu8JGhC2RjA5vPjwutOvvH76ov94N3A9u4PjV7pGcyGKBQKGXdrMq1sbbEdCL0bPKGIirBOxGy\nXjYtfiqICLtxm2/ar/iuvT0svLpWmufu3FXuVK9o9a5SF+zSBPF5umTtNEN++f0evdgyXyvy88+X\nma9dwC24YeFVDzBgDIKHOME7HbrBxbaUNOFRFry7Dwl2fzweoOAHJKufHo8NrMxf2OccB+ccsY2J\ngcQPSDyPJAhww1Wvg/horNd0dBRxEHffUEQ1Jal7Qs8mfNvZ5v7RK/aSrMCo6hf5ab/warFQmfAV\nKjW7LkUQn7VLVpo6vnnU5MmrNsbA5zcX+HR94eOOgZwovBKbIMbH87Lbyr6f3ZYs+ebi5ws7e2qA\nwu7xu2qN4wEKjRu5GaBgbUosKTGG1POJfY848JHaEt6p1dokI8+f8sYZToRnvQO+OXr5WuHVRnWZ\nu1p4pdTY5OOZ95xEhKNuTBx/eJes3YNsFRxGlrlqgZ9/foWF+vlXwWnYRQ538SUm8D38wFAoBhQD\nM7InORN18Hf6AxR2H58aoHD7eGxgDgYopC4ltgmJ8Ui8gNg3xKUS/3979x4jWX4ddPx7f/dVz+6u\nfsz7seud2TvrXTs2AQUCJI5ILBwpAkVIoCQggnjJQTIKUiBGshAKElKEgRBAJhASJIQlR+ZhoUSW\nwAngPwJCJGbH8Z1dm92e9/Rrpruqbt3X78cf93Z1dU/PTHdvV92a7vORRtPTr/nVTNc9dX73/M6x\n3NldtxemM8+cTpvpgG91HxJ2H9LNi5+NjlvnRuscr7fOHGl6jBDi6E5kINba0B+kxGlRBHOQLllZ\nrgnfe8z7D7awgNcuzXD98tzRsuA8g6hL3YrpuHX8hkZZ42zyoVGPHwwnF9mbzxigsHAZ7OkdoJCm\nCTE5mbJJlUOsLHK/Ds7uoPsS1mhXLtOa/9df5fe6D7k7eAwUhVdvtM5xo3WWs/4Yz8CLY5cbDYbh\n+XGFwsZCWRQtTCt+Wdp2ayTq8HN5T5DNw3zyiQrEgzhjkOSk2XYGbB2oKHb9yYBvvLtGf5DRqrt8\n9PoCc+1D9j4uC6/MIMK3EmbqDspS1Gs2g+j4nxRWEhVZ7+r2AIVBsQxLkS1cGVY46+b0DVAwWpPo\nlMQYUmWTKpvEttDNFtaentPTtfKXz0rc5VvdB9zqrZCUs1TP+zPcaJ3jtaYUXk0TYwwGgzHWsGpe\nYWGXdS22ZaGsIuA6ysFVxcen8QXUfK1B7uUv/sQT6gt/9McO9SrkpQ/EWZYTxTlJmmM43DSZPNeE\ny495715RxPOhizNcvzJ3uPu0ZeGVSSIcy6JVV3jjyDqNQW3uHaBQ0H6L5PJHdgYoONOztVgUUcWk\nKBLbLouobHKvgdrz7zR9l5OXgy77So/OW+1lCe9Fa6wmI4VXs5fKwqvqm5WcJtqYYXMdi6JuwKYI\nrBbWsJbAUTauZU99bYE4focOxEEQuMAvA1cBH/i5MAy/ctwLex5jDFGcESdFU3mlio5Th/nR3diK\n+catVXqDjEbN4buuL9KZOWAWPCy8ijF5AsqmVbNpHNPUoqF0gLO6PDxepJKijaCxLPLOxakcoKC1\nJs4TImUT6ZxHjo1pLjxVRCXbyy+W6vypCT3DoeYj4/GiZ4zF2y68utE6y5X6/EQu7kVWBzXl0HQ8\nBioZ+985jSxgxq2R2zmusnGUPcxshdjrKBnxjwMrYRj+2SAIOsDvABMJxEmaE8UZSaqHHbHUITsN\n5drwzvJjvnO32MJ/5Xyb4OrcwebuJoPi2FFWdLwy2lDzHFr+MT3BjEF1V3Eeled6H9/DKrvma69B\nevFNsqVXpm6AQpYl9E1ObDsMHA/dXMBSCrfVxMp6kumOMGWv6O1xeL09M2eHgTYvWls+j1uOxZtz\n68MZq6NDBRa85sQKr4wx2Cgajk/bqaEsi8V6C+Od3qkPnVqDzD2927Pi4I4SiL8E/Fr5tgKy41vO\n03YVXumi8Oqox4geb8V84501ulFKo+bw0WsLzM++IKDlWdFuMt3peKWNwlXQbhzDGd8swVlbHh4v\nUoPtsYGg587vtJKcOTM1Wa/RmoFOGFgWke2Q1ttY3s4Fv8pVGmN4nEWsxt1hK8aq2JFirdfbFWSj\nPEU/dzAe1JXLrFvbGWJu7z97tur7u9uTvOrKpeXW8F+S429CTBtr+8l0WEEQtIH/CPyLMAy/+JxP\nPdJfEA1S+oOMOM0/8NnaXBvefmeFm++uYQy8frXDx984g/OMPszGGEzch0EfsmQ44F0bg23BTMOm\ndsRtaGMMbK5h7r1b/FpZLgI8gFfHOv8hrAvXsM6/hlWbnnt5WZbR0xkDZTOwHUythbKrDQS51qwM\nujyMNnkQbfIw2uRhtEWqpy8LcSxFy/VpOj4t13/q7Zbj0XJ9Go63q+vWNMpNTs0ugm/b9WW7VYin\nHepJcaRAHATBZeDLwD8Nw/BXXvDpZmXlYB2NdgqvNAZzLE/wzW7C776zylY/pe7bfOTaAotzz+gS\nlCU72W/Z8Wr4IIyh4Vo0/cNdJDudBhurT0bGBr6Hip4xQGHu3AuHO0yKKe/1DpRFpBwSr4Z1yDm8\nxzkGLtU5q0mX1aRX/t5lPenvyi4tikEEi16LRa/FjFurNDvvtJvoSNNwPDzLfqkDljEGC4uG7dF2\nfJwDZONLS20O+tw/iU7z4z/Njx1gaal9qCf7UYq1zgJfBT4dhuHXDvv1e+1feAWHK716Wppp3ru3\nybt3nmAMXD7b4sYrHdy9WbDWEPdRSQQ6LXoqjwRDYww1x6Llq0NdSK24j/MgJP+dZVoP39s9QOHc\n9Z2xgVM0QCHXGVGeMbBtIsdBN+exygvuJEPIIE9ZTbqsbAfeuMvjbPdcY9tSZcBtsui3WPJazLuN\nAwWISem0m2zkL/c8Wm1MUXjl+tLoQ4gxOcpNnc8Cs8DngiD4XPm+T4VhODjMN0mynGiQkWbFtuxR\nCq/2Msaw/iTm9qMuD9b6aG2oeUUWvNTZkwXvKbwCdg02MNrg2tA6zBxfrbFX38O98zbOo+9gmSJf\n0+3FnQEKc+enaoDCII0ZKIiUQ+z5KH+n29a4g68xhl6elAG3y2pcZLvb3Z62eZbNBX+WRb8MvF6L\njtuQIx5joo3BQdFwvGHhlRBifA4diMMw/AzwmaP8ZfsVXh3Hdl0UZ9x91OXOox79QVE71qg5XD7b\n4sq59k4WnOfFsaP02aMGjTEoigB80PvAVm8D985N3Ls3UXGRAeXtJdJLb9J8/SNsJdPTzSrXOVGe\nENsOkW2TtztY5b3ecW6KG2N4kkWsJj1W4u5we3mgd9f6NWyXK/XOcHt5yWvSdmov9bbuy0AKr4So\nzkSebYMkZxBnuztefcCrvtaGh+t97jzqsrJRJOO2srh4psnlMy06M2URiTEQ97DiAVYe72Sj+2Tf\n2/eBG94BXiBkKc6DW0X2u3G3+HrHJ7nyXaSX3hpWOVvNBpTnf6sSZwkDNJHtELs1rPbc8GPjCm+b\nacTy2gbLj9dZSbqsJb2njuO0nRoXarPDoLvotWhOUTOS0yDXGr8889t0pPBKiCqMPRA/WO3R7SeH\n6nj1PFu9hNsPu9xb6ZGU29pzLY9LZ1ucX2zi2hbkKTrapJ/2IYmLmqvtC8w+zQ+0Bs+Bhg+pBU+e\ndSDLGLzNRzTvfYvGg3dR5QjBQecivYs3iJZexWxnEuU9TT2AzXSCgdgY0BqjHLRjM7Bd0tbMsIvV\nuC6zudHcHzzh/WiD5f76rnu6FjDnNlgaBtxie1myrmrsKrzyDlZ4JYQYn7FfCbcbb3wQaaa5v9rj\n9sMuT7rF2VDPVbx6vsWlBZ+2Z7B0htVfJUtTBjohNmX2vf1X71McbgwoCxo1cOzyQPQ+n6eSiPaD\nd5i5dwuvtwFA5jd5fPktti68TjY6u3dP1pfobDhc/dhpDRiMcjC2i3ZctO2i3dqujH9cW87dLGY5\nWuf9/gZ3Bhtkpnhh5FiKV+rzvD5/lpb2mHeblZ95FaCNpqZcKbwSYspMbUpijGF9M+bOwy73y8Ir\ngKUZl8sdm3NNgyIGnUCiyHTOQKfEeYZSzx8xaEzxAqHugf+s27da01i/Q/teSHP1fSxjMJaie+ZV\nNi8ERPMXJ3vUqDxrbGwHbReBN3d9jONPrNFHbjQP4y2W++u8H62zPpLpzzp1rjY6XKnPc6E2i22p\nYz2+JI5GCq+EmH5TF4gHccadh1vcedSlHxfBp+FZXJlTXJxzqHvbwc8CbDKdE2UDku0A/ILtb63B\n96Du7h+/nP4TZu7fon3/Fk5cBJq4Nc/W+YCtc9fQhzxLexRWnmMsayTLddBuDWM/Y9Fj1MsSbkfr\nvB9tcCfaGGb3tqW4Ui8C75V6h1n3GWezJ8wYUzReUQq74o7WtmVVugbXUlJ4JcRLoPpnaJ6h44hH\njwfcXo1Z2Sou9MqCS3OKyx2H+cbTxVOJzhjkKanODxSAjQbHgVYN9p5GsvKM5qPvMHPvFvXH94tl\nOR5PLr7B1oWAeFxDFYwpjjhZapjlasdFOzWMU02ltTaGR/FWseUcbbCadIcfazs+r9fPDLPeqreb\ni6k24CiFiyqn1yjqjoc9BY1Rllpt/Kj6p5gQYrpN7iphDORp0b0qzyBP6fYTljc0d59okvI26lzd\n4nLH5sKsXRRe7ZHojH6ekpu8GIj9ogBsituljTq49u4P+JsrtO+HtB58G7ssvIo659m8cIPe0is7\nhVfHwZji1YBlD+/lGttFezVMxQEtylNuRxssR+ssRxvE5ZEihcXF2hxXy8x3zq1XVlWrjQEMjlUE\nW8ey8WyHmnJlu1UI8VIbeyDWm2uozS3Ii4t7ahT3nuTc3sh5HBX3fT0bXl2wudyxmantn8nEeUqk\nU3KjiwD8gouvMcXm9d77wEXh1bu074X4I4VX6/sVXn0AltYYZaMdj8y30Lb3VBFVVYwxrCTdYeB9\nGO+0omvaHq+1znGl3uFifQ5PTT6j00YDFq5l41gK17LxbQdPORJ0hRAnzvirptMEozXrkcXtjYx7\nT1LKuiuWWoorHZuzbfXMzHaQpQx0ikYXR6BecCHWpqiA9m3wtgOw0TTW7o4UXunjL7wyBoxBOx7a\n9cm9BsZxqc81yai+YCnOM24PiqNFy9HGcIatBZz3Z7jamOdKfZ55tzHRrDfXBmWxE3SVTU25uOrl\n7s0shBAHNfZAfPPugHceJPSTIvo23GLr+VLHpu4+40JrDFFeBODt4Q/P6z2ty2NIrgM1t3gbY3D6\nm2Xh1Ts4ZceruNlh60LA1rnrH7zwSmtQNrnroZ0aud+YmlGFmdY8zvos94st5wfx5vBkVl25BK2z\nXK13uFTrTKSYZ7SIysUu7utaNnXPlXOsQohTbexX4N9dHqAsuDiruNyxWWg+Z3hCGYAjXZwVfl4A\n3h4a5djQdAz1ZBNvYxV/a638tYqdFh23cts9nsKrYdbrlllvc6JFVcYYYp09NUB++1dv5P3JntaR\nZ/02V+rzXC3bR44728x1cU/XGw26U1JEJYQQ02TsgfgPvFpnvqb3LbzaZowhypOy7/Dzxx+aTNNI\nNpiJ1mj2V/G7ReDd7nK1La21iOZeobf0Cr0zrx698EprsFSR9br1sWS92phdAXVvoO1lCVH5vvwF\n451ryqFlezS8Fi3H52Jtjsv1DnV7vC8YjDEYigENvnJouh4X2h1WBqd3FJoQQhzE2APx9XM+m1v7\nD2YyZQCKt+9XFr0ohx+38ox6f416d41Gvwi8td4GaqRTlcEibc4StxeJWwvE7UWS9kKRsRqzM6+2\n7Pr0QmV1s3E8cscn9+qY0f7HRu/bfWvfb4VhPe5xf/Bkd4DNRoJsnjDYp+3mKFW2I1zwWjRsl4bt\nFb8cb/h20/ao2+5EM05tDAoLXznUHZe67UkxlRBCHFIlhxy10fTzhERnxfazZWFnMfXeKo3eGo3e\nGvXeKrXoCdZI1DOWIm7Nk7QXidsLxO0FktbCrmxXG8N7WZ+bvQ2+k/U4YPithGfZNGyPebcxElRH\nAm0ZYH3lTEXhUpH1GlyrKKiq2540ixBCiA9oolfR3GiiLEbHmzT76yyUgbfeW8WPu7s/13YZzJ4t\ngm0ZeJNm55nHfzbyhJvpFt9MtuiVGfOCcmmr/bZkTZHVWhZGKYxlj+VYkYXFXL2OnauR4OqW2atX\neUOMg9BGY2FRUy4126Hh+JL1CiHEMRp7IFa9DWqPlrG3HuB3V2j013DTaNfnpE6NJ7OXGLQWyGYX\nyGcXSeszL7wXmxrNrbTLzWSLu3mx/e2j+Kg3w5tum7P2yFg3XRws1o5H7tbI/eZEzvS+bP2Wi+pm\ncJWiporsXLJeIYQYn7FfYTu/+YVdf479Fo87V+k3F+k1inu6VqOB71oHqoEyxvAgj7mZbhEmWyTl\n1vVlu8ab3gzX3Cbu9n1SnWNUUeGcuXXMBPpEv4yKrFfhK5u67UrWK4QQEzT2QLx59nU2ax2i5gL9\nxgKZUwRD1wHfgfoBd2f7Ouf30i1uJpuslcVNLcvm494MH/bazCm3yHrL40W565dZ7/Rv/07aMOu1\nFDXboW771CTrFUKISoz96nv/rU/S68fDjld1+zmjB/fQxvB+1uftZGtYeKWA606Tt7wZrjh1bKPR\nyiV3PXKvgXYnNxbwZWLKg9e+cvBth6bjy5leIYSYAuNvccmejlcH8DhPuZlu8s1ki+6w8MrjLa/N\nG06TunLKpho1Ur9Z+dCEabTdycqx7DLrLaqchRBCTJfx3yNuKzYP0NMhNZp30h43k03ulIVXXll4\n9ZbTYskp7vHmXp2BW5Osd8ToOEAHhSudrIQQ4qVR6Y1BYwwP85i30y3CpEtSnvq9ZNd4023xWq2D\n7dXJvQZJRfN5p42MAxRCiJOlkkAcDQuvtlgt+0q3LJuPeR1uNBaYqc0WHa0si+wF3+skk3GAQghx\n8k0sEGtjWM4i3k42+fZI4dU1t8UbjUUuts5iucU9zPy53+lk2h4HuJ3pusrGVy6ejAMUQogTbeyB\neCNL+O3BOjeTzWHh1bzt8+HGItdmL1F3TlcB0eg4QAd7eD+35jq4coRICCFOnbFf+X/hwS0APEvx\n4eYZbsxc4MwExvDBzpGdqhggL198yDhAIYQQ+xl7IL7S7HCtvsSHGosT6a08OhHItx3sCbSxfJ7L\n7Q5r8cvT4lIIIcRkjT0Q/8T17xlrr+VpnwikJPMVQgjxHNMTsQ5BJgIJIYQ4KV6KQCwTgYQQQpxU\nUxvNZCKQEEKI02CqArHWBkcmAgkhhDhFKm9xCeVEIOXQ9GUikBBCiNNl4oE411omAgkhhBClsQdi\nQ9FJyrccarZkvUIIIcSosQfipVqLRs2VfslCCCHEPsaemjZdX4KwEEII8QyyRyyEEEJUSAKxEEII\nUSEJxEIIIUSFJBALIYQQFZJALIQQQlRIArEQQghRIQnEQgghRIUkEAshhBAVkkAshBBCVEgCsRBC\nCFEhCcRCCCFEhSQQCyGEEBWSQCyEEEJUSAKxEEIIUSEJxEIIIUSFJBALIYQQFXIO+wVBECjgnwEf\nBWLgL4Zh+O3jXpgQQghxGhwlI/6TgBeG4fcCfwv4B8e7JCGEEOL0OEog/sPAbwCEYfjbwO8/1hUJ\nIYQQp8hRAvEMsDny57zcrhZCCCHEIR36HjFFEG6P/FmFYaif8/nW0lL7OR8++U7z4z/Njx3k8cvj\nP72P/zQ/9sM6Sib7deCHAYIg+IPAN451RUIIIcQpcpSM+N8DPxQEwdfLP//kMa5HCCGEOFUsY0zV\naxBCCCFOLSmyEkIIISokgVgIIYSokARiIYQQokISiIUQQogKHaVq+oVOez/qIAhc4JeBq4AP/FwY\nhl+pdlWTFwTBGeB/A38sDMNbVa9nkoIg+FngRwAX+MUwDH+14iVNRPnc/5fA64AG/lIYhmG1q5qM\nIAi+B/j7YRj+QBAE14Bfofg3eBv4qTAMT3Rl7J7H/zHgF4CcIgb8uTAMH1W6wDEafewj7/sx4K+V\n7aCfa1wZ8WnvR/3jwEoYht8H/HHgFytez8SVL0a+APSqXsukBUHwCeAPlT//nwA+VOmCJuuTQDMM\nwz8C/F3g71W8nokIguBngF+ieOEN8Hngs+U1wAL+RFVrm4R9Hv8/oghCPwB8GfibVa1t3PZ57ARB\n8HHgLxz0e4wrEJ/2ftRfAj5Xvq2ArMK1VOXngX8O3K96IRX4JPB/gyD4D8BXgP9U8XomKQJmgyCw\ngFkgqXg9k/Iu8KMUQRfg94Vh+N/Kt38d+MFKVjU5ex//nwnDcLvZk0vxc3FS7XrsQRAsULwA/evs\n/Hs817gC8anuRx2GYS8Mw24QBG2KoPy3q17TJAVB8OcpdgS+Wr7rQD+MJ8gS8N3AnwL+KvBvq13O\nRH0dqAHfotgR+SfVLmcywjD8MrtfcI/+zHcpXpScWHsffxiGDwCCIPhe4KeAf1jR0sZu9LGXce5f\nAT9N8f9+IOMKjoftR33iBEFwGfivwL8Jw/CLVa9nwn6Sovva14CPAb8aBMHZitc0SavAV8MwzMp7\n44MgCBarXtSE/Azw9TAMA3b+772K11SF0etdG3hc1UKqEgTBn6bYFfvhMAzXql7PhHw3cI3icf87\n4MNBEHz+RV80lmItilfFPwJ86TT2oy6DzleBT4dh+LWq1zNpYRh+//bbZTD+K2EYPqxwSZP2P4DP\nAJ8PguAC0AROy4Woyc5u2AbFtqRd3XIq83+CIPj+MAx/C/gU8F+qXtAkBUHwE8BfBj4RhuFG1euZ\nlDAM/xfwFkAQBFeBL4Zh+NMv+rpxBeLT3o/6sxRbUZ8LgmD7XvGnwjAcVLgmMSFhGP7nIAi+LwiC\n/0mx6/Tpk14xO+LngX8dBMF/pwjCPxuG4Um+P7jX9v/z3wB+qdwN+Cbwa9UtaaJMuT37j4H3gS8H\nQQDwW2EY/p0qFzYBe5/j1j7v25f0mhZCCCEqdGoKqIQQQohpJIFYCCGEqJAEYiGEEKJCEoiFEEKI\nCkkgFkIIISokgVgIIYSokARiIYQQokL/H/ZcSqHuFxjCAAAAAElFTkSuQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 8 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Although using arrays as input objects allows for a very compact specification of a relatively complex plot, they lack semantic information about what variables are represented on each dimension. You can, however, pass that information as seen below. If you use `Series` objects, the names will be used to label the axes and legend. However, any sequence will work." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "step = pd.Series(range(1, 16), name=\"step\")\n", - "speed = pd.Series([\"slow\", \"average\", \"fast\"], name=\"speed\")\n", - "sns.tsplot(walks, time=step, condition=speed, value=\"position\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 9 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For single-condition data, you can set the color of the plot with any valid matplotlib color spec." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.tsplot(sines, color=\"indianred\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nAAAAAElFTkSuQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 10 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For multi-condition data, you can additionally use any valid seaborn palette spec." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.tsplot(walks, color=\"husl\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 11 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you are providing condition information, you can further use a dictionary that maps condition names to colors." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "color_map = dict(slow=\"indianred\", average=\"darkseagreen\", fast=\"steelblue\")\n", - "sns.tsplot(walks, condition=speed, color=color_map);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAeIAAAFVCAYAAAAzJuxuAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XeUXPd93/33vXf6zPaKLWikOGwgCECFqhTVKEHFph8l\njnPiJ5H9SI4k21EiRY6iI9mOHFk6kv1IdhTZlksURUkeJ5ajRCZFiWpUl0mQIMAyIInGxS627069\n9fd7/pjZQSHaArtzp3xf5+AAB4uZvXd3MZ/53fv9fb+G1hohhBBChMMM+wCEEEKITiZBLIQQQoRI\nglgIIYQIkQSxEEIIESIJYiGEECJEEsRCCCFEiCKX+wfZbPYlwCdyudxd2Wz2euA/AQo4DLw3l8vJ\n/ichhBDiKl1yRZzNZj8IfAGI1/7qD4F/m8vlXgUYwM9t7uEJIYQQ7e1yl6afAX6BaugC7M3lcg/W\n/nwf8LrNOjAhhBCiE1wyiHO53FcA/6y/Ms76cxHo2YyDEkIIITrFZe8Rn0ed9ecuYOVyD9Baa8Mw\nLvfPhBBCiHaxrtBbbxA/ks1m78zlct8D3gR867JHYxjMzxfW+Wnax9BQV8eefyefO8j5y/l37vl3\n8rlD9fzX40qDeK0y+v3AF7LZbAx4Avif6/psQgghhDjHZYM4l8sdB15W+/PTwKs395CEEEKIziEN\nPYQQQogQSRALIYQQIZIgFkIIIUIkQSyEEEKESIJYCCGECJEEsRBCCBEiCeJ1+M53HuAv//LPwj4M\nIYQQbUSCWAghhAjReltcNrWTJ0/w+7//u1hWBK01b3vbPXz729/EdV2Wlpa4557/i5//+bfz7LPP\n8NnPfhqtNT09PXzoQx8lnc7wJ3/yH3jssUdRSvGLv/iPueuu13Ho0EH+6I/+gEymi1gsRjZ7U9in\nKYQQoo20VRA/9NDPuPnmXbz73b/BY489yrFjR1ldXeVzn/sCnufxT//pP+LOO1/LJz/5e3z4w7/D\ntm3b+drXvsqXv/yfue2225mZmeY//sc/x3Ec/vk/fwcvetEdfPrTn+D3fu+TTE5u5U//9HNhn6IQ\nQog201ZB/Ja3/Bxf/vIXef/7f5NMJs2LXnQHt9++F8uysCyLnTuvY3r6FCdPHufTn/59AHzfZ3Jy\nK0ePPkMu9xS/8Ru/BkAQBMzMTLO0tMjk5FYAbr99L48/fii08xNCCNF+2iqIv//977F79x7e8Y53\n8s1vfp0/+7PPMz4+DoBt2xw/fozJyUkmJ7fxkY/8O4aHR3j00QOsrq5iWRZ79+7jgx/8ML7v86Uv\n/RXj4xMMDQ1x9Oiz7Nx5HYcPP4aMdBRCCLGR2iqIb7zxJv79v/8dotEoQRDw9rf/Qx544Bu8733v\noVAo8I53vIvu7h4+8IEP8bGPfZQgCDAMgw996KNMTEzyyCMP8973vpNKpcyrXnUXqVSK3/qtj/CJ\nT3yMZDJFT08PO3bsDPs0hRBCtBFDa335f3VtdFhzKR955GG++91v8S//5QdD+fzQ2XM5O/ncQc5f\nzr9zz7+Tzx1gaKhrXZdO2377klxKFkII0cza6tL0+fbs2ceePfvCPgwhhBDiotp+RSyEEEI0Mwli\nIYQQIkQSxEIIIUSIJIiFEEKIEEkQAwcOPMRv//a/DfswhBBCdKDQq6ZX/vdXKB88sKHPmdq9l963\n/cKGPqcQQgixGTpyRXzy5Ane/e5f4dd//V28973vZH5+rv6xb3zjPt75zv+b97zn/+HjH/9dfN/n\nV3/1l1lZWcH3fd7whjt5+ukcAL/yK/8E3/fDOg0hhBBtIPQVce/bfqHhq9cLTWkCyOdX+cu//DP+\n6q/+K8lkkj/+4z/kq1/9Cq985Z389Kc/YmhomLGxcf7+739KJBJlcnIrkUjoX0IhhBBNQGnNStnl\nn/zJg6n7P/Lm8pU+riNXxG95y8+RyWR4//t/k7/5m/8Py7IAmJ4+xY4dO0kmkwDs3r2XY8eO8qpX\n3cWPfvQDfvrTH/Oud72Hhx76GT/84YPcdddrwzwNIYQQTaBke0yvlDk2V2C55AAk1/P4jgzitSlN\nn/3sf+TVr34tX/7yf8YwDLZsGePYsWPYtg1Ue1Vv3bqtPj7xqaee4KUvfTnlcpkf/OBBXvrSl4d8\nJkIIIcLgB4r5vM3x+QIzqxUcL8A0Da6mqXJHXlc9f0rTP/gHv8gTTzxOT08vv/qr7+I3fuPXME2T\niYlJ3vOe3wRg794Xcvr0NIZhsGfPPo4fP0Y8ngj5TIQQQjSK1pp8xaNge9iej2VW17KWeW0zDdp6\n+lIz6OQpJJ187iDnL+ffueffbuduewGrZZei4wFgXmKYkNaaj37l0cH7P/LmxSt9/o5cEQshhBCX\nslZ4VbQ9XF9hmcYlA/haSBALIYQQNWXHZ7XiUnZ8DKM6SvdaLz1fjgSxEEKIjuYHipVS9dKzr3R1\n9bvJ4Xs2CWIhhBAdR2tNwfbIVza28OpqSBALIYToGBcqvFoL4bBIEAshhGhrjSy8uhoSxEIIIdrS\nWuFVyfExG1R4dTVCD+ID0z/j5OqxDX3OrT072Dv24g19TiGEEM1Pac1SwTmn8KoZw/dsoQdxGEql\nIp/4xO9RKhVZWJjnNa95PQ88cD//5b/8DwD+8A8/yQtf+BLGxyf47Gc/jdaanp4ePvShj5LLPcXn\nP//HxGIx3va2e4jFYvzt3/5PfN/HMAw+/vFP0d3dwx/8wSfJ5Z5ky5YRTpw4ySc/+f9iGAaf+tTH\ncRyHeDzOBz/4YYaHR0L+agghRHtYKTkslpz6ZedmD+A1oQfx3rEXN3z1eurUFK973d3ceeddLCws\n8Ou//k6y2Zs4ePARbrrpFh555GH+xb/4AO9+96/y4Q//Dtu2bedrX/sqX/7yf+ZFL3oJnufxhS98\nEYAvfemv+NSnPkM8nuBTn/o4P/3pT0gmExQKq3zhC1/Esjxe//o3APC5z32Wt7/9H3HHHS/joYd+\nxp/8yX/gox/9WEPPXQgh2k3Z9VnI23hKNdW93ysVehCHoa+vn7/+6//Ggw9+m1QqQxAEvO1t93Df\nfV9jcXGRV7ziTizL4sSJY3z6078PgO/7TE5uBWDr1m315+rt7eP3fu93SCaTnDx5gltvvY0TJ45z\nyy23AdDf38+2bdsBOHr0Wb70pb/iy1/+IlprotFoI09bCCHaiucHLBQcyq6P2WQFWOvRkUH83//7\nl7n11l38/M+/nQMHHuLHP/4B+/a9iM997jPMz8/z/vf/FgBbt27nIx/5dwwPj/DoowdYXV0Fqjf8\nAYrFIn/5l3/GV77ydyil+Ff/6tfRWrNz5/Xcf//fAb/E6uoqzz13EoBt27bxS7/0y9x6620cPfoM\nTzxxOJTzF0KIVqa1ZrHgsFpxqwHcIpegL6Yjg/jlL38ln/nMp3jwwe+yY8dOUqkUnudx112v46GH\n/p6xsXEAPvCBD/Gxj32UIAgwTZN/828+wvz8XD2IM5kMu3bt5td+7R309fUxObmVxcUF9u9/Kz/5\nyQ9597t/hS1bRkkkEkQiUd773vfx6U9/Atd1cByH973vX4f5ZRBCiJazWnZZKjpodMsH8BqZvrQJ\nTp48ztNPH+G1r30DkYjPm9/8Zv7mb/6OSKSz3ve02wSW9ZLzl/Pv1PPfjHO3XZ/5go1T2wfcrGT6\nUpMYHh7l85//Y/76r/8bpgnvfvdvdlwICyHERgiUZj5foej4LbEV6WpIOmyCRCLB7//+HwCd/a5Y\nCCGultaapaLDatnFaNMAXiNBLIQQoqkUbI/Fgo3SGqONA3iNBLEQQoim4PgB86s2TqDqLSk7gQSx\nEEKIUCmlmSvYFG2vNpAh7CNqLAliIYQQoVkqOayUXAyjdVpSXojWmhOLJQ4cv+Ji6bqODOIgCHjf\n+96D7/t86lOfJZPJXPYxR48+Q6FQYPfuPQ04QiGEaG8l22O+aBMo3bIdsQDyFY9HTy5y4PgSi0Xn\nqp4j9CD+rz8+ys+end/Q53zxdUP845fuvOjH5+fnKZfL/MVffOmKn/M73/kWAwODEsRCCHENPD9g\nLm9T8QMsozXbUgZKk5tZ5cDxRZ6ezaM0REyD3Vv72LOtn//0/WfX9XyhB3EYPv3pjzM1dZJPfOJj\nLC8v4boui4sLvPOd7+aVr3w1f/qnn+PRRx/G9wNe/erXcPfd+7nvvq8Ri8W48cabuPHGm8M+BSGE\naClKaxYKNoWKh2kaWC0YwHN5mwPHF3n05BIlxwdgvC/F3u0D7JroJRmLcDVNskIP4n/80p2XXL1u\nhg984EP89m//W17/+jdimiZ79uzj8OHH+Iu/+FNe+cpX88AD9/PHf/xnDAwMcO+9/4fBwSH2738r\nAwODEsJCCLFOq2WHxaKDYbReX2jHCzg0tcyB40s8t1QCIBmzeOn1Q+zdPsBoT/KaP0foQRyGtXcs\n/f0DfPGLf8HXvvZVDMMgCAIAPvrRj/H5z/8RS0uL3HHHy573OCGEEJdXcX3mW3A8odaak4slHj6+\nyOGpFbxAYQDXj3Sxb/sAN27pIWKZG/b5OjKIofqF/vM//zxvfes93HHHy/i7v/vf3Hff1/A8j+98\n5wF+93c/jtaaX/7lf8hrX3s3pmlKEAshxBXw/ICZ5TIl169tR2qNEC5UPB45ucSB44v1wqveVIy9\n2wfYu62fnlRsUz7vuoM4m82awJ8DNwAKeGcul8tt9IFtNsMweM1rXs/nPvcZ/sf/+O/ccsutFAp5\notEo3d09vOtd/4x4PM6LX3wHo6OjZLM38rnP/RHbt+9gz559YR++EEI0nbLjs1pxWXQDbD9oie1I\n9cKrE4s8ffpM4dVtk33s2z7A9qHMpr+RWPf0pWw2+0bgHblc7hez2ezrgH+ey+XefomHdNz0pbN1\ncq/pTj53kPOX8++M8/cDxUrJpeh4+EpjmQZ9fWmWl0thH9olzedtHj6v8GqsN8ne7QPcNtlHMnZ1\nF4wbNX2pAvRks1kD6AHcq3gOIYQQLUprTcH2yFc8bM/HMqv3S5t9Bex4AYenVjhwYpGTi2cKr+64\nboi92/vZ0psK5biuJoh/CCSAp4AB4K2Xe8DQUNdVfJr20cnn38nnDnL+cv7tdf6257NccMhXPIhY\nZLojXKwdUl9fuqHHdjFaa47OFvjxkdMcOLqA61cLr24c7+Vl2RF2bR0gGtm4wqtGbV/6IPDDXC73\n4Ww2OwF8O5vN3prL5S66Mu6EyzMX0ymXpy6kk88d5Pzl/Nvj/JXWrJRdiraH66srWvU2w6Xpgu3x\n6Ilq4dXCWYVXr7ihnz3bBuitFV4VC5UN/byNCuI0kK/9eRmIAtZVPI8QQogmtVZ4VXZ8jNokpGa/\n9AxwYqHID47MceT0ar3walet8GpHAwqvrsbVBPGngL/KZrPfpxrCH8rlchv7lkIIIUTDBUqzXHTO\nKbxqlQYcK2WX+w+d4vDUCrAxhVeNsu6jy+VyK8A9m3AsQgghGqxVC6/WuH7A94/M8YPcLL7STPSl\neNPucbYOXH6YT7No7rcJQgghNoXjB6yUXEqOhwZMw6iHcCvQWnNoapn7D02Tr3h0JSK8/tYxdm/t\nb8rLz5ciQSyEEB3iQoVXhmHQWrEFp5bL3HtwipOLJSKmwauyI7zqxhHikdYsV5IgFkKINteqhVfn\nK9geDzw+zSPHl9DAzWM93L1rnP5MPOxDuyYSxEII0YbWCq9KrocXtFbh1fn8QPHjZ+b53lOncXzF\nSHeC/bsn2DncHvu0JYiFEKKN5CsuBduj4p7p9dyKq1+o3gd+aibP1x+bYqnkkopZvHXPJPu2D7Ts\nOV2IBLEQQrS4CxdetXZQzeYr3HfwFM/OFTANeOn1Q9x102jTb0W6Gu13RkII0QHapfDqfGXX5ztP\nnOZnR+dRujoD+E23jTPcnQz70DaNBLEQQrSQslsrvLJbu/DqfIHSPHRsgW89MUPFDRjIxHnTbePc\nMNqN0WLbkdZLglgIIZpcoDTLJYeS0/qFVxfy7FyBew9OMZe3iUdM3rhrjJdcP0SkhfY1XwsJYiGE\naFLtVHh1IUtFh68fOsWT06sYwL7tA7zuli1kEtGwD62hJIiFEKKJuLXCq2IbFV6dz/ECvpeb5UdP\nzxEozbbBNPt3TzAW0jzgsEkQCyFEyJTWrJarq992Krw6n9KaR08s8c3HpynaPj2pKHfvGufW8d62\nvw98KRLEQggRknYtvLqQk4sl7j04xanlMlHL4DU3j/LyF4wQi3TGfeBLkSAWQogGOqfwSmkso70K\nr863Wnb5xuFpHntuGYDbJvt4w61j9KRiIR/ZxtNaE2gA/PU8ToJYCCEaoFBxyZ9feNXGl2O9QPHD\nI3M8mJvFCxRjvUn2755g22DrjCe8Er5SWIZBMhYhFYvQlYxy/0fevLqe55AgFkKITdIJhVfn01rz\nyLEF/ubHR1kpu2TiEd5y+wS3b2u98YQXorVGaYhFTJLRCF3JCInotUWpBLEQQmyAQGn8IMD1FW6g\nKGrN6cVS2xZena9gezx6YokDxxdZKDpYpsErbxjmVTeOkoi25njCNYHSGAYkYxHSMYuuRGxDbydI\nEAshxEVorQmUxvMVTqAIlEKp6t/5ShMoRaA0SoEGMKr3fA3DoC8Ra/vVb6A0R06vcuD4EkdOr6I0\nREyDF103xMuvH2KghccTBkoRsyySMYtMIrqpPa4liIUQHUcpjRcoXD+oBapGKY1fC9a1X1qDNsDg\n4o00DMPAqi/42jt418znbQ6cWOTRE0sUnWpd0lhvkr3bB7htso+xkR6Wl0shH+X6BEpjAMmYRTIW\noTvZuDdSEsRCiLZjuwFlz0cpVQ3aQJ8bsNX1K6ZpXPS+ZTtXMl8Nxw84PLXCgeOLnFyshmwyanHH\ndUPs3d7PlhZsxuEHqn6vN5OIkoqHE4kSxEKItrJYsFmpuBcOWAMsy6BTVq7XSmvNc0slHj6+yOHn\nVnADhQFcN9zFvu0D3DjWQ9RqnX3ASlevctRXvYkokSY4fgliIURbUFozvVzG8YO2qM4NU7FWePXw\niUUWCg4AvakYL9/Wz95tA/SmW2cPsB9oYhGDRDRCOl791WxdvCSIhRAtz3YDTq+W61uExPoFSvP0\n6TwPH188p/Bq12Qf+7b1s2O4q2W+tkppElGLZDxCVzxCNNLcVdsSxEKIlrZSclgsOS0TEs1mvmDz\nyPFFHjm5RNGuFl5t6U2yd9sAt23tI7WJ1cIbLVCa7mSMwa54S/08tM5XWAghzqK1Zna1QsmVS9Hr\n5fgBj9cKr07UCq8SUYuX7Bxk746BlpuCpJQmGYsw1BVv+tXvhUgQCyFajucHTK9UCJSSEL5C1cKr\nMgeOL3JoahnXV0C18Grv9n5uGuttqcIrqNYFREyT4d4E6XjrzjCWIBZCtJSC7TGft+vTisSlFW2P\nR08uceD4EvMFG4CeVJSXv2CYPdv66Uu3ZtMNpTR9mTj9LXr8Z5MgFkK0jLm8TaHiyh7fy1grvDpw\nYpHcTLXwyjINbp3oZd/2AXa2UOHV+QKlySSiDHcl2ubnQIJYCNH0AqWZXi7hBqptXnw3muMHHJ0r\n8vTpVZ6cWa0XXo32JNlX63gVVsOKjaA0xC2Tsb4E8Ra8D3wprftdEUJ0hJLjMbtawTAu3gWrE2mt\nWSg6PH06z5HTeY4vFAlUtWNYMmrx4p2D7N0+wFhvsqUv4SutMQ2Dke4EmUTr3ge+FAliIUTTWio5\nLMvWpDovUBybL3CkFr7LJbf+sdGeJDeMdnPDaDcT/em2GDihlKY3HaM/HW/pNxOXI0EshGg6Smtm\nlstUvKAtAuVaLJecevAenSvg11a98YjJzWM93DDawwtGu+hOtk63q8sJlCYTjzDUneyI778EsRCi\nqdhuwMxqGbj4xKN25geKE4sljpxe5cjpfL3FJMBwd6K+6p0cSBMxW2u70eUEShOPmGzpSZGItdd9\n4EuRIBZCNI3VssNCsfMuRa+WXY6czvP0bJ5n5wr1Pb5RyyS7pZsbRnu4YaS7pXo8r4fWGgODoa4E\nPan2PMdLkSAWQoROa81svkLJ6YwuWYHSPHfWqnc2b9c/NpCJ11e92wYzLddkY72U0vQkYwx0tfd9\n4EuRIBZChKpTumQVbK9e4fzsXAHbC4DqYIUXjHTV7vV2M5Bp/QYVV0IpTSoWYbBF21JuJAliIURo\nirbHXJt2yVL1VW+eI6dXmV6p1D/Wm4px22QfN4x2s2Ooi1ikvVe9Z1NaEzVNRvqSLTVQYjPJV0EI\nEYr5vM1qxW2bgizbC5hZKTOzUmFquczRuQIlp9pUwzIMdg5luGG0mxeM9jDUoZdhldYMpOP0tkFb\nyo0kQSyEaKizu2S1aggXbK8eujMrFaZXyufs6YXqqveF2wd4wWg31w13EY927uVXpTRdySiDXYm2\nvv1wtSSIhRANU3F9ZlbKLdMlS2vNStlleqVyVvCWKdTaR65JxSyuG+5iS2+y9ivFCyb7WVkph3Tk\nzSHQmmTEYrgv0fH3gS9FglgI0RBLJYelotO0q+BAaRaKdj1s11a7a0VVa7qTUbJbuhnrTdVDtycZ\nfd6l5k689LxGaY1hwJauJOk2bUu5kSSIhRCbqhm7ZHmBYi5vM71cC9zVMrOrFbxAn/PvBjJxrh/p\nYktvirHaareV595utkBpopbBYFec/g6+FL9eEsRCiE3jeNUuWVqH1yXL9gJO11a5a5eY5ws26qzM\nNQ0Y7k7Wwra60h3tSXb0fd0rpbQGDelEhN5kjEQswkBXknm7EPahtQwJYiHEplguOkwtlRo+ttAL\nFA8dXeDEYomZlTJL5xVRRS2T8b50/X7uWG+K4e4EkTZvnLHR/ECTiJl0J2J0J2MdfSn+WkkQCyE2\nVKCqXbLijt/wEJ5aKvGVh04yX6h2qkpGLXYOZeqr3C29KQa74i1RKNaMAqWxDINMIkJvKiYFWBtE\nglgIsSG01iwWHfJlF8M0SGUat8L0A8V3nzrN93OzKA0vuW6Ql79gmN6UrNQ2QqA0yZhFTzLWtjOB\nwyRBLIS4ZvmKy2LRqTbvb/AqeHqlzFf+/gSzeZveVIx79m1l53BXQ4+hHQVaEzUNMvEofel4w69u\ndBIJYiHEVbPdgPmCjeNXK6IbufoMlOZ7T53me0+dRml44Y4B3rhrXAqsroHWGqWpXnquFV6JzSdf\nZSHEuimlmcvbFB0PyzQaXhF9erXCVx46wcxKhe5klHv2beX6ke6GHkM7kcKrcEkQCyHWZanksFxy\nMY3Gb0kKlOYHR2b5zhOnCbRm77Z+3njbOElZua2b0hoTKbxqBvLTK4S4IiXbY75oEygdStXxXN7m\nbx86wdRyma5EhJ/bu5Xslp6GH0erk8Kr5iNBLIS4JNcPmM/bVPwAK4Qe0UprfvT0HN96fAZfaXZv\n7WP/7gkZobcOUnjV3OQnWQhxQUpr5vM2RdvDNA2sEFbBiwWbrzx8kpOLJdLxCP9gzyQ3j/c2/Dha\nkdYajUE6Xl39yuX75nVV35lsNvsh4K1AFPgPuVzuixt6VEKIUK2UHBZLDqZhhLJ6Ulrz02fm+ebj\n03iB5taJXt5y+yTpuITJpaxVPcejJl21witpXtJYbuDymW99JvG+177PvtLHrPunOpvNvhp4aS6X\ne1k2m00DH1zvcwghmlPZ9VnI23hKhfYCvlR0+NuHT3J8oUgqZnHPCyfZNdEXyrG0gkBrLAwScYtU\nNEJXMirhG4JABRTdAm7gAqSBzQti4A3AoWw2+7+AbuBfX8VzCCGaiOcHzBccKm61LWUYL+RKax46\nusD9h6ZxA8VNYz28bc+kFBSdZ23VG4uYJKMRupIRElG5UhAWrTUlr4Dt2xiYGKz//87VfPeGgEng\nLcBO4H8DN17F8wghQqa1ZrHgsFpxqwEcUhHPSsnlbx8+wdH5Ismoxdv3buO2yT7Zz1pTrVSHRCxC\nOmbRlYhJwVUTsL0KJa+I1mAYV9/S9WqCeAF4MpfL+cCRbDZrZ7PZwVwut3CxBwwNdXa7uU4+/04+\nd2ju818pOcyvVrCSUQZSsU35HH196Ut+XGvNj3OzfOWnx7C9gFsm+/ilV1xPbzq+KcfTaJc7/4vR\nWuMrTSJqkopF6UrFWu7+eDP/7F8r13fJ23miMUWfce73WGt9kUdd3NV8Z38A/AvgD7PZ7BjVa+GL\nl3rA/HznzqUcGurq2PPv5HOH5j1/2/VrbSnVpjbk6OtLs7xcuujHV8suXz1wkqdnC8QjJvfs28qe\nbf1o12fZ9TftuBrlcud/vkBpDAMSUYtULEJ/Mlb9/vgB5XyF8iYe60Zr1p/9a6W0qt4H9p2LroAb\nEsS5XO7vstnsq7LZ7M8AE3hPLpdb/2cWQjRUoDTz+QpFxw+lLeUarTWPnlzi3oOnsL2A64e7+Pl9\nW+nZpFV5M/MDdeZebyJKssVWvZ1Ca03ZK2H7ZcC8psvQF3JV3/VcLvdbG3oUQohNo7VmqeiwWhtP\nGFYAAxRsj68eOEluJk8sYvJzeyfZt32gY+4FB0pjAImYRTIWoTsRJWI1blykWD/bsyl7RZTWGx7A\na+TtlxBtrGB7LBbs6otIiAGstebQ1DJfe3SKihuwYyjDPfu20tcm94IvxQ80sYhBMhYhHY+Silkd\n88ajlfmBT9Er4CuvWg29id8zCWIh2pDrB8yu2riBwjQI9YW/aHv8n0eneOLUClHL5C23T/CinYNt\nu9fVDxSGYRCPWPSkYrLqbTFKK0puAad2H9hg8793EsRCtJnqdKRaV6yQs+7w1DL/55Epyq7PtsE0\nv7BvG/2Z9lgFa60JNJgGxCMmsYhF1DJJxSLEoxZDQxnmkfKZVlL2SpTdMoZhbNpl6AuRIBaiTbh+\nwOnVCl4QXlesNWXH5399+ykePrpAxDR4023j3HH9UOjHdbWU1iiliZgmscjaL4t0PCKr3TbgBg5F\nt4hSKpSrRxLEQrSBpZLDctEJrSvWGqU1jxxf4puPT1NyfCb7U9zzwm0MdSVCO6b1UlqjFUQsox64\n8YhJKh4NtdBNbDw/8Cl5RTzlbvp94EuRIBaihXl+wMyqjRcEoXdaOrFQ5N6DU0yvVIhaJj//4u3s\nmeht6lWDZAoBAAAgAElEQVRwoKqXjqOWUb20XNtKlIxZTX3c4tporSm61baUZoPuA1+KBLEQLWql\n5LBUdDBCXgWvlF3uP3SKw1MrAOze2scbbh1j21jfuhpabLa1IqqYZRKLVle6yahFIipVzJ2k4pUp\nuyUwDMwG3ge+FAliIVqM5wfM5m1sLwj1UqnrK35wZJYfHJnFCzQTfSn2755gcuDq2jpupEBrjNpg\nhHhtpbtWRCU6kxu4FN0CWilosjdeEsRCtJCzV8FhdsY6NLXCNw6dYrXi0ZWI8NY9Y+ze2h/65Vyl\nqwVVw10J0jK1SXDueELTMJsuhEGCWIiW4AeK06uV0FfBp5bL3HtwipOLJSzT4FXZEV6VHWmKlabS\nmv50vCOahIjLq44nLGH7FQya5zL0hUgQC9HkVssOC0UX0yC0EC7YHg88Ps0jx5fQwM1jPdy9a7wp\n9gQrpckkogx1J0JfkYvwaa1xfJuyV6q1pWzcz4TtVziy+OS6HydBLESTCpSur4LDWgT7geInz8zz\n3adO4/iKke4E+3dPsHM4/BF3gdYkIxaDfQnikfBX5KLxtNa4gYuvPALlE+gAXwWYGGAYDQvhQAUc\nW3mGY8tPE+hg3Y+XIBaiCa2WXRaLDoZBKCGstSY3k+e+x06xVHJIxSzeevsE+3YMhr6XVmmNZRps\n6UrKfeAOorTC9V0C7RGoauAG2n/e/t9GXoLWWjNbmuaphcex/QoxK85N/bs4PP/oup5HgliIJqJq\nq+BKiKvguXyF+w6e4pm5AqYBd1w3xF03j5KKhf9yoTT0peP0y33gthaoAC9w6mHrqwClgwuEbnhX\nQvLOCk/OH2bZXsTAZEfv9VzXfwOWEZEgFqJVFWyPuXwltB7RZdfnO0+c5mdH51Earh/u4k27xxnu\nTjb+YM4TKE0mHmW4OxF64xKxsfzAw1UeSvn4yidQAQqNgdE0oXs2x3d4eulJpvInABhOj5IduIV0\nLANUV8nrJUEsRMiU0pzOVyi7PlYIxUaB0jx0bIFvPTFDxQ3oT8fZv3ucG0a7Q290EShNLGKxpSdB\nItYcL8Ti6mit8ZWPp1yCeugqNOrckDWM6j3eJqO04sTKUZ5dzuErn0ysixsHb2UwNXzNzy1BLESI\nSrbHbL6CYRihhPCzcwXuPTjFXN4mHjG5e9cYd1w/RMQMd6uHrlW7DnUl6EnFQj0WsX7VIioHL6je\nz127vGzAOVONDMPAoPnfYM2XZnly4RBlr0TUjHLT4C4me7Zv2P1oCWIhQqC0Zna1ugoOY8vNUtHh\n64dO8eT0Kgawb/sAr7tlC5kmKH5SStOTjDHQFQ99RS6uXHXbkIMdVPDzRVbt8jlB1cz7eC+m6BZ4\nauEwC+U5DAy29uzg+v4biVkb++ZQgliIBivZHnN5GwwaHsKOF/C93Cw/enqOQGm2DaTZv3uCsb5U\nQ4/jQgKlSccjDGbiRGU7UsvwA5+KX8YN3PqVDNOIt2TwrvECl2eWcpxcPYZGM5Ac4sbBW+mKd2/K\n55MgFqJBlNbMrVYoOX7DC46U1hw8ucQ3Dk9TtH16klHu3jXOrRO9oa86ldZETZPRviTJJqjMFpen\ntabil3F8B1/59dAN+2fpWmmteS5/gqcXn8RTLqlomhsHb2EoNbqp5yY/9UI0QMmprYKh4SF8crHE\nvQenOLVcJmoZ3HXTKK+4YYRYJPwVi9aawUycnpRsR2oFbuBQ8Wy8wIFaVXMrr3zPtlhe4KmFQxTc\nPJZhccPAzWzv3dmQam0JYiE2kdKauXyFou03vBFGvuLyjcPTHDy5DMCuyT7uvnWsKYqflNZ0JaIM\ndklbymantKLslXF9G6UVhmGeU3DV6speidzC48yWZgAY79rKDQM3EY8kGnYMEsRCbJKi7XFioQg0\ntke0Fyh+eGSOB3OzeIFirDfJ/t0TbBvMNOwYLiZQmmTUYrg7IfeBm5zt2dhBBS/wzrr03D4B7Cuf\no8tPc3zlGZRW9Cb6uWnwVnoSfQ0/FgliITaYUpq5vE3UWX/P2WuhtebxUyvcf2ialbJLOh7hzbdP\nsGdbc4wntEyDLT3SlrKZVQuvKriBA1pX9/S2UfhC9f/JTHGK3MITOIFNIpLghoFb2JIZD+0etwSx\nEBtEa81yyWW5XJ2UlGjgZKKZlTL3HjzF8YUilmHwihuGufPGURJNMJ5QS1vKpnaxwqtmnNt7rVbs\nJZ6cP8yqs4xpmFzXl2VH3/VEzHCjUIJYiA1QtD0WijaB0g1dfZYcjwcen+HhY4to4MYtPbxx1xgD\nXY27v3UxgVIkYxGGu6QtZTNyAxfbr+D67Vd4db618YTThecAGM2MkR24hWQ0/G17IEEsxDVx/ID5\nvI3tB1iG0bAQ9pXiZ88u8J0nT2N7AUNdCfbvHuf6kc3Z57geSkPcMtk+3EVhpRL24YizKK2oeGWc\nNi28Ol+gAo6vPMvR5SMEOqA73sONg7fSnxwM+9DOIUEsxFVQWjOftynaHqbZ2PaUR06vct/BUywU\nHRJRizfvnuBFO8MfT7jWzGG4O0FXIkoiGqEQ6hGJNe1eeHU+rTVzpRmeWnicil8mZsW4sX8XE91b\nm3KvswSxEOu0XHJYKjnVKUkNDL/5gs3XHzvFkdN5DODFOwd5zc1bSMfD/2+slaYnFaM/I20pm0Un\nFF5BbU5x4OD4Dk5g4/gOM8UplioLGBhs772O6/qyRK3mLRIM/3+wEC2i5HgsFBx8pRp6H7ji+nz3\nydP85NnqeMKdQxnetHuC0Z5mGU8YYag7GfqKXFRHCtqBgxe4LV945Ssfx7dxAgc3sLF9uxa41b87\n8zHngo8fSo2QHbyFTKyrocdd+0qv656MBLEQl+H5AXMFB9uttqZsVAgrrXn42CIPPD5D2fXpS8d4\n465xbhrrCX3VGShNPGKypScl4wlDVB20YOMqFy9wUfrMSMFmXP1qrfGUe9bq9exQtc/5+0Bfevuf\nZUSIR+KkoxnikQRxK17/PR3rorfB+4GVViQiCTKxLt732veV1/NYCWIhLkJpzULBIV9xsczGXoY+\nNl/g3oOnOL1aIWaZvP7WLbz0+mGiVhOMJ0TGE4bJD3ycwMatrXqNWsUzGA1px3g5Za/E4tw0S/n8\n88LVDRw0+pKPj1lxUtH0eeGaIB6J135PELPioW85WqNRRMwoPdFeItbVHVNznIkQTWa17LBYrF7y\nauQl1+WSw/2Hpnn81AoAe7b187pbxuhOhn9/S8YThmNtvKCrHPzAI9BB0616AxUwW5phKn+CpcrC\n8z5uGiZxK0FPorceqjHr/ICNE7NaZ2rTWnFiV6z7mtthShALcRbb9ZnL27iBamgAu37Ag7lZfnhk\nDl9pJvtT7N89wUR/umHHcDFKaVKxCINdMp6wUdZWvV7g4SnvrFUvTbHqhWoQ5Z1VThVOMF04ha88\nAPoSA1w/shO8aH01GzEjbfXmTWtNMpoiHduY/58SxEJQvec5m69QdqrDGRoVwlprHntumW8cniZf\n8ehKRLl71xi3TfaF/sK1Np5wpC9JSsYTbiqtNW7g4gQOfuASaFVfGTbbCtENXGYKU0zlT1Bw8wDE\nrTiTvS9gonsr6ViGvr4Uy8vruk3aErTWxCIxMrGuDf2+yP8u0dG01iwWHfJlF6OBAQwwtVTi3oOn\neG6pRMQ0uPPGEV6ZHSHeBKtOpTUD6Ti90pZy01Sba1TwlIsf+ABnrXqbK3y11ixW5pnKn2S2OING\nYWAwkt7CePdWBlPDTXfMG0lrRcSMkI5niFobXxshQSw6VsH2WCjY1Xs9DQzgQsXjm49P88iJJQBu\nGe/l7l1j9DVB6Cml6UrKeMLNsLbqdQMHX3kEyseoXWYO++rHxZS9MqfyJzlVOIntV3fkpKMZJrq3\nMdY10dBRgWHQutqyNh3rJhHdvHOVIBYdx/YC5vI2XqAwjca9CHqB4sfPzPG9p2ZxfcVoT5L9u8fZ\nMdTYfY4XEmhNMmIx1Jcg1gQr8nahtKr2cw7WVr263tHKaJJ7vedbK7w6lT/JYmUeAMuwmOjexkT3\nVnri4d82aQSNIhlNkYqmN/18JYhFx1gbT1h0vOp2pAa9lmiteXJ6la8fOsVyySUVi/DGPePs2zEQ\n+qqzPp6wS8YTXi2tNUorAu3jqwCtVfVXocJiOV8vrlrbYtSs8s4KU/mTTBemziq86me8exujmbGm\n2S602ZRW9f3JltmYN0ud8ZUVHW+p5LBcqo4nbOR94NnVCvcenOLofBHTgJddP8Srbxol2QTFT0rG\nE16S0gqlAnwVoKiGq9Jnfle1ANYo0NQGKJz52UrpSNNUOF/MmcKrkxTcVWCt8Op6Jrq3kY5lQj7C\nBtIa07ToincT24T7wJcS/quBEJuoZHvMhzCesOz4fPNHz/L9J2bQwAtGunnTbeMMdYd/T63aljLK\ncHfnjSfUWqPRBCrAV349WDVr4VoN2OrfVZuXcNbWofMZhoGB1cwL3ec5u/BqrjRTncKEwXB6lInu\nbW1feHUxqVg6tLGIEsSiLXl+wGwI4wkDpfn7owt8+4kZKl7AYCbOm24b54YtPQ35/Jc7tljEYktv\ngkS0uVdq1yJQAbZvo3XwvHBVqNq/uvQABMMwWylbr0jFK3OqcJKpfGcWXl2IptqWMh3tCvW+twSx\naDsrJYelolPdjtTA/1zPzOa59+Ap5gs2iajFPS/ZwW1j3UTM8NtSmobBSE+Srja8D3x2NbIXuOd0\nnjqHYWDSvm9ALiRQAXOlGaaeV3i1lfGubfQmOqPw6nxKK2JWjHQ0c9VtKTdS+EcgxAapr4K9oKH3\ngReLDl9/7BRPzaxiAC/cMcBrb97C5JZelpdLDTuOC9FK052KMdBm4wkvVY3c7PdlN5vWmoKbZyp/\ngpnCFF6t8Ko30c9E91ZGM+MdU3j1PFpjmCY98R5iVvPURnTod0O0m2pvaBejgcVYthfwvadO8+On\n5wm0Zvtghv27x9nSG859prMFSpOORxhuo/GE1RF4Lr5aG/HXGtXIjRAon6XKIvPlWeZLs1T8aler\nmBVnR+/1jHdvbfg4wGajtSYVq25HajYSxKKlBUozs1Ju6CpYac0jJ5Z44PA0RcenNxXj7l1j3DLe\nG/qqM1CKdCxKf1e8KTp0XYvqqtfGC1y8wENWvecqeyXmS7PMl2dZqiygdPX+d8SMMJoZY6xrgsHU\nSEcWXp1Nr21H2uC2lBtJgli0rNWyy0LRaeiWpBMLRe49OMX0SoWoZfLam7fw8hvCH09YrYSOMJBJ\ntfRgBjeozqqtrnqD+rADWfWC0kF11VsL37J35rZHJtbNUGqYofQIvYn+pg2cRlobT5i5hvGEjdLc\nRyfEBSilmVmtYHtBw5pyrJZd7j88zaHnlgG4bbKPN9w6FvpMXqU06USUwUycSMhvBq7G2ateP/Cq\nW4aadNhBGCpemfnyHAvlWRbL8wQ6AKoFV8PpUYZSIwymRkhGkyEf6RlaV7eHaa0u/483iWGYZKKb\n25ZyI0kQi5aSr1T7QxsGDQlh11f84MgsPzgyixdoxvuq4wm3DoR7n0lp6EpEGMy03l5gL/BqI/7O\nXfViGLV9u51LacWKvVRf9RbdQv1j6WiGofQIQ6kR+pL9TXd5vjqfF5LRFFt6Roh6xbAPqWVIEIuW\noJTm9GqFsuc3ZEuS1prDUyvcf+gUqxWPTCLCW/eMsXtrf2htKbXWaA09yRj9XfHQ22Neqepg+wpO\n4OErD62VrHrPYvs2C7Uiq8XKPL6qTmIyDbO24q1ecm7GIiOgWolsGCRjaZKR5Fm3EsSVkiAWTa9g\ne8zlK5hGY/YFTy+XuffgFCcWS1imwSuzI9yZHSEeUhMMrTVQDeC+TGsEsB945O08y5VlAu2BPtP+\n0ejw8NVas2IvM1+eZaE8S95ZrX8sGUkx1jXJUGqE/uRgw3odXw2tFaZpkYqmSITUkapdSBCLpqWU\n5nS+QtltzCq4aHs88PgMB44vooGbxnp4465x+jPh7DdUtUYcvek4falYU68yqqteG1dV7/UGOsB0\nu2rtE1urBeRmcAOHhfIc86VZFspz9b29BgYDySGG0tWVbzqaaervM5wJ4HQL3YNtdhLEoimVbI/Z\nfAWjAatgXyl+8sw8333yNI6vGO5OsH/3BNcNh7PvUmlNxDToT8fpSTVP04Hz+YGPE9jVphrKP3Ov\nF9lepLVmsbjI0aWTzJdmWXWW6x9LRBKMZMYYSo0wkBpqmeYaGoVlREjFMh3ZDnMzXfVPQDabHQYe\nBl6by+WObNwhiU6mtGZ2tULZ8Te9CElrzZHTee577BSLRYdkzOItt0/wwh2DoTTBCJQmapm1AA63\nGvtCqqteB1c5tVWvqt/j7aR7vYEKcAIbx3dqv9s4gVO9IhBU/67ilc9Z9fYlBuqFVplYuH2N10tp\nRdSKkoo2fipRp7iqIM5ms1HgT4Fw+/eJtlKyPebyNhhsegjP5W3ue2yKZ2YLmAbccd0Qd908SiqE\n8YSB1sRMk6GeBJkm6wW9tur1lIcfeHDOqrd9wldrjae8WqhWQ9YNbOxa2Lq1oHUCu15MdTGWYRGP\nJJjon6AnMshAcoio1Vzf1yuhtSJqxUhGUxLAm+xqX3U+BXwe+NAGHovoUEpr5vIVSvbmr4Irrs+3\nnzjNz47OozRcN9zF/t3jDHc3fh9moDTxiMVwOka6SQJ4bYCCEzj4gXvOqrcVi6yUVvVQdWrB6l5k\nNavRl3yuqBkjEUkRt+LEI4mzfk8Qj8Trv1tGBMMw6OtLsbxcbtCZbpxqAMdJR1NEWvANRCtadxBn\ns9l/BszncrlvZLPZD9HxZRjiWpSc2iqYzV0FB0rz8LEFvvXEDGU3oD9dHU+Y3dLd8MuEgdIkoxZ9\nPfFQVuDnU1pR8Sp4am2AAi256q3uwV2ubwOqXh52L/kYA5N4JE53vPecMK3+fiZsY1a8pb4WV0PV\nWkGmIumm70TVboy1rRFXKpvNfg/QtV+3Azng53K53OxFHrK+TyA6gtbVHtH5kodlbW4Q5qZX+Jsf\nH2V6uUwiavHGPZPcectYw9tS+oEinYgw1J0kGXIAO56D7VdXhkEQYIY8qvFqVbwKM6szzKzMMJOf\nqfWkrr6ByMQzJKIJktHk835Pxqp/jlnNXY3eCFprEtEEmXhzjARsE+v6oVp3EJ8tm81+B/i1yxRr\n6fn5wiU+3N6Ghrro1PO/2LmXXZ/Z1UqtE8/mvQguFR2+fugUT05XxxPu2d7P624Za9hM3r6+NMvL\npeoghniU/kx4gxguNUBhs2zGpVmtNavOMvOlOebLs+SdlfrHEpEkw+lqy8f+5GDo1cjNfmm6Ogwh\nQTqW2fDVfie/7gEMDXWt64VN3v6IhtFaM1+wyVc8LHPzuu84XsCDuVl++PQcgdJsHUizf/cE432N\nbTpQvQQdYSATC2UQw7ljA4Oz7vW21gAFN3BZqPVbni/N1S83Gxj0JwcZSo0wlB5piT24YVtbeCWi\nCVLRdNtfbm8V1xTEuVzuro06ENHeKq7PbL6CUnrTtgYprTl4colvHp6mYPt0J6PcvWucXRONHU+4\nNgnpBVu6WVps3MaCdhmgoLUm76xWg7c8y4p9Zg9u3Eow0b2NodRwbQ+uFBNdibWrT8loklQ0LW9Y\nmoysiMWmqq6CHQq2i7mJPWifWyxx78EpppbLRC2Du24a5RU3jBCLNC6AqlXQJlt6kiRiEawG3Hdt\nlwEKXuCxWJmvdZ6axQkc4Ow9uMMMpkboijW+uK6V6Vp3tkQ0RSqakq9dk5IgFpum4vqcWCyilN60\n/sj5iss3Dk9z8GR11bRropc37Bqnt4ENMbTWGBgMdSU2vRGH0grXt1t+gILWmqJbYL427GDFXqpv\nH4pZ8Xq/5cHUEFHZw7p+tRVwKpYmURvEIJqXBLHYUFprCrZHwfZIuD5asykvAl6g+OHTczz41Cxe\noNjSm2T/7gm2D2Y2/HNdSqA0vckYA13xTXux8wMPO3Bqq14Pg9YcoOArn8XyfH3Yge3b9Y/1xPtq\nnaeG6Y439lZCq9Nao1BYhkXEsLDMCBEzKn2gW4gEsdgQjh+wUnIpOR4aMA2D9CZcmtVa8/ipFe4/\nNM1K2SUdj/Dm3RPs2d7Y8YRKaVKxCINd8Q0vxDp7gIIXuCit6r2bW6mH89qqd23YwVJlEU11WHzU\njLIlM14fdhCzmrendjPRWqPRWIaFZZpEzAiWESUWibXUFRFxLglicdWU1qyUXYq2h+ureiX0ZsXh\nzEqZew+e4vhCEcsweMUNw9x54yiJBo4nVFoTNU1G+pIb2ozj7AEKgfZBG/Xq5lYK30AFLFUWmC/P\nsvTcPEXnzHD47nhP7XLzCL2JPln1XobSCtBYRoSIeWalG7WiErptRoJYrFvZ8Vm1Xcq2T7UuyNjU\nIQklx+Nbj8/w0LHqeMLslm7etGucga7GXnpTWjOQjtObvvbV2/MHKAT1wDUwW2l3EWWvxHypWuG8\nVFmoBQhErSgj6bF6oVVCJvZclK59zSzTImJGMI0IUStK1IzKG5YOIEEsrkigNMslh5Lj4QXVLUib\n3RfaV4qfPbvAd548je0FDHUleNNt47xgtHtTP+/5lNJkElGGuhPXdPm7PkAh8PCU17JjA5UOWKos\nMl+q7u0teWdWvZlYV31f747RCVZX7Us8U2dSOsDArK9yLTNC1IwSMSMSuh1KglhcUr7iUrA9Km5Q\nX/U2YkTgkdOr3HfwFAtFh0TUYv/ucV68c6ih4wkDrUlGLIb6EsSu4j7w2qr3QgMUWu3SYsWr1Pf1\nLpbnCXQAVCcNDadH65eck9EzwzNatW3mRtFao2pXOtaKqEzTIm7FsczWeeMlNp8EsXgexw9YLbkU\nzyq8alQAzhdsvv7YKY6czmMAL945yGtu3kI63rgfVaWrK/4tXcl1T0U6e4BCkC9RcMotPEBhqXbJ\neY6im69/LB3NMJgaYSg9TH9yoKVW8xtFa129nFzbr20aJoZR/R6v/epOdmM4yZb6votwSBALoBo+\nq+Xq6rcRhVfnq7g+333qND95pjqecMdQhv27Jxjtaex4QqWhLx2n/wrvA6+NDXRr24uUDjDW7vVu\nYgOTzWD7dr2N5GJlrj531zRMBlPD9UvOqWg65CPdPNX724rqnXoTwzAxDaMerkbtl2Ws3cs1L/o9\nTsVSlIzO7bcsrpwEcYcruz6rlcYVXp1Pac3DxxZ54PEZyq5PXyrGG28b56axnoaGmNKadCzKcHfi\nsve+q60kK7jB2tjAM60kjRZaHWqtWbGX65ec885q/WPJSKreVKM/OYAV8gCFa1Hd8lMthqrelz8v\nXFkLV7N2z9Y65/69EJutdf93iat2TuGV0ljG5hdeXcix+QL3HjzF6dUKMcvk9bdu4aXXDzd0PGGg\nNLGIxZauBInYxUP03AEK/pkK55YboODU9vVWC608VR0baGAwkByq7+tt1QEKWmswNFEzVit+qq5e\nLcPENC25TCyakgRxBylUXPLnF16F8GK7XHK4/9A0j5+qjrDbs606nrA72bgG/mtN8Ed6khcci3jp\nAQqtterNOyvM15pqrDpnBigkIglGMmMMpUYYSA229AAFpRUR0yIeTZKMSE9l0VokiNucW+t4FUbh\n1YWO5cHcLD88MoevNJP9KfbvnmCiv7H3HJXS9KRiDGTObUvpBi6O79THBrbyAIWF8lytleQc7vMG\nKIwwlBohE+tq6cCq9viGWCROMpIkYrXuGwnR2SSI21DYhVfn01rz2HPLfOPwNPmKR1ciyt27xtg1\n2dfQtpSB0qTjEYa7k1imUV31ehXcthigkK8NUJh73gCF8a5JhtIjDCSHibZBWCmtiFpRElaCeCTR\n0m8mhAAJ4rZScX1WQiy8upCppRL3HjzFc0slIqbBnTeO8MrsCPEN7s98KWttKUf7kkQsTcUvtcEA\nBY/F8kJ9epETnGmc0Zvoq+/r7Y43tuhts+jaHuxYJEEqmmqpN0pCXI4EcYtrlsKr8y2XHL722Cl+\n+vQcALeM93L3rjH6NqA95JWq7vXUZBIGyURA2V9Gea07QKHkFevzeqsDFKqr3qgZY0tmolZoNdQ2\nAxTOFF7FSUYTbXNeQpxPgrhFNUvh1dm8QPHEqRUOHF/k6Hy17eFoT5L9u8fZMdTVsOPQWlP2KiRi\nmu40mFh4QSsOUPBZrCzU9/ZW/HL9Y2sDFIbSI/TE22uAwlrhVSySlGH2oiNIELeQZiq8Otv0cpmH\njy/y2HPL2F619eG2wTR33jLOdQOpht0Hdv1qsVU8phjujmFZrRO6ay42QCFiRhitVTgPpoaJt9kA\nhbXCq2gkTkoKr0SHkSBucs1WeLWm7Po8dnKZh48vcnq1AkBXIsKLdo6wd1s/g10J+vrSLC+XNvU4\nlNbVlpKBQyZusqU/3tACsGt1ZoBCNXzL3pmvV1esu9ZKsjo2sB3vi54pvIoTjyRl9Ss6kgRxk6oX\nXjkBBropCq+U1hydK/Dw8UWenF4lUBrTgJvGeti3fYDrR7obcozVtpIejm8TaJ+eZIzRVOu8iFe8\ncn1r0cUGKAylR0hEGtves1HOFF7FSUZSMgBBtAWtFMq20Y7NgX/1nsTeL33pikePSRA3kQsWXhkQ\nduem5ZLDIyeWOHBikdVytRPTYFecfdsHuH1rP5l1Dka4WkHgYwcObuBiAt2pKN3J5r+HqLRiubJU\nbyVZdM/0H05HM/V9vX3J/pa6h70eZ3e8SkaTUnglWpoOAlSljPZctOuhXBcd+NWdF9XXozQgQdxK\nmrXw6snpFQ4cX+LoXAENxCIm+7YPsHf7AJP9jQlArTVOYOMGLp4fELNM+jPRC3bDaia2b/Ps/Awn\n5p9jsTJ/1gAFq77iHUwNt/UABTi38CoZlUlEovVo30eVyyjPRXse2nVBBWBaZ7Y+AoZVjVOt9bo/\nhwRxSDw/YLnkUnJ8FLp5Cq9Wyhw4vshjJ5eprBVeDaTZu32AWyZ6G7b/1wu8Wpcrr9YP2mSoO9aw\n1dt8130AACAASURBVPd6rQ1QqF5yvtQAhcG2vhS7NmDBMizikTh9iagUXomWoVwXZVfQbi10PRcC\nBdZZoWsYYG1sdEoQN5DWmpVyterZ8aqFVxhghnzpueL6HDy5zIETi8ysVAuvMokIr9wxzJ7tAwx1\nNaZCV2ld6+/s1PYAQzRiMtAVJR1vvvByA4f5UrWV5GJ57qwBCiYDySG2Dk6QMfpJRdNNf/n8aikd\nYGJgmVEiZoSIGSEWiWMaJr2pLrySjAEUzUdrjXIctF2pBW4tdLWur2yh1uQnsvlXcSSIG6BSGzVY\narLCq2NnFV75tcKrG7f0sHf7ADeMNqbwCsDxq/N8q1ONDAIF8ahJbypK8hITkRrtzACF2doAhZX6\nxxKRJKOZcYbSw/Qnh4iYEfr6Uiwvly/xjK1F6QADk4hZm8VrRohb8bZe4YvWVy+icm20Ww1d5XnV\n7oNn/ewaIf4cSxBvoqWSQ/7/b+/MYyvL8vr+Oecub7fLdj279q276/VSXTN0z4QwGQ0DIRBQEMwo\nUqKQRAFlE0QiIhIJRBpFEZESoRCSkEQJCYGgKEjAEAYQaMQiSEaIMN09vfft6aWqq2uxXS6vb7v3\nnnPyx33veWnXYpffu3b595FK3p7t81z2/d7f73zP72ssc0utfWO8WmrGvHh1gZeu3mGpFQNwtJoZ\nrz52dnJke6/GGDomC1jAOVCqVwEr6rXgnpGEoyQxMbdb84OWc2yyn5lCMVk6Sr08zdFHIEBhK85Z\nHAxE19M+oQ6lzbxPcdaCNdg4waUJWJvremIdky4N9+jiPXF2g4kqQSkPpdcrW73PZgyIEA8Bax03\nlpp0U8tUIdgXxqu3bizzwpWFdeOVp3nu3CTPn5vi9ORoWqfrxqsEY83gvK9xUAo0Ryr+SGdQ322N\nq/HKYJTkYufO4GMFr8DJ2plegEL9kQhQgI37un5PdD1Cr9DL8310bi4OIs4YnEmzSs6kYC3OmPWX\nxuCswVmb3eIrvUlw8iL1DXY1/25QZqLa/3+nIsR7TCc23FzOfgHzHixxc6nFi1fu8PK1O7TjzHh1\npme8ujRi41VsuiSmt4eqVK8F7agUPI6UA4IR7MPcjdQm3G7ND0ZJbg5QmKRenqZemaEWHvwAhc2i\n6+FpH18HhF544J/bQcE5B8Zg0xTiGOcMGNsT3ayyzcTWDLpFKH3X/x+lNMrLX3yF3SNCvIcsNbss\nrHVzC11IjOXK/Bpv31rh7VvL3GlmbdRqwefTF6d57uwU9bHRGa/Wumssd5az8YVKDS4kxjmqoceR\nSoCfwwUkC1BYHRitFrcEKJyonaJenmGqPE3ohSNf317RF12tdFbpqp7o+uHQjxGZdotkMSVdybE9\nmTOxjkkWlgci269inbUo50DfvXpVALL3fmgQId4DnHPcXGrRTuzIRXix2e0J7wrvz6+SmExQCr7m\n6ZNH+PiZCS4eG8/FeDUeZpOh+gJsLVSLmQCP2qyW2pQ77duDUZKdtD342HjhyGCU5HjhyIGuDJ0z\naOXheyGh9glGaKZyxmCWl7GtNZwxJKaGXc6/PZkXqU5wrfam9ymlUPtsf1LIHxHihyRODdcXWzjn\nRtKKTq3lg9vNgfjOr663Ueu1IhePjXHx2BhnjlbwR7RXtJ3xqv+zyKqybA71kXIw0huVZrw2cDgv\ndhY2BCgEmcO5PH3gAxScsygUvhdkM5v90Q7NcM5hm2uYtTVcpz04+pGnA1UQDhoixA/BSjvh9kob\npdVQq6jlVszXZzPhfXdulTjNBCXwNI3jY1w8Ns7FmTGOVEbXRs2MV9m4yY3GK7YR4PFKMJKbFGOz\nAIX+KMnNAQrj1CvT1MszjB/gAIVsVKTFU5nwhl4hl/a57XYxqyvYVlbxZpWeXE4EYTfIX84umVtp\nsdpOh1LhGeu4dqc52OudXV6veqeqhUHVe/ZolWDEe6x3M171Sa2j6GuqJZ/T02WWltp3+1J7Qitp\n9UxWsyy0b2MHAQo+M5Xjg1GSBzlAwTqDpzx8LyDUIQW/mEv73FmbiW9zDZckqA0j/gRB2D0ixDsk\nNZYbS00S4/ZUhNc6yaDqfWd2dZDr62vF4zO1THxnxpga0ZSrjVjn6KYdYtPF4VBs7gBY6/C0olTw\nGC/5AwPWMC7SWYDCAvOtOW5vCVCohrVsr7c8zURp6gBXvVm72fN8Ah1S9Iu5Ds0w7RZ2dRXbbg3c\nu9J6FoS9Q4R4BzS7CbMrHRQPfzTJOsf1Oy3evrXC12dXuL5hAtORcsjl0xNcPDbG+XqVMKeztVsn\nXkE2yKKPcY5S4FEr+ZSHOICjk7aZb2bCe7s1N4gN3BygMEM5KA9tDcNkv7SbN61pi/FKaU/EVzj0\nOGOwrWbmi2iuYZvZ6/2XptnEttZ2/HVFiB+QhdUOi634ody+rW66oepdoRX3BQUu1KtcPDbGE8fG\nqdcKubX81o1XXXBsMl5lH3cEvqJS8Bkr+UNpzVtnWe4FKMw3Z1mNVwYfKwcV6uVMeCdLUwd2vKJ1\nFk/p3NvNGxHjlXAYcc7h4jgT1LuKbBPTzP4u7onnocs7T1QTIb4PG6dk7VSErXNcu73GV9+e5e1b\nK3x4p0k/IKtWDHj+3BEuHhvjwnSNYpDfxW5745UaTOPsG6/KocfY+HCmX3XTbs9kNcft1hxpL0BB\nK83Rnru5Xp6hElb3/HuPAucsoPB77eaCV8DfJ+YmMV4JjyLOWmy73RPT7UR2vYolTe75tVShgK5U\n0fU6XqWKLleytytVdKWSva9SRRWynO25n/5XO1qr/LXdg91OyXLO8dLVO/zu6zdZ7fQTebKpVk/0\njFbHxku5V0D3M14Z6yj0jFfVwt6OO3TOsdxdHLSctwYoHK+epF7JYgN9ffB+TZ1zWGfRShHokNAP\nCb1C3ssaIMarg8t6e7QnJK3N7VHbbGb7+bvIxd0r7ngaa/Kbd93/Gd3zZ6AUulzBn5zcLKgbRNar\nVNCVCsp/8DGZkke8hyw1uyw0uzveC15pJ3zpxQ+Ibq0Q+po/88Q05ybKPD5ToxTm/+N+EOOVVopy\ncbPxai+ITcztnslqvjlHYrcGKMxQr0xTCQ5mgIJzFqUUvg4peAEzY3X8eH9Nllo3XrWhN+1MWs/7\nAxt377rnuPFt177PkBSt0aUy5Dhz2lm7K0HaK5TvExw/ie4Jab9i3VTJlkr7Yi43iBB/BOcct5bb\ntGKz4yr4lWuL/NbXPqSdGC7Uq3zu+bOcPzXB4mL+F+P7Ga9S6yiHHrWiT3mPsn8HsYHNrOW8tClA\nocipsbPUy9NMlev4ev8PZt9K1rJ3+Noj8EIKXnFTEMR+cW1vb7zaH2t71HHOYVutTWLa328cvN2r\naF1yn/ZoWMiEZWoqq9bu1h4t5u83eNQiQIeNCPEG4tRwY7E9aCk+KGudhC+9dI03bywTeprv/vgp\nPnnhaO5/DHkYrxKTsNCeZ745y52r87STdXPDRHFyMEqyFo7l/vPZDRsnWYVeZrLaL4K7kbyMVy5N\n6Hw9ovPWGyx125gc25O54hwLnTbp2tr926OlMt6RiS3t0ExcdXn9fSo4eDerwoMhQtxjtZMwv7zz\nKVmvfrjIb750jVZsOHe0yueeP8NkNZ+9QOcciU1JTExqE+xg7Ob2xqvamP/QJjHnHGvx6sDhvNS5\nMwhQKPgFTtRO91zOdYIDGKCwser1dUjBz/9o0VactdhuF9ft4JIEF8dZJi0KpfXQjVfOOdLZW7Rf\nf4VO9Aau2wVAh2Ge25T5osCvVAiOnVgX1a0iW6miS2XpTggixABzK21W28mOKsJmN+U3v3aN1z5c\nIvAU3/Wxk3zjY/WRRx9a54hNh8SkGJsC64M0hmW8ygIU5gfpRZsDFCaoV7KhGmePnRj6ZK1h0Hc4\nB15A4GUDNfZL1dt3grq4OxBdmyZZFN6GC/oo9n1tu0Xnzddpv/4K6e15AHSlSvljz1F85jL18ycP\ndXtS2rPCg3KohTibktUiMTtLTXrj+hJfeukazW7KmakKn3v+DEdHOPEqczsnGJf0jhttP8lqo/Fq\nrOjvOvPXOUcraQ6q3jvtBRy9edc6GDicj5anNzmDD1Lr2TqDr/1e1bs/HM7OGGy7lYltnOCSGJem\nKG+zw1mP8KiRs5b4gyu0X3uF7ntfB2NAawqPNyhdukx49rxUeIKwQw6tEDe7CbeW2+gt+6b3ohWn\n/NbXPuSVa4v4WvEdz57gU09MD70KzqreLqlJSD9S9erBY6yDwFO9f5pi4O3aeJUFKNxmvjfNamOA\nwlhhvDdKcobx4pF9Uy3uhPWq1+9VvaNNLdqKTeKs0k1iXJxkxh1rYMOxIkXmBs2DdGmJzhuv0H79\nVexaNlbUmzpK6dJlSk9eQpcP5lQzQdgPHEohXljtsNSOdySg0c1lfv3FD1jtpJycKPP5T5xlemx4\nVfDmqtdmF+HecRNjHTiXCa6vCbSiEHgUQ/1QNwWtpMl8MxPehfb8htjARyNAoR+eEHjhYIzkqKv2\nbIpPF9vprItuGoNlU06tUgpyHqqRGa/epvP6K8TXrmbrCkNKz36c0qXL+DPHD1TXQxD2K4dKiK1z\n3Fhs0U0f/GhSJzH89ssf8uLVO3hK8W3PHOfTF2f2PNh+Y9VrbNozOStS41AKAj+rcgNPUww1BV8/\n9EXQOsNi+86g5dxM1mekVsPaYI7zkeLkAa16HeCyrN4cwhOctZh2e4uJKgXcpj1cpTzYJ0d5nXOk\nc7O0X3t5k/EqOHma0qXLFJ9ooIL9ZVYThIPOoRHi3UzJemd2hV974QNW2gnHj5T4/CfOcmx876rB\n1KTENnM4p8ZgHXhaEfaq3MDXlEK9p6EP7aQ9yOtdaM0PAhQ85TFdOTZILyodsAAFZw2m20GlKdqA\nl1o86whVMLhhSXv/RkVzuUS61N5iotqfNzS23abz1uu0X3t5k/GqdPk5Ss88iz8xmfMKBeHR5VAI\n8U6nZHUTw++8ep2vvr+AVvCtTx/jM41jD10Fu17VG6dZ27kvuqVAE5Y9SoGP7+11pW1Z6twZDNVY\n2yZAIRslOYVW+6Qsuw/WJNhuF20MOgUvNShjqeoCuj9QQ5F7lam9/T04w1lLfO0q7ddepvvuRuPV\nRUrPXCY8d2Ffr18QHhUeaSF2zjG73Ka5gylZ782t8msvfMBSK2ZmrMjnP3mWE0d2Xx3GccJqpwkq\nxfMsgfYYr3iUwvKet7f7dNPOIK83C1DI6sB+gEJ2rnf6QAQo2HSz6GpjKBjwvRDdn8alAxC9eGDM\n8hLt11+l/car2NXsxsybnMqMV09d2lV6jCAIu+eRFGJjHYvNLmudjUMt7k2cGr786g3+5L3baAXf\n/OQMn33qGP4uKgJjDK2kTbHgKBTLHJ/w0EMMLlgPUMhazivd5cHHSn55MFQjiw3cv//ladyFuIs2\nDi+1aOMoWvBEdB8al6Z03nmbzmsvrxuvgpDSpY9RvHSZ4NgJMV4dIJwxgAOlQOnM6NfvwHh6k/Ev\nD/yxKjo91H+oK/d/yDr796q8C1baMaudhHZsBtXmg1xcrtxe44tfvcpiM6ZeK/L5T5zh1OTOqoJ+\n27mTdAl9y7GJEp5W1Eohi52935mMTZfbrbleetHmAIWp0tHBKMlKUN13F1jnLKbbhSTGs6ATi2cN\nResRBBvO73rk3l4+6CT9iVdvvb7BeHWK0jOXKV58UoxX+wjnHFiTGTW1AuVlgqo9VF9cvd6kND9A\nBwHohzdtDoNwqoZvD+/v1nO/+Iv3Hhy+hQMvxHFqWGrGrHUTHJkR60Fbvomx/O5rN/jjdzJzyqcv\nTvOtTx8n2EHiUD9KsJvEBL7m6FhIKdx79cgCFJYHDufl7uLgY+sBCjNMlY/uqwAFZw1pt4NOzYZK\n11JRPt7W/VwR3V3hrN0ci9dcw66t0n3366Tzc0DfePUNlJ65LMarEeOsBWtxqjdyVOusevW8TEj7\n1azno8NwvbIVDg07FuJGoxEAPwecBQrAT0RR9Bt7vbB7YZ1juZVVv3Fq8XrzoXdyX3htocmvfvUq\nC2tdpqoFPv+Js5yZerAquB8lmNiY1KRopZmshYyV9lYAswCFuYHRKjZZRaNQTBSnqFey/d7qfglQ\nsAbT6WCTBN+CitsEd5o9E1XvV03xCNz+jQYbx5nArm0Xar4h7Lx1lzGKWlN47AlKlz42MuOVcw6c\nRRdLeNUqqjv0b7kvUUrhj9XwbAhBr3r1JPNZ2J7dXBK/D5iPouhvNBqNCeBrwEiEuBWnLLdjWp00\n2xrZQfXbJzGW33/jJl95O6sUvunxOt/2zAnCBxj/GJuEOO2QugSFxlhHrRQyUQn2ZLrWvQIUQq/A\nydppju6XAAVjsHEHl6SYOAaTElhFwStQ1NmgjKpfIgkO69T/7XHO4drtTFD7Itt73fTFtZlVty6J\n7/m1VBiiK1WCialtAwWC+vTIjFfOGlQQ4FUqeLVxlNYU6jUCRjf6db8RTtXw7GreyxAOALsR4l8G\nfqX3umbIRzP7xqtmNyGxDk+pXcf1XV9s8at/epX51Q4TlZDPP3+Wc/V7O4eNMXRtl8TEOOdQSmFt\nNlTjWCXY9fzmPqlNWWjN90ZJztJJO4OPrQcozDBWGM/tbtr1nMsuTSBJsUmCcwbfCwmUR1UV8P39\n4bR1zmEW75DOz2JW870Ipp6jdXsR09oQ9t5qgr13NKAuV/COHNmcM1uufCTFJ+/93f7AFF2q4I+N\noQv5z+cWhIOIcrvMKWs0GjXg14H/EkXRL93jobv6BsutmOVml2Y3xd/Bnu12pMbyOy9d48svX8M6\n+MzTx/meT56jcJcIQOcc3aRLJ+2QmnQggM45PE9ztBZSKe6uv+qcY7Wzyo3lG9xYvsH86vooydAL\nOT5+nBNHTnB87DiFYLQXNuccLkkwnQ4u7QlukmR5qjqLUwy1T6gDil5h5ElTH1lvmtKdn6dz8yad\nmzfp3rhBZ3YWF9+7kswD5fv4tRp+tYrXe+lXq9n7+m/Xanjlcu6O1/thjcErFrN1j+2TbRFB2F/s\n6I9iV0LcaDROA18E/kMURT9/n4e7+fkHq0y2M149LDeXWvzqV68yu9zhSDnkc8+f4cJ0bdvHpial\na7LqFzY7rq1zjJcCjlR2tg88MVHm9sIKd9oLg5ZzO13f0xsrjPfO9c5wpDgxuouas71En242djFJ\ncKYfKOFlwfI4fOURKI+CCvF3ePRpL2PgbByTzs+Rzs+SzM2Szs9mE6A2VpdK4U8exZ+exp8+hjd+\nJFeRqE1UabkAXamgwsKBFiznLCiVVedj4+gHCJ+o12s86N/+o8hhfv6H+bkD1Ou1Hf2x78asNQN8\nGfjBKIr+YKefv5W9MF5tRztO+eN35vnDt25hHXzi/BTf8exJiluq4I3Gq36k4CYBto5K0WeyurN9\n4G7a5dbadV6ev83syuyWAIUT1CvTHC3PUPRHsIfmesHxvRg9khTbq/TVhhnSjuxn7yuPQPsU1OhD\nESAbt5jMzZLO3SKZnyWdm8Us3tn8IM/Hn54hqM9kL6eP4R89ivL3j2O8MlEmPuB5tM6azHhVG8OT\nhCVBGAq76a/+ODAOfKHRaHyh977vjKKoc4/P+QjtOGWpHdPqGhRuV8arrVjnuDK/xotXF3j9wyVS\n6xgrBXzvc2d44tjYpseuG69S+rK/MdjAWkch0EyOhw8869k6y+3WHNdXPmCueWtgtKqGY9TL08ML\nULAGm6Zg0uygv3U4a8A4XJrgjMmOTWzMsO2FDlhns0Qi5RHqkGCEAz+cc9i11Z7ozg6q3f60pz6q\nUCA4dToT2/oMwfQM3uSUHPEYEs4YVJhV8n3jlSAIw2PHV90oin4Y+OHdfLNtjVcKdthO/wjLrZiX\nrt7hxasLLDaztvJUtcBz5yb55PmjlEK/9/0tXdPZZLzaWnv333+0VqBSfDABbsZrXF/9gOsr1+ia\n7H6kFo5xcuwMF09cIG7t4vm57OyhTdKsZewMzmRHQzA2O/hvLGDXd+HVRw/3KzbH61nnQEGgfALl\nU1DBSJKVnHOYpUXSuVmSuVvZy/lZXLu96XG6XCE8d4FgOqt0/foxvPH8jGqHhc3Gqxq6cHjdzoIw\nakZS/qy2Y1a2TLzyHvLCmlrLWzeWefHKHd6ZXcEBgaf5hrOTPHduirNTFZRSmfEq7RCbhNSmg/by\ndhd2a2Gs7HOkHNz3wp/alNm1G3y48gGLnQUgazufGT/HydrZgcu5UigTbzznaQ3OGlyS9g769wR1\nILA2+7hz2S3ClvbxRrI1eve9j9lY9QY6IBzBwI90aYml999i5cq1XrU795HjON7YOP7JM73W8gx+\nfQavuv/nXz9KOGNQxQJ+pYau7r8pbIJwGBi6EH/9xhILq50dTby6F7PLbV64ssDLHyzSijNz0anJ\nMs+fm+LSqQkKvsZYQydukqytDoxX9C4w2x0csdZRDjWTZQ8vUZjlbR5Eb7pVusqNziy34vUIwYlg\nnJOFY9QLU3jKg47FdLLJV920SbrU7ImtyapXBXD30XRKaXZ7PeybrHR/r1dle73DrnpdmhJf/5D4\nyrt03393856uUniTU9l+7kxvX7c+gy5K1ZUHuzFeCYIwPIb/F6jUQ7ufO4nh1WuLvHBlges980ul\n4POpx+tcPjPOZNXHWkNs12i1YlyziYuTu1aSfRyOQCsmS5qCDyTbn++MbcLN9DY3knmaNmulFlTI\nmfAYJ4KjlHRPUGIDmM3fo6DB2F51e//qdSdkbWaHRuMpD19pPDLxHUW72ayu0H3/PeIr7xJ/cCU7\n6gTgBxQuPM6RpxoktSn8o3VUsH9MVIcVaw2eGK8EYd+xb2+FnXNcub3GC1cWeOP6EonJWrWPz1R4\n9lSNc9MllLJASmwMLk0wrRYkMVm1eXchcr1EpomiR7Ww/eOsc9wxy1xP5ridLuEyTzHT/iQngzqT\n3mj3La2zoMDDw1MajSbUAb4a3dg8ZwzJzesD8e0HyAN4E5MUzl0gPP8Y4cnTKN/f0+NLwu7YaLwK\nxHglCPuSfSfEK+2YF6/c4cWrt1lsZhXWkbLPpVNVnj5V3TLPWePShLTZE2CluV9GnnOOaqiYKG0v\nYC3b4UYyz81knq7Lvn9VlzgRTHPMnxrJ/qpxFtUTXV952f4u2ctR7+GZtTXiq+/Rfb9X9fYSfPB8\nwnMXKJx/jPDcBfwjEyNd193oJ9igPVTeLdfeMP+8UEEoxitBOADkLsTGGDomJrqxzMvXlnl/roUD\nfK14+mSVZ09XOTVZ/IgA2TjJxgWaBNA9Eb471jlKvmKy5H1kUpdxhtn0DjeSeZZMdgjdx+NUMM2J\noE5NV4YigM65rNJWCk95eGh85eHj7Xh4xp6tyVqSWzcHe73p3OzgY3psnOKTz2Tie+pM7u1mZy3O\n2UFqjQoDVBCiS/tjOlW5XqNZOrxDDQRBeDBGdrV3zmGsIXUJxhqMM8wtd3j12ipv3mjSjrP92WNH\nCjx7usqTx6sUgo+Kq+3G2HYTTEpW/d5/H9hTinrZo7jh6znnWLFNbiTz3EoWML293QlvjBNBnWl/\nIjNe7eHz74uur7ysxaw9whHt594L227RvfJ+Jr5X3sN1ekfCtSY8fZbw/GMUzj+GNzGZ37xra3E4\ntB+ggt6/QhFdLEq7VRCEA83QhXilvcJKdw1jM8NSnDreurnGq9fWuLWUtTlLoeb582NcOl2jXtt+\nkL3ttrGtdtZ2fEABzuICPWob9oHvarwKZjge1CnrvWnj2d4+dKAysfW1PzIT1f1wzpHO3qJ75V3i\nK++R3Lwx+Jiu1ig++yThuQuEZ86iw9EP8nc2uylSQbhZdAsFEV1BEB45hi7EiUmw1nL9TodXr63x\n9s0mqc2MV+frJZ49XeOxmfJdjzbZThvbfnABts5R9BXV0KMSZo91zrFglrmRzDG/xXh1IqgztQfG\nK+ccTjl85ePjUdAhvvY4EpZx+6BNajsd4qvvD8R3kGGrFMHJ01m7+fwF/Kn6SKteZwxo1RNdv9da\nLmVvy5lWQRAOAUMX4j964zYvvLPIUis78zte9nn2VI1nTlWple727R2m3cG1W9mgi/sIsHUOXyvK\ngWKs4OHpbJDHdsarii5xMqhzzD/60MYr6yxaaYJexVtQ9x8EMipcmpIuLhC//x7dK++S3LiepSiR\nTa8qPv1sJr5nzo3kPO8mE1W4Lrpeubyv5kMLgiCMmqEL8e+9Mt8zXlW4dLrG6W2MV+s4TKu1Yeyh\n4m4CnI3kUxR9qBU0zktYNU3eT5qsmhYrtkniMvH38DjZM16NPYTxKqt66RmqdK/qHe1sZtftYJtb\nQ+Q3Bstnbw/czT2C4ycHLmd/emboNwzOmmzyZhCiwyALsd8nJipBEIT9xNBV5C994hjnJwvbGq8G\nOIdpNddNQveYepFaC34H63VIdIs522K108RsmZlVVAWO+DWm/Qmm/cldG6+ssyilstnMQ0okctZi\nW03s2kZB3SCyzSamlb3EmHt+LVUs4VXH0DMVdG2M8MxZCmfPo0vDH+DgnAPn0KUyXq1K5cwMrUMc\nhSYIgvAgDF2IP/n4BKurdwlmuo8AWyxdWnRo0VVtYtWio9tZqtGGIVYVXaKmy9S8yuBloPxeEpGF\n1OK2HW653ZL6e73ZMaKCKmRV7yC2OeVBE5ydtcQLbeIbt7dUresia5rNrAV/L7RGV6r49Wm8cgVd\nqaIr/ZdVvP7r5UouFaezBhWGeOUKXm1MDFWCIAg7IJ/DqtaSNpsQd8hazwpDmgkubTqqRZcWMZ1N\n2qxQVHWZMa9MTVeoeWWquryp2nXW4q58QPLGm7grVzcHx+8AA3SB5sM8T2D+Hh9TYSET1KmpnqBm\nYtoXWq8ntKp4r3Z+PjibzSvW5V5aTw7uakEQhEeBkQqxM5a0uUqaNOnSyardnugmanMyj0YzrmuZ\n6HoVarpCRRfvevzHLS1h3ngL+1YEzUw+1eQk1LZP83E4NFkMor5HAMNDoRSliXFSv7BexZbXA+Fk\nJQAACN5JREFUq9i8B2LsBmsMXqm4fpOwz24QBEEQDhpDF+JO0mS5M0urc4euXaVDG6PTTY/xnE/Z\njVFRZSaDClNhlZIq3Pci75IE+8672Dfewt3onYUNQ/Szz6Cfego1vX4Up7/X6484h/dRmLfsrEH5\nPrpUIRgfF8OVIAjCHjJ0If7azd9bf0OB70Kq7ggFVyJ0ZWpemfGwQK2gHyilyTmHm53DvvEm9u2v\nQy/xR506iX76KfSF84NK0zqLRo80h/dRYd14VcKr1fBGYPYSBEE4jAxdiCe8o/hJgQJlipTReCgU\nZR/GiprAe7Cq1LXb2LeirPq908u6rVbQH7+M99STqPFx7MBopQlUMLKq91FCjFeCIAijZehCfL7w\nBM0k3jDxSg8mXt0PZy3ug2uYN97EvX8lM15pjXr8Mbynn0KdPoVToHpDNULtE+yjoRoHhXXjVRm/\nNoYuiPFKEARhVAxdiBVQDdcnXj0Ibnk5M169+da68WpqEv30U6iLT6DKxQ17vaFUvbvEmRRVLOFX\nxXglCIKQF0MX4jMTIasr9z9C5JIE++572DfexF3fYLy69AzqqSfRM9OEOiBUUvU+DM4alOehy1U8\nMV4JgiDkTq55xM453FzfePUOxNkRJnXyBOrpJwkef4IgKFJQWYCCsDvEeCUIgrB/yUWIXbuNjd7O\nqt+FnvGqUkFffpbC089QmDhKKFXvQ9EPWVBhiFepivFKEARhnzIyIXbW4q59mBmv3nt/YLzSjz1G\n+MwlymcfJ5AUnvvirAVncYDSGqU98LxMZD2dtZq1h/I8VKGADrbPdxYEQRD2B0MXYrO4RPqnL2fG\nq7X1iVeFZ56l+tRlvEpl2Es4EDhjAAdKgeoLau/lRoH1A3QYZK9LhSsIgnDgGboQL/3Hn8teCUMK\nlz5G5dmP4c8cH0nb2bkHjWcYHtYYrElRWoHyNlSsG0XWBz9AB0EmvtKSFwRBODQMXYhL584RNC5R\nfKIxktnKzhjwNLpQwisWIU9XsFKUj0/QqrWlehUEQRC2ZehCfPYHfmCos5adczhn0WGILpSyYIV9\nNJBChyFKd/NehiAIgrBPyfX40m5x1mRGr0IRr1RGVypScQqCIAgHkgMhxM45nLXoMEAX+1VvMe9l\nCYIgCMJDs2+F2FkDSqMLBXSpjFetStUrCIIgPHLsKyF2JkX19npVpZKZrQRBEAThESbnEZfZDGpd\nKKJKJbxKVWYfC4IgCIeKkQuxMykqCFCFEl6lLHOPBUEQhEPN0IU4O15ksqq3WMKr1qTqFQRBEIQe\nQxfiwvQ0YfWoTIsSBEEQhG0Yug3Zr0rgvCAIgiDcDTkPJAiCIAg5IkIsCIIgCDkiQiwIgiAIOSJC\nLAiCIAg5IkIsCIIgCDkiQiwIgiAIOSJCLAiCIAg5IkIsCIIgCDkiQiwIgiAIOSJCLAiCIAg5IkIs\nCIIgCDkiQiwIgiAIOSJCLAiCIAg5IkIsCIIgCDkiQiwIgiAIOSJCLAiCIAg54u/0ExqNhgb+I3AZ\n6AJ/O4qid/d6YYIgCIJwGNhNRfy9QBhF0aeAfwL8671dkiAIgiAcHnYjxH8O+B2AKIr+BPjEnq5I\nEARBEA4RuxHiMWBlw9um164WBEEQBGGH7HiPmEyEaxve1lEU2Xs8XtXrtXt8+NHnMD//w/zcQZ6/\nPP/D+/wP83PfKbupZL8CfBdAo9H4s8Are7oiQRAEQThE7KYi/jXgLzQaja/03v7+PVyPIAiCIBwq\nlHMu7zUIgiAIwqFFTFaCIAiCkCMixIIgCIKQIyLEgiAIgpAjIsSCIAiCkCO7cU3fl8M+j7rRaATA\nzwFngQLwE1EU/Ua+qxo9jUZjGngB+PNRFL2d93pGSaPR+DHgu4EA+Jkoin4h5yWNhN7f/n8FLgIW\n+DtRFEX5rmo0NBqNbwT+ZRRF39JoNB4Hfp7sZ/Aa8ENRFD3Sztgtz//jwL8DDJkG/M0oiuZyXeAQ\n2fjcN7zvrwH/oDcO+p4MqyI+7POovw+Yj6LoM8BfBH4m5/WMnN7NyH8GmnmvZdQ0Go3PAt/U+/3/\nLHAh1wWNlm8HKlEUfRr458C/yHk9I6HRaPwo8LNkN94APwX8eO8aoIDvyWtto2Cb5//TZCL0LcAX\ngX+c19qGzTbPnUaj8Q3ADzzo1xiWEB/2edS/DHyh97oG0hzXkhc/Cfwn4GbeC8mBbwdebTQa/xv4\nDeBLOa9nlLSB8UajoYBxIM55PaPiHeDzZKIL8FwURX/Ue/23gW/LZVWjY+vz/6tRFPWHPQVkvxeP\nKpuee6PRmCK7Af2HrP887smwhPhQz6OOoqgZRdFao9GokYnyP817TaOk0Wj8LbKOwJd773qgX8ZH\niDrwPPCXgb8P/M98lzNSvgIUgbfIOiL/Pt/ljIYoir7I5hvujb/za2Q3JY8sW59/FEW3ABqNxqeA\nHwL+TU5LGzobn3tP5/4b8CNk/+8PxLDEcafzqB85Go3GaeD3gf8RRdEv5b2eEfP9ZNPX/gD4OPAL\njUZjJuc1jZLbwJejKEp7e+OdRqNxNO9FjYgfBb4SRVGD9f/7MOc15cHG610NWMprIXnRaDT+CllX\n7LuiKFrIez0j4nngcbLn/b+ApxuNxk/d75OGYtYiuyv+buCXD+M86p7ofBn4wSiK/iDv9YyaKIq+\nuf96T4z/XhRFszkuadT8X+CHgZ9qNBongApwWC5EFda7YYtkbUkvv+XkxkuNRuOboyj6Q+A7gd/L\ne0GjpNFo/HXg7wKfjaJoMe/1jIooiv4UuATQaDTOAr8URdGP3O/zhiXEh30e9Y+TtaK+0Gg0+nvF\n3xlFUSfHNQkjIoqi32o0Gp9pNBr/j6zr9IOPumN2Az8J/PdGo/F/yET4x6IoepT3B7fS/3/+R8DP\n9roBbwC/kt+SRorrtWf/LXAV+GKj0QD4wyiK/lmeCxsBW//G1Tbv2xaZNS0IgiAIOXJoDFSCIAiC\nsB8RIRYEQRCEHBEhFgRBEIQcESEWBEEQhBwRIRYEQRCEHBEhFgRBEIQcESEWBEEQhBz5/6wSqosA\nhdNaAAAAAElFTkSuQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 12 - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Specifying input data with long-form DataFrames" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There is also a second, substantially different way to pass data into `tsplot`. If you use a `DataFrame`, it is expected to be in \"long-form\" (\"tidy\") organization with a single column containing all observations of the dependent variable and other columns containing information about the time, sampling unit, and (optionally) condition of each observation.\n", - "\n", - "Let's make a third dataset with two gamma PDF traces in this format." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def gamma_pdf(x, shape, coef, obs_err_sd=.1, tp_err_sd=.001): \n", - " y = stats.gamma(shape).pdf(x) * coef\n", - " y += np.random.normal(0, obs_err_sd, 1)\n", - " y += np.random.normal(0, tp_err_sd, len(x))\n", - " return y" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 13 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "gammas = []\n", - "n_units = 20\n", - "params = [(5, 1), (8, -.5)]\n", - "x = np.linspace(0, 15, 31)\n", - "for s in range(n_units):\n", - " for p, (shape, coef) in enumerate(params):\n", - " y = gamma_pdf(x, shape, coef)\n", - " gammas.append(pd.DataFrame(dict(condition=[[\"pos\", \"neg\"][p]] * len(x),\n", - " subj=[\"subj%d\" % s] * len(x),\n", - " time=x * 2,\n", - " BOLD=y), dtype=np.float))\n", - "gammas = pd.concat(gammas)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 14 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When using a DataFrame, you must provide the names for each of the relevant columns as arguments to `time=`, `unit=`, etc." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.tsplot(gammas, time=\"time\", unit=\"subj\", condition=\"condition\", value=\"BOLD\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 15 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Although this is somewhat more verbose, it produces a plot that has rich semantic information with no additional effort. Everthing else you've learned so far works with this style, so you can specify the colors in any way you please." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "color_map = dict(pos=\"indianred\", neg=\"steelblue\")\n", - "ax = sns.tsplot(gammas, time=\"time\", unit=\"subj\", condition=\"condition\", value=\"BOLD\", color=color_map)\n", - "ax.set_xlabel(\"time (seconds)\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 16 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Becaues the confidence intervals are generated with a bootstrapping procedure, you can pass in any arbitrary estimator to collapse over the unit dimension. For instance, you may want to use the median instead of the mean." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.tsplot(sines, estimator=np.median, color=\"#F08080\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nRK5CYII=\n", - "text": [ - "" - ] - } - ], - "prompt_number": 17 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "By default, the 68% confidence interval is plotted, which corresponds to the standard error of the estimator. However, it's easy to change this." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "pal = sns.dark_palette(\"cornflowerblue\", 3)\n", - "sns.tsplot(walks, ci=95, color=pal);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 18 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you want to change other aesthetics of the plot, you can either pass additional keyword arguments (which get passed to the main call to `plt.plot()` that draws the central tendency, or you can provide a dictionary to the `err_kws` parameter, and those arguments will be used for the error plot." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.tsplot(gammas, time=\"time\", unit=\"subj\", condition=\"condition\", value=\"BOLD\",\n", - " ci=95, color=\"muted\", linewidth=2.5, err_kws={\"alpha\": .3});" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 19 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The entire plot is constrained within a single axis, and you can provide an existing axis to plot into." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "f, (ax1, ax2) = plt.subplots(2, 1, sharex=True)\n", - "c1, c2 = sns.color_palette(\"Dark2\", 2)\n", - "sns.tsplot(sines, color=c1, ax=ax1)\n", - "sns.tsplot(-sines, color=c2, ax=ax2);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nYII=\n", - "text": [ - "" - ] - } - ], - "prompt_number": 20 - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Different approaches to representing estimator variability" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Because of measurement and sampling error, the mean (or other aggregate value) at each time point is only an estimate of the true value. It is important to communicate this variability in a way that accurately represents the precision of your estimate and facilitates comparisons, for instance, between different conditions or against a baseline value. `tsplot` can visualize the uncertainty in a variety of ways. Each has advantages and disadvantages, so choose the approach (or set of approaches) that is best suited to what you want to communicate with each plot." - ] - }, - { - "cell_type": "heading", - "level": 3, - "metadata": {}, - "source": [ - "Visualizing uncertainty at each observation with error bars" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "By default `tsplot` draws *confidence bands*, as they help emphaisze the underyling trend in the data. However, a somewhat more common approach is to draw an error bar with the width of some confidence interval at the point of each observation:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.tsplot(sines, err_style=\"ci_bars\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAesAAAFVCAYAAADPM8ekAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3Xl4HPd95/l3VV8AGo2DIAACvM8iKUq8JFGXLR+Ro9hO\nYnvkzHgST+LNNdl5dhPP7pMnm5nJ7DxPZnf2yRPnSZ6dzO7jyHEm65HHdmxPbI1pO7FiWyfFmxTF\noniCxA3i6EYDfVXX/oEGBZMggG50o6rRn9fz6BHRher6olDob9Xv+P4M13URERER/zK9DkBEREQW\npmQtIiLic0rWIiIiPqdkLSIi4nNK1iIiIj6nZC0iIuJzwVJ2siwrBHwB2AxEgD+0bftbc7b/LPBv\ngBzwBdu2/6IMsYqIiNSkUp+sfxEYtm37vcCzwP89u6GQyD8HPAM8DfyGZVkdyw1URESkVpWarL8K\n/MGc98jN2bYHuGzb9oRt21ngZeC9pYcoIiJS20pqBrdtOwlgWVaMmcT9r+ZsbgIm5nydAJpLDVBE\nRKTWlZSsASzL2gh8HfiPtm1/ec6mCSA25+sYMLbQe7mu6xqGUWooIiIi1aaopFfqALNO4HvA/2jb\n9kt3bb4I7LQsqxVIMtME/kcLvZ9hGAwPJ0oJpaa0t8d0npZI52ppdJ6WTudqaXSelqa9Pbb4N81R\n6pP17zPTtP0HlmXN9l1/Hojatv15y7L+JfBdZvqzn7dtu7/E44iIiNS8Uvusfxv47QW2fxv4dqlB\niYiIyLtUFEVERMTnlKxFRER8TslaRETE55SsRUREfE7JWkRExOeUrEVERHxOyVpERMTnlKxFRER8\nTslaRETE55SsRUREfE7JWkRExOeUrEVERHxOyVpERMTnlKxFRER8TslaRETE55SsRUREfE7JWkRE\nxOeUrEVERHxOyVpERMTnlKxFRER8TslaRETE55SsRUREfE7JWkRExOeUrEVERHxOyVpERMTnlKxF\nRER8TslaRETE55SsRUREfE7JWkRExOeUrEVERHxOyVpERMTnlKxFRER8TslaRETE55SsRUREfE7J\nWkRExOeUrFfY0aELHB264HUYIiJSRYLL2dmyrCPAf7Bt+/13vf5Z4FeB4cJLv2nb9qXlHGu1OB/v\nA+DZjr0eRyIiItWi5GRtWdbvAr8ETM6z+RDwadu2T5X6/iIiIjJjOc3gl4FPAMY82w4Dv29Z1o8t\n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\ncMuyPg48Z9v2L86z7deB3wBywB/atv1i6SGKiIjUtpKStWVZfwp8CDg1z7Z1wP8EHAbqgZcty/q+\nbduZ5QQq1UdzlkVEyqPUJ+tXgG8AvznPtkeBV2zbzgJZy7IuAw8Bx0s8llQpzVkWESmPBZO1ZVm/\nCvzOXS//im3bX7Es63332S0GTMz5OgE0lxyhiIgURa1aq8+Cydq27eeB54t8zzgzCXtWDBhbbKf2\n9thi3yLUznkKXJsZ+1jKz7ucfWuRztPSreT1uJzr+NPtR4rep5x0TZVfJcqNHgP+vWVZEaAO2AOc\nX2yn4eFEBUJZXdrbYzVznhwnD5R2XThOnkDArJlztRy1dE0tV6nnqtRreTl/A17SNbU0xd7QLCdZ\nu4X/ALAs67PAZdu2v2VZ1p8BP2Zmatjva3CZiIhI6UpO1rZt/xD44Zyv/2TOv/8C+IvlhSYiUv3U\nfyzloFW3REQqSLMipByUrMWXlvM0sq+pm/r6cBmjERHxlpK1+NJynkae7dirQS4isqqoNriIiIjP\nKVmLiIj4nJK1iIiIzylZi4iI+JyStYiIiM8pWYuIiPickrWIiIjPKVmLiIj4nJK1iIiIz6mCmYiI\nD2kBEJlLyVpExIe0AIjMpWZwERERn1OyFhER8TklaxEREZ9TshYREfE5JWsRERGfU7IWERHxOSVr\nERERn1OyFhER8TklaxEREZ9TshYREfE5JWsRERGfU7IWERHxOSVrERERn1OyFhER8TklaxEREZ9T\nshYREfE5JWsRERGfU7IWERHxOSVrERERn1OyFhER8TklaxEREZ9TshYREfG5YKk7Wpb1ceA527Z/\ncZ5tfwo8CSQAF/iYbdvxkqMUERGpYSUl60Iy/hBw6j7fcgj4kG3bo6UGJiIiIjNKbQZ/BfgtwLh7\ng2VZJrAT+LxlWS9blvWZZcQnIiJS8xZ8srYs61eB37nr5V+xbfsrlmW97z67NQB/Bnyu8P4vWZZ1\n3Lbtc8sNVkREpBYtmKxt234eeL7I95wC/sy27RSAZVk/APYDCyVro709VuRhapPO09LpXC2NztPS\n6Vwtjc5T+VViNLgFvGxZlmlZVgh4CjhRgeOIiIjUhJJHgzMzytud/cKyrM8Cl23b/pZlWf8ZeA3I\nAl+0bfvt5YUpIiJSuwzXdRf/LhEREfGMiqKIiIj4nJK1iIiIzylZi4iI+JyStYiIiM8tZzT4shWq\nnf058BCQBn7Ntu0rXsbkV5ZlnQQmCl9etW37V72Mx28syzoC/Afbtt9vWdYO4ItAHjgP/AvbtjWS\nkvax+0UAAAMISURBVHvO00HgW8A7hc3/ybbtr3gXnT8Uppx+AdgMRIA/BN5G19Q97nOubgHfBi4V\nvq3mryvLsgLA54FdzMyi+ufM5LwvssRrytNkDXwMCNu2/UThQ+SPC6/JHJZl1QHYtv1+r2PxI8uy\nfhf4JWCy8NLngN+3bftHlmX9J+DngW96FZ9fzHOeDgOfs237c95F5Uu/CAzbtv1py7JagTPMrIOg\na+pe852rfwf8sa6rn/BRIG/b9lOWZT0N/B+F15d8TXndDP4kcBTAtu03gIe9Dce39gMNlmV917Ks\nvy/c2Mi7LgOf4N1a9Yds2/5R4d/fAX7Kk6j85+7zdBj4iGVZP7Qs6y8sy2r0LjRf+SrwB4V/m8zU\ni9A1Nb/5zpWuq7vYtv3fgN8sfLkFGAMOF3NNeZ2sm4C5S2c6haZx+UlJ4I9s2/5pZppPvqTz9C7b\ntr8O5Oa8NHeBmUmgeWUj8qd5ztMbwP9q2/bTwFXg33oSmM/Ytp20bXvSsqwYM8noX/OTn5W6pgrm\nOVf/CjiGrqt72LbtWJb1ReBPgS9R5OeU1x/4cWBuEVnTtu28V8H42CVmfrnYtv0OcBvo8jQif5t7\nDcWAca8C8blv2LY9u8ztN4GDXgbjJ5ZlbQR+APxn27ZfQNfUfd11rr6Mrqv7sm37V5gpyf0XQN2c\nTYteU14n61eADwNYlvUYcNbbcHzrM8z052NZVjczLRL9nkbkb6cK/UIAPwP8aKFvrmFHLct6pPDv\nDwLHvQzGLyzL6gS+B/yubdtfLLysa2oe9zlXuq7uYlnWpy3L+t8KX04DDnC8mGvK6wFm3wCesSzr\nlcLXWvt6fs8Df2lZ1uwv8zNqgZjX7EjK/4WZ9dTDwAXga96F5Euz5+mfA//RsqwsMzd/v+FdSL7y\n+8w0Sf6BZVmz/bG/DfyZrql7zHeufgf4E11XP+FrwBcty/ohEGLmerpIEZ9Tqg0uIiLic143g4uI\niMgilKxFRER8TslaRETE55SsRUREfE7JWkRExOeUrEVERHxOyVpERMTn/n8ijlLFa+lmgwAAAABJ\nRU5ErkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 21 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It's also not actually necessary to plot the linear interpolation between the central tendency estimates. This is arguably a more pure approach, although it sacrifices some visual immediacy." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "ax = sns.tsplot(sines, err_style=\"ci_bars\", interpolate=False);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 22 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Perhaps the optimal approach is to fit a statistical model to the data and then plot that on top of point-estimates and confidence bars." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "ax = sns.tsplot(sines, err_style=\"ci_bars\", interpolate=False)\n", - "xmin, xmax = ax.get_xlim()\n", - "x = np.linspace(xmin, xmax, sines.shape[1])\n", - "out, _ = optimize.leastsq(lambda p: sines.mean(0) - (np.sin(x / p[1]) + p[0]), (0, 2))\n", - "a, b = out\n", - "xx = np.linspace(xmin, xmax, 100)\n", - "plt.plot(xx, np.sin(xx / b) + a, c=\"#444444\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nVCIGsyRJJfI/aln7fJiMl1EAAAAASUVORK5CYII=\n", - "text": [ - "" - ] - } - ], - "prompt_number": 23 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A problem with the error bars style is that it can become confusing when you have multiple traces on the same plot, as it is difficult to visualize the extent of the overlap." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.tsplot(walks, err_style=\"ci_bars\", ci=95);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nRK5CYII=\n", - "text": [ - "" - ] - } - ], - "prompt_number": 24 - }, - { - "cell_type": "heading", - "level": 3, - "metadata": {}, - "source": [ - "Drawing comparisons with overlapping error bands" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This kind of comparison lends itself well to the tranlucent error bands, where it is easy to see the overlap in as the bands get darker." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.tsplot(walks, err_style=\"ci_band\", ci=95);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 25 - }, - { - "cell_type": "heading", - "level": 3, - "metadata": {}, - "source": [ - "Representing a distribution with multiple confidence intervals" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This starts to give the impression that there is some region around our central estimate that we consider to be reliable.\n", - "\n", - "But, it still binarizes \"trustworthy\" and \"untrustworthy\", when in reality we have a continuous distribution giving the likelihood of observing the central value with repeated samples.\n", - "\n", - "To get a better feel for the shape of this distribution, we can use the fact that the error bands are translucent and stack several on top of each other by supplying a list of confidence intervals." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "cis = np.linspace(95, 10, 4)\n", - "ax = sns.tsplot(sines, err_style=\"ci_band\", ci=cis)\n", - "ax.set_ylim(-2, 2);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nrkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 26 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This can make for a very informative plot, but it can get cluttered when you have multiple overlapping traces." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.tsplot(walks, ci=cis);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nSQhXlpRDAAAAAElFTkSuQmCC\n", - "text": [ - "" - ] - } - ], - "prompt_number": 27 - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Visualizing the data for each sampling unit" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The above methods compress the information in the data to visually present a statisical inference about the central tendency. It is often the case, though, that you will want to visualize the data for each sampling unit at some point in your analysis. Although this does not present the most informative production graphics, it can be very important in the early stages as you being to understand the structure of the data." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.tsplot(sines, err_style=\"unit_traces\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 28 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You may want to make the trace for each unit a different color, to make the structure more interpretable." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.tsplot(sines, err_style=\"unit_traces\", err_palette=sns.dark_palette(\"crimson\", len(sines)), color=\"k\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAeMAAAFVCAYAAADc5IdQAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XdsHNm+4Pdvdc7N1MxJTC2Kkigq5zz53kn3vvfW+2Ds\nwsDCxsILrwEDxrMXNmB4vVgv/ADDcb1OgN/FSzfMnbmj0SjnLFGiAtnMmWyyyc65u8p/9IhzNUoM\nTTZJnQ8gjIbsqvqxVKxfnVPnnJ+kKAqCIAiCIOSOKtcBCIIgCMK7TiRjQRAEQcgxkYwFQRAEIcdE\nMhYEQRCEHBPJWBAEQRByTCRjQRAEQcgxzWI2cjqdKuD/BJoAGfgnLpfLlc3ABEEQBOFdsdiW8fuA\n2eVyHQT+G+BfZi8kQRAEQXi3LDYZRwG70+mUADuQyF5IgiAIgvBuWVQ3NXAdMABdQCHw86xFJAiC\nIAjvGGkxy2E6nc7/gkw39X/pdDorgQvAZpfL9coWsqIoiiRJS4tUEARBENaOBSW9xbaMzUDgh797\nAS2gfm1EksT0dHCRh3p3OBxWcZ7mQZyn+RPnan7EeZo/ca7mx+GwLujzi03G/wb4f5xO51Uyifgv\nXC5XdJH7EgRBEIR32qKSscvl8gFfZDkWQRAEQXgniUU/BEEQBCHHRDIWBEEQhBwTyVgQBEEQckwk\nY0EQBEHIMZGMBUEQBCHHRDIWBEEQhBwTyVgQBEEQckwkY0EQBEHIMZGMBUEQBCHHRDIWBEEQhBwT\nyVgQBEEQckwkY0EQBEHIMZGMBUEQBCHHRDIWBEEQhBwTyVgQBEEQckwkY0EQBEHIMZGMBUEQBCHH\nRDIWBEEQhBwTyVgQBEEQckwkY0EQBEHIMZGMBUEQBCHHRDIWBEEQhBwTyVgQBEEQckwk4yxK+vxE\nBoZzHYYgCIKwxohknEWe89eY+u4igSdduQ5FEARBWENEMs6SxIyXuNsDwOzVO0SGRnMckSAIgrBW\niGScJSFXHwC2thYktYrpM5dJeGZzHJUgCIKwFohknAVKOk3Y1YfKoCd/VxtFJw6iJFO4T50nFY7k\nOjxBEARhlRPJOAuiw2OkozEsTXVIGjXm+lry924nHYowdeo8cjKZ6xAFQRCEVUwk4ywIdfYCYNnY\nMPc1W9tmLM2NJKZn8Zy7iiLLuQpPEARBWOVEMl6idCRKZHgUnaMAXVHB3NclSaLw8B4MlWVEBkbw\n3ryfwygFQRCE1Uwk4yUKdfeDrGDZ2PjS9yS1muIPjqItsBN49ExMeRIEQRBeSSTjJVAUhVBXD5Ja\nhblxwys/o9LrKP74BGqjQUx5EgRBWCP87U+Y+v4SSjq9IscTyXgJEm4PyVk/pg3VqA36135Oa7NS\n/NHxH6c8zYgpT4IgCKuV/8FjvDfvE+kbyvR+rgCRjJcg2NUDgKX55S7qn9KXOig6npnyNHXqgpjy\nJAiCsAoFn7rw3nqA2mJCUqsIPHyKoijLflyRjBdJTqaI9A6itpgwVJTOaxtzQ2bKUyoYZuq7C2LK\nkyAIwioS6u5n5spt1EYDpZ++j7lxA0mvn+jgyLIfWyTjRYoMDCEnklicDUiq+Z/GuSlPUzNiypMg\nCMIqERkcwXPhGiqdlpKfv4c2z45t22Yg02293K1jkYwX6ce5xfUL2k5MeRIEQVhdYmOTTJ+5jKRS\nU/zx8blpqrqCPEwbqoi7PcTH3csag0jGi5AMBImNTWKoKEFrty14ezHlSRAEYXWIT3mY+u4CKArF\nHx7FUFbywvdtbT+0jh8+WdY4RDJehFDX81bx2wduvc4LU56u3SE6PJat8ARBEIR5SHr9uP9wDjmZ\noujEIYzVFS99xlBajKG8hOjQ2LLOhBHJeIEUWc4UhdBpMdVVL2lfc1OeVJkpT7HpmSxFKQiCILxJ\nKhBi8pszyLE4hUf2Ym6ofe1n51rHD5avdSyS8QLFxiZJBcOY6mtRabVL3t/zKU9yIsnIV2dRUisz\nwVwQBOFdlY5EmfzmDOlQhPz9O7Fuanrj543VFegK8wn3DZIKhJYlJpGMF2iui7q54S2fnD9zQy22\n1k0k/AECjzuztl9BEAThRelYHPc3Z0n5g9i3b8G+reWt20iShK2tBWQF/6OnyxLXopOx0+n8C6fT\necPpdN51Op3/KJtBrVbpWJzIwDDafDv6EkdW9523sxW1QY//wWPS0VhW9y0IgiCAnEwydeo8iRkv\n1s1O8va0zXtbc8MGNFYLoc5e0pFo1mNbVDJ2Op1HgX0ul2s/cBSoy2JMq1a4dwAllcaysQFJkrK6\nb5Veh2PfduR4Av/9jqzuWxAE4V2npNJMn75EfHIac1MdBYf2LOg+LqlU2Fo3oaRSyzIDZrEt4/eB\nx06n8yvgG+Dr7IW0eoW6ekElYW5anmePgm2b0NgsBJ+6SPoDy3IMQRCEd40iy0yfv0p0ZBxTbRVF\nx/YvqkFlaW5EZdATfNKV9RUUF5uMHcAO4JfAfwT8KmsRrVKJmVkSUzOYqivRmE3LcgxJrSZ/7w6U\ntIz31oNlOYYgvI6iKKSCIcJ9g/judRAXo/uFdUBRFGYu3yTSN4ShooSi9w4jqdWL2pdKq8G2tRk5\nliD4rDurcWoWuZ0H6HS5XCmg2+l0xpxOZ5HL5fK8bgOHw7rIQ60Ok4+fYDLrqNq3Fdsy/iw1ezej\n9PcRmZjAnIhgqih5+0bvoLV+Pa2k152rdDRGdHKa6MQ0Ufc00cnpFwqYpFwu7J+exLrEKXxrhbim\n5m+tnCtFUXBfuoU8PExhXQU1f/oJar1uSfvMP7KTHlc3cm8fRcd2LTqx/9Rik/E14D8B/tLpdJYD\nZuCNj9HT08FFHir3lHSasbtPkCQVMVs+8WX6WRwOKx5PCM2WzUR6T9P7h0uUfvFR1t9Pr3UOh3VN\nX08r6fm5kpMpEp4Z4lMeEm4P8ekZUv4Xz6HaYkJfUoq+uAhJp8V74z5dv/qGomMHsDgXtuzrWiOu\nqflbC+cqHY0RGRgm3DtIbHQCbYGdwiMHmQ3EgfiS96+qqcHf0cngjQ4sG189s2ahDyyLSsYul+tb\np9N52Ol03iHT1f1PXS7X8teYypHI0ChyLI6tdVPWnoLexFBWgqmumkj/MJGBYcx1Nct+TGH9CfcP\nkXwwy3TPCAmvD+Qff0VVBh3G6nJ0jiL0xUXoS4pQm4wvbK8rzGfq1AU856+RjsWwt759Cogg5Mrz\nBBzpHyI6OjF3vetLHTjeP4LaaMjasWytmwg86cL/8AlmZ31WGkyLbRnjcrn+8yUffY34sShE9uYW\nv03+3u1EBkfw3XqAqaZyRR4ChPVBSaeZvXaH4NNuTGYdybiMvsSBvrgInaMQfUkRGpv1rTcQQ1kJ\npZ9/iPsPZ/Fev4ccjZG3Z7voqRFWjefTTSN9gy8kYF1xIeb6Wkz1NWht2e9S11gtWBrrCLn6iA6O\nYtpQtfR9ZiGudS0VjhAdHsvcyArzV+y42jw71hYnwcddBJ/1YNuyccWOLaxd6WiM6TOXiI250RXm\nU/eL9wiq9Asq8/nHdIX5lH3xMe4/nMX/4AnpaIzCI/sWvT9BWKpcJeCfsm1rIeTqw9/+GGNt5ZIf\nUkUyfouwqw8UJasrbs1X3o6thF19+O4+xNJUh2qJAw+E9S0x42XquwukAiFMddUUnTiIsayA0BLf\n72lslkwL+dR5Qp29yLE4RScPo9KK24ewMt6YgOtqMDXUrkgC/mO6wnxMtVVEBkeIT0xhKF/aYFvx\n2/QGiqIQ6upF0qgxN2xY8eOrTUZsbZvx3W7H3/6E/L3bVzwGYW2IDIwwfe4KSjJF3q5W7Dtbs9qd\nrDYZKf30faZOXyIyMMLUt+co/ui4eEAUll3wWTezV2+jpGUgtwn4p2zbNxMZHMHf/kQk4+UUd0+T\n9AUwN27I2U3HtnUTwacuAh3PsLY0obFachKHsDopioL/wWN8dx4iqdU43j/yxuozS6HS6Sj5+ATT\n568S6Rti8vffU/Kzky8N/BKEbAl29jBz6SZqowFb66ZMF/QiasgvF0NpMfqyYqJDoyRmZtEVFix6\nX+LFzxvMDdzKQRf1cyqthvzdbSipNL67D3MWh7D6yMkUnnNX8d1uR2MxUfrFh8uWiJ+TNGoc7x3G\n2tJEwjPLxG9PidXihGUR6u5j5tJNVAYdJZ++h337llWViJ+zPy+v2L60AhIiGb+GnEwS7h1AY7Vg\nqCjLaSzmpjp0hfmEXP0kPMtX3FpYO1KhMJO/P024ZwB9qYOyX3yC3lG4IseWVCoKDu/FvnNrpibs\n706L61LIqnDvIJ7z11HptJT8/P0ltTiXm7GmEm1BHuHegSWVVxTJ+DUifUMoyRSWjdmZQ7YUkkpF\n/v6doCh4b97PaSxC7sXd00z85lsSUzNYNtZT+ukHK95VLEkS+bvbKDi0h3Q0xuRXp4mNTa5oDML6\n9Hz8g0qroeSTkyv2kLlYkiRlWsdLLK8okvFrzNUtduaui/qPGavKMVaVEx0ZJzo8lutwhBwJdfcx\n+fvvSUdi5B/YSeGxA0ia3M1Bt23ZiOPkIZR0Gve354gMDOcsFmHtiw6PMX3mEpJKTfEnJ9CXZrdU\n7XIxN9SisZoz5RUXWQJXJONXSPoDxMbdGCpK0dhWz4Cp/P07QJLw3ryPIsu5DkdYQYosM3vzHp5z\n15DUako+OYG9tSXnvTYA5sYNFH98AiSJqdOXsr6AvvBuiI6OM3X6IkgSxR8fx1C2dtbll9RqbK0t\nKKkUwUWWVxTJ+BVCXX1AbgduvYqusACLs47EjJeQqy/X4QgrRE4kmPruIoH2p2jzbJR9+THG6opc\nh/UCY1U5pT9/H5Vex8ylm3gu3ViWAuzC+hQbdzN16iIoCsUfHsNYmdtxOothaW5AZdATeNy5qPKK\nIhn/hCLLhFy9qHRaTBtW35rQebvbkDQafHfakZOpXIcjLLPErI+J354iOjSaSXhffow2357rsF5J\nX+qg7IuP0BbYCT3rYfRXv8V3r0Ncp8IbxSencX97DkVO43j/6Kp70JwvlVaLbctG5FiC0LOeBW8v\n5hn/QFEU0qEw4f4h0qEI1pamVbnCkMZixra1Gf+DxwQePSNv59ZchyRkiaIopHwBYhNu4hNTxMbd\npIKZ0Zm2rc3k79+56peh1ObbKf/TTwl19uC78xDfnXaCT7vI37Mdc1Pdqo9fWFnxKU8mEafTON47\nnJU1nnPJunkj/vanBDqewck9C9p29WWbFaAoCulwhMT0DPHpGRLTMySmZn588a6SsDQ35jbIN7Bv\n30yoswd/+2OsmxrFogtrlCLLJGd9xMbdmQQ87n5h8IfKoMe0oQpzY92yzx/OJkmlwtrixNy4IXNj\nevQUz4XrBDo6yd+/A2Nlea5DFFaBxMws7j+cRU4kKTpxEHN9ba5DWjK10YCluYHg44W/N16VyViR\nZUJdvYSe9SBp1KhNRlRGA2qjEbXJgNpk/PGP0fDWikapcITE1AzxaQ+J6VkS0zMvvc/SWC2Y6qrR\nOQoxVpWjLy5azh9xSVQ6HfadrcxevY3v3iMKD+/NdUjCPCjpNPHpGeLPk+/EFHLix3dLaosJc+MG\nDOUl6EuL0RbkrYoBWoul0unI39OGdVMjvjsPCXX34/76LMaaCvL37ljRwivC0imKQnRolEiiEEWt\nX1IlucSsD/fXZ5FjCYqOH8DSVJfFSHPL3tpC8KlrwdutqmT8/B/be+s+yVk/qCRQAOXNpZJVBn0m\nSRt/TNKSRkNyxkt82kM6/NPEa84k3qJC9MWF6IoK1lzr0rqpkeDjToLPurFtaV617xEFiAyOEHj0\nlLjbg5JKz31dm2fDVF+DoawEfXkJGqtlTSff19FYLRSdOIh1azPeG/eIDo0RHR7H0txA/u62Nfe7\n966KDo8xdeoCIbOOaELGUFaMoaIMQ3kJOkfhvF9BJH1+3F+fyVQAO7pvRUvTrgSNzUL+noXXEVg1\nyTg+5cF78x6xMTdImW7ivN3bUBsNyLE46Uj01X+iMdLRzN+Ts/6X9qu2mDBtqELnKMzUcnUUrotf\nfkmtJm/vdqZPX8J76z7FHx3PdUjCKyS9fqbPXEZJy+gK8tCXl2SSb1kxGrMp1+GtKL2jkJJP3597\n4A496yHcM4C9bQu21k2rcoyG8KPQ08yUtbzmRpJ9o0SHx4kOjwOg0mnRl5VgqCjFUFGKrjD/lck5\nGQgy+fUZ0pEoBQd3Y93UtKI/w0p5vkTmQuT86k8GgvhuPSDcOwhklhbL37v9hS6s563dt1FS6bnE\nLCeT6Ary10XifR3Thmr0ZcVEBkYy86KXWDVEyC5FlvFcvI6SSuP44Mi6eCe2VJIkYaqtwlhdIQZ5\nrSGpYIjI0Cj64iIqPjmGbjpIKhwhNjZJbHyS2Ngk0aFRokOjAKj0OgzlJRjKM8lZW5hPOhTG/fsz\npEMR8vftwLa1Occ/1eqSs2ScjsXx3+8g+KQr02ooLiR/744lzS+TNGo0Vss7U9lIkiQK9u1k4ren\n8N66T+kXH63Lbs61KtDRSXxyGnNDrUjEP/GmQV5FJw6K98mrTLCzJ1PXveXHlqzGbMLSVDf3vjcV\nCs8l5tjYJJGBESIDI0DmVaKkUpGORMnb3baoluN6t+LJWEmlCTzuxP/gMXI8gcZqIX/vdkwNtSKR\nLIK+1JHVAtdCdiS9fnx32lEbDRQcWtgUh3fJ3CCvliZ8t9sJufqY/Oo0xR8dF9fyKqGk04Q6e1Dp\ndW+s666xmLE01WNpqgcgFQhlkvMPCToVDGPfsVVMx3yNFUvGiqIQ7unHd7udVDCMyqAj/8BObC0b\nc7q27nrwY4Hrx+IGtgoosoznwjWUVJqCk4dQGw25DmnV01jMFJ04iKGyDM/F67j/cJaik4cw162+\nhXfeNZGhUdLhKNYtGxf0Xl9js2CxNWDZ2ICiKCiJZM7qwq8FK5KMQ0NjTHx7mcT0LJJGja2tBXvb\nFtQG/Uocft37scD12JILXAtLF3j0jLjbg7lxg0gmC2Rx1qM2Gpj6/hLT319GPrwHa4sz12G9054P\n3FrKv4MkSUgiEb/RioyUGPr7b0lMz2JuqqPiH3xOwb6dIhFnmX1b5h1M4OGzHEfybkvM+vDdfYja\nZKTg4O5ch7MmGasrKP3sA9QGPTOXb+G9047ylumNwvJI+vxER8Yz05cK8nIdzrq2IsnYUl1B2Z/8\nDMfJQ6uqCtJ6YqytzKwJ3NM/t4SisLIUWWbmQmb0dOGRvaJ7egn0xUWUfvkRGrsV/70OZi7dFJXK\nciD4wxrLlnU6BWk1WZFkXPOnn6z6AtFrnSRJmdaxrBB4JFrHuRB4+JT4VKZ72rShOtfhrHlau42y\nLz5CV1xIqLOHqe8uiqITK0hJpQl19aIy6MXrlhUgJvStI+bGDagtJoLPehZd4FpYHNE9vTzUJiOl\nn76Psaqc6NAo7q+/F9f2Cgn3DyLH4liaG8Qg2xUgkvE6kilwvSlT4HoRa6MKizM3ejotU3hkn+ie\nzjKVTkfxx8cxN9URd3uY/Oo0qYB4FbPcglkYuCXMn0jG64y1uRGVXkego1N06a0Qf/sTElMzmJvq\n1nwJuNVKUqspOnEQW1sLSa+fid+dIjEzm+uw1q3EjJf4xBTG6nK0Nmuuw3kniGS8zqh0OqybNyLH\n4oRcvbkOZ91LzHjx33skuqdXwPMV5/IP7CQdjjL5u9PExiZzHda6FHwmWsUrTSTjdci2JbOQSuDh\nUzECdRkp6XRm7enn3dNiut6KsLe24HjvMEo6jfsPZ+fWtReyQ04mCbv6UJuNGGsqcx3OO0Mk43VI\nbTJicdaTCoSI9A3lOpx1y9/+lMTUDBZnveieXmHmxg0Uf3ICSa1m+uwVAh2duQ5p3Qj3DCAnklg3\nNYmiHStInOl1yratBSQJf/sTsWDCMkjMzOK//wi12Uj+gV25DuedZKwsp+SzD1AbDcxeu4P31n1x\nrWdB8Fn3XBlbYeWIZLxOae02zPU1JDyzxEYnch3OuqKk03guiO7p1UDvKKT0y4/Q5tnwP3hC4OHT\nXIe0psWnPCSmZjDVVqKxmHMdzjtFJON1zLatBciM9hWyx9/+hMT0LJaN9ZhqRfd0rmltVko//xC1\n2Yj39gNi4+5ch7RmielMuSOS8TqmLy7CUFlGbHSC+JQn1+GsC5nu6Q7UFhMFB8To6dVCbTLieP8I\nANNnLpMKR3Ic0dojxxOEewbQ2CwYqspzHc47RyTjde55EW/Rfbd0P+2eFuXgVhdDWQn5+3aQjkTx\nnL0iZhIsUKi7HyWVygzcErXlV5xIxuucobIMnaOAcN8QSX8g1+HMWYsDbea6p5sbMIkpH6uSbesm\nTPU1xMbdeG8/yHU4a4aiKASfupDUKiwbG3IdzjtJJON1bq6AhKKsitaxoij4Hz5l5P/6a2av30VJ\np3Md0rzExt347j3KdE/vF6OnVytJkig6th9tno1A+1MiA8O5DmlNiE9MkZz1YdpQjdpkzHU47ySR\njN8BpvoaNDYLIVcf6Ug0Z3GkI1Gmvj2H98Y95ESSwKNnTH59ZtW+30tHovgfPWX8775h8qvTICsU\nHd0vuqdXOZVOh+ODI0gaDZ7z11ZVj1A2yMkU/odPCfcNZm2fz9eyFwO3ckeT6wCE5SepVNi2tTB7\n5TaBx13k72lb8RiiI+N4zl8jHYlirC6n4NAefLfbCfcOMvH3f8Dx/hEM5SUrHtdPyckU0cERQt19\nREfGQVZAJWGqrcLS0oSxuiLXIQrzoCssoPDIXjznrzH9/WVKv/gIlTb7t7v4lIekL4C5vgZJvbyV\njRRFITIwjPf6vbma5YkdXvJ2b1vSO950JEqkfwhtgR39KvgdfFeJZPyOsDgb8N15SPBJF/a2FlS6\nlWndKek03jvtBNqfIqlV5B/YiW3rpkx34nuH0ZUU4b15n8mvvyd/7w5srZtWfPCIoijEx92EuvuI\n9A0hJ5JAZjS6uakOc0Ot6LpbgyzOemITbkLPepi9dpuiYweytm9FUQh2dDJ78x7ICr477eTt2oa5\nccOyrFqV9PqZvXYn84CokrBtbSYyOIL/fgepQJCiYwcWXeYw5OpDSctYNznFwK0cEsn4HaHSarBt\nbc4k5M4e7K0ty37MpD+A5+xV4lMeNHYrjvcOoy8umvu+JEnYW1vQO4qYPnMZ7417xN3TFB3bvyIP\nC0mfn5Crn3B3/1xLQ2M1Y928EXNTHbqCvGWPQVhehQf3kPDMEursRV/iwLqpacn7lBMJZi7dJNw7\niNpowFhbRbi7D8/5a/gfPCZv1zZM9TVZSWxyMonv3iOCHZ0oaRljVTkFB3ejzbdj376FqdMXCfcM\nkAqFKf7w2ILLdyqKQvBZN5JGg7mpbsnxCosnrdCoVmV6OrgSx1nTHA4ry3me0rE4o//fr1HpdVT+\n+ZfL2q0W6u5j9spt5EQSi7OegkO735hgU+EInrNXiI270ebbcXxw9LXJcCnnKR2NEe4bJOzqI+7O\nzL2WtBrM9TWYm+oxVJSuq9bBcl9Ta0EqEGL819+gpNKUfvERekfhS5+Z73lKzHiZPnOZpNePvqwY\nx/tH0JhNpAIhfPc7MpXSZAVdUQF5u7dhrKlc1PWkKAqR3kFmb9wlHY6isZrJP7AL04bqF/anpNJ4\nLlwj3DuIxm6l5JMTaPPs8z5OdGQc9zdnsTQ3zLvnQFxT8+NwWBf0Dy+S8SqyEhf57LU7BDo6KTp+\nYFmmMMiJBLNX7xBy9SFpNRQe2YulqX5e2yrpNN5bDwg8eoak1VB07ADmhtqXPrfQ86Sk00SHxwi5\n+ogOjaKkZZAkjFVlmJsyRR5UWu2897eWiBtnRmRwhKlTF9DYLJT98mcvLWE6n/MU6u5j5tItlFQK\n27YW8ve0vfRAm/T58d19lKkkpSjoSx3k7W7DWFk271gTM7PMXrtDbMyNpFFj29aCvW3La995K4qC\n785D/Pc7UBl0FH94fN7jL6ZOXyTSP0zZLz5GX+KY1zbimpqfFU3GTqezGLgPnHC5XN1v+KhIxvOw\nEhd5Khhi9Fe/RZtno/zPPstqKzA+5cFz7ipJXwBdcSGO9w6jtdsWvJ9w72CmNGEyhW1rM/n7drxw\n05vPeVIUhcSUh1B3f6YKTSwOgK4wH7OzHnPjBjRm04JjW2vEjfNH3tvt+O93YKqtwvHRsReu/Ted\nJyWVZvbGXYJPXKh0WgqP7cdcX/vGYyVmZvHdfUSkPzO1ylBRSt6eNgylxa/dRo4n8N19SOBJF8gK\nptoq8g/snPfvULCzh5nLN5EkicKj+7E43/wQnAqFGf2r36ArzKfslz+b971AXFPzs9BkvOh3xk6n\nUwv8WyC82H0IK09jtWBprJtrJWZjbWVFUQh0PMN36wFKWsbW1kL+7pdbDfNlbqhFW5DH9JlLBDo6\niU/PzHUHvk0qGMok4O5+kl4/AGqjAVvrJizOenRFBYuKSVj78na1EndPExkcIdD+BPv2LW/dJhUI\nMX3mMvEpD9qCPIo/PDqvbmBdYQHFHx4jPuXBd6ed6PA4k7/9DmNNJXm7t73QVa4oCmFXH96b90lH\nY2jsVgoO7Frw76a1uRGN1cL095fmpnTl7Xr9SOtQV6ZL3doiBm6tBksZwPVvgP8N+IssxSKsENu2\nTYRcffjbnyw5GacjUTwXrxMdGkNtNFB08hDGLKxrqyvIo+zLj+cGyrxp+pOcSBDpHybk6ssUCVAU\nJI0ac0MtZmc9xqpyUZdVQFKpcJw8xPiv/4D3dju64qI3dh9Hh8eYPncFOZbIjHs4vGfBrzP0xUWU\n/Oy9zKIxd9qJDo1mHoLra8jbtQ0llWL26m3ibg+SRkPenjbsrS2LHhltrCyj9IuPmDp1Hv+9DlKB\nEEVH97+0P0WWCT7rRqXTYm7csKhjCdm1qGTsdDr/MTDtcrnOOJ3OvwDEY9UaoisswFhTQXRojNjk\n1Bu7zl5HTiaJjU4yc/nm3NzhouMHszoFSKXTUfTeYfQlDmZv3pub/lR0cg+KLBMbmyTk6iPSP4yS\nSgFgKC/JTEeqrxWLcwgveV5QYvKr03jOXqHsT372UqlARZbx3+/Ad68DSSVlxj0scb1mQ3kJJZ99\nQGx0At+2DGKQAAAgAElEQVTtdiJ9Q3Nd2CgK5oZa8vftQGO1LOXHA358kJ367gLh7n7SoTCOD46+\nMNI6OjRGOhTButm5bsdLrDWLemfsdDovA8oPf7YBLuAzl8v1utpla28h4nUuPDLB4N9+g7W+huov\nPnjlZ5R0moQ/SMLrJ+H1E//hv4lZP8lQ5u2EpFJTfGgXhTu3LGtXV2R0kpFvzpEKRzBXlZPw/hiD\nLs9O3qZG7Jsa0OUt/B218O6Zuf+YyYs3MVWUUvunn8y9UklFYox9e4HQ0Cg6m5XKT09iLJ3fwKb5\nUhSFYO8QnpsPUBSZkqP7sNRkfzEZOZli/PQl/K5+9Pl2qr74AP0PMxSGf3Oa4MAw9f/oFxheMbpc\nyIqVHU3tdDovAv+hGMC1dCs5MEJRFCZ/e4q420PpZx+gKAopf4CkL/Mn5Q+QDAQzK1D9hNpiQmu3\noc2zYWlufGHu8HJKR6JMn7mMyu8llsos82nZ2IC+xCHeeb2GGGzzaoqi4Dl7hXDvILbWTTg/P87o\n436mz14mFQxjrKmg6MShl0ZdrzWKouC73Y7/weO5kdZqi4mxX/0OfUkRZV9+vOB9imtqflZsAJew\ntkmSlFk04LuLTP7++5e+rzLo0RcXoc2zobFlEq82z4bGbs1Zt5baZKTk0/expGKEVPpFv1cThMyI\n430kZrwEHj1j3Khh8u4TFFkhb3cb9h3L29OzUiRJIn/vdjQ2CzNXbuH+5gz6UgcoiliHepVZcjJ2\nuVzHshGIsPKMtVVYW5qQ4wk0P7R0tfZMwl3oSj4rRVKpMFWUEBZP5sISqXQ6HO8fYeI3p/A+7kLS\nail+/zDGyqUPQFxtrJua0NgsTJ++RGzMjcqgw1RXk+uwhD8iWsbvMEmSKDyyL9dhCELO6ArzcXxw\nBM20G9XGjS8N5lpPjJXllH7xETOXb2JurFuWwhnC4ol/DUEQ3mmmmkocO5vfifegusL8Rb0nFpaf\nmHwpCIIgCDkmkrEgCIIg5JhIxoIgCIKQYyIZC4IgCEKOiWQsCIIgCDkmkrEgCIIg5JhIxoIgCIKQ\nYyIZC4IgCEKOiWQsCIIgCDkmkrEgCIIg5JhIxlmkyDLJaCzXYQiCIAhrjFibOksS0ShPvj1N1B+g\n9fOfYyksyHVIgiAIwhqxLlvGnoEh7v7N3+MZGFqR4yUiETq+PkXIM0s6mcJ1/hLpZGpFji0IgiCs\nfesuGSeiUXouXyPqC9B55jzjTzuX9XjxUJhHX39LxOujYksL5ZubCc96Gbh1Z1mPKwiCIKwf6y4Z\n9127STIWo7ylGa1BT+/VGwzcvouiKFk/ViwY5NHX3xL1Bahq20rd/j1s2LMbc0E+4087mRlcmZa5\nIAiCsLatq2Ts6R9kum8AW2kx9Qf20vrFzzHm2Rhp76D74hXkdDprx4r6A3T8/ltigSA1O9uo3b0T\nSZJQazVsPHkMlUZN96VrxEPhrB1TEARBWJ/WTTJOxuL0XruBSqOm6cghJJUKo83Gts9+jq3Egbu7\nl6ffnSGVSCz5WBGvj45vThELhandvYOanduRJGnu++aCfOr27SEZi+G6eHlZWuWCIAjC+rFuknH/\njVskIlFqdrRhys+b+7rWaGDLzz6msLYa7+g4HV9/Szy8+NZqeNZLxzeniIfC1O3bQ/X2ba/8XNmm\njRTWVuMbm2D0YceijycIgiCsf+siGc8MDePu7sXqKKKydctL31drNWx6/wRlmzYS8szy6HffEPZ6\nF3yckGeGjq9PkYhEaTi4j8rWza/9rCRJNB05hN5sYvDufYJT0ws+niAIgvBuWPPJOBWP03v1Biq1\nisajme7pV5FUKhoO7ad29w5ioTAdv/8W/8TkvI8TnJ7m8R++IxWP03j4AOWbN711G63RQNOxw6BA\n1/lLWekiFwRBENafNb/ox8Ctu8RDYWp2bX/rQhuSJFG9fRt6s4nuy9d4/O1pNh4/SlFd7Ru3C0y6\neXLqe9LJFE1HD1LibJp3fPmVFVS2bmbk4WP6rt/CeezwvLcVhFwITLozv1eRSOYLz8c8KApzox9+\n+Jryx9/74a8qjZoNe3dT3FC3YjELwlq3plvG3tExJjpdmAsLqNq2dd7blTib2PzR+0iSROfZC4w9\nfvraz/rHJ3n87WnSqRTOE0cXlIifq9m1A6ujCLerh6mevgVvLwgrIZ1M0nf9Jo9+/y0B9xRKOo0i\ny5nEqyggSUgqFZJKhUqjQaXRoNZp0eh0aPR6tEYDWqOBVCxO96UrhGZmc/0jCcKasWZbxulkkp7L\n15BUEk1HD6JSqxe0fX5VJa2ffsKT787Qd/0WiXCY2j27XhgV7R0d49npcyiKTPPJ429tQb+OSq3G\neeIo7b/5it6r17GWODDabIvalyAsh9nhUXqvXicWDGHKs9N45CD2stJF7WtmcIinp8/RefY8bV9+\nhkany3K0grD+rNmW8eCde8SCISpbt2J1OBa1D4ujiNbPf4Ypz87Iw8e4Llyam4s8OzzK09NnM4n4\n/ROLTsTPmfLsNBzcRyqRxHXhcqbFIQg5lozFcV28wpNT3xMPh6nevo3tv/x80YkYoLC2hqptW4j6\nAvRcuS6m9gnCPKzJZOwfn2Ts8TNMeXZqdrx6atF8GW02Wj/7GbbSYqZ6+nly6numunt59v1ZAFo+\nfI/CmupshE1xUyOOhjoCk1MM3W/Pyj4FYTEURWG6f4D7f/tr3K4eLEWFtH35GbW7d6DSLL3DrGbX\nDuylJUz39jPxrCsLEQvC6ienUkw86+Tu3/z9grddc93U6WSK7itXM1OHjh7Kyo1DazSw5ZOP6Lpw\niZmBIXxjE6g1Glo+eo+8ivIsRJ0hSRKNh/YTdE8x8uAR+RUV2MsX3wIRhMWIh8P0XbuJZ2Doh8FW\nu6jcuvm1MxEWQ6VWs/HkMR78+iv6b9zCWly06B4sQVisdDJJ1B/AXJCf1ev7p1KJBBPPuhjreEIi\nEkWlXvix1lwyHrp3n6gvQOXWzdhKS7K2X7VWw6b3jtN/8w6egUE2nji6pK6619Ho9Ww8cZRHX3+L\n68Il2n75BVqDPuvHEYSfUhQFt6uH/pu3ScUT2MtKaTxyEFOefVmOp7eY2XjiCE9OnaHzzAXafvG5\nuNaFFeMdGaXnSmYchM5kxFG/AUdDPdZixwtjg5YiEY0y/vgZ40+fkYon0Oi0VG3bQvmWlgXva00l\n44B7irGOpxjtNmp27cj6/iWVivoDe6nbvydr/1ivYistoXpHG0N3H9Bz5RrN7x1f1uMJQjQQoPfK\ndbyj42h0WhoP76e0eeOyX3f5VZVUbW9l+P5Dui9dYdMHJ8W1LiyrVDxO/607THZ2I6kkijbU4J9w\nM/b4GWOPn2G023A01FHcUP/Cao0LEQsGGX30mMmubuRUGp3RQO3uHZS3NKPRL+6Bc80kYzmVovvS\nVRRFofHIQdTa5Qt9JW4W1W2t+EbH8fQP4u7qprTZuezHFN49siwz+ugJQ3fvk06lKKypov7gfgxW\ny4rFULOjjaB7ipnBYcY6nrxylTxByIbZ4RF6rlwnHgpjKSqg6cghLI4i5HQa7+gY0719zAwMM3z/\nIcP3H2IpKqS4sR5HfR16i/mt+w/Pehl92MFUbx+KrGCwWqhs3UKJs2nJOWnNJOPhB4+IeH2UtzST\nV16W63CWTFKpcB4/woNf/46+67cyXe4Oa67DEtaR8KyXvnNnGOsdQWsw0HjkII6GuhVvmT6/1tt/\n83sGbt/FWuxYlldAwrsrGYvTf+MW7u5eVGoVNbu2U7Vt69yUV5VaTWFNNYU11aSTSWYGh5nu7WN2\nZJT+mzMM3LqLvayE4sYGCjfUvvQ6JeCeYqT9ETODw0CmGFDVtq0U1W9Y8LTa15FWaNqBMj0dXPTG\noWkP7b/7Gr3JxPY//XJdzVuc7h+g88wFLEUFnPwn/5BZbzTXIa16DoeVpVxP613E62P4wUOme/sx\nmbSYyyup278XndGY07j845N0fHMKndlE2y8+y3k8f0xcU/O32s6VZ2CI3qvXSUSiWB1FNB499NbV\nGJ9LRmN4BgaY6u7DP+kGQKVWkV9dRXFDHWqtltGHHfjGM0sn20qLqdq2lYKa6rc+1Doc1gU99a76\nlrGcTtN9+SqKnOmeXk+JGMBRtwFvcxOTnd30XLtNYcv8VxIThD/2x0lYlmUSyQQbj+zGsfH1BU1W\nkr28lNrdOxi4fQ/X+cts/uQD8f5YWLRkNEbv9ZtM9/ajUqvYsGcnla1bFjRqWms0ULapmbJNzcSC\nQaZ7+5nq7WdmYIiZgaG5zxVUVVLV1oqtrGTZrtlVn4xHHz4m5JmldGMT+VWVuQ5nWdTv30tgws3g\ng0dI1gIKqtfnzyksj7DXy8iDh0z3DqAoCgablWA8RkySeXzvPmW+MM7du1At49SO+arctpXApJuZ\noRGGHzykZkdbrkMS1qDpvn76rt0kEY1hK3HQePQQ5vz8Je3TYLVS1dZKVVsr4VkvUz19pOJxypqd\nWBxFWYr89VZ1N3V41kv7b75CazCw40+/XPQotbUgNO2h58wZ4imF7X/yBTqTKdchrVqrrZssV8Je\nL8P3H+LpyyRhS1EB9poq+ru6iEeiFFdXIaXjuMemKCwvo+3EcQyr4LpKRmO0/+Yr4uEImz/5gPzK\nilyHJK6pBcjluUpEIvReu4mnfxCVRk3trh1UbGlZ1jnEi7XQburV9xP8QE6n6b50FTkt03D4wLpO\nxJBZmrPp0F4S0djcqHFBeJXwrJfOcxd58He/Y7q3H3NhAZveP4GtoY6uhw9JxGI079nNrg8/4OS/\n9yeUbqhlZnyCq7/5LTPj47kOH63RwMb3jiOpJFznLxEPh3MdkrDKKYrCVHcv9//ut3j6B7GXlrD9\nl18suFt6NVt13dRyOo3b1cNI+yNiwRDFjfVZW45ytavZvpWBxz3MDo8y/vgpFVtXx7s+YXUIz3oZ\nvt+Op3/wh5ZwITU727CWlvDo8hXcg0MYzCbaThynsCwz40Bn0LPjvZMMPH5C5+3b3Pr2FBt37aKu\ndWtO39faSorZsGcXfTdu03XuElt//tG6uakK2ZNKJAi6pxh/2snM4DBqjYb6A3sp37xp3Y03WDXJ\n+KdJWKVRU765mdrdO3Md2oqRJImmY4d48Pe/Y+D2XezlZViKCnMdlpBjz5PwdN8AAFZHEdU7tlFQ\nU01gZoZrv/uKSCBIYUU5bcePvdQVLUkSdVu3kFfs4MG583TevsOs2822o0fQ5rDHqXxLC/5JN57+\nQQbvPmDDnnfnd114tWQ0hn/STWBiEv/EJKGZGRQ500uYV1FG45GD67biXc6TcSYJdzPy4BGxUBiV\nRk3Flk1UbtuK3vz2Sdjrjc5kounYYZ6cOkPXuYu0/eLzZV3gRFid5HSa2aER3N09c3MbrY4iqne2\nUVBdBcBwVxdPr99ETqdp2N5G047tbxykVVBaysEvv6D9wkXcg0Nc/d1X7Dh5EnuOHvgkSaLpyEHC\nM7OMtD/CXlYy97MJ74Z4KPxC8g3Peue+p1Kr5uak28vKyK+qWHet4T+WswFccirFpKuH0fYfk3BZ\n80Yqt215J5MwvDgwou/6LcYeP6Ws2UnjkYM5jmx1Wa+DbRRFITTtwd3dw3RvP8lYHABrsYOanW3k\nV1UiSRKpZJIn164z2t2DVq+n7fhRiqtf/SrnVedKlmW6792nt/0hKrWaLQcPULUxdyvAhTwzPPzq\nG9QaDW2/+HxFVwd7br1eU8thsedKURRigSD+HxJvYNJN1B+Y+75ao8FWWoytrBR7aQnW4uI13RBZ\n9fOMnyfhkfZHxENh1BoNlVs3U9G6OSdJWJZlIoEAwdlZArOzBGe9pBJJKpsaKKurQ52FqlCLsWHP\nTvwTE0x0usivqlxyPWVh9YqHwkz19OLu7iXi9QGgMxmp3LqZ4qaGF15VhHw+7p89R3DWS16xg+0n\nT2CyLmzlNpVKxcbdu8gvKeHhxYs8unyF2clJNh88kJPr3VJUSP3+vfRcuU7XuQts/fnHWanGJqwO\niqIw3dvP4O27xEI/DtbT6HUU1lRlkm9ZKZaiwqytZrUWrVjL2D3hZbKrm5GHHXNJuGxTpiW8EtN4\nFEUhFg4T9HoJzsxm/js7S9DrQ06n5z4nJVOoIzFSNjM6k5GaTc3UNDdjWIEHhZ8+cYa9Xh7+5mtU\nGnXOWgyr0XpoxaSTSWYGhnB39+Abm0BRFFSazJJ9xU0NFFRVvjSgaay3j44rV0knk9RubmHT3j1v\nvXm97VyFAwEenD2H3zODrbCQHe+dwGxfnipOb6IoCq4Ll5nq6cOUn0fjoQMrWl50PVxTK2Uh5yoZ\njdF77QbTfQOoNRoKaqqwl5ViKy1Z9rKGubbQlvGKJOOh9sfK44s3iYcjmSTcspHK1uVLwnI6jW96\nmsDMLEHv7FzyTcYTL3xOpVZjzc/DWliIxW7HOO1D1TOMnEjgV1KMmtUkAEklUVZXx4bNLeSXZK9s\n40+96iKfeNZFz5Xr5JWXsuVnYsQprN0bp6Io+Ccmcbt68PQPkE6mgMwSeyVNjRTVbXhpTdx0KoUs\ny7ju3GXw6TPUWi1bDx+ioqF+Xsecz7lKp1I8u3mLoWedaPU6Wo8ewWazo7daV7SbMJ1MMXDrDhPP\nulAUhdKNTWzYswut0bDsx16r11QuzPdczQ6P0H3pKolIFFtpMc5jRzDal2fwlaIoKLE46XAEORxF\nDkeQQxHkSAQllUbjKERbUoTGUYA0z9a3LMsosrzo3qIVScZOp1ML/N9ADaAH/luXy/XN6z5/+i//\nVyUWlynf3ExF65ZlWZNWURT8Hg+j3T2M9/WRiMZ+/KYkYbHbsRbkY83Px1pYgDW/AJPNikqlIjE2\nSejqHVIzPiS9Dk1RAcmxSRS1mnBVMcMhLyGfH4C8kmI2bG6hbEP2Fgh/7lUXuaIodJ45j2dgiNpd\nO6jesS2rx1yL1tKNU5FlwjOzeAaGmOrpJRYMAWCwWihuaqC4seG19YTTqRQ3/vrvGOnqwlJehr3Y\nwY73TmLJm3/Zt4Wcq9Hubh6eu0hwYBi9otCwfy9bvvj5ig+aCbin6L16nZBnFq3BQN2+XRQ3NS5r\nHGvpmsq1t52rdDJJ/83MQ5VKraJm5/YlzwdOB0Kkg6G5RPtC0v3h/0nLb92PpFGjKSpAW+pAU+pA\nW+pAbc40CtOpFMFpD77JSXwTk/gnp1CpVez47OeLqvm9Uu+M/xyYdrlc/77T6cwHHgKvTcYbdrZh\nqa1fliQcDYUY7elhrKeX0PP3bQYDNS3N5BcXYy0owJKX98qnm3QojP/GfeI9gyBJGFsaMe/ehmQ0\nEOvqI3TtLpbBCbbVVBBv285wfx/u4RHaz1+k03yb6uZmqps3LuuqRpIk0Xj4IMFpD0P3H5BXUZap\n8CSsSslojMDUFIHJKYJTUwSnpudawGqthtKNTRQ3NWAvK31rcum+cYuum7dIJ1NYi4rY/9mnaJdp\nbXY5lUIKRbGlZGY8M8xMe5jqH8AfDNL22ScLegBYKltJMW1ffsbY46cM3X2A6+JV3K4eGg4dWHT9\nWWFlBCbduC5eIeoPYC4swHns8JKnZ3rvPGT8139Ao9GgMRjQ6vVoDXpUGg2SSoXKZEBTVIDaZERl\nMaMyG1GbTajMJlRmI6hUpNwekpPTJN3TJKdmSE5OI8sy8VCYeDpFRFIIyUkSBh0xlUQiEUdSqYiF\nQsT++m858Of/ANMyT6labMvYDEgulyvkdDoLgTsul+tN/WZLqtr0U8lEgsmBQUZ7epgZnwBFQaVW\nU1JbQ0VDA8VVlW9stSqpNJGOTiL3HqMkk2hKirAe2o225MX1R9PBMMELN0iMTiAZ9FgP7SZVnM/Q\ns05GXC5SiSQqtYry+no2bNmMvWhp65e+6YnTNz7B42++Q28xs/2Xn6/7FcneZLW0YhRZJuz1EXRP\nEXBPEXC7ifp+HB0qSRKm/DxsJcXYy8sorK1GrdXOa9/+qSl+8y//NalkkupNm9BqNDTu30P11oXV\nAp7PufINjzJ4/RZRvx9ZlvH5fAQCfmY7u5EkFaWH9lBcX0dlYyPl9XXoDMvfbfxcLBii7/pNZgaH\nUalVVG7bStW21qx3n6+Wa2oteOUI/XSaoXsPGH34GIDK1s3U7Ny+5IF4sizT8Rf/HXHPDMmyImSd\nFkWrQdFpUVlMmAoLMebbMdntGO22zH9ttpde90CmzKJvchLvyAizT7sJ9Q2heP1IgRDEEyiShKRW\nozLoId+GYrcwGw4yEwpiLMijekcbhWVlFJSWkF9aislqfeMD9Yq+M3Y6nVbg98D/4XK5/uYNH13y\ni2lZlnEPjzD0zMVYXz+pVKa1UVReRu2mjVQ1NqB7xT/AT0UGRpg9f4uk14/aZCD/8C4sm5vmTurs\n+CSjz1zU79yGOc+OoigEH3bivXQHOZXC3FRL4ckDyBo1g51d9LR3EPRlWuR5jiLqWjbR2LY8lZe6\nr92i/84DyjY2svWjk+t6zt1qlIzF8U248U+68Y5P4p9wk0r8OA5Bo9NhLyshv7yUvLJS7KXFr7wp\nvE0iFuev/ut/xdTAIHs++4R9n3/Clb/6O5LRKPv+5HMKKrJTzzsWCNJ96TrTPQMgSZRuamRsYJh4\nNMquzz6i+8ot7v/690g6LcX7d6DWaFCp1FTU11K7qZmSmirUKzT61d07QOeFq8RCIUx5djYdP0xR\nrZiTvBoEp2foOH2e4LQHo93Glg+OU1BZnpV99353kb5/9zfYtjXT+I9/SdjrJ+z3E/b6CPv8hH1+\n5Fd0T+uMBkx2GxqDjmgozNTgCN5JN4lojHgshizL6M0mDDYrBqsZg1aHDQ1mGfTxFLpEKvPQqSgM\nd3XjScZQqkowNlTDD/ddk8VCUXkZRRVlFJWXYS8qRKVSZbrpb9yl8cj+lUnGTqezCvgt8L+4XK7/\n9y0fX1TLWFEUAjOzjPX0MNbbSzySqfVrstuobGykorEB8zy7DtL+IKHr94gPjGS6pLc4Me9qzTwF\n/SAeiXD3178jEY2iUqup3dFG9dYtqNRqUv4AwQs3SY67URkNWI/sRV9fnVkzdXiYB2fP0X+/HVmW\nOfhnv6Tt+PEF/7xvezqX02k6vv6WgHsa5/HDlDQ1LvgY2ZJKJBi6e5+C6qoVr6a10q0YRZbpu3GL\niaddL6wZbsqzYy0pxlZSjK20BFN+3pIfkNKpFJd+9df0XLtJaWM9n/5n/ykqlQrfxATt33yHzmhg\n1y+/mPcrn1e2YlIpxh89Ybz9EelUCltpCdX799B75x7esTE27NjOhp3bURSFy//T/854xxOKnI3U\nHjvExMAAwR8WZtAZDT/8HjauyMIhqUSC4XvtjD1+iqIoFDfWUbdvT1YGgoqW8fw9P1eKLDPa8YSh\nu/eR0zJlzU427NudtTK3iWiM9v/qX6PyBmj+F/8cyysevmRZJh4OE/X5Cc7O4hkewTM6im/STcAz\nQzqRBDKDcHUWMwabjfzyMgorK7AWFWHJs2PJy8Nst7/wCkhOJkm5PSSGxgg/62H0fjupRIL8hjqk\nmnICBg0zoQCJH9YCANDqdVgsVqLDo6jSCn/23/+LFRnAVQJcAv6py+W6OI9NFpyMQz4fD85dIDAz\nA4BWr6e8IdNVlldcPO+bnpJMEWl/SqT9CUoqjba8BOuh3WiKXiy3pSgKj06dZnZ0jNKmRmZHxkhE\nI5jz89l4+CD20hIUWSba0UX4djtKKo2hqQ6lpZ7OO3fxud2kkklGulwowJF/+Gds2rdvQTfn+dwQ\nooEA7b/+CkVR2P7LL5ZtdOKbyKkUT747g29sAkkl0XT00Io+GKzkjVORZVwXr2Sm3OTZKarbgLXE\nga24OOujfOV0mrvfn+XJ92cxGAx8/M//GfllP07vGXrYQd/tO+RXVND68QfzKon403PlGx5l4NpN\nYoEAOpOR6j27KGpqoO/2XYYfdVBUU82WD96bu24T0Sjn/tX/gH9iktojB9n1Z78g5PNlBkr29pGI\nZQZK2goLqWzKPCDrl2FsyB8LTXvouXqD4NQ0Gr2ODXt2Utq8cUkPQiIZz5/DYWW4b4zui1fxT0yi\nMxlpPHIw6zUEOr/9nsBvvqOobSv1/+w/eOF7iqIQ9vvxuqfwTU3hdU8RmJ2FP8pnBrMZs82KxWbH\nUVONtbAAo8Wy4FKiiizjfdJJ11//Fo03SEVjI1qDHnW+HaW8mIjFgDcUYKzjCZ6nXShpGUNxEf/x\nv/vLFUnG/yPwJ4Drj778kcvlir1mkwUl43g0yvWvfk8kEKSktobKpkaKq6oWNMRcURQS/SOEbtwj\nHQihMpuw7N+BvrH2lb+0w48e03vrNoXV1Wz98D1SiQT9t+8x1tkJQEVzM3V7dqLV60nN+vCdvcrU\nwyd4fbNEa8so2tHKxn176X3QztW/+3t0RhP7vviM5r175n2TmO8NYaq7l64Ll7EWO2j97JMVnSiv\nyDKd5y7i6R8kr6KM8MwsyVicun17qGxdmcIWK3XjlNNpXBcuM903gK3EweaPP1i2d/WKovDo0mW6\nrt8k4fWx4+MPaTlx7KXPPP7+LJ6hYWq3t1G3a8er404kiPcOEesewF5oJVFcglKUx/DdB8wODiFJ\nEqWbN1G5sw2NXo+7t4+n5y9iystjx+c/f2m96tmhEa7+z/+WeCxKw/vHaf3oA9QaDXI6zdTICCOu\nbqaGh1FkBUkl4aiqwmyzoVKrUavVqDTqH/6uQaX+4e9zX1P/8DUNao0ajU43rwGRiiwz0eli8PZd\nUokkthIHDYcOLHqwkEjG86MoCnH3KPe/vcD/z957B8l55nd+n845TIfpnpwTBoNBzjmQBDO55HJX\nt7sKVrC1Vac7+3wuy3LV2nV1dXZJPp8sleSTLOvW0mqX3CUJJuRMEBmTc+iZ6Z7QOed++/UfDQ6I\nBUBiQIDkSvpWdXVV99vv2/308z6/55e+XyFfwNZYT8uuHY99Yxr1+hj4j3+JLpam/b/7r5FXO4n4\n/SU76akAACAASURBVISXvIR9PiI+P/nsHa9UJpdjstsoKy/H7CinrLz8sXNDLI5PMHL6LPqilOa6\nBoT5JcSCQLFYJBqPEclnECwmLF0dCEoFe1966slXU4+Njf0B8AeP8tkvglAocPPESVKxOC0b1tG2\nceXk8UIiSfzMZXLuBZBJ0a7rRLuxC+kDwicxv5+pq9dRarR07N2FRCJBoVLRtnsHztZmRi98zPzI\nCP6ZWVq2bwGZjNF8HEEhYpDIqBQUWGValAoFq3ZsJ7rkZeDix/SePkNREOjcsf2x5nfLW5sJuT34\nJqaYvXGLhi2bHtu5Pw+iKC5riZoqnKw+/BTpWJzBD48xffkq+Uya+s0b/1HksouFAiOnzhKcmcNU\n4aTz8KHHFn77ZYiiyPDlK8wNj5CPxanpaKd56+Z7jpNIJHTs3cP1t99lpqcXk9OB9XaKQBRFCt4A\n6ZFJshMziPk8giAgDSjxH79I3OujYNRhbG2k7qXD6J3lAMQDQUbOX0SmUNL11MH7CkdY6mroev4w\n/e99iPvqDeRKJasP7kcqk+Gsr8dZX082nWZhagrP2Di+2bkvNR7rDx6gsqnxc4+RSKVUdnZgra9l\n+vI1/JPT9Lx9hMatm6ns6vxHMQe/aUhHY0x/coVMwItEKqV9/x7sLU2PfayLxSLjZ86jCESwdncx\nvejBdfrkXV6v1mSkvLZm2fgaLZYn7pRUtLYQ9wfwDA4xr4RVv/4asd4h3O8dJ7/kQ6fVYjFa0Sby\nqFtWnrr72rip73uQKNJz5iwLk1NUNjexbv++Ff/RhWiMyJGTFONJlDWV6HdtQl724B6xQi7H9V+8\nSzoeZ+2zz2C5j9B5URCY6x9g4vJVfK5ZBLGItaGOlk0bqautI3n+GoVACKlBh3H/dopmIxd++jNm\nhoax1tbQvH4dXbt2fmF4ZCW780Iux62fv0s2nqDr+WcwVz2egonPw+yNW8ze6EFvs7DmhWeXvcRM\nPM7Ah8dIR2I4O1pp2bXjiZKTPGkvRsgXGD5+irBnnrLqKlY9ffCJkl9M9PQwdu0GmVAYk9FI284d\n1HaXqqbjXh8Jn//OfSCRkAyHGT3/MTKFglU7dyALhClMz1GMlnqYJXotRYeN4elxJLE4FrkGTSaH\nyWJFa7UglctR1lcjq61k4MZ10qkkXU8dxN5Q/7lj0vfm28ze7EFTUU712m5WHdh73zmdisfJZ3MU\nhQJFQUAQhNJzofRceq30XrHw2fcLuMfG0JlM7Hn9tRXd+6E5D+PnLpBLpXG0tdCya/uKKnn/2TN+\nMHKpFHO3ektkLEWRmvYGKjZsfmKMgJ7BYVxvHsEYSZJoqCCsVaI1GalsbKTM4cBcbn/iqZBUIkHv\nxYsk43F2v/DC8vWKgkDvh8cILyxgdTrJBUIIhQKO5ibKy6zkpuco+Eqp1c4f/fCbzU39eZi4eYuF\nySnKHA669+xeuSEOhom8d4psKExCr0bjMCOEQuikElTG+5ehj398mXQsRt3a7vsaYqBUPSeTUUBE\nKApIRBGFCCqlCoXdStlrh0le7yd25RbBnx/F/r1XWHvoIPlsFr9nntmhYYqCQPfePSvOVzwIcqWS\n9gN76TvyAWNnzrP+tVeeKFPRwtAIszd6UBsNdB5+6q5wrdpgoPul5xn88DhLI+MUMlnaD+z9leQX\nFvJ5ho6eILKwhLWuho5D+5/o75gdGWHs2g1kEilGgxGD1Ur16lWIoshi3yBzV69zz4ZZFFHHUmTG\np5n+uAeDxQIyKcUyI4K9jKJayvz1K2SSKZQqOcVKM3v/ze+jkUjJTM6UQtgTLrxHTyFJJKjZ0I0R\nGWJBQCK/v3chU8hpObiXTDRGwO1maWICqUxGx75771OtwQAro8u+89OKRdxj4yy5XFQ0fr53/FlY\naqtZ9+pLDJ84jXdsglQoTMdTB/6ZQvZLoJDLMd8/iKdvACFfQGMyUr95Ax1b1hAIJJ7INbOpFFNX\nriLOeIgplURVMuo6O1i1detXwpsuiiJz4+P0f/IJhXyp+Gvg8mU23i7IlcpkdOzdxak/+VO8N/uo\n6min6+XnsXyqHbChi0I4WuKuWCFkP/rRjx7Pr/h8/CiVyn3uAfOTkwxduozGoGfr88+tWGc17w0Q\nee8UYjpDWCUlQoH4kpfgtIulwWEW+weJzLlJ+oPkUylEEQJzc8z09GK021m1f899vbmI10fP8RPM\nj0+g0mpZd/hpqttaCc3MMt/Tx/SlK4RdswS8S0SjEVLDE0THJjFvXINCqyGfTpOKx8mkUiQiEZz1\ndQ/0GnU6FV80Tp+FSq9DIpEQnJkj4fdTVlP90H2sK4F/apqJcx+j1KhZ8+JzqO8jTCBTKLA3NxL3\n+QnNeYh5fVgb6p5I6Gil4/SwKGSzDH50nOiiF1tj/RM3xIvT0/Seu4BCpcKkN1DM51i1fy8ag57p\nC5dY6BtAqdXSsHMbtpZGyioqMBRA749iFmXIiyJ5hQxVVxs1v/YK5i3rKGtvIZaIkcqkqe7uouup\nvQQjUQJuN472VvRN9Wi62liKBAkuLqHX6bEajGQnZkgPjiGEoyCTITPo7pmnKr0ORJF8IkE2niCd\nSpJPZbDW1jy2UKXeXMbM8DDJSJTaFRZlyZVKHC1NZJMpQnMe/JNTGBzlD2WQn9Sc+lVEURBYHB5l\n5MRpQnMeFGoVDVs307pnJ3qr9YmO1eDJ08ydvogmmUPSVMOa11+hac2ax+bEfB7SySQ3zpxhor8f\nqUxG986dZFMpvB4PZXY7epOJZDDE+PHTiLk8uUwGVUU59Zs33dXCKNWoUVY50elU/8tKrv+NMMah\npSVunjiJTCFn2wvPr1iFJje/RPT904j5PLK1HSz4ljBWOGnauxOtxYJSo6EoCCQDIRI+H+HZOeZ7\n+hn88BjZSAxnUyNCNouQLyBTKpDK5aTCYfqPn6T/6HGi7nnUUhkmjY6oa5bkkheFREYmHCW2uERk\nYRGZSol97WqkmTzZGTf+aRdSjZpsOoNUqUCmkBMNhgh7fZhtNjKxGHF/gOiSl6DHg981A0IeuW5l\n1dFGRznJUJiwex7f+CSaMvMjUbc9CJH5BUZOnEYml9P1/GF0lrIHHiuVybA3NZAKRwjNeQi757E2\n1D32DcKTWAzymSyDHx4j7vNT3tJY8uyfYA7K75nn5slTSGVyWrpWE3DNYK2tpbqzg7GjJwnPzqEv\nt9Px3DOocgLF0WkKA+NIowkUKjWGtatwvPwMQY2MuFjA3t6Ko6WZTCbDVH8/xgon217/Fi3rO8lk\nCnhdM/hcM9hqa4gsLDJ5qwdVbRVdv/fraJvqkCoVCLE4+QUv2XEX6cFxiskUMqMe6WciLgang6h7\nHnIFkEmJBvwIuTyW6sejNatUq0lEIgTm5zHb7ehXOJclUinW+loUahXBmZIQh1KjRm+zfe73+2dj\nfEddafjEaXwTU0ikEmrXr6P94F5MTsfy5uxJjdX41Wt88rOfUxZMUF5VxZp/9XsPjlY+RoiiiGdq\niivHjxMNhbBXVbHj2WexV1ZSVl7OzOgo/sVFtKKUyVNnyafT1G5aT+2OrQTn3ITnF3C2ttyzXqzU\nGH/tOeNkLMald46Qz2XZfPgZ7NUrS3xnZ+eJHTuHKIoYD+3CNTpKxO1h1YvPYqq8mxxByBdIhyMk\n/H76PzhGbGmpxKTymfagTCqF3zNPPpmkKBRRatTYa2vRGPSodDrUZhMas+n2s5lcNs3UjR7S0Sgq\nrZbq1lYS754gueTDV6Yhk88T8nqRaNXIjQay6Qxao4HKpiaksrt3ezqdmrWvvopqhX2ToiiyMDCE\n69oNigWBys4OGrZu/tJ5zoQ/QN97HyIWi6x+9umHzkuLxSITFy+xNDKOxmyk67ln7utNPyoed34v\nl04z8MExksEQzvZWWnY/2Zx3xOfj8gcfIRYFNhw6xNTHn5BNpek6uJ+5T66SjcexNTVS09FB8sLV\nkrcKyO0WNB0tqFobkKpKxWTJcIQb7xwBoPvZZ+g9dYpsOsOmZ54mdb2f8pYalF1dzPQPMHr5ChKx\npEym0mnZ+MqL6MrubK5EUaSw5CczNUt2Yobi7b5+ZU0lmtWtKOtLSlKpUJiBXxwBiQRBrSKTTFC3\nbi1Nm1debHk/xEIhLrz1C8yOcna89OIjG/nIwiIjJ86Qz2RwdrTSvGPbAyMd/9RzxmG3B9fVGyQC\nQaQyKRWrOqhZ333ffvbHPVaf6nPfePsIsniKzY5aqvZsx/zsvi/+8JdENp2m9+JFFmZmkCsUdG7e\nTMOqVXfNucFPLtN75AP0MgVVLc007duNpb7UxjX+8Sd4hoaxNzSw+tD+uz73jdcz/izy2SzXjx4n\nl8nQtWvnig1xZnKG2MmPkUilmJ7dS06lIOL2YKysuMcQQynvpS+34XW5kJsNrNq0no59u8nG4iQD\nQYJuN2d//BPSoRBSjZqOHdvoPngArcWC2mx8YDWttb6eub4BZm71MtXbi1ynQCUBU06koNei1mhI\nhMJIMjmMFQ7yUimpbIbVu3aiMehRajREFpfwj4/im5qmpmtlLUISiYSqNasxVVUydvocC0MjROYX\naD+wF7390Sg609EYgx8dp1gQaD+0b0UFYhKplJbdO1GoNbh7+uh79wNWP/fM53rVXxeyySQD7x8l\nFYlS2dlB086V9YavFIlIhGtHjyMUCmw4dICUP0AmkcDidDJ95jxCPk/1um7MgoTYeycBUHc0o+lq\nQ2G/t21HV2amffdOBk+d5dRf/hVah532zZtI3RggveQjEI9i1BmpX9OFUChw/v/9MWJBYP/v/NZd\nhhhK80hRUY6iohz9tvVkZzykB8bIuRfIuReQGfWoO1vRdDRTs2kDs1euYTLakMikzPb0IpPLqF+/\n7kuPkdFiwdlQz5JrhsD8AvZH9I7MlRWs+9aLDB8/zdLI+HIe+evQTf+mIu73M3P1BmHPAhKJhPKW\nJuo2rUfzhHmYP0XE5+PWmbN4xyaQSaWs71iNXW9C290BlIrHPDd6CExMI5FKkClKUUapQoFMrkCq\nkN/92l3vl96TKhRI5bISg9zyQ8bSnJu+K5fJZ7NYnU427N17D4lU0h8gNz2HJJUmJs+zZfe2ZUMM\n0LxtC4lQCL/LxVxvP3Xrusnn88xMTmK339sR8Xn42jzjoiBw7ehxAvPzNK7pYtW2rSs6YXpkkvjZ\ny0gUckzP7UdZ6WDkw+NE3B7aN29GNTGHvLkOxepWJKo7RjTkmaf3o2NoDAY2fevlZQMbXvLywV/+\n3wQXFiivq0NZZkIml1PT0sKa7dtRPkQOOxWNEfP5kCuV5C9cRwyEMR/eh7y2git//zPcN3swlJUh\niCI5qQTH6na2v/oKSrWabCpF3ztvI9UY2PjqSysai8+iWCjgunqD+YGhkmLKpg0lxZQVGJhsMknf\nkQ/JxOI079pOZWfHI38fT98g05evolCr6Hzm0GMRuXhcO/NMPMHAB0dJR2NUr1lNw7bNT9QQpxMJ\nLr37HplkkjV7dmGvquLKT98iGwhhNJmQK5XUd3cjn5qjEIwgMxowHNiOsvKLx+zjn/yU4bMXcLY0\nsmntRhKTM+hqq5BEwmQKItWvv8DQ+Qu4bvWSy+ewNdSz8bnDmOz2Lzx3IRgmPTBGZmwasVBAIpeh\nbKrDszBPLJWgZssm5kZGyMTjNG/dslwJ/mUQ8fv5+O13sVZWsO2F57/UuYR8gYkLF/FNTKPSael4\n6gBGR/ldx/xT84zT0Rgz12/in5wGwFJTTf2WjQ/Vp/04xqpYLDLV28f4zZvk0xnyoTBORyUtBRnK\nchvGV55isX+Qxb5BhHwelV6PTKlAyBco5vMI+fxdOvQPhFiyNdLPFCYKhQJet5tYKIREIsVRW4Ot\nqgqZXF7ippbLlw13fMlLsShgqKthfHYao8XCvldfvauYLJtKcePtIyQiUXQNNSwG/eSyWf7VH/6b\nFS0mX0vOWBRFBj++xOK0C0ddLWtWWDmd6hshcf4qUo0a84uHUDrtxL0+5q7ewOh0YluKUAxGEDxL\nFAbHEXN5pBYzeaFA74fHKBYEup97Go3RiCiKuHr7+PgXb+Obm6PWUs629Ztp37+HSDiEz+PBMzlZ\nUn/6gt2iQq1Cb7WgNZvQ1FeTG5umsOhDt6aD6vXdxFJJkrEYFruNXDSOf2QM9+Aw9vo6DHYb2WgI\n76wHR3MTikck45dIpVhqqzGUlxP2zBN0zRJbWsJcVflQfbKFbCl3mgpHqdu4jpq1X45n2+gsR200\n4J9y4Z+YRm+zovmS4vWPI2eVjsYYeP8jMrE4tevXUr/lyfZH5zIZrrz/Ial4nPYtm2hYvZrhM+dZ\nvNWHWqnCaLNSX1OHODhBMZVG09mK8dm9yM1f7KHEQyFcvX3kUimMOZGiN4i5sZ6K5w5hMOkIjEyx\n0D9IKB6lqr2Ntl078LpcLE27sFRUoNZ/vqco1WpQ1Vej6WpDqtUgRGLk55dQxVNkpmaJe320Pv80\n4YVF/C4XCo0aY/kXG/nPg1qnI+L3E/DMY6uuQqN/9KpoqUyKtaEeuVJB0DWLb3wCpVZ7V9Ton0rO\nOB2NMXv9JuPnLpAMhjHYbbQf2EPthnUPTSv6ZccqFY9z4/gJPOMTqLU6TDoDSqWSRks58lSGnMPC\n1PXrhGfdKNQq6rZuoXn/bpyrV1GxppPKtWuo3rCW6vVrqVizGufqVTg62rC3tWJracLaWE9ZfR3q\nMjNej4dQwI9Mq8FSWwNKBbPT02QLeQx2G60bN2BxViynL4qFAoVMhlwiSSYWQ6FR03roALUb15HN\nZPC63UikUuyVdyKFsViUee8iI5cvszgxidZupXPdehpbGr75BVzT/QNM9fZhtFrZfPiZhy5ZF0WR\n1I0BkpdvIdVpMb94EIXdUjrn+Y/JxGI01DcgnZlH3lyHvK2Roj+IMLdIfmCM2U+uEcunady+FUdT\nI9lUit6Tp5nu68c3MUW1VE1rXQNiOgOpNJ3PP4NUKmVpbg73xATZdBpbRcVDFfZI1SqQSMi53Ii5\nHJqmOiyVFfjm5ymqVXQ/dZBEMETQNcvM9ZsIiST2ukq8cwvIVSrKvmTfsMZkpLy1mXQsRnjOg298\nArVB/7mh4mKhwNCxk8S8fipWtdOw9fF4inqrFb3Nin/ahW9iCo3BgM5qeeTzfenFIBxh4P2PyCaS\n1G/aQN2m9U/UEOdzOa59dJRYMERjdxetGzbgn3Jx6x/eRCIUqWtvo0qqRlzwIdVpMD29B213x0OJ\noAuFAjc/Oko2nWZ191riPUOkM2nqX38RTZkZe3M1ro+vs3ijB3W5jXWvvbxcJ7E0Nc3S5BRlFc6H\nMnYSuQyF046mqw2lsxxJUYRwlOzkLOn+USo62vF5fbjHxhDyefSWshV3RXwWGoMB99gYmVSK6pYv\nR7cqkUgwOh0YHXZCs278Uy7y6Qzm6kokUuk/amMsiiLRhUWmPrnC1KUrxH1+NEYjLbu307h964pD\n0l9mrDwTE1w/dpxkNEZFYwONqzrwjo9jstnQuhYIzswSkIsgkVK1vpuWg/swOB33vT8lEglSuRy5\nUolCo0ap06I2GFCbjAQWFxi/1UMBEYPTQSqbZWJwgNmZaZSWMja++Dw7vv0aVV2dlHe04ezsoKKr\nk8ruLqrWdd8x9t1daG4XEVqdTtwTE/jcbirq6ggGAly/dJHea1dJJJMYysowKjXUVlaxdt9eDAbN\nN9sYL83M0H/hImqdlq3PP/fQzduiKJL85CapmwPIjHrMLx9Cflvb9FOv2OR0YvNGEfMF1M/uRV5f\njWJ1K1KdhtDgKMnhCazpApVV1UTSKW6eOUPM7ycx4aIsnae6ppaKTeuQ67Sk5uaRKZXUrF+Ls66O\n0NISXreb+elpzDYb2odYvBQOGzmXh9zsPIpqJ1qHHaVajdflIl8osPM3vocglRDyzBOYdiErCGQE\ngVwqTfXqVV/aQMgUCuxNDah0WkKzbnyT02TiCcxV924oPqW5DLvnsTXW07Z312MtYtKaTZgqnARc\nM/gmp5GrVPeECh8WX2YxSIbCDHxwlFwqTeO2LdQ8IYWtT5HP5bhx/AThJS/Vba2s3rGDhM/P+T/9\nC7KxOO2tbTgEKWRzqNubMD27H7n14TV7xy5fwTc7R2VFJcr5AGqjgaTNSDQYRGkpo5hJMXKrHzEQ\nxllRiW1dF1K5vBTpKStjcWqaxckpzA4HWuO9RXZFQSCdShGPRAj7/QQWFliam2PR72WxkCGgkhFy\ne0hMzxIenUDqD5GY8zA5Mc78xCRCNovGYEClW7mYg0avJ7i4SGB+gfK62sdCb6gxGbE11hNdWCI0\n6ya6uISlthqjWf+PzhgXCwW845OMn7uIp3eAdCSK0WGnYdsWmnduQ2+1PtIa8yj3XyGfp/fceSZu\n9iCRylizexfN69YydOos6XAEXShOemSSvL2M8u2baH1qP5a62hV3NKRiMXpPnsI9PIJcoWD13t3Y\nmxqZHR8nFg5BQcBitVFIZ5BIJOjLzA+8hkQiuWt8ZDIZGr2e/qtXuH7xIl6/l2Q8jqOykk3bd7Dt\n0CEUstL9kI7Gaehq/+ZWU0cDAT458j4A21964aH1f8VikcSFq6SHJpCVmTC/eBDZZ0Jry7ni1nYU\nIy4Ua9pQ7bpDERkPBLnx9hF0sRRttgqi41OEl5ZI69VkZRJisRhGh4MNv/MD9A21FFJp3G+9j5DO\nUP3KYdQOO0KhwMjNm0z09wPQ3NXFqo0bv9Crzy/5Cb99DJnZiOXbz4NMSt/pMyxNTdO0fh3NGzcw\ndv0Gg8dPIQaDGJwVKMtMbHj5RUyPaKzuh1Q4wtiZ88T9pQW7/cDeZWMoiiITFz5maWQcc1UFqw8/\n9cT6axPBEIMfHiOXSmNvaqB+88YVi108Ss5KFEXC7nnGzpwnn8l86Vz4wyCXyXDt2HEiXh/OhnrW\nHzxAZGaOm//wFuGJaRrK7NQ2NyPTajDs24aqfmUFjL6ZWW4eO45GocSZg2Iuj+3gThY8Hm6dPEUs\nm0ZWLKJT61jV1oF00Y+iyolm3SqE24xXofkFXD29iMUiVatXoTWZEAoFCvk8uWyWbDr9ud9BJpej\nlMmIDYyiTWapclQgS2fxLyzg0UgpaFSUV1dTs6qd2u41lFVVrsgA+D3zXP3wIxz1dWx6+qkVjc/n\nQcjnGTt7gcD0DCq9jp3feZG8/MurP30TkE0mWRweZWl4lFw6g0QqwdbYQFVX5yNvgD+Lld5/QqHA\ntWPHCc4vYHaUs27fXnQmE6NnzjN89AQqpRJHOI1Oo6X2v/1ddFXOLz7pL0EUReaGhhm/eo1UPI7G\nZMRcXUUyHicSCCCRSGhbv57q+nrmhkdYGBtHEAQUKhXV7W3UrV79uemaTDrN+PAQE8PDpT74cJiu\nbdvY89xzWGx3UjJFQaDng6NEl5Z444/+9VenZ7wCiHOuRS69e4RMKs3Gpw7irK9/uA8KArHTl8hO\nzCC3WzA/fwCp9o43Hff6GHznfUzlDuqiGcgX0H7vZSTaUs61kM9z4+0jpCIR2vfswjM2TnJoHJNr\nEX0sTSISoeC00vlvf4h21R1d45RngYX3TyI3Gqh9/fllXuvg0hI3z50jGYthMJvZsG8fZV9QBBO/\neI10/yi6TWvQbV5LPpvlk1+8QyaRYONzz2KtqmTyVg+3/v4fSATCGOpqWHPoAG27djzCUD8YJQHw\nHjy9/SCB2g3rqF3XzeyNHuZu9aK3WVnz4rNPjIP5U6RjMUZPnSPu8y+3UdSuX/vQDGIrXQyiC0vM\nXL9JdHEJiURCy56dONtbH/XrPxQyqRRXPzpKPBiiurWFrt27WOwdYObSFcLXerBJlNRvXIduVQv6\nnZvu6uWFUoHLp/UKiWh02Xh+Si+ZTaXwDAxBLk+NRIVcKCI0VJE16ZidniI5N49MKCKRSiiolGjL\nbTRmJajzRYTWOsSyOxugdDSKf8oFUFKlslmRyWQo1WrUWi1qnQ61RoNaq0Wl1aK5/ZpKo0GhVCKR\nSAhMTTP8/jFkahXlTgfy0VkiwSCz0jyBQhaFVEZ5dRW2mmpqu9dgb6i/h8gh4PXSd/M667ZsxWIt\nbdRFUeTSkfeIeH3sfu1VjNbHJ9UoiiLunj5mr99Cq1UgyFQYHXYMdjsGRzn629ShvypI+APMDwzh\nn5qmKBRRqFU4O9qoWNXxWJnIVnL/CYUCN06cxO/2lDakB/ZTyGaZOn+J3nfeRyaX0d7dTVkkhWlj\nN8YDD7/miaJIMhZjcWaGgbPn8M+5yedymKsq0VrKSmFsmQxLeTld27Zh/ozzl8tkmBsaZm5omFw6\njUQqpaKpkfo1XRisVoSZefK3hkiFwkw12JiemykZb6WS+sYm5kfHkMvl7H/tNXS/1Lb5aUHXy//6\nd795xjify4nv/T//QCwYpGPrFpq6Hy40KBYEYicukHW5UVSUY3pu/3J/5adY9oobmlFMulFuWI1y\n69o775+/yOLoGAaHg3g0Qi4SxRjPUqbTszQyigIpLQ1NJZWZcivKDauRNVSXFpgrNwnfGsDY1oTj\nwK7lcxbyeYauXWN6aGh5x9W2du0Dwx3FXI7QP7xHMZ3B8u3nkVvMRLw+rr73Pkq1mu2vvYpKo8E3\n2MuJ/+tviEXCWNtbefUP/+2Xyrk9CJGFRcbPnCeTSKIxG0lHYmhMRrpfeu6xaMM+DERRxD/lYuba\nDTKxOHKlgpp13VSu7vzC/uiHXQziPj8z124S9swDYK2roW7ThkdW9nlYpBMJrnzwEclolLpVHXRs\n2YLrwiUCgyPk+0ZRFArY29uo/s5LqJvqlj8niiKRQAD3xASeqallr1QqlZbaNORyZDIZEqmUxdFx\n0pEIVSjRSeUoG2tJ2IxMjI4gCAXs5Q7UiQy2qnIiEinz7jnk+QI1wTTOujqqvvUcCp12+bxRr4/+\nM2eRAN0H9+NoaHjg7xMKBRLhMPFgkFggRDwYJB4MEh6bJBMMo6tyYjAYcYSS6LR6/Gops9IcGvzU\nNgAAIABJREFUSW8AjUKBtaICg8VCbXcXzpYWZAo5yUScY+++Qyadxu5wcuiFO/3F3tk5rh87TmVT\nI+sPHnjs/1dozkN8doqFKTf5z+jTSmVSdFbrXQZa/QBa3a8LYrFIcGaO+YEhootLQCklVLWmk/KW\n5ifCyPew919RELh58hTe2TnK62pZt3cP3sER5nv7WRgeJZ/Lse6NV7GFU+Tdi5R9+7n7tu99inQy\nWVJu+vTh8xHyzBOeX0AUBDRlZurXrMFeXUWZ3U5ZeTliKsPcletIy61g1FMsFhAKAoVCAUEokM/m\nCHnmCc3OkY7FMcTTVKSLGBRKpEplaa5bjaTWt9HWtYamtnYUCgVz4+PcPHeO8upqth8+fM+cyKUz\nVNXav3nG+OMjH4oTA2PUdrTTtWvnQ03mYjZH7Ng5cp4llDWVGA/vQfpLE2vZK7aXUxdKgwS03395\nuZXJOzXN4InTpBJx5DotslAMK3KMFgv+bAqvEpo3rGdVQzP5W0MUpt0ASMtMKDasRtZYzfyR42R8\nAZwHd2Fobbrr+l63m1unTpP2+dHLldRV16Izm6k6uPue4pv01Cyh906CxYRs/1ZymQyz/QPM9PWj\nMZuoXNVBXWM1i9cGGTh+ioxMSv32Lez+zuv39L49DuQzWSY//gT/5DRKrYbul5//ynoLP4tiocDi\n8Chzt3rJZ7Ko9DrqN62nvKX5gTnrL1oMkqEwM9dvEnTNAlBWXUndpg2PJUT3RUhGo1z58CPS8QRN\n3Wto37KZ8ZNniFy+hWopSCadQlFbRfd//0Pkt72VVCKBe2IC98QE8UgEKDFRVTc1UdPcTNkv6Xe7\n+voZu3wFYyKLXWtAW1fNokXD2OAgMpmMjdt30NTWDkB5uRG/P45vaZFrFy+SHBpH6w3RsGs7nd/9\n1l3nDS0scOvYCQRBoHv/PpxNjeQyGeKBILHbBjceDJKIRBGLxeXPSaRS9GYTWqORQO8QFAUUFQ6y\n0Rj6SQ9mrR5VUx0unZRIMEAmGEarUGKyWlHrdFR0tDHimiIajaDV60klEux56mmq6+qB0ibl4i/e\nIRYKsffbr6E3P3xO/WFhtxvw+WKkozESPj8xn4+4108yFKIo3PmtCrUaQ7kNg6O8ZKDL7XdRIX5V\nKGSzLI1OsDA4RCZe4om21FRT2dVJWc3jYUN7EB7GGBeLRXpOn2Fx2oW9uoqODRuYOHGGXCpFNpki\nkUpSva6bzq1bCf/sfRRVTspevjcNkYzHcQ0P45maIp24w4ddyGZJ+PyI+QJ6k4nVe3bTsnEDis9E\n9RbnPVz7P/9ymTRHVKso2MwIdjOi8o4tkRSL6HxRDK5FCEUo5PPE9CqilRbaRDX1Vgfl334eZdsd\nrnRRFPnk6FF8Hg8b9u2j9j4Fhisl/fhKjPHP/uOfiVqznc2Hn36ohHwhGCZ67DxCJIaqoQbjU7vv\nS2D/qVfcVl2HcnYJ5bZ1KNd3IggC81PTDHx4lKDbQ5nTgS6SotxiQ2sxIzZU0z8+jMlmY89LLy3n\nfYuhCLmeYQpjLhBFpCYDtNaz2DsAUgnOZ/Yh5PJk/AEy/iDpQIhcPIHP4yESLOUlzDYb2q4OsJnJ\nptNk02lymQz5XA79uBtVKEaioYKsw3Kbfm6KdDSGubqKisYazEYbydFJZkdGkDlsONpbWbtvH47H\nLNwNtz2x+QU0JtPXTqhfyGZx9/YzPzBEsSCgs1po2LIJS+29edQHLQapSJS5m7fwT7pKjGzOcuo3\nbfhKFK2gxBx17aOjZJIp2jZtpHndWvLJFP0/+mNUqQyCUknMbqDz+29gcJSz4HLhnpggsLgIlOhE\nnbW11La04Kipue+9EgsEuPLue8j8Yap0JmRlJqY0EAyHMJrN7Nx/kLLPhHI/O1ZCocBQbw8Tf/dz\nJIkU5j1b2fLKi+j0d8Js4aUlbh49Xurt1GrJJJN3XV+uVGKwWDBYrRhtpWd9WdnyPRRxzzPy4TEM\nDgeW1e1MXr0KN0dQxlOYmhuQ7FjP1PQk6UQCIZ5EK1cQDASIRiM0rO1m67PPcur4RxhNZp791mvL\noezF6WlunjxNdVsra/fuebx/HA+eU8VCgUQgSNznJ+bzE/f5ycTuPk5nKcNaX4etoQ6d7dGKoh4G\nhVyuVAk+7SLs9lAsCMjkcspbm6nsWnUPicuTwhcZ42KxSO/ZcyxMTmGtrGDtrl0Mv/cR+XQaZ9cq\n3GMTCEKBLd/+FoVbQ6SHJjAd3ouqsbTGiaJIYGGBqaEhFmdnQRRRqlRYnE7MNhvZWJyliUkQRex1\ntXTu2nlXcZ8oigz19jJ4+gyq0RkqujrR26zkF7xIxFKkSVNdgaGxDk1OQDI6DeksUrkMRUczmboK\n5mZdLE5OIUmmqRxfwFpXi/2H30f2mULEZDzO6bfeQiaXc/D11+8pRv5GGuOjf/v3YvfBpx8q5JoZ\ndxE/exmxUEC7rhPd1nX39ZCWvWKLhZpAClEhR/ntZygUBS6eOsXoqbMkF5YwKNU4VTosDgf6pjqM\nG7sZunUThVzOgddfx/iZCSwIAmfOnOTS2bOIS36k/jByoQjZPPlsFqlBh666AqVCiVIuQ63Xl4jT\nbVZyYpH56Wl0M4voTSZY3YJUKkWpVqPSaEoPJKiuDCCXy1E/vx+1xYxEIqHvxCkKuRwai55oPE2Z\nICEwNIrUVoaqslTM0Lx+Ha0b1n8lhOlfJzLxBLM3buEbn0QURcqqq2jYsvGuntBfXgwy8QRzt3rw\njk0gFkX0Ngt1mzZgeYwCBl+EiN/PtY+OkctkWLV9K41dXSUFor/5KYFL11HWVeEr0yK3laF22Fmc\nnV0mLbA6ndS2tlLZ0PC55DKFfJ7Lv3iHpGuOComSolrJhE5KHpGGlhY27diF4peiR/dbOAMzM9z8\n078ikU5RWN9B99ZttHZ2Ls+tiM/HwHsfUZRJ0Vc4MFqtGKxWDFYLWqPxC8d07NgpQjOztD11AHNd\nDe7hETy/+BDJ3CJSjRrrS0+R0iqZGR1lyeMh6JnHYjDS3NqGTK6gYNThi4XZunvPsocviiLn3/o5\nyWiUfd95Y8X89V+EleRBc+k0ca+PuC9A3OcjuuSlWCj9l2qjAVtDHbaGegyO8i89/4R8ntBcqRUr\nNOdevo62zIyjtRlnR/tX7pl/3liJokjf+Qt4xsYpczjY+NRBxj46QTIQpG7rZpKpJO6BQRo2bqBu\nVQfBH/8CqVaL5V+8hCAIuCcmmB4aIhYOA2C22Wjs7KS6qYlcJsPQ+YsEPB4UKhXt27ZS2dpy1xhn\nMxkunz/L/OwsitEZpv1LLOrkVNTX4yx3YJHIMUZSlC0EMYUSyJCgtFvQ7tiIdt/WuwqDM8kk0z29\nBM9cwuBaRFpfReVvfQdbzR0HYXJggIHLl6lubmbTbWWnz4zTN88YpxNJMZEufu4xoiCQ+OQm6f5R\nJEoFkjXteIJe8pkMolAsaZ8WixQLAsWiQGB4lGw4Sp3GiCGeId5QQcJiYGJwgKjPj6IoYkdBmdUC\nahXpKjsFq4nFySlSsRj22hoqGhsxmcyIUgkXLl3gvfePML8w/6V+q1Ku4PcOPM/v/9EfYWltvudm\nTA+NEz93BVVjLabDewEIeua58dFRdHo1vkgcIZ1BtuBDBLrf+BZzU5OkYnHs1VWsO7Af5SMSgvwq\nIREI4rpynbBn/i6aPrXBsLwY5FIp3D19LA6PUhSKaMvM1G1aj62h/ivN6wUXF7l+7DiFfIE1u3dS\n296OWCwSO/Uxng9OEstnmbNqiEQiWNqakatU6M1maltaqG5uvqcA5EEYPH8Bz40eTKEEeakEj1WL\n1GRg0/YdNLa23fc3//LCKYoisViU8TPn6T16gslokIBKQq5QQKZQEPL78M65CURCrHHW8r2XX+e5\n738PVe3Dhz4Ti14GjnyIxmRkzbdfQSqTUcjncb17lODJCxSLAsXVLSjaG7l49gy5ZJKm1jasZRZI\nphGLRZbyafR2Gy98+w3k8tIGwzMxQe+Zc9St6qBr186H+i4Piy/DKiXk84TcHgLTM4Rm5xDyBQBU\nOi3W24bZVOF86FZBIV8g5HYTmHIRmnUjFErn05pN2JoasDc1fi3UsqIokncv4mirJnyf9VwURQYu\nfszcyCjmcjubDz+D69zHhGZmKW9vw97Zzo13jqA1Gtn02itk+kZIXulBtm4VC9ICs6OlPLJEIqGq\nsZHGzk4sjhL73PzYGKOXr1LI5bDX1tC5a9c91c9Bv58LJ08wMNhPf18P13pvkck/uAVLq1Di0Juw\nmctwmC04yx3Ut7fT0N1NbUszVVVV6PUGEpEIC3/+Y1KTLiLNVei6V9G6ZRMmu51isciFI0cI+/1s\ne/ppnHV3akC+kcaYzxGKABCSKWLHzpNf8iMrMxGvKcc1MoIoFks8olIpUpkcqUyKVCYjl0jiHxhC\nr9PTmBTJy2XM1JQx1tdLOpnELFNSrzVS096BtaMF+55tFGRSBq5epf+TS2gMBsobG+jr6+H0+bP0\nDvQhCAJKhYKNVQ1sqmtGY7chM+qRG/XI1WrkvhCZnmGEfB5ZpQNJQxWC1US+WCSXy5LN5shmM/z8\nrZ8RiUZ4atN2/uKnb2Iw3J2HFUWRyDvHyS/67grNzAwM4u7vIeQPk8hmEBb9EI7R+tR+1n/rJXrP\nncc3O4dGr2fDoQOYy598/vObgBKB/XUSgRBSuYyq1avo3LmRoUs3WRgYRigUUBsN1G1cR3lz0xMV\neLgf/B4PN46fpFgUWLtvH1XNTYjFIvEzn5AYGGN2dIShdJSsWo6lvpZVe3ZT29qK+QtUhH4ZS9Mu\nej/4CNmkm7woEq2xY2ioZeeBg5gt9xKohEJB3nvvXTweFzMzbpaWFvF6l/D5vKQ/p11JAlhUWgwK\nFTOJkndSrjPynR37+d4Pfou6XVuRPKDqXRQEfFdvEewbRFCrSGTTNOzchnP1quVjEhPTzP3454Tm\n3EzmE0TLzWx77jni4RDxWTeFYBSJWkleJiVn1rNx925WdZcKMovFIufefItMIsG+77zxpVi5fhmP\niw6zWCgQnl8g6JolODO7XBCmUKuxNtRia6jHXFlxT5W2kC8QdnvwT7sIzcwtG2CN2Yi9sRFbUwO6\n2xXCXxeyLjfRj85isBqRbFyLurl++T1RFBn65DIzg0MYrVa2Pv8si70DLPT2Y6qspO3ZQ/R+cJSo\n18vaZ5/BXOFk9s//lpBnAXdjOaJMikqjoaGjg/qODjS3w84Rr4/Ry1eIeL0lDfdtW6lqa71rHERR\n5JML5/nrv/oLrt64RiAUBKDSZOGp1evZ0baaXDBMSiKyFA2zmIrjFfMsxiMsLM4TuV2ncT9UV9fw\nwx/+S777wquk//49AvMLzNZZKSoVOJsaadm4kYJQ4Ozbb6PW6Tjw2mvLeetfOWOcm18iduIixVQa\nWV0VnmKW4OICKq2WVQf2UXYfwYeRD48TnnVTKcrJjbvw2HSMhH1kMmkabU5aTXbUBj22nZsxdpTC\nGNFgkHPvvIOASJgif/f3P2ZoaACAhvpGXnrpFdZJdYiBMPmGKtI61fIN8SnkkTi266PoUznklQ5k\nRj1iWz3SzhZ0ljJ0ej3pbJbf/f536Z8co6Gunr/+m7+jq+vu6vFCKELozQ+QatRYvvvicttUIR7k\n3M8/ILS0RCwYIu9yY6mq5OX/8L+i1GiY7Oll7MZNpFIJnTu2U9u+Mr3X5T9DFElGY0T8PmKBIPaa\nmkcm4/8qIBaL+CanmL12k0wiiU6nJJnModLrqF3fjaOt9YnKHT4Iiy4XPafPABLWHzyAs76u1M98\n/Dyhyzfxeb0Met1ktUqqu9fw3L/84SNFNdKJBJd++iaJK70UiwL5hipqd29j4/add4Wlc7kcp0+f\n5Gc/+wknTx4jf1scHUp5Mru9HIfDidPpxOFwYjUYUcws4ijKcJosFKMx7MmSopOmppKwXslbZ0/w\n86HrxPNZpBIJ+1q7+MGL3+LAG2+gqKlYnn/5eALPyfOkvb7SBSUSYhIRuUrJ2u++hvwz4fe018+V\nf/cnZFxuJHot2C2oZXKUWi3RUJBkPkvcoCGFgLVrFa/8i++juj1uc6Oj9J+/SEPXajq3b3uEf+3+\neBLc1EVBILq4RNA1S8A1Q+62CpZcqcBSV4OtoQEkEgLT0wRn7njUJWKSBuxNDeislm9M9Xb4nePk\nF7zoTToS0STq1kb0uzcjUSoYuXqV6b4BDJYytr3wPJGZOabOXURjMtHx/GGmrt9gaWICa10thpoq\n5s5cRNozQtppQbVpDY2rV1PV0LBce5COxxm/dp3FySkAHA31tG/fdtcGLJvN8sEH7/Gf//LP6O3v\nRRRF1Co1zx58mv1qKx15BVqdFrVMQS4URl1Xje3155C3N91Vg5RMJllcXMAzO4Orr5+ZkRHm3R58\nsTC9s1Nk8jnKbXZ+/7Xv8W1LHaLVzKRWQjQQQCKVUtPRTgGYGh6isbOT7h2l9qxfGWMsiiLp3mES\nV3pKL7TWMbHgJptKYamuYtX+vfeV7/JNTnHtv/yEYiJJbTRHTq1g0KGlqJDTqDJSrdajMOipOLwf\n9e0yeaFQ4O//4s/58ORxrg/0kkgmkMlkHD78PL/5m7/Nzp27iQyPsXjhMob6WqqfKcX+s9kMqUSC\n5O1HKpEgNjBCtncEeV5AhQRZQaAokxF1lhGtKEOq1bB13Sb+5Ef/Mz+9dAaVSsW/+3f/Gz/4wW/e\ndVMlr/WRvN6Hpqsdw+6SuofdbsA14abn+EnmRkcJ9A8hzxfZ8/u/w+pnDpV+v9tNz+mz5LNZqtta\n6dq54wuJRzKpFBGfn4jfT9TnI+L3k8/eCd/IFAp2vfrKirVjv2oI+QILQ8Pkgz6UNicVq9q/tEzk\no8IzMUHfufNIZXI2PnUIa4WToNvD0jvHSA+OIWhVTCUjxKJRag/t5enf+o27Kj0fFsVikavvvsfM\nkaNIcwVkLXWs+94bNLW2AbdDgwN9/OxnP+Htt98iGCx5BR0dnbzxxq/xwgvPoFQasNnsyD6zYRGz\nOfI9w0Q/Okt6bh5VTSX66kr8rhlm5ueYV4G5vJydL7wAI9O8e/EUf3f9In0+DwDVZivf3XGAX/vB\nb2CsqmDh0jWETAZjcwMqswn/jV4w6IkmYlStXUPVum7SC0ukF7z0nz5DzDVHRQ4MEhnJfI6wzYBY\nZkCn0ZKaXyKcTrJQSCMxGtjzve+yftv20ngIAmd/+ia5TIb9v/adh2bw+yI8aaEIURSJLXmXDfOn\nFdCfQm00YG9qwN7Y8ESLwB4VeW+A8M8/QllXRf1L+5j+2TEK3gBSvZZAuZHJuVn0ZjNbX3iObCTK\nyPvHkCkVtBzaX2KJm5lBADDpEUUR8/AMZUoN1b/3PWzNd6qUC7kc0719zPYPIAgCJrudtm1bsFTc\nccoGBvr4yU/+P37xizeXvdrWmnp+/ekXebFtLcKtIVJzC0g0KvS11RjWdeL1eUnLoObZgxjqv7gY\nNh+LExubYq6njx8ffZe3r10knc9h1er57fW7+K/+xz8kYzEwcf0GyWgUqVRKKBJGodex95VXsDqd\nvxrGuJjLET9zmezULBKtmni1nVlXiXSgYdN66tZ2I5FISCcSy9q+/rk53MMjTJ+5QDYSo0Vrolyl\nY7KmDL9cpCqapdJYhsZhp+LwfuQ6Lfl8nqNHP+DP/tP/Qe9AHwBOZwXf//5v8L3v/ToVFaUq21ws\nzvSbR5BIJDR+5xUUn0PdJ4oiC++fJOVZwLKxG1k2T+bmAPlYnHQux3Qxg7SukkaTlXNXL/G/f1ia\nMK+++hp//Mf/Cf3tylWxIBB660OEcJSyV55GUVG+vCAUcjn6zpzl1rHjRHqHKaup4jf/9j8vL6ap\neJybJ08R9QcwWq1seOrgcvtTPpcj6g8Q9ZeMb8Tnv6slAEBnMmEut2OyWZnrHcA378HZ0sKOl198\naJ7wrxNft8LO7PAIAx9fQqFUsGrLVjLhMN6JKRidRuENIrdZUe7ZxI133kNlLeOl/+l/eOT2tL5z\n57n51z9GlcpiWt3Ozj/4bzBbrXi9S/z852/y5ps/YWRkGACbzcarr77OG2/8GqtXr0EikdybMy4I\n5AfGyN8cRMzmkGhUhCIRcHkwOx0I5RZGo34WAn5SwRBKvZ6dP/xtymNZhOuD9M1O8Xc9H3Nk4Drp\nXBa5VMpeZwOvdG1m32/8AMu+bSAUGf8vPyUXCJFIJSlEYpQ3NSJTKPAuLuBbWkJfXUH3/n1I530U\n/SFyElgwqwnHozA+Sz4YJpBJMpeJo6+q4Hf+w7/HaCq1NM0MDjF46ROa1nbTsWVlMnUPwlc5p0RR\nJBEIEHTNIYoitsY69CtMW3zViJ64QHZiBvOLh6ha34rPGyV1cxD3+8cJeOahqYb1P/xtJMDA2++R\nikRRVDuYvtVHPPz/s/fe4XGdZfr/Z870XqRR792WbMm9xS3uTpwOyUKA7JIlEBa2sAvf77IsWZJd\ndn8sbFiWXrKBQAhJnObEsePeqyxLsnobaWY0I42mF0075/fHGBPHTkgghHyvi/u6fEmXPNI55z3n\nPPf7Pu/93E8AjdWMzGzEYLEwt7oWa98E+oYazJd7FouiiKt/gMGz50glEmj0ehqWLaG4Lqe78ftn\nePbZX/Hkk0/Q3Z3LaFq0etZW1POB5sUsb25DEAQy8QQzg8PEEgn0S1up+vN7kBt0zM4EGH3mJQS1\nitoP3opC9/ZtmFP+ABPnO/jh4z/mqcN7iaeS2NRaPrbjTu77xKdICTDa2UnAO8XEyAjFdbXc+qlP\nUlr+Pqwz5nVknPEHc2VLgRCyfCsupYh/ehq1Xk/zhvVYinPq4cGLF+k4epRkOALpDGqNBpUgJ+Xy\nUFFRSYNMy+CMl6F8LSXBWUrshZjqayhYv4pwLML3v/8dnnjicbzeXCF8c0MTf/MP/4ft23dcldqT\nJInxXXuJOd2U3LgaS2Pdb72YTCzO+FMvIqbTlN95EyqziUzvEKn2Hlx9/cxMeTFq9WiUSkIVBXxp\nz9Ocu3CemppafvSjn9LSkmsxl5qcIrjzVRR5FqwfuImCIsuVgCBJEoNnz/HyI/9BJhhmzu03c/Pf\nfvbKOWQzGS6dOMl4bx9KtYrCykqC09NEgyF43T1V67RY7HYsBQWXCTj/SqrU2d3DwPET+FxuNEUF\nVM9vYd4N764w5g+BPyYZD3dcpPv4CdKxGAWFRWRmZ0GS0E7OYEmKmOuqsd9zC3t/8hhTXT0s+sAd\nzN+88W3/fUmSIJ1BTKY4/8puep55EV00QcHiBSz4209w8MhhnnrqFxw8uB9RFFEqlWzevI277/4Q\nGzZselM1tSSKZPpHSJ3pQorGkKlUKBfORTGnjtnn9hJ67SgZq4lgQykypZKaO26mY89eRg4cRWbU\nM/fu25k3pxnpTBfZvhGCkTC/OH+Up7pOMxrwAVCTV8AHW5aypW0p2USSqHsSQaclMZvAXFuNdm4D\nHYM9aIsK2XrXnWg02lzzl/ZuYqcugFKBNL8Rl3+KqT2HSc8EGPV5mcomKWxu4uOPPIzOYCCbyXDg\nyV+SSWfY8KF73hVB4x97gvd+RjYcZebnz6OwWbB+8KYrtevDFzsZ2HcIi3OawoJCkgIM+rz4wwEw\nGohHwgDk19cRkTJkZaAzGrGNerDGM2g234B93hxkmQxj7R1E/AEUSiXVba1UzZ+HXKEgnU7zja/9\nO9/69qOk0mkUgpyV5bWsKahgcVElFfX15DU3IS8pQF5sx3O2A9feg2gqSqm99y70pb9ZUc90dOM9\nefZK9vOdTn4kScI7Msp3/+Vf+Nm+l4lmUlh0ev5s1UY+fMfdZFUKzp89hc/joaSmhk9//eH3LxnP\nDo0ROXASKZ0mW1bIcGCaVHKWvIoK5qxbg+qyMGSgvZ0jzzzLbDiCJElIoog+z4YiEkenUNKaV0Jk\nwsV4gQFrRkZpaRl5SxdgXTSfPXt28w//8Dd4vR6MRiPL2xaxetEy7vnEA1fZof0agZ5+Jg+fwFBZ\nTvm2DW/7BsUcTtwv70NlNVN+180ISmVu1eGc5MyvniXrcFEYTqJRqDA0N/LNs/v4/sFXUKtUPPKV\nf+ejf/5xZDIZkUOnSFwaQL+sjaptq64JCOdeeJFDj34XmVrJwo99iLV3f/Cq8qaJvn66jh1HzGaR\nK5VY7PmXyTdHwBq9/rrXlEokOPXLp8mkUoiiSCyRQK7XsWjTBopraq75/PsJf4zAmc1mOb/7VXqP\nHCMTn6W0vha1VkdeeSm2RBaFawplvg3rrZsYHOjn1I9/ikGn45avfAl5OkPWNQXJFFIqhZRMISXT\ncOX7FKTSV74Xs1mGz7cTGXEgz4q4jSqOiGFe6esgMpvbd2yta+KD23Zw647bya8oQ2Y0ILtOyj4/\n34DnTC+pUx2IgRDI5SjnN6Ja2AxyOendhxHHJ4ln0gwODoBeS8P9H8baUJtLgT/9HH2Hj5GyGslb\n1MrK9euRj7kJPP4sskAYhdlIV56aJ47uY1/XedJiFrWg4LaGVm5fsBx7RTmZwjyC09NMqnLtGLfc\ncttVtdAAib4horsOQjKFcn4TYkEew/sOMjM8SpdjiGAmRUFTPTfe+2GaV61ktKub3lOnaVi0kIbF\ni37v+/snMn5zRI+fI97Rg2nDKjRNtdjtRg7v2k/7vv2k0ymsRUVoxyaR9wyTSaWI2i0ky+yY7Xaq\nli+hb3iQZDxOhSUftcVEetdhomSZrCsi7JwkGQ6j1mgobWxk7srllFZXYzSZGR4e4sFPfZyOix0U\nm6x8bMka2mzFZHUaFKWFLLh5G+aaqiuizdkpH/3//SOSkQjld9xE8dqVV12HJEk4XnyVuNtDybob\nsMz53TqBSZKE58kX+NGvnuDxC8cIJ+KYtDruWbGe2xavYmRokJlElH9+5VfvPzKWsllp7KUjxDt6\nQKEgUmTF4XUjQ0btsiWUz29BJpMxG4tx7tU9nH9tH4IgMGfpUhqXLUVjNDDefYn+l/c/NwLaAAAg\nAElEQVSimE1hnvQTy6bJ5lkor6uj6pYtKCvL+OIXP8/OnU+jUqn4u7/7PAvrmgh4PDQvW0ZDa+s1\n53VVevru21D+lr6ub8T0sTMEO3swz22gYN1vbnwkHGb3zmcQxidpnBUoKCzCoNOxr+s8f//8TwnN\nxrl16Wr+4/8+hKmmksCrhyGVov7BPyOYzT1YUiaLlEqRjsbY9eV/ZaqnH3m+hYZlS1mwbh0Kco0B\npGSKVCSKZDVhW7XkbaeZew8dYbJ/gJolixi/2EUqmSIpyJAr5ay+844/iOvXu4U/RuDc96OfMHT6\nHEq1ioZlS6lsnUdhXS3prn7i57qQW0xYbt9CJB5j/89/QaxvmEU3bWPu9s0kntyFGAy/6d+WKRWg\nUiFTq0hLWdpf3s2sy0uALD+YGaLHm9unLTRbuX3+Mm6ft4R6+7XCRplei2A0IDMZEEwGZHotGvck\nocEJkMlQNNWiWjoPwaBHSmdIv3IYcWISeXUZ0yY1Y0+9iEqrYc5n7kdfkRP0ZVMp+p9+gdGuLgIm\nLUpRojgtYFIoMYoCGucUslQaSaMiNLea3VOj/Pz5Z3BOe1AKcm6es4B7P/QRhi9eJKtWsfEv/pxy\niw0pGEbyhxADISR/CCkaR4wlSDmcpG1mpOJ8xMv7rB6/j9PDfUTFFKbSEopra2nbcCNjl3qQK5Xc\n+KF7fqf9+NfjT2R8fYjJFDM/fRaZUon5npvpOX8eR3cn/ecvolAqKWmsx2CxoAhGYHwSrcuHWqNB\nWVyAbP0yeh3DCNEEdZIKs1qLWgRFJovPpmdg2k0sEgGlAsFiRrjsiiVJEmfaz7Fz1/OkUilumbuQ\nz2//IMNCipBGQfncOSxdveaaLJDj6Rdx7z6Iqa2Zpk9+FPl1nolUOMLI0y8CUPOBW1Bdp1PZ2xqX\nSIzEL3cRno3z86iL7//k+4RCIYxaHbe1LWeJ0c5n9vzy/UfGk0/ukvz9DiS9FrdaRiAcRGM00rxh\nPebCApKJBGMXO+k7dZqxvj6UGg3r7v4gTcuWXlkF9r68B9/wKPQOI3O4mTFpEawmMvUVtDuGeHLX\nc4QjEea1zOdb//N9tDKBjqNHsZeUsOqmm65ZHebS068Rc7ooWX8DlqZ3PksSM1mcO18m6fNTvGUd\nhtqqK/83PjLC0df2oB8Yp6FxDg333oUsGMFx/gKfeviLXBgbpNpWwDdv/jDVch0J5ySaikKEmipk\nooh0ubgfwDMwiLenj2QqhajTYC8poWxO0zXiFVV5CaZNN1zTdOCNCHm8nH/hJQw2G4vvvI2xc+2M\nXejAXF7K5IQTS4Gdlbfs+KMolN8O3uvAOXz+Aq9974fobVa2PvgA+ZfNRGJnO4md6UBuNmK5bTOS\nRs2hnTuZ6uqhwGBmwV23YUqJJA+cRFFdhqKhGlRKZOoc8cpUKlArr1inTvb2c+Df/pPMTIAJKc3P\nBtsJhENs23Yz9933cdasWZd7HxJJxEgUKRxFDEcRQ1GkcAQpEkOMxK7aptDpVKSKClEtb0Ow5gR6\nbyTiRHMto7v35ZpDpLMojQYq7r4V+WVb2WQwRN+TO/GePEcsHIKMiK6ihIrGRgyFBRgzEmp/GJkg\nIJQVIS1v5elf/IxHv/c/jAd9KGQy1hVV85GqeaxYuhyN+WqhoMygQ2YzI7OYEHtHELNZMvPrSQ45\nSE3PEO4bYmp6ilExQcigRjTmGlbkUtZZFmzeSNuN63+vPdc/kfH1Eb9wieiJ8+iXL6B7ZpK+c+eJ\neD1ojGaWbd9G5ZwmImPjDO4/TMDrxVRehn02S9IzRTAcJmvWU1JUjFZvQKZSEnntGAmVgvDKFvQ2\nG43Ll2GvKM+VfPpn6O25xFce/jIXLl7AoFLz6ZaV3GgtJaNRIRMESsrLsRUV5SabZiMykwFMBtLZ\nDD3f+ylZpZymf3gQU1X5m15TsH8I94Gj6IqLqLxly+9cDpnuGSJ58BTyihLSaxfx2GM/4rvf/RZ+\nvx+9Rks0EX9HD+R70s/Y9+qRh+J6LcOZOLFEjPyqSlq3bUGp1TByoYPO/QeZHB7BNTaKpaSE7fff\nT828lisvV8TjZeT4SfwDw1ic06jzbNTeuo3C27by3z//MU+/9BxZUeS2Tdu4e+sOotPTeMbHUSgU\nrNy+/bquRsG+QfydlzBUlFGwYvHv9CLLBAFtSRGRviHi404M9dVXApjZamV2dhav201megZrYSHa\nynI0BgPbV6wh6vNxuKeDnd3nMGahSVQjeXzEEwnUlaWoigpQ2G0oiwuQF+bjDcwgGnSEjVo8UpJk\nvoWCNcux3LCEnniQ5PQM2miC5NAYyuLCq2zbXg9RFOneu59UPE7Lpg1oTUb0NhvOSz1I6Qx5VRVM\nTzjJZjLYy99ZS7/3Cu9lI/hYOMyr3/k+2VSKTQ/cT3F9TlASO99F7HQHclOOiOVGPd2nTjHpcKCb\nzWArLKJ61XJSrx2DrIhmx43Ii+wIZiOCQYdMo0amVCATBKRsls5nnufUN79HJhzhLAkev3CMTDbL\nf/zHN/jylx+muroGQRByPVaVCgSDDiHPgrykAEV1GcqmWpStTSgXtaBsqkFeVYa8tJD8G5eSaai9\nUhsspTOkXz6E6PQgrymHtYsZ270fKZul9vabUBr0xMYmEGdn0VWWkXB58B0/g+/gCVKT02hVKuSN\n1QStOmZsBsrWraRgzXLkjdUQjiKOT0LPMHPlej7athLrVJCB0Awd4Wl2uQYZDc7QtmkjtiULUCxq\nQbFqIYrFLcgba5BXliLFEkheH5q2ZvTrlqGuKEGuVJKdmEQZiGDJyrDrTci0akKJOGHfDJ7hEZLR\nKIJcjul3VCK/l8/U/yuQslnCrx0FCUa1cP7gQZKRGPNXLGHDhz5MRWMDsUkvnTtfZGbCibmuhpKF\n8xkVMgSDAawuHwUp0KrUmDevwXGxi+zAGGm1guodW5m/eSMGq/VK3+B9+/fxV5/9JKNjo6xtXsCP\n77qfJfYy5EYDUomdyhvXYC0rzb0z0TiiL4Do9SGOuwk8vxf5uBu7xYYhIyGOTCC6vEgzQaRwNJeJ\nCYaRwhHUShVpr4/E2AQk02iMhtxWUTIN6QxksiDmjKayknhVFcLrIeRbEb0zZMfdaPJsrNxxM/fd\ndz8mk5mOzgt8/vOff0f9jN8TMp7oH3iob2YaEahfsYzqxYuY6Onl4r4D+JxOJJmMSCyKubSElTff\nTFnt1Q0Z+vYeoO/kKcrGfZjUGvLv2M5ZIc5fPHAfl3ousWTJMp78xTNs23YTKpWKWDhMOpVi8fr1\n5F12cHk90pEoE68eQJDLqbhp0xUC/V0g12qQazVEh8dITs9gbKy9EgzyjBbcQ0PEOnqIdfeTcXuJ\nDI6Q8k6zpKqe5oYmjvV2sm9ikOmGMjbMnYc0m2Y2z4Rh4TzMKxairi7H0FCDa8pDKBDAUl3BrM3E\n9JSXcCTMxc4L+ONRppUSCpUKcyLNbN8QglaDwn5tjaKrp5fJvn6K6uspn9+SuwalgvTsLH6ni/Km\nJmKRCF7HOKb8/D+IKf/vi/cqcGYzGQ789Al8I2M0rVpB66Zcx6D4hUvETrYjGPVYb9uM3GTA63TS\nefw4akGBVa3FXl+DeTZDZnAM5bxGlPVV1z1GfHKKE//1bQZf3Uc8k+al2Wn2XDxDYWERTz75DNu3\n73hH5CKTyZBp1AhmI3K7DWOh9cpYSal0bkXs9CCvLUe+eRUT+4+S8PkpWb4ES20V2iI7MYeT2LiL\nUM8Anv1HmTl7EUGlxDSvCXV1OcULW7EvX4zb62Z0aJBEPE5RTTXKObXICvJyPr92Kz6dkrjVwM36\nEpqNeQzHgpxxj/K/u5/HnYoxZ/EirG/QccgsRrKd/RBLoGiuQ2E2oZ/XiLapFm//IJlgCJ2gIF+m\nIE+SI2SyxOMx0kBkZgaFQoG16J33w/0TGV+L5JCDeM8go9EAR04cRcxkmbtsKRvvvh2lzkTY4+XE\nDx4jOOkhv2UOxYvbuNTbQ2ZglAJRjr2hHn1+PmqjgYnDx8n6g5gK7Bjn1qORhJwHhFwgHA7xuc/9\nNV/96ldAgn/+wH3806rtWJUadGYTmpJCUuWFZDQqkhYD2cpiaK5DMb8J5ZxakoIM14nTYDZQtHU9\nsqwIM8EcWbu8iKNOxOFxxCEH4sAY4sAommgCqW+EdEcvctcUUt8I2c5+shf7SJ7rwvvqQUaf3cXE\n3oMoSgowFl/7TMlkMuSlBWR6R8hOuFHUV6E2Gli2bDmf+MSDGI3a9x8Znzl45CGFRkfLlo0k4nEu\n7tvPlMOBXKGgcl4LwWgYSZAxf8UKqufOvep3p4ZHOfHYT8lz+SnW6JGtaOMrx17m0W9+HYCHHnqE\nr33tUewFBRjMZooqK6mbN4+a5ubr9hmWJAnn3sOkAkGKV6/4nRpZvxHqfBvpYIjYuIt0KEJ0xIHv\nxBkC5zsxpLKEXG5mZ/wYK8ooWLoI68J52Fcvp23zRm6/8wOcP3+GgyeOcmjKwdY5C1DNpglP+8hE\nY+jKShAUCpKxOMHJSTRqDdbSYhIyiYGOi8QCASwGE6bCQtyzERQFeViTIslhB2I0jqqsGJk8l4ZJ\nJWbp2rsPmSBn/tZNKF6352KwWXF29xIPBJi/cT3OgUGmxicorav9vffj3m28V4Gz++gxeg8cwWSz\nsuH+P0ep0RC/2Ev0+DkEgw7LbZtRmI2kZmc58corZDMZqkvKSEeiVCxaAKcugiSh2bIGmerq/S0x\nnWbyyElOfvdHeHr7mVTCz129dA/1s2zZCp555kUaLtcS/z749VhJqXRuRezyIq8tR7H5BmZ6+vF1\n92IsL6F09YockQsCmkI7oUv9hAdHyWTSGBpqaPjLj1Bx82aS4QjRCRd5+fnMWbWSaa8H98QE4yPD\n5NkLMJQVI2+qIWDWcbjrPFmLkdbtmylV6vhowyIKRYGRaIBj507zk5/8kImJcRobm7Bacy5iMo0a\naWoG0elBqCzJmYIAanseinwLYyPDxDJp1CWFmHUG9AjIPT7kvhBalZqZWBh7ZeWVksh3Ok5/Qg6i\nKDLx1AuMne/grN+NXKNm80fuZfHmTeQX2pgad3Pw698iMu2jaMF8jM0N9J45i2rAQaUpj5KGekp3\nbMK2eQ1DXZ2kxpzY8vMp33oj6spSYg4nYiJBp2eCu+++nZMnj9PWuoDHP/H3rLWVIZcJCDIZmA2M\ni0kSoTCzgSAxzxThsQkCA8P4evuZGhhkbNceYtEouvUrULXNRaqvQNY2B2VLPYqaCoSyIuSVJQjl\nxQjlRQilhcjLipAV2wkFg6QE0M1rIiJIjE57GHA58M3GyShkqKIJQue7ENUqzHXXthWVqVTIdBoy\nQw5EfxBFY3WOpOVy9Hr1+4+MI9O+h/QlZfSfPo1nZASZIFDT1src1TfQ19FBJBikobWVpkVXqyJj\n0Qh7Hvn/UIxNUqM1cVYr8lcv/pTu7k5WrryBX/5yJxs3br6meYJMJkPxJkKmK+np8lIKVi5BJpMh\nZrPEJr3M9PQzeeIMk6fPk4pEURr0KN9GPZpMJkNbVkJ0aJTEpJeUP4BMkKMvL8E2bw6a+U04R0eJ\niRnmffgDaPKsCAo5UiSG3uXjjrImQj4f+/o62T/YzU2FNRjybMRm/MTHXWjLilEbjUxPTJCcCaJW\nqRmZ9hBLJrBarBQXFWPS6ECnwe33oaguJ09QkXK4SI27UJUVI2jUDJw4SdjrpW75EmxvcNxSqFSk\nYnH8LhfW4mJsZSVMjowSnJqm7C3aGf4x8F4ETtfQMGdf3IWYTLL89lspqq8j0dVP9OgZBP1lIraY\nkCSJ9sOHCUxNMWfRIhJjTuQKBWUmG9lRJ8oFc1HWXL1/FXe6GXzqBTpf2YNvyku3KsMTF04w5Zvm\n/vsf4Lvf/RHmy/uqUiyO5AuAUoHsd6gB1+vVxIKx1xFxBYrNN5DwBxnfdwiFRk3NzZuvErtkEgmC\no2MIOg2WliZqPrADzeX0r6milNCog/DYBJaSYpqXLyObzeJ2TjA80A+A0WjiwO6XSadSrNm0hZLG\nBoKjDlIaJfPqGtmutNFYWYsjm+DwscM89tiPGBsbpalpDlarDZlWQ7Z/FDIZ5LW/MWgwlZcxMzVF\n3DOFKImkakspWbaYdCZD3OlGCEYQRZFQepbSxoZ31FTlT2T8GwS8XjqeeZ7AsbM4E2GE2gpu/dQn\nqWlpzglt/T5efuTrxPwBSha1kSm2M3n8NAa3j5rqWspWLaN4yzpUFhND587jnHRjbGmkurUV/dJW\ndNWVBIdG+cZj3+OLX3uESCTC3/715/ja1nuwRZIIKiVCOoPMZGAq30DMH6BwcSsVG9Zirq7AUFKE\nNt+KSq8nPu4k2j2AoNOiqiwl6pokODyGv3eA6d4B/E4XoWAAv3cK/5QXn9eDz+vF5/EQiIbxz8ww\nPjzEpUtd9E2M4vX7SAky9PZ8LGVlSHI50piT6MVLJIIhbAvmXZOpEvKtiFN+shO5Uj55Qa5S4H1J\nxl3HTz003jeAJElUzWuhbdMG8kpLObN/P36vl8rGRuavXHm1Q1U4zGtf/TrZniFUGhXfnezlR+cP\nI8jlPPzwv/PVr/4ntut48r4Vfp2elsnlFK1bRWTCxVR7J64jJ/F39pK+NIhyxIXW7SM55MDfeYmw\ny42gUaOymN+SkASFHF1VGZqCfPJXLCJv+SKM9TVoiwrIr6rEN+EkOOIgFo1SjJLM8fNkjp1HHJ9E\nnkhy47oN6PQadl04ze7RHjbLLBSpdKSck4QHRtBXlxPw+wlOTeEeG0Vt0GOvrMRSYEetUpGKxymr\nqCQpZXFNulHWllNgyyflcDHbP8IsEkNdneitVprWrblu6lNvs+Lq7iUeDDJnzQ3EQiGmJ5w5Y4LS\n949d5h86cEaDQU69uAv/iIPqeS0suGkbsz2DRI6cQa7V5Ij4shhqYnCQ/gsXyCsqorqmjqnefgpq\nqtH2jSET5Gi2rL5CotnZJNNHTzP44m4GL1zAm4xxbNbPzpOHEeQCjz76bT772b+7skclTk6TfnoP\n2UuDZNt7yF4aQhx3I037kaKx3N6WSnnd9qK/hk4pEPrVHkT31GUiXoUoiozu2kMmMUvl5vXoXtfQ\nPTw8xsTu/YjJFPZFrZSsv+EqohYUCgylxQQHhgiPjWOpqaK8vh57UREelwunw8FgXy+pZJIFS5fl\nGlgIAjJBIDLuRD9/DtF0kupImr9cu52mRQsZCkxx5MghfvKTHzIyMszyzZvRTPqQ3FPI59RelVXI\na6hj8OJFZOEY6lSGIBlqt29iIjOL5HAjhCKENbkxsVe8uYjnjXi7z1QmnSbs9+Nzu3EPjzB2qYcZ\n9yQF72GHsD8UEtEoXUeP0XPiFHQPkg6EEBfMYeVdd1zp1zvZP8CR7/yQyLSforZ5hASJ2dMXsIgy\nGhYtpPK2bZibG5HJBbyjo/QcO4HOZGLRbTvQ1VUhqFT09ffxwENf4MCFM5Ta7Pzsxz/jNmsFuKcQ\n9DqERBKZVkOitR5PTz+6gnxMbc2odDq0FjPa/DwMpcXo7Pl49h5GoVAw9+8eoHjVUoxlJegK7KjM\nJhRaNVI2SzoaQ7rcIU2Qy5GrlGRlMBWYwRUNkgwEkZIpLHU11C9fSsOiRdhrqjCWFKG25zGrkpMe\nc5Hq6mf6+BkEuxWVyYj8shYpl64uJNMznEtX11Ui06jfMRm/J2rq57/5XclWWUPNgjbUOh2SJHHu\nwAGcw8MUVVaybNOmq2axMbeHo9/+AaHuftpDXp4Z7iSSnGXNDWv4xqPfpqKi8i2Odn1kMxmGn9xJ\noHcQdWF+Lr2QTKMKhNHOptEhoDYZURoNyJRKkjN+ElM+kuEIokqJlGfG0NaMecUiVK/z5H07kGJx\nYue6uPTfP0SRTFO2bBEWWx6y0kLkdRUINRXIdBrsdiOPfOWrfOnL/4hFpeHxmz5Kc0llTlAjF5gx\nqjnnc5KSC1Q2NbLq4/dxYu8eYuEwagkUcgVz167mYtdFwsEgc1vbaDTYiB45jbu7h4jVQMsnP4b1\nLYi15+BhPAODtGzeiLW0hKM7nyMeCrN0+1YKyt9+cPtD4g+pfM2k0xx//gUc7R2YTWYW3XITZn+U\n8Zf3EQoGUK5bSl5jPbbSEuTqnHoa4MY778Rz/iJT/QM0VdaiHHGiWtaKanHO4CU64sB7+CQT/f24\nPG6cYpLXhnvoHOyjvLyCxx57gvnz266ch+j1kX5hP2SyCHNrIRJH9AeRIrFrzlmm1yHLMyNYzcjy\nLMisZmQ2M8hkaA6fJNQ/jryuEsWmlcjkciYOHsPfN4i9tZmSlTkHK0mSmD7bge98B4JSScmNqzHV\nvPl7Fhwew7H3IGqzkfo7dyBXq0nOznL2+DEcI8NU1daxcv1vjBXETIahX+xETCYxLZrHxOETFEUz\n5NlsYDKwTxbhv374bXp7e2htXcDT//ZNNKe7cyKvFW1XHfvC6VMMPfMihYKajCBDVlOGvrgI56Hj\nyLoG8JMhVlFI7aJF5FeUoTeZ0JlM6ExGdEYjOpPpqi0auPaZSiYSRINBooFg7uvl79/oZqd2+1BE\n4tR//EOUNc/5rc/X+xGZdJqhjouMXOxEzGYxa7QoTl4krlZQcM8tzFuxAkmSGGvvoPO5l8gGg+jK\ny4jEosjd0xQUF9O4ZSN5yxYiXK51jwYCnHruBSRJYvltt2DMy0MURb7//e/wr//6EKlUintuuZOP\nz11Knj+GubAAuc2CEI4ikwmwaQVDR44jZrJk51bT0dMNgE6nw2y1YjKZkc73kDrbSV5rC82f++Rv\nzYRIksSk20XfpW4mxh1IkoRao6HGXoS6fxx9npWaD956TUlUNplksqOb0e89jsrtQ2Eyolw2H21N\nJZbaKsw1VaiMBtL9oyT3HUdeWojm1o0UFJjef6VN8XBEiuWalyBJEp0nTjBy6RJ5RUWs3L79SkpZ\nTKWYOX2Bnlf2MNjbyxMD7fT4JjGoNHzpgc9y3z998R2RYCoSJTLhIjLuxHehi/DACFqtFlt+PgZJ\nQCcTUJlMyDVqhKJ8hOpyhJoyZBZTToXnniI5NEaso4dZlwcxm0UGqAry0bXOQdfSgFBSiCw/pwiU\nJAkpk0VQKpBiccThCbJDDqTJaSRJYqZ3gDHXOJHmGlb+1QOYiq8Wl/06IPz8p//L5/7hr9Ep1Tz+\n8NdZlFeC9/lXCY47SSVn0ZQVIy8twrRiEaZVizi6ezez0SjKrITJaqVt2xaOHTpAJBSiZcFCbCkR\n18+exag3ULp2JaaNb17+FAsEOf30sxjz8lh8x62EZ2Y4/vwLKFQq1tx5x1VNvP9Y+EORsSRJdBw8\nxOjFTlIzAWqa51KvtxLuHWRi3EGyuZa0XHbls67RUUQk2tatY87SpfS+8DIKZDRkFMjkcnQfuRWU\nSqaPnMJ3oYvxsVHcqTij8SAvnD6GZ2aaNWvW84Mf/ASb7TerU3HaT/r5fZBKo9i8CvnrxF9SKp2r\ny/WHkPxBxMtfpWj8muuRKZXoVAKzxUVXiDg4NILjtcNo7XnU3bYdQaEgm0rh3n+UyNg4SpOR8q03\nosn77VmnyVPnmLrQhamyjKptG6+8m6FgAKPJfE1w9Hf34Tl6Etu8uXjdLhIzfuZU1SEfcYIgoFzR\nxucf+x+efPIJVixfyePbP4JWqUL10duvMjRJJZO88Iufo7g0TJUln5lEFGVtBSXzm/E++QLBgRGG\nNRKa0iKK6uuvW3uv1mnRGS8TtMlIvt2CyzFJNBAgGgyRmp295nc0eh0GixWDxYzBYkGbyjK77zjO\n/n6EfBtLv/KFK6ul/xcgSRLO/gH6z51jNhZHo9fRuGQJM68dIdzehXrNUpZ89M8AGDx2kuETJ4mM\nuzDmmYnPhFBmRKrmz6Phzh1oXxfL0skkp557gVgoRNvGDRTV1uD1enjwwb/k6NHD5OfbefTR/2HT\njZuZ+voPSfYOoamvxmTPR5bJoti2BselS0Qm3BSuWMzBrvOI2SwFhUUEgwFi0SiCL4j2XA/yrEh8\nWQtCSQFmiwWzxXr5nwWzxYLRaCKTyTA8OEBfTzehy17Wefl25jS3UF1Ti1yhYOp0O772i1ia6ilZ\nf30XwkQizvn//gHK9l7UghxFSwNZWy5Dpi8qwFxbhXbYBe4p1KuXULJh8fuPjHmdHWZfezu9585h\nslpZvWPHFSu72NgE00dP4e7t59VTR3li4ALR1Cyra5r49w/eT91f3Zery3wbiE/P4Dx4lMRMACQJ\nwR8i0zuMQYT8hvrLe7YKZGWFyKvLEarLrghFrnvykoQ4HSB09gKR812ITi9CKo1Co0Zrz0dbUohQ\nWsjkxASJSISq6hoEXxBJknKimGI78rpKZo0aOn7+NONBH5ZVS9m842ov6NeTzPOPP86nv/A3CILA\nI//ybygzWSz94+Q5Z9DNptFZLKBSUNQ2n0SeibMeB4HMLHq1luLaGuauXcP+V3YR9gfQBKMU5OXT\nnF+M6PEhNxkwbVmLsiDvutfb/doBpkZGaN2+lbzyMka7u7l0/CR5JcUsu2n7O9qL+0PgD0XGjt5e\nOg8fJe6dwm6yUq82osyKOLyTBEtsLLvzdrQmI36Xm67jxxk4dx6tVktpbS3JQIjEhItai51yjQHj\nptXoly/Af76T8X2HGfO6ccvS9DnHee74QZKpJJ/5zN/yj//4z1eVToi+AOkX9sFsCsWGFcib3p4b\nmpRM5Qj6somG6A8iBSNY59UQa21BJggkwxEGn34BJIn6u25BbTGTCoWZePUASX8AfWkxpZvWofgt\ndepXjimKjL7yWi5oLm6jaMmCt/y8mMkw/ORzZGdnyVu9nKFDR7CUl1Hf0kJy/0mkxCyUF/GZJ77D\ny4f2sWHhMn6w7V50G1cib77aB6C3q5P2Y0epDKXQJbP4xRSGOfXUz23G8b9P4fZN4SvLo3XtWmoW\nLSQeCRMPhYlHIsTDYWKRCIlIBEnMxT+dXkU8lgKZDJ3JiNFiwWCxYLD+hnyVr+uB+xwAACAASURB\nVCNaKZPF/9RLZEMRvLEwod5BipcupOb+D7/ltsH7BTOTk/ScOEnIN4NcoaCmdR61ra2MXezE9b0n\nUFlMLHrk/yKXK7h04CBTQ8MEB0fJ+AJoNSoUGh0tt2yjfP3qK6thyMXK9j17mXaMU9PWSsOypbS3\nn+O++z6MxzPJ1q3b+frXv0W+1cbsq4fJjEwQdLoR/CGMVeXo79xKIJPCdewUpooyIiV5tJ87Q9ui\nxbQtXAxAIhym5wc/I3yhG3lTDeml8wiHQ0TCIUTx6j7LgiDkPKszGQRBoKqmljlzW8gvKLi6DWM2\ny+jOl5n1zVC29UZM1dfPCmUyaU4/8RTCsXZ0SjWFq5cS02uITXqRJAkhncE85kFjMdP8rS++IzJ+\nT7sCjPb20nvuHDqDIVf/q9GQiSfwHTtDqG8I7/AI3zmxlwOOfpRyBQ9tvot7C+pQWmxkTl3MmSPI\nBVDIQRBALs89+L/+uVyeW7HsOUAmFCZPoUKXypJ0+8kqNRhqq9AvaUWoKUOoLEV2nZKmgN+P2zVB\nNpNFFEWyYhYxK+YaopuVZNbMJ+0PkhkcQz7qQjU6hKanB/lsClHMotCoiQTiWJYuQF5XgbyuAtnl\nml89UNrSTPTkKXyOcS6cOc3ilauuO1a3fexjaEIx7v/qP/F/vvQF/vze+/jcF/8R37EzjO/ai0ml\nRiVKaGZmyBMEFiYVnDLo8fumEeRybCVDbLx5B08/+t+4xp0UNNSRd8c24ue6iJ29SPC5VzGsXop2\n7rVmJ5ULWpkaGWGsvQNbWSlVzc3MuCfxjI4x1H7hXbEffL8h5PNx6fhJMvEEeUoN+ZMB1JVGgkYN\nAWUeFS0tWApzPaS1FjOJdIrqJYtYuXUrUd8Mvbv3MptKI0x4cGs1TLWfQXPuDNGOS0TFDFMmNcf7\nujnYfhqdTsePv/szduy49apzEP0h0i/sR0okUd749okYyJmIFNuh+OoKAr3dSHw6gpjNMr7vMNlU\nmvL1N6C2mIlOuHDtPUQ2lcI2fy6FK5a8I6GeTBCo2LCWwWdfwnuuA609D/NbdMQRFAryF85j8shJ\nsjN+zKUlBCecROe3YLp7O8l9J8hOePjPVTsIT7jY336av/H4+caoE+WG5cjNRmQmI4LJQJ29mH6D\nEScxFiqNpEccRIZGuRgKYc2zYAmFmPHM0Hf2LMV1tRTXXjuWoiiSiEaJhyMY9QpSogK92fy2XOxi\n5zrJBsNo5zdR29rEmYe/jq+jG9urh7BsW3fFyOX9hkw6zcXDR5gcHgGgtL6OpqVL0BoMeMbHGXrp\nVYxyOY133YoMGR2vvErI4yHlC5JxeZALAvnN9cy/90MYy6/d7ho63860Y5z8sjLqlizmqad+wd//\n/V+TTqf58pcf4cEHPwNZkdlXj5B1uJGXFWFTqQhPXyAoZhD0Gib3HEehUVO4aimnXnoOlUrF3OZ5\nV44xc6odpv3k1dVQffcdGBtypbDZbJZoJEwoGCQUDBIMBggFg6TTaWrq6mhonIP2TVT2Mrmc0g1r\nGHnmRSYPnUBXaEdxnc8qFEpWfPRDnM2zEHnlMOLRU9Ru3kD5h+4g7HARGh4lEomRHnS843vzngi4\ngIeGLvVz/tAhVBoNq2++GZ3RSKRvCPfu/QT6Bhkc7Odf9u3kvHeCcoOZx7feywZMyDJZ5FZzrtzB\nM43onkJ0ehEnPIjjbsQxV67Ae3gccdBB+OBJxIFRrAoVlrx8spkMkUwaYXEL9k9/FEVjNUKe9arZ\nqyRJeD2TnDpxjLOnTuB2Opl0u/Bc7jAzPeXFNz3FjG+agH+GUCJGRKskbDcT0CqYjgaZiYZJyCSC\n2QyTJhW1D9yLqqLkmpIWuVZL1uMjFgwxmYhgseVhtlqBq0UkoigSSCao9UY5NTHMuYsXqKtvYOVN\n25kYH4dUmlQmgz+bpHDdSnTBGGZBwbRBxbRjnHQsjsliJTHpJZ5KktAoUanVlCyYj6Iwn+SYk+TQ\nGNloDGVxQW5ic3mmqNbpCE/7CLhcWEtK0JqM5JeW4h4ZwesYx1ZUhO53tJF7N/BuC7jSySSnXn6F\nZDyOKRDF4PBQVF2NalkrvZ4J1DotCzZvQpDLyWQynHjlFZKJBMs2bSK/pASDxUKgb5ByuZbSklLk\ni1vwRYJM7j1MLBxmUq/g+fYTnOvtpqamlmef3cXKlVenwsRgmPRz+5ASsyjXLb1mJfi74tdj5T3X\nQXBwBGt9DQWL2/B39uA+kDN0KFm7ivyF838nAZKgVKAvLroi6DJVV7zlylqTZyXUP0zc7aF01TJ8\nwyPEfH6KWltQNNWgKC1CVVzA1uWrOXmxnUOOfmbCQdZaS5HCUbITk2SGHGR7h7G6/UiDYygyWWwZ\n0MVTyMqLCYtpFG4fQjCCIxokHg1T1dKC4g2ZNZlMhkqtRm8yUVxRTFaSv62sT3p6hsiBE8iNekxb\n16LS6YjpVIQGRhBmgqiyoKp5/wm60qkUZ1/dw/T4BJbCAhZv3kh1SwtKlYrQzAwnX34Fw5CTyqYm\nzDeupGP3HiI+HwpkzBw/g5DOUrJ6OTse/gKi5lqiygm2jqMzmWjdvJGHH3mIhx/+MgaDkf/9359z\n991/BqLI7J6jZB2uXIlROossEkNoriMky+I6dAKZXkflxjU4pj1MOMZomd9GWUVukhcedeA9fpr0\ntB/bvLkUrF1xZZwFQUCj0WK2WCksKqayqpqGpjnMaW6hqLjkGvvMN0Kh1SAoFURGHaRCEUx11de9\nhzKZjNKGegIWHcH+YWK9g+hDcWyrlmBrbca8oAWSKewr295/amrPhPOhQy/sQq5QsOqmm9AqlHh3\n7ce//yjJs11c6OzgSydfYTIRZXNeBY+tuZOygiJklSVo7rkJ5ZrFyOfWIZ9bi7yxGnlDNUJ9JfKa\nCuTVZcirSpFXlJDSqfFOTCArtmPfsh5aG3FNecjmWyj/wI5r2mZJksSEY4zjRw7SeaGdcCiEvbCQ\nRUuW0dg0h/rGJhqa5tA4p5k5zc3MaW5hbst8mue3Mq91AfMXLqKitAJlKkt+XQ2lq1cwPjFO1OUm\nPDCCUqlAodWi0Ouu3FSV2Uh0bBx1KktAkHC5nVTW1uWCwuXAmc1kOHHoAKNDg5Q3NvDB0ib29Hfw\n/EvPY8vPp666lihZ8gsLiY6OE56azpFCMouqMB9/Ns20w4G76xJGq4UVd97O9Mw0E6OjqNRqiurr\n0NRVkZ70knK4iF+4RKKjl9m+YZIj46Qn3GgyEsH+IRJTPmzFxcgFAWtpMc7BQXxOJ6X19deIYN4r\nvJtkLEkS7fsPEPR4yffHUI26MJUUU/rRu+gdGSAeDjNv/VpMl80puk+dwjM+Tk1zM7UtOdOUmeFR\n/D0DFESSaIqLGDDJCZ5oR6XVol+9hO/s3snwhIMtW7bxy18+S2np1c5mUjBC+vl9SLEEitWLUcz/\n/euLfw29Xs3UoAPn4ROoTUYqNq/He/Q0Mx1dKHRaKm7e9Lb6u74VlHodKqOB4NAoMZcHa0Ptm1qp\nygThcsAbR6nVoDCbCDldqE0mDPl5CCYD8oI8tNUV7LjrAxzY+yoHhnvIWo1s+LvPoCgrQijMRzAb\n0VjM+L1eEtMz2K15CB4fJpWainvvYjYcRub1kQyFGZtwEA+FqVm44E3J9u0+U1I2S+jlg4ixOObN\na1DYcqY4ZrudQY+TzOQUxowEsylUlaXvG0JOJ5Oc2b2HgMdLcW0NS7dsRmvMTahn43GOv/wyTHio\nNtkwtDXT3XGBRDiM0WrD+eIemE1StnkdKz/zACaL/pqxigYCtO/eg0wQqF+1gk/+1QPs3PkrGhoa\n2bnzJRYuXIyUzZLcc5TsmBOhtAiFTECanELeUIXm1o34zl8kPDCMsayEovWrOLRvHzKZjLU3bkSh\nUJCdTTLxymskxt0YigspWLMCTcG1zX9+H2gL7CQ8U0THnSgNerT262/lyWQyiioqSJbZmRweZrZ/\nFOWEB21VOco8K/rWue/P0qY93/vJQ8JUkIVltXD4HKEfPoV0+AwMOPiho5P/HO9AAj7btppHvvAl\njJtXo1q9BPXapSgqipFpNcj02pyH7WVPUsFiQrCZEfKtCHYb2MyMnj1HSqWg/J5b0cypxX36PEl/\ngKIblmF4XUolm8kwNNDP0UP76eu5RDwWo7yiklVr1rFg0RJseXmYzGaMRhMGgxG9Xo9Wp0Oj1eZa\nOarUKJVKZmf8jL52ELlSxZwP3kbxkgWYm+oYOXOe+LQPnVZLoH+Y8KgDSZRQmU3IlQoEpYLEhAuL\nxYI3EcXn9VBd34DBoCEUjHL4tT24xscpKCpm3W23UWazs0ZnZ+9wN7te2YUxL49Km52CJQvIxuLE\nRifIKhVoQjHsah2ZmjJcg0MEHA5M+XZaN22gqLCI8aEhxgcHEbIieqMRWUkBmWQKpdGQq3tOzJL1\nB8nMBJEFI2RcXmaHxhDc06T6hhH7RtD5QiT6Rgh29WLISMjNxt/qhf1u490k45HOLsYudmL3hpCP\nT4JeR9PnPoUvEsTRfYnCqirqLqflf+2yZbBYWPq6CoCJM+eQdQ6gkAm0Z6OEOnsxyJWUbd3AF7//\nX4yOjfLgg5/l0Ue/jUbzhglhOErq+X1I0XjOGrLtN4rcTGIWz/EzBHsHyCQSCAolcq3mHQV4lSDS\n/atdiJkMZWtX4j16mui4E22BncodW9DYrO/CKII2z0Y2mSTscJIKhTHXVr3peWpsVkIDudVx2eoV\nTA8OE/VMUTh3DoL8N2Sp0WjYfvOt7H7mafZePItGq2XlLTcjL7ajqCpF2VQDTTV0JPxk6sqxx7Nk\nRsbRFtopv/NmlP4QqniSyWgQ98gIAacLvdmC3mq96jjw9p+peEcPyYERNE216BY0X/m5QqlElEQm\nYxH0s2kUgTCIEqqyaxt6vNdIzc5y+pXdBKemKa2vo239uiuTpWwmw4ndu4n4/dSl5WiVKoYSIdLp\nFLbCIkafeRExEqPoxhu44W8/jSAI14xVOpnk3K5cZklfVc5ffOrjdHZ2sGXLNp588hmKioqRMlmS\nrx0jM+pEKC1EodMijjoRKopRbllNYnqG6Z5+pFgCncWMOxrC6fPS0tpG+eXqGc/RU0QdTmSpDPqy\nEgrWrXzX/Q9kMhn6kiJCfUNEx12Y6qrfUpSXX1iIsqacEaeD5MAY8hEXOns+gt32/iTjya/9+KGq\nmIhw8iLZzl5Efwi3LMPfeDp4ze+kwGLjMw9+hk//1zfQzZ+DvLgAwWK8JsX7lsc4fZ6wYwL7/Lnk\nzW0kNDDMTEcX+tJiCm9YhkwmI5VM0nOpmyMH9zEyPEQ6laK2voE16zcwt2U+BsPbT70mIxEGX9qD\nmE5Tu/nGK3ZpFrsdfySEb8qLqriQ0rpaYh4vYYeTme4eksEQusIC4m4P8ngSVWUpk5NuspkMpeXF\nvLzzeaY9HkorKlm7eTNKlQqhKB+LL8LG0joOTAyy/+hBkuk0jcXl1G/ZgN/jITLuJDLhJtnVD1Mz\nON0u0oEgCX+A0IiDuGsSTTzFTP8g4+0dBIZH/3/m3jpKrvNK9/4VMzYzc7eYZUkWg0mGWHFwMk5m\n7ORmMhO4k8zN3AnNJJk4OJ7ABJ0bx3Zi2WKWxYzNXc1UXV3MDOf7o2TJsiRHcuDLs1avs7rq1Dnd\nb73n3e/e+9nPJmSdwhvwEZNLKXt0E9p5bajntqJqrkdZW4m0tBC72wk6DblN9YhVSlQaDTG3h8jU\nNGm3F7Hbh7Kh5l0JUrwbhAMBHKMjxONJFGr1H+V5uG02OvYdwDQ0hS6RISoTU/yhx9GWlXBl/wHE\nYjFzNq5HJpffpLK1ZONGNNe8imQ0xsDOfQjdA7ilAgHSlOpM5C2Yw8e//WUGBvr5+Mf/gX/7t6/e\n4pUJoQiJbQcRAmGki2YhnXtjcQ+OTTKx+yAR2zQJn5/whBVvdx++nn5ibi9CKnUtrHbnZ0QQBKaO\nnsQ3OY25thpfVx8Jnx9DQy2l61ci/RP0AX4rtCVFRKamCUxYcXf3ERgZJ2yzE/P4SEWiZNKZrKCD\nVIpYmvWOJVIZ6uJCfBOTiKVS9MU3K+JpNBrWzVjArr072XP0EPn5BcyadYMoptPrsU/bsNmnqVix\nlMzVXlL9o6jntaEsL0UViGDMyWHU68IzMYmQSuEYGiaTyaAxm67nh+/GGKe8foIHTyBWKjFsWnnL\nnDfk5DBm6ccvhUK1juSYFZFUmk0D/f+ERCzG2V17CLjclDXUM2PF8uvzUBAELh07hmNignJDDnp3\ngCmfm6TZgDk/n+Hte0l6fOTMn839X/j09c+9dawEQeDqocP47A4mokE+/cX/jdPp4NOf/hzPPfd9\nlEol6clpYruOkJ52IiktRJZrJtM9iDg/B9mDK8kIGUZ2HSCTTFH5yEaik1P0nzxLJt/E/WvXI5VK\nCU1OYT99nkwwgspsJHfJfJQFtyos/ikgUciRaTUEBkcIT06R8AWIuTwkAkFS0SiZVDpbO38ttWc0\nmdHXVdPnmiI5OIZoZBK1VIauueqvzxhHf7n9S4I/RDQaJWhQczBXzGc7jzAR9DJ/zjye/tBHePx9\nH8BoujcRjzcRnnZgPXYahUFHxbpVJMNhJve9kSWYPLCWRDpFx9XLHD9ymMmJcQSgqaWNFavWUFvf\ngFL1h1W23opUPM7Azn3EgyHKly0mp+5mLe3i2hr6T57GY7fT8NhDVC5egEylIhEMErLa8A4MEff6\niLvclNbV4c0ksI6PMT48hMvpprq+nqUrVyORZB92kViMuCAXzaiNTW3zOD41wtn2SzicDh587Akk\nCgUio46UVIzM5UNstYNBi1BaSCQWJZFMUjVnFrmVleRXV+IJBwmlEuTX1GDMzSFgmyYZjWGuLM/e\nSyFHotWgLi3C7fPijYUpXb0c/exW1DMayVm+kKGQl6DXhzIYQxKLo6i7syf0p0A8GqXv/AU6jh3D\nZZ1kqKMH68AAsUgEuVKJQqW6p/vHIhEuvvwqqs5BCnLz8QhpJHOaaFqzip4TJ/E7nTQsXoipsJAx\ni4XLx44RDYVonj//unZ6JpPh4t69ePYcQS6WIK4pp9ycj6Q4n4//9zfp77fwzDP/iy996Wu3dg0L\nR7KhaX8Q6YIZSOdnCSqZZJLpk+ewnz6PkM6Qv2AORfcvRZljRiyTkggEidodBEfGcLd3ExgZJ+EL\nAAJStfq69KkgCLg6uvH1WsikM1kBh2SKgqULMM5uIxIJ4/V4cExPM2WdYHRkmEFLH73dXXRcvcyV\ni+ex22woVSp0Ov1dja1ILEZXXkIyFCYdjxPz+oi6PISsNvzDo3h6LDgud+Dp7ScRCBIYHiM4Mo65\nqQHf+CRBu5P8pnokb9tgGEqLWZ5QsKv9PDv27KS2to6mpqxsrkgkwmAwMmTpI5RJUlJRSbJ3CGHK\ngXrlItJuL5p4GklRPk6/l2QigSEnB8/EJNbuPtKJBBqzCb1R+47GWBAEAnuPkg6E0K1eettKhKxh\nF7BPWlHWVqGNJogPjSFWq+5YufDnRCwS4eyu3QQ9Hiqam2hbvoxMKkXPyVN4p6eZGMpGyswFBeRP\n+3H2WEjUlWPMy2dk32GSLjfGGc3c96lnUWi116/7VmM8eOky4909HLx8ju/99IfIZFJ+9KOf8fTT\nfw+xOPFj50icuoyQSCKb0Yg0x0T6Yicigw755jWIlAqsJ84SstrIn91G/tyZjE+M4+7po1Rvori5\niYQ/wNSRk6QjUaQSCQqTgfz7l/5ZVQEVZhOpcITwhJWow0nYaiM4Mo6/fwhvdx/uq524r3Ti6+0n\nMDiKyBvAZMphJOQlOTpJemiMsic2/PWJflx66O8FZzJOpLqE31ousfWN/SiVSt7/5PtpqW9g3uKl\nNLa1/eEL3QaZZIqBrTuI+wJUPbSeVCCI/fQFMokE2lktTCQjDA30k8lkUKnUNLW00tDUjOJdegWZ\ndJrBvYcITFopaGuhbOnC257XtfcA517fjra5jsc+8QlkMhmCIBCZduDuteDrH8ZzuQNEInJXLqVz\nahRFvpGqumZmLVh428UvcamLxNmr+AtMvO/7X6HX0svq+1bw61deu05OmD54HPd3fgZSKZoPbuby\n1CjjPT1UtbbwwLPPIJXJ8LhdHN69i2QiwaJlywl19hF2ualZuZz8hpuJQ+7xCdr37ie/pprWNatu\nvG6zcXbnLjQ9o2iSGfI3raJs4+o/edlTKplkpLOLofZ2Uokkar2O2fctYHRgjOnRMdLJJAAag4Hi\n2mqKa2rQmd459JrJZLjwi/9H8uxV8oqLiRXn4halaVu/FiRiLu3Zh9ZsJr+2mqHubmKRSLYsoqmJ\ntsWLEYvFeFxOzh0/juuNU1QO2ymeM4tMUS4hCXzqxR/TZ+nl7/7uWb761W/cxhBHSW47RMbrRzq3\nFcmimYhEIiJ2B1OHT5Lw+1GYTZSsXoYy9+ZFXBAE4m4PoYkpwpM2Irbp6+pCiMRIVApSqTQBt5tQ\nOAQ+HwmxhLRMQrKmjKhCSuramN0OIpEIpVKFTC4j4PcDYDKbaWmbSVVN7R072Nx2nNNpksEQcX8g\n++MLkAhkj8lQmKjDRXh0AmV+LoJKgW98AmNlObOf+dubVMEAUld6uPrqdra8+AOiiTgvvPBb1q7d\ncP39E4cOMD4yQk1lFfknu5DbnJjWLke+YgHerXtBo+LgWB/OiQlmrlpJdUMjE51dJKJRxBIJzYvn\nkNPQckcmdbTTQvD4ORTV5Rg23n/H/zmVTHLk5VdIJVOs2LiRyN5jZGJx9GvuQ1l/q7bxnwuxcJiz\nu/YQ8vmobG2hZclikvE4l/bsw+90EvB4mBoZQWMy0VxTj+T0FcgzI5vZyPjRU6RCEfT11TQ/uIGy\nBTdXT7xZWmgfHeXMjl38fMernOu4TFlZOS+88BItLa2k+oZJnL6MEIsjzjOjuH8hiYkpUgdOkZGI\nSS+fR1oqJjA2wfTZS0hVSnLbmklFo7SfP4fE5qJAZ0RdVoyyOFvDLFcqEaJx8pYtxNj2lxFYSUUi\nJMNRUuEIqei1YzhCKhIlFYmSDEdIR6IIQrakKpFIMGyxYLR52HJy619fnfGef/iiMCWT8r3dv6Pb\n0ktdXT3/9Ml/ynqW1dXct2rNu/aqbGcu4LjahbGmEuJJwpNWUoLAtEqMLRVDIBvKap0xi9q6+rsq\nW7gTBEFg/PhpnL0WjBXl1KxfdcfdWTISYd83v4vD7aR5y6MsXrbipvfT8TjD2/YyffIs8hwzaY0S\nXWEOLe/dclPd3k33z2SIvnaAjN1FbPEMHnvmQ/SNDrNmzXp+/vNfo1QqubRtJ5mzVylxR0iZ9USr\nirk00o/T72XOmtUsf88TALidTg7v2UUqmWTmzFmEO/pAEGh59CE0bxF8EASBi69tJ+h2s+jJJ1Ab\nb/SiDXq9DJ49T2jbQUSpNOmFM6hevoSSuto/uhdyJpNhwmJh4NJlYuGs91s3dzYFZeXoVGKSUjVC\nJoNjYoKpoWEcY+OkUykAdGYTxTU1FFVXozXe3DtXEAT6fv073MfOoMnNoeixjfS0X8VUUkLb+jUc\nf+kVbCMjqPJyEUklSGUyKpuaqG1rQ6XRkEol6bx0id7ODtLxOGXn+iiRqZHUV+JVyvj0qz+n19LL\nRz/69/z7v//nrYY4GssaYrcP6exmJEtmQyaD81IHrsvtAOTMaCFvwWzEdzFXI8EgYyfPMn3hEoGB\nERK+AKlUCsQiBLUSmV5LQqMiVV+B0mhEpVajUqtRq9Wo1RrUGjUqtQaVKvuaSq2+vqFyOR10d7Qz\nOjKMIAio1WqaWtqob2x615vZ699vKkXc62fw5deIe3zkLpjNyPHTRJwuclsayZnVikQqy3IspFLE\nAij2neKSy8pHX/s5AgK/+tEvWLR4KRKZlHgiycmjh/F5veSHElSPedDrdOjX3kcCiHb2QUstW7dv\nJRGJ8OCzz1DZ0oKtv5/x9k7E6QRyg4m2dWuRKW/OEaaDYTwv7QCxCPNTD9+xPembGO3qpuvUaapn\ntFFXV49v2wGEZArDhhUoqv78KnbRUIizu/YQ9vupntlG08KFJKJRLu7ZS9DtwVBQwMiAhZDLgzIt\noB6eQp9Ik64qIeT1IVbK0VeUk99QS9tjD98yD/PydIz0j7P9Zz/n27/+KZN2G0uXLuOnP30Bs1RO\n/Og50lY7IpkU2YKZSFtqCe0/gX/rXhCLCDdVkdEoSSeTuLv6EDIZcpobkGk1uLwexq0TFJeVYfJG\nEdJp8lYsQlVchO/8ZcQKBRXvfxzxX1EttyAIpKNvGucoUa+XiydP8uEv/++/vn7Gu04c/dK//Ph7\nTE5ZefLJp/jal/+DyaEh9EYj96/b8K4NZNjuYOLIKTLhCJlIlKQ/gCw/lx4hhjsVIzcvnwWLl7Bo\n6TLy8vP/aK/N3t7F9NUO1Lk51G5a+46LpUQmQyGWMNVrwe51U1hTjU6nv/6+WCrFUF1B3OFCplZj\nrqtGEgkTjyVuW78H1zRQi/NJ9g4ic3hZuHET7T2dnLl4jhMnjlKdX0TYasMws5WyomIk3iBJsQiD\nSoN/ysbo+BiGvDzySktRazQUFBUzNjzE1JSVaCpJxhsg5nKT11B33ZiKRCJkSgWOoeFsj+PKG8Xw\nCpWKorpaDDWVxPpHSExMMR7yMTk4hFgsRmc23/OYC4KAfWyMSwcPMdFnIRGKYM7LIyc3B8/oOKOX\nrzDdP8BU/xBqo5H8igqKq6upamtFZzYjCBl8DgeuSSuj3d3Yx8ZIJRIoNRokiJh8eTvTR08hMeho\n+cwz9Hd2kozHqVu6mFPbttFz9hwStQpDYQH1M2cyf9UqiisrkcnlTE9ZObpvL9bxcbQ6HS2GfHR9\nY4jFEvwFRj7z2q/o7e/lIx/5KF//+nO3GuJYnOT2w2TcPiQzGpDeN5eE18/43sMEBoeR6bSUrV+F\nqaXhtpu8RDyOw2FndGCAziPHuPj6Dtpf28FUTy+BQICYUk6mohh9ayM5qfmHowAAIABJREFULY3k\nlZTSfP9iZr7vKeYtW07bzFk0NDVTU1tHeUUlRcUl5OZllYs0Gg1yufymv1mt0VBZXUNtXQOIRDgd\ndiYnxunr7SYaiWAwGFG8S8UpkViMTKNGodcTc7rQlpZQsmoZtrMXiTicpKViwi4XwWk7gSkbfpuN\nuNNFvjtMaUM9b/S2s2vvLoriAonRSZw9fagiSZBIcJEi4nKhD8VRpASUTTUknG5weSlZvYyBq+2M\n9fRQv2A++ZUVFDc1QiLK1OAI7vEJcisqrpdBCYJA4OAJ0h4fuhULkRff2pL17dCZzVgHBnFP2aiY\nNQN1RSnx/hHiQ2PICvOQ/BnLAiPBIGd37SYSCFA7exaNCxYQD0e4sHM3Ia8PQ2EBnoAPkVhCRUUF\nKsSYPSGEVAqb0048k0bQapAqFdSvW4Uu/9a8rEwCz3/163ztJz/A7fPy9NN/x3//109Q9I8RO3AK\nwR9EUlGC8oGViCUSknuOEzh8kgygfHQ9urlt6OtriHo8iBQKqjZvonrzJsyz27gwMUS6IIf1T3+E\nnBnNxO0uhGSWHxGbdpKzYDaqu/gO/pIQiUSIZTKkajUKox5NYQF1C+b/dRK4lqxZ9SWxRMx3vvNf\n/P3HnuXUkcOIJRJWb3oQ9VtyEfeCTCrF4NZd+Lr6UGg1yLUaTPNncdE5SSgaZubsuSxfuRqT+dae\nvu8G3uFRxo+fRq7RUP/QBmR3wSBW55gJjYzjmZjEnopT19B4U5hPLJORjESJOZzkzWqDWAT3wBia\nogLkd3hgRUoFIrmM1PAEGomEqto6QpkUJ8+e5rUd25ArFDz2sY+hKCtGPDGNWqVGyDOiFsSEBkcY\n6O+jfGZbVs5PJqdSbSCFgDMUwOt04hkdIxONUdjUcH3c1EYjzuFRvFYbhfW1NykRASjzctDodajD\ncRQCeEhjHx9nwmIBQUBnNt9VeNMxPsGZ17fRdfQ47tExiMbRabUIyQQRnx+RWIy5pJj88mKmRyew\nDwzidzjQ5eag1GjQm82U1NZQ2dKCzmTM1mpPT+Pt6ce29wie/UfxDQyT0mto+cyzBHw+xjo6SYpg\noKebkSvtqLRalm95D/NXryavpIRwOMRwfz/tFy/QefkSyUSCprYZLFuzlsDLu0iPTBKuL+Uze16m\nZ6CPD3/4ab75ze/caojjCZI73iDj9CBprUOybB7erl4mDxwlFQpjaKilbONqFKYb/aMFQWB4cICO\nq5e5cPoUlw8cZPjICWynzhMaGSMTjqAzmyic0UrD+tXM2/IY8x/YSMOSRVTOn0vponnULppFShD/\nUc+AXKGgpLSMxqYWFEolHrcbm9VKX08XXo8HrUaL5l0+xwqzEf/AMBHrNEUL56DNz0OUzlC5fAll\nixaQV19Lbm015qpKNFUVqO0eGsqrKFuygP3H3+Bcfw/r1m6gqLyMVDSKLJ5EJhbjSyXxezwYkhnk\n4TiyhmoSNgcGvQFRcT4TvX1Yh4ZoXLAAmVxOzawm/N4g7vFxnCOjmMtKkKtUxPtHiFzpRl5ahHbp\nvLsaR7FYnN28jYySTqUobm1Glp9DbGCE+OA48pJCJNo/vaxs2O/n7M7dRIMh6ufNpX7eXKLBIOd3\n7ibs9yNVq3D7faTjcbQiCSSS5ETTSKZd+CQCkqpSctuaiXq9oFbh8XqyEad0GpVel2WMZzL822f+\niW/97IdkhAzPPfd9PvXevyG59zipwTFEaiXKVYuRtdaRPnOV1KnLJKYdBMgg27SCwk2rUBXkEbY7\n8PYPY6gup2zVMsQSCZa+XkaHh2hunUF5ZRUyXXZOhUfGiTtcSFVKClYvu86LuKsxcThxdHRnGc5/\nIZLpm/iLNIpoaGgQAz8EZgBx4KMWi2XoTue3tLQIP/nJr6iurmH/ttfx+7zct3oNFdU1d/rIO0LI\nZBh4+XWsb5xElWumZMUSjPNncehIVsy/beYs5sy/fd713SDscGLZsReRSETDI5tQ5949GWP06Ek6\n3ziCUyOjcclilrwtXJ0IBBn67WvIzUaaN6/hwi9+j0yrof42guVvQhAEYtsOkbJOY0mGCBs09Dqn\neO7HPyAQDjFv3gJ+8IMfUWnzk2rvQzyriYBEoOf323B09qIy6lnywAPIInEQBEQaNYmV87na08HQ\nwaNkgmHKlyxg6eOPodNnvXlb/wC9R45R0txEw7JbVcOETAbftgMkbQ7kC2dhS8cY6+khlUgiUyio\namulsqX5uvypIAhEA0H8djv2oWEGzl/EbbWCABqjgdySEszFRegL8jEWFmAoKEBlyBKJ8vJ0jPSN\nMXDmHF6rFZFITHFTA1Xz5iC/RsZLB8PELEOEOvsIjE8S9HgJJePE80zUvedhNCYjB370PwTcHnJb\nGgjaptFosoZYkMuxjo8xNTFOKHhDdjOvoJC5i5eQk5dHuHeQ0X/+D/wS+BdbOz3DA3zwgx/hW9/6\n7q2s6USS5I7DWSnSphqE+a3Yjp0mPDmFRKmkaMWSW5oyRMJhTp04hrWzG5HdjTQSQ6VUoVar0Rfk\nU9jaTEFzI+r83Hec538O6dB0Os3YyDBdHe143C4A8gsKaWmbQVlF5T1HQ3x9A0wdOYmppRFTayOW\nV7ZhqCqncsPqW85N7jpCetSK/IkNvHhwF5/5zD9QVFTMzp37KTDl0H/wDUIOJ+F4DH+3BXUsRUtF\nNTm11cTEkA6E0G9ex87f/gbrwCCN9y1h3Qc+QEGBAYcjwPjVDobOX0CqUNC6fDnpI2chlcb83oeQ\nGO7eo81kMhz//VbCAT/3P/keNAYD8aFx/PuPIVLIMW1ehzTnT1NWBtc6je3aQywcpmHBPOpmzybk\n83FxV9YjjqdSxBNxxOk0uTl5iIHcvHxir+4n7PMRmtfEvPc/ydjRkyi0WgoXzMY2NIJzYgIhk0Ek\nFqMvKOCHv/kF+48ewqQ38MIvX2R2Sk6yZxBEImQtdcjmt5HpHyV9rgMhmUSUn4Mt4CWSSlCzZTMK\nk5G4z0//73cgkoipf3Izcq2GdCrF1t+9RCIe5/H3vh/VtedYyGSY2nWIyOQUuYvnYZrdetdjkgiF\n6Xl1O6lYDLXZTN2D65DdY5/rPwZ5ebo/f5j6+eeffxRoslgsDz///PN9wFc/+clPvnyn859++ukv\nabUmTh99A4fNRkNrG80zZt7zfQHiXh9Dv9vO1NFTyDRqGj/6AQyzWjh0aD8+r5fm1jbmLVz8rg1x\nJBRicmSYaDiclcOMRhncc5BMIkn1ulXo3lZ68YegNBmJjE4SdDixp2Lk5xegN9zIY0oUimzpyuQU\nBS11pGUKAqMTpONx9HcQY3gzXJ3uG0Ls9OKVCpi1OjauWE1ao+LIkcO8+OKvUVaXM1NQIbT3IROJ\nyUGGZGIaqcOLf3AY06xW5FVlZKadyOweajesoaCpnqnuHlxDIwxbx0kIGcx5uRjy8rAPDOKbnqao\nof72akZlRcT7R0hP2iheupCqubORyGT4HA6cExOM9fYSDYXwTVqxHDvB0PkL9J44xdDlK8RCYUwl\nRcxat4Z5D2ykacVyyme2kVdZgS43B5nyRn2tRqMgJUgorKtFn5dH0JUtW5nq7iUz5UDoGSR86iLJ\nyWnEAhhmtVC8eQOFD65FWpiLbXKCc9t2EHA4KGpuoLCinJDHi6BUMO6YZrjfgtuZbe5RWlFB08xZ\nLLhvGY2tbag1mqxYzDd/hHdsgn+2XqZ3YpQPfODDPPfc9241xKk0yV1HyNiciOsriZblM7nvDeJe\nH9qKMioeXIvqbaHAkeEhDu/fg3dsAoPNQ3VZOdVtbdSvWEbjpnVUrliKuaoCuVbzB+f5n6PdpFgs\nxmTOob6xicKiYuLxOLYpK6PDQwwPDSASiTCZTYjFd5fXU5hNBAZGiExNkzurlZDVRtjuJKel6RZv\n5q29jmc/9jBqtYZdu7Zz8OB+Hn1iC5WzZ5KKxUl4fYjTApFAAH8sgiwax2AykUwkEHxBajZvYvDy\nFewjo8i1GmpbGohEEhiLClHqtDiHR/EdPI4imcG0aimKintrISoSiZCrVNiGhknG4xRVVSE1GxDr\ntMQHRogPTyCvLkOsfOcwfyaRIO0LkHR6SE7YiA+PE+sbJNLRR3JyGpFMSjiVuK4g17x4EbWzZuK1\nTXPipVew9lpwTVoJu9xIBYH8gkJkMhmFFZV49x8lOTlNqqmS+77wT0ycOEMqHqdx41pyKsopqq2h\nrLkJpUZDV1cn/+dbX6O9t4u6yiq2/uBnVA87SE85EJuNKDeuQGIykDpwkrRlBJFchmzZPKKVhbj7\nBzHUVWNqaczqme89TCIYonzVfWgKsyHn/r5eRoYGaWppo6LqBtFNJBKhqShFZtBjaKm/awZ1lmh7\nkJjfj7Ygn7DLhX9kHENFGdK/UDOPv5Rn/G3gnMVi+d213yctFkvpO3xEOPHGaS6dPU1eQSGrH3jw\nnliZkN0hudu7cZy7hKuzF4lKScuzf4O6tJj9e3bhdjlpaGph0dL73rUhnhwZ4eLxYyTi2RZTQjpN\noncIUSKJvqkOU0Mdaq32lp+3kl5uh+FDR7F2dDGYiaIpLmTz408if8uEiLncDP9+BzllhchrarBf\n6SDuD1K1aS36ijsPa7J7gODeo4xYJ/A3lDFj0wZMai07Xvot/+f738AV8DO7sJxv1C2mNr8Y8Yr5\nZHKMHH/xJXx2O+bKChY++zSaSJzkpW7EOUZUm9fid7o4+5uXcLiciGvLkatUNM+cjV4qY/D0Wcpn\ntFG7+PYs8vjoJP7dbyAx6jE9+UA2FJ9IMHT5Ku2HDuGbnAJBQJ+bQ0oAmUqJsaiQtvtXUFxTfVff\n3S3t7qadWA8cxXXmIkI8jkyuIGdWK3nLFqKsy/ZQTadSXDlxgomBAZLRGL6BYZRGA9r6KobPnEck\nFpHX0oQ5L5/isjKKy8vJKyi87Tz1HTlN/9ef5zNDZ7AE3Tz11Af47nefv9UQCwKpw2dI9w0jKivC\nqRATGBlFLJNlS4wa6276f+OxGOfOnGJ4cACpAKX+BCadjuoH1t2RR3CvY/Xngs/robuzg+HBAdLp\nNGqNhjnzFlBdW3dXnvJ177i5AYleh+3sRUqXLyanpfGm8wRBIPnybgRvAPmHNiPSqvn617/Cd7/7\nHE1NLfz+99vJz8/H0dfP8PFThC52EIqE0ZpMFMnUFJSXkZFJ0a9eykTYx8EX/h8SpZItn34WY+GN\n6IT91HnGfvkyabWSsr//AKUtzfc8JoIgcGLr6wQ8HpY/8Rj6a/3XIx29hE5cyDZs2Xg/JFOkQ2HS\nwTCZYJh06MZRiL/zRioejTIxNkpEp6Jg0Vw0RYVY+yz0nz5LwOsBiQSlTkvVjBnUzZ+LuaQEcSzB\nhZ/8EknXAPK8HGZ/9ys4+/qZau+kqK2FyqWLrl8/mUzy7W9/k+9//9tkMhm2PPgo//HQk2DNXls+\nvw1pXQXpcx1ZIywSIW6uRbpoJigVjGzdRczpovrJzShzTNgvXmX6whVMddWUr8lGCW/yire87476\n0feCiVPnsHd2Y6qpomjpQrxdvdgutyNXq6l7cD2qP5HYzTvhXj3jd2uMfwpstVgs+679PgZUWSyW\nzO3On7ZOCa/8+iUUSiUbH30c9T224Yu5vdiOniLqcBJxuhEkEoqWLSJ/0VwO7t2F0+GgrqGRJctW\nvCtDnEqlaD97lqGebiRSKU3XRAUmj54iNGVDnGsiU5Bzx5IQkViMSq1Go9ORW1hEy9y5Ny1AUY+X\n7t+9jicWwaqWUFvfwLL7V910DesbJ0hOTBCJxEnF4gTGJ1EV5NH0t+9HfQfJN0EQiO06wvSx04gK\ncsnLyc12vgE8kRBfPvQaO6+cRSGV8dnlm3jm059DsWAGPrudnV/9BqJxG0XV1bQ8vAmTVE6ysx9J\nYR7Kh1Yx2dHJxPnLxKQi3EoJiXgclUqF1OFFbzCw5H3vRX6HvHno1EUiV3tQ1lcjmd/GZEcXU5Z+\n0qkk0XCYpCAg1ahR6bTUz51LWUP9PbGv8/J02MedxAZGiPUOkXJ5suMhl+ETZbCFA2SUMoxFhdQu\nXohCp+PcgQNMjY0RCgWJuTykgmH0NRVEfX7EqTRt969g5rJl18Pyd0J40sbw/32OTx55lZ6wly1b\n3sf3vvfftzXaqfMdpM53kFTIsEkypOIxVAX5lKxehtxw832skxOcOn6USDhMbl4eVUkJKZeXooVz\nyJ/z7qJIgiCQn6//ixjjNxGNROju7KC3u5N0Oo3JbGbu/IWUlJW/47MpZDIMvbyNZDBE+cPrGdq+\nF01hPjWbN91ybrpnkOQbZ6/3OhYEgS984bP84hc/paSklBde+C0zZswi7HTR/dJWgpe7CMlEGCVy\nDIKEvOIiFOUlmN73MKe37+Tq0aMU1Vez6qn3Yy4oIBNP4HlpB1G3hzG1mIQYqubNpXLOrHteX+xj\n41zYt5/CqkrmrVt7/fXwxQ7C567e8XMimQyJXoNYq0Gi1SDW3XxEKWfy7AW6X34dsc2J+lqtfUwq\nZjLoxZFOoCouoLixgaWPPExOURHJZJLx9k4GX9xK0uEiV6mm4X1PIG2to3vbLhQ6HTPe8yiSa9Uc\nFksfn/jE39HRcZXSklK+8+xnmSeoUMslxHNykN83D2HCRvpcO0IiiTg/B+mK+YgLsutVaHyS8d0H\n0VdXUrp+JRG7k8Ftu5GqVTQ8ufm6spWlt5szJ0/Q3DaDBYuW3NP43g6eoRGGDx5BaTRiMyoZmxxn\nxYqV6CNJJs6cR6pQUPfAOjS3Iaf9KfGXMsbfBs5aLJbfX/t9wmKx3JGz/+LPfi5EI1EeePxRit6h\nsf3bkUmnmb7Qju3MJTLpDJqifLyTU6jMRpqf2syePXuwTU3R0NTEqrVr3hVb2utyc2zvPrxuD+bc\nHFZs3IDBbGLwjZPYrnZjriqnZfOGbAu6eJxwIEg4mP0JBUPZYyBAOBgiEg4jCALzly2lde6cm+7T\nvX0fzoFhplRivMk4mx5+iKrqG51kBEEg5vHhGxzFNzjK1MV2AuNW1LlmSpfMw1hTgbG2CnXBzTnC\ndDCM55evk4nFkWjVyCuKkZUXI68oQqLX8tprr/Hss8/icDiYV1HDr15+iZZF8+k7d5HdP/oZiikP\nZeXlzH/mg2imfcR6BpFXlqDfvIqOHfvxjE1SvmgOXtJ0XrlKYNxKwuFm8UObWPbYQ7ddnIR0mpH/\neZnpK134zFrS+SY0RgM182ZR2pxl5nrtDgy5Ocjusi3mm0g4PfjOXCEyOIaQziASi1DXVKBtq0dV\nVYpILCbk9dF7/DTTQ6PEYzE8Pi8ykwGX34dcISc8PEFxdRXNK5Yyfrmd/PIylr1n8x9caONeP1f/\n88d87IfP0Rlw8cQjm3l566u3NcTxrkHCe46TkUkZz0QRxGKKl8yjcP7Mm0JtyWSSMydP0dnejlgs\nZv6ihRSkJUyevoCxsozGxzbdswFIuH34rvQQ6BlApteSt3Ix6orie7rGH4tgIMj5s2ex9PZmw/1l\nZSy+byn5BXdmwrp7Bxje/Qba4gLSYhFBq43ZH30/yrflaoVkCt+PXwEBjM9uQSSTIggCX//61/ni\nF7+IUqnkl7/8JVu2bCEeiXDqX5/DNzJO3KRF6wpm6+JbGyl+YCXK2U1se/4nDHZ1U7dgHo8/81EC\nx84T7LBgWjoHaVM157buJBIIUjGjhdZVy+5pnREEgTdeeQ2Xzcaap95DzrWwrCAIBK/0ELPakeq1\nSHUapHotEr0WqV6LWCG/5XvPZDJ4Jqew9g0wdKWdoavZDU9xZQUVuQWInR6mr3QRCYZQ55goXDiH\nps0b8UoFRkeGmRgcRrjYgygapyQnj7raWor/5jGu7jlI2O1lzpMPYyorIZPJ8IMf/IDPf/7zxONx\n3r/pIb68/AG0IikSrRrtivlI9Foih86QdnoRKRWols9FMeNGGFkQBPpe2k5oyk7Lhx5HYTTQ8Zut\nxLw+mt/zEIbyrB1Ip9O8+KsXiEajfPAjf3PPjtrbEfF4ufLiawAkakvo6LcA2XD3hg0b0CcF+vcf\nRSKT0vzwekzvEHn8E+AvYowfAx6yWCwfaWhoWAT8q8VieeBO5//P934gNLTN+YN54nQsTtznJ+H1\nE/f5CU1Yibs9SNVqCu5byPTFK8Q8Pio2reZs11Wmp6aorK5h+cp7F5sQBIHh3l6unjlNOp2mtqWF\nmQsXIZFKsXd0M3H6HGqzmYbNm+5IpHo7YtEo+1/9PalEgjWPPobBfKNeN+xw0fvaDiQGPZ0RDwqF\nkkeeeM9NWsVvDSkmgiF6X3iZ4NAYmoJcFNc8KalGg66yLNv7s6QIkURCJhiGVAqR8fZKSW63my98\n8uNsO7QXhVTGP//Lv/LMM/+LS3v20XHwEEZXELXRSMOHn6TEHSE9akVaXYZ4+Ty6XttJIhKh6aGN\nyPQ6rpw7y+XXdiAIGRrWrqKirg71NTatWqMh5vczfrUTT/8gqq4hFCoVhR9+goLZM/6o0rJMMknk\nfDuRjj7UKhkJpQplYy3K+irE6tsrqA1evsLx375MNBBAqlAgyjFiUKrJNZqZ/eBGOo4eIxGLseSJ\nx9Aajbe9xptIR2NYXtrKp771FdrdNu5vnc1v9hxAfpucX8ZqJ7njMIJYjE0O0ViUktXLMdTfTFh0\nOhycOHqYgN+PwWhi+cpVKOIphnfuR6pRUf/EI/fUWzg2ZiXc1Ud8wgaQlS8VC4TDcVS1lRiWzP2z\nsHjfCR63m0sXzmGdGAegqqaWOfMW3DYCIQgCU4eP4x8YRmrQE/UH7hgZSJ29SupiF7KVC2/qcLV/\n/16eeeZpwuEQ//iPn+Xzn/8isSk7vT/+FaFgkKRUjGrSiSqeRDdvJlWf/Agut5uTr7zE1MQUs+fN\npz4qQpZjwvSeTYgkEuLhMO17DxByu8mtrKBl1crr3uPdwD01xZmdu8krLWHhA7d6+u8EQRAIOl3Y\nB4dwDA0TC0fwORx4nU4Uej2z1q6iYeFCXFYrh154gYDNQYkph1K1HiEYJhQIkJKKiZn1yFIZtHIF\nJW0taFwBlFVl+HN0WK+0U9jSRNWyJUxOTvCpT32cEyeOYTYY+Y9HPsC6qmZECjmyua1Iq8vQWAbx\nnu8GQNJci3TxLERvm6ehySnGd+5HV1lO0cr7GN17iPC0g7yZLRQvWXD9PEtvD2dOHqe5tY0Fi2/f\nTvZukU4m6Xt9F1GPF3FtBZdG+jEYjCxevJQ33jhIKpVizZr1GDIihg8eBaBqzQpM1ZV/1H3vhL+U\nZyziBpsa4CMWi6X/Tucf3L1HmDk/m8sVMhmSoTBxr5+Ez3/j6POTjkZv+ayxsY6CJfNxtndjv9SO\nqbEWSyyAdXKC8soqVqxac8/553gsxqUTJ5gcGUYmkTBj1hxMegPxQJCYz49ncBipSknTow8h191b\nyYZ1bJRT+/djys1l1SObb/rb+nftJzBphfoquoYsVFXXsGL1jdDVLbnQN1mHIihePJ/otJPQWJbc\nBSCWy9GWlaCrKkdXWfaOOsUA27/6db7wi+dxhYPMnTuf5/7zuziuduLo6EY6aUeWl4Nh+SLa4mLk\nvhDSxmriTVX07tyHTKWg7YnNyNVquo4d5/yuPaR1atRFBdk8nj9IZNpOKhRBJpehy8ulJCefHKsb\neV4O2odXozEaUWs0KBR33+xAEATiw+OETl4gE4og0esof+R+QjrjO15jzGLh6okTCIKA0WCk8/gJ\nJIioa26mpLER5DLGu7upnTvneiOIN++XCoaJuz0k3F7iLg9xtxe/w8nnXnieq1Nj3F/ZyFe+9g0a\n16265b4Zr5/kq/shlcJbYMRjt2NsaqD4/hvht3Q6TceVy3RcvQxAU2sbc+ctIJNIMPD7HaRiMWoe\n2YSm8A9rGqejMSJ9g4S7+kkHQwAoigvQtDWirCpDLyQY3vYGCbsLkUyKbu4MtDOabmohetN4ZzL4\nxyZw9fUjlsrQFOShKchDnWP+o0pDbFNWLp47i9vlRCwW09jcwozZc25pnJFJJhl5bTdRh4tYKISu\nqoL6LbdGLYRwhMSvtyEy6JA99eBN7/f19fKhD72X0dER1q/fyA9/+FOCJy7gbu8iLJeQGZtCMmJF\nLBajWreMlo9/hLB9gt9+4/vkjdipb2mh/lMfQ1ZwIz2USiTo3H8I79QUhsJCZqy/VRzknXB21x5c\nViuLH3qAnOI/HKUIe33YB4ewDw4RDQSy/7MgEAgGyYhAm5vLzPtXUFhZweDlK+z/1QsEfD7UhfkY\nykqQKRRIIzHyYhnykqAMREg5PSiKCzHMaCLt9iJfNp++M2eRazXMeM9mXtu2lS984XMEgwHWtMzm\n3zdsIddgRF5dnm1ja3eRmbSjVkiIqjVIVyxAXHj7FNrotr1EbNOUbVqD7dwlom4vprpqSlfed6NJ\nRTrN6797mWg0wuNb3vdHecWCIDD6xnHcA0PIigu55JpEKpXyyCOPYTAYmZ62sXfvLgRBYO3aDRhE\nUob2HyaTSlGxYim5jfXv+t53wl/EGN8rJk+cF5zj9qzX6/PfkO+7/leIkOm0KIwG5EYDCtObRyNS\nlZKIw8Xg67uQqFXYzGomp6yUllewcs26P2iIBUEgFY0S9weJBQI4RsboPnOGmD+AWi6nuLTsllCp\nVKmkbtM6NO+yPdf5o0cZ7bfQMncuLXPnXX89ODWNZcce9GWlDKTDOO127l+9lsprJV63I9u4Onuw\nnjyHvrIsW+ohCERsdoKj4wRHJ0gGsufLDQYqH930jp6UEI4w/dOX+L+7XmJ7x3kUCgX/9KnPMqug\nlGhHL3j8JAtyEJcX0RLMkKfUoJjVjFsrZ+zcBfTFRTQ/uIFUMsmZ375CLBYjt7GOiY4ugk4XyWQC\niVaDPNdM5lroUDdiQ2P3Esk3EqjOLkISqRStTsfcxUsoKrlzmCjlCxA6cZ7E+BRIxGjmtKKe3Up+\nsemOeVBBEOi9eBHLlSvIFQrmrVrFhbNn8LpdNFZUI0mmKWlr4eo6VOM5AAAgAElEQVTBQ6i0Wuau\nXEnK688a3msGOP020syAy85XX/s1Q1MTrK1s4hPv/SBNjz+CqeLmzIwQiZHYug/BHyJeX451aAhF\njpmqRx+4rqrm83o4fuQNPG4XGq2WZStWUVhcjJDJMLzrACGrjeLF88mblS3hEDJZGsbbWaRJl4dQ\nZx/R/hGEdBqRVIK6vhpNayOy3BvklLw8HQ5HgEjfEIGzl8lEY0gNegzL5qMsv5EyyiRTuPsHsXd0\nE7smg/lWiMUS1Lk5142zpiAPuVZ7TyF0QRAYGRrk8sULhIIBZDIZM2bNoam1Fan0xkYy7vMzsnUX\nXssg8oI8mj+8BdVtygmTB0+Rtowgf3g14vKbuyN5vR4+9rGPcPz4ERoaGvnp93+C9FwHIpWCoFJG\n+ug5xP1jRE1axO/dxMa/+wBnvv1zBl/dQcCkZdW/f5HCysqbrplOpeg9ehzH0DAak4mZG9ejvMvN\nus/h4OTr2zEXFrD44YfIpNNcOX4c9/Q0Gr0etU6HTCol5gsQdjhJhMPXFchyK8pJITA1Mkomk6Go\nuorW+5aSTqc5+MrLXNl3gEQigbm2hoL6WnLzCyivqqKsqgq9wUh4bBLri68hCkbQFRciFouR5JmZ\nFBJEvF4Kly7kK9/5Ort370SjUPKvqx/liao2pEYdYqUS3sKVERv15KyYTaCk5I7M5rDVxtiOfSjz\nckkk4sT9QXJaGilZtuim+dLf18vpE8doamll4dv6e98rnN19jJ04jUSnpTMVJJlKsWHDAze1K52a\nsrJv3x4ANmzYhEEqZ2D3AVLxOGWLF1Aw8+7Lpu4Gf5XG+MJzPxEikThimSxrZI36a0cDcpMBuUF/\nx113Jp1m4NUdRN1enDkarAEvxSWlrH4H5a7QtB17RzdxX4B4MEg6mcwK509P45yaAiC/tJSS+jpU\nRgMKvR6FQZc96nXIddo/Ss4xEY9zYOurRCMRVj+yGXPeDaJA37bdhKbtlK5byaFjh5FIpWx+Ygsq\nleq2xlgQBIZ37idktVG28j7MjXU3vRf3ePF09uLr7UdVkE/Fw+vf0YN5k/xywD3O51/5OS6Xk7lz\n5vG3mzaj659EIZESqykhIRFROuygNDcf4+r7GPU48IyOUTJ7JuUL5zF84RKjl69kLyoSkV9dRcWs\nmeiuLZrpdJpoJELY7yew7QAJu4toWw0hg4ZoOIzP60EilbJh86PoDTeHiIVkisiVLsKXuyCdQV5W\njHb5AqTGbGjzTgzhVCrFlWPHmBwaQqPXs3jDBgYtffR0tFPf3MKcufPxdffRuXsfMYeLkooKlJob\ni6lIJEJm1KPIMSPPMSEyaPnRSy/wXz/8Ael0mg/MW86HZy8lUVvG3A89ddMcEZIpktsPkZl2ITRV\nMzYyDEDVEw+hMBqym4SuTi5dOJdNi9Q3sGDRkuus+ukLl7FfbMdQVU7F+lWIRCKiHh9D+w+TisUw\nVlVgqqpEFksQ7uknYXMAINFr0bY2om6suaVMJpPJkJ+vx+XKesyZWJzAhXbCXVkxFmVVGZq5bXjG\nJ3F295KMRhGLJZjrqimY2YpIIiFsdxK2Owg7nERcnusavAAytQpNfj6a/Nysgc7Pu6XJw23nYCqF\npbeH9iuXiMfjqDUaZs+dT01d/fVURmBkjMGXXic4OUXNls2UrriV2JNxuEn8bi+SihJkD6287Xz4\n8pe/yE9+8kOMRiP/+YnP0SLTkbd8Ma7xcaIv70YYmcRVlovqsVUUj3rweL0ct4+iNpt46gufR2t6\n29wUBAbPnGOiswuFRsPMTevRmu+uwc2F/Qewj44xb/06xvotTI2OIkJEyOkk5vGSCIYREBCJRCj0\nejT5uejycvHYpomHw6i0Wkqbm5BrtbhcDrqOHidwrdtV4/0rmLdqJWWVVWh1N3LsqVCY8d/tJJNM\nUvrIBuQGHfExK067DWuvhYGIj2/+z3/j9LiYX1DGt2avpTy3AElJASKFHJFKibisEHFpEeKyQkQ6\nzR9k6I/t3I9/YBhBKQeRmPw5MyhcMOdmrks6zeu/f4VIOMTjW973rkVjIJsCtGzbjSAWMaQU8Ecj\n3HffcpqaWm45d2JinIMH9yEWi9m48UH0MiUDu/aRiEQomjOT4vlz3hUJ+Hb4qzTG/tEJISzIkGru\nveXd9IXLTF+4ynQ6xrRSTGFxMWvWb7xpJ/0mMuk0totXmb7agSAIWUlKvR6RQsbwyBD+UBC1ycSC\n9espqqq87c4ueLmLcGcvmrYmNG2Nd9SJ/kOwWyc5tns3epOJNY8+hvSagfSPTzKw5wDmmipihTmc\nP3ua8soqVq5Zd0fmayIQpP/32wGyRfJv242/Ndemr6miZO2dWeWCIGQlGSenCS5q5V9+8j22b38N\nvU7P0w8+xuyIGENhAcoVCxi3WCjoGsWs0VH46EaGxoaJBYI0blyHtiCfzgOHUBsNlM+cgdpwZxZy\nyhfA+7vdAJiefACpUc9wfz9njh1BbzSy/uHN141SfMxK6Ph50oEgYq0a7X3zUVTfzMS93WIQj0Y5\ns38/XoeDnMJCFq5bR8Dv4+DOHWh1OtY/9Aj23YeZ7uzGY51CX1hA+bw5KHJNyHPMKHJMyM2m6993\nV1cnn/zkM3R3d1JaUsp/rHmcmSoTozkqCma1Ub3ixk5eEARS+06QHhpHXFPORMBD3OOlZM0KDHXV\nhEJBTh47wvTUFEqliiXLllNeeaOWMjhhZWT3QWRaDXVPPIxUqcA3Os7I4WOkk0mkEinJ8SlwuBEL\noDIZ0bc2kLN0AcrK0tvOY6/Lxcn9+ygszqN5/tKbFruky4PzwHF87d1EvF6Eglyk5cXkz2ghr7UJ\n+R20lzPJFBGXm9A14xy2Zz24NyESiVCZTGgK8jDVVKErKXrH5z0Rj9PZfoWeriwRyWgyM2/BDeb1\n9Onz9P3qZRQ5ZhZ+7Qu33SAnth4gY3Mgf99DiM2G29wFXnrpN3zuc/9IKpXiHzY8xlOrNlD+1P9H\n3XtHyXWeZ56/yjl0V1fnnJFzzmjkQDCKpJK1tmzJcWyN1/bsztnx2jOyPbYseWzZWmlsybZWpEgR\nJEAQOefYQAOdc6iuqq6unNO9d/8ooAmwuwGQ9pnDfc6pU3VO3br3Vt2v7vt97/u8z/Myk9dvE/ru\nP5HyBfAXW8jqdai2rcUfDDDSdp/Cygpe/tbvof1Eq40kSYy2PWDgxk2UajVNG9ejetg+9+ghZrMI\nmQxCVkDMZslmMkQDATqvXiMaDqPW6TCYTBSVliFJIpl0Gq3FjN5mywXMRAJHbx/D7R3EY1FQKkCj\n5tHdWojGIJkmz27npd/9Haqbm6d9b0kQcHxwnOTE5BPmCtFJL60//Bd+cuIDjnTfQy2X83sNK/n6\novWoaytQ1Fc9DMDFyArypl3DpwXjuNtD309/QczjwVBTRcnq5RQumW4C1NfTzZWL52meO4/V6zbM\nuK/nQTaZouu9Q6QiUZxGNROJCPPnL2TNU+rPw8NDnDlzEqVSyZ49+zFrdfR9eIJkOEzhvDlUrF/9\n7xKQ/5eIfnxaaK2WP05mpU/9BeOTPsbOXMIx4cJj0VBYUsq2XXumHIoeR8IfoP/oKQKDQ2jMJup3\ntlC5cS0Zg5b73R0kZVAxfx6bXnyRvEL7jOeSHHYQPHcVKZMl5XCR6B1ErtWgtE0fkM+C0WwmlUzi\nGh1FyAoUV+RSmhqzKacINe6icfUqvAE/TscYZrOF8orSGQUaFBoNSr2W4MAwKX8Aa2PdE+cjk8kw\nVpYTd3mIjjqQsgLGipnrUjKZDHmJHbFzAM1kgAP/+7eoqKnl1KkTXLp7i4BGTq2kwpCVWPzGq7iE\nJNn+YcL3OjDV15BMpwmMjFHY3EjFwvkUVFU+s3Ym12pQPBQ7yLg8aJpqUSkVuUmEw0Eo4KfMXkz0\n3DViN+4hZbLoF83BvHMjKrtt2m//SSGLcCDA5Q8/JBIIUFFfz8pt25DJZZw7fox0KsWGbTuIXmvF\n392HLx5BOb+R1b/9a+QvmIOhqhyt3YbSaECmkJPJZPje9/6K3/iNrzMx4eYrX/kaP/jKb5HX68AR\n8hNJJajZtgn9Y85QwrW7CJ39yEsL8RrUxBzj5M1tomDpQuKxGEc//ICAz0dlVTXbdu+l4LFMSToa\nY+ijk0iiSO2e7agtJtytbYxeugoyGUUFdgyRJGoJFAolgtVIym4hJgmEXG7S0RgKtQqV4WMBkInx\ncS4dP0YqmSSdSjDQ2YUl34bJYiHmmWT87n0m3S7SQhZFMotJr8dmKyBvXhOaGX7vqbGjkKM2GTEW\nF5FfV0PRovkUNDdiLC5CY8qlrBOBADHPJL7efgIDQyCBxjpz5kuhVFJaVk5dQyPpdBqXc5zB/j6i\nkQilZeWYK8sJ9vQTGR5DodNgra+dflJqFWL/CEgSiuqZSx4LFixkw4ZNnDx5nLNtt3B5Jlg7dyHF\na1egKMgje7sdfSpDWBIYTkRIySTi8TihCQ9hzyQ1Cxc8kYWTyWRYi4vQmk1MDg7jGRzE3dePZ2CQ\nyaFhfCM5CdyA00XI7Sbs8RD1+khFYzj7B/A/tKasmTMHU4GNqkULmb91Cw2rclrZ8USC9uvXcQwM\nkEUiv7YGa0UZ9tJSKusbsJmtqASRqoYGXv+D36eoYuZGFu+VW0QHRzA11GJbvQwyWeKXb3P6j/+c\nP/rgn7kzMUazycaP93yVF375l9G9uhNVy1qUc+qQl9iRGWa2JX2akMzQB0eZvNOGvrKcyu2bsS+c\n3p8tiiIXzp4im8mwuWUH6k/ZUfEIkiQxeOossUkvfq2S8VSUiopKNm3a8tT7tdWal7PdHOhnaGiQ\n6rp6yhbMJTLmJDg6RioYxlJd8W+2aPxcalMDf/xpVYBS4Qijp84z1NOF16qjoLKS7bv3Trtwkigy\n0dbO0KnzpOMxCpobqd/Zgspo5N71a7RdvwbA0nXrWbhqFcpZ0mjZUATfkTMAFOzfhlynIz3uJjEw\nQnJ4DIXZhPJTyOEB2EtLcQwO4h4bw15SgsFkQiaTodRq8A8MIWayNK9dQ19vN07HGAsWzieTnXlf\nWls+iUkvkTEnSq0W/SeMtWVyOabqSiLDo0SHR1HodOhmqXnLtBpQKhAHxyCeZNGBfezff4Bbt25y\n4/5dbk+OU5aVoRckVn31i6TzTMTbe0j0DBLXqckKWRK+AAWN9c8csJIk5RSRZBJxpxt/6wM6j53g\n3pUrhIdGiTpdxO92kL7ail4ETVkx1j1b0DbXIZulVPD4zcDjcHD12DFSiQRzli1jwdq1yBUK7ly7\nxkh3N3aLldit+zguXiWSSSHVVrBg21asRdPJUV1dnXzpS1/g4MF3KSoq5n/+z3/m69v2kTp7jXA0\nips0UjqDQpDQ5eehsZgR2nvJXruHPM9CYm4Nk3fvo7XlU75zC1lB4NSxjwgFgyxeupzV6zc8MZGU\nRJHhY6dJBUKUrVuFsaKU4bMX8bR3oTYaqVq0gGz3IAqTkbyNqyl5ZTclW9ZjqsithhP+ABGXG293\nH76efjKxGG6Xk5tXcsS11VtbqG2spb+7j6Fbt5m43Ua4d4BkMIS+wEZly0aqXt2H2qAn5XCT6Bsi\n4/GhLix4pjLUIyjU6txKvbyMguYGihbNx1JRjiSKxNwegqNjTHZ0k47GUBsNqGZgvqvVGiqra6is\nqsHn9TI+NsrYyAilZWXk1VTjuX6bhMuDtbl+Wn+2zGpC7BlCcnlRzG9ANkuJpqysnJdeeoWrVy5z\n+f4dLl++yI7de7EvXoBcklAFQhiMZgqCceQePyRSBPw+nINDRMMh6hYvmrYyN9ls5JeVotbrsVWU\nYauqxF5bQ1FdLUUN9ZQ2NVI6p5nyeXOpXLiAlCQQy6QR5DKa1qxmyy99mdI5TWTlMkaGh7h38ybn\nDx3i7umzBL2T6POsLN66hSXr1rF60xZWbNiIWW8gOOagqKKCLV/+IsZZ7EIjvYN4r99Bk59Hcct6\n0jfb6PrrH/Bfvv8dvtN5lZiQ5Ztrt/P3P/oxVb/8BsqmGmRm43MFoNmCsaf1PgPvHkZlMtL81S+Q\n/wk71kcY6Oulv7eHxua51NbPvM3zwH33PpOdPURlEgNigvx8G7t27Z3KQj4N+fn5GI1GBgb6GR4e\npLq2jtIFc4m6PYTGHMS9PvKqqz6VDvYn8b9EgeszQHqW8EAmHic67iI67ibqcJKORHCMjeIlS97y\nxezcs2+abVsqHGH43CUiLjcqvY7ydavBqMfvnWSwq5uQ34clP5/VW1ueaDP6JMRMFu/7x8h4A1i3\nrMUwpx6AbCRG5OZd4r1DIEloKkqwrFmGquD5akQAvokJzh4+hN5oZMcrr6JSq5GknE50MhBi/puv\nMOQY5fqVSxSX2JEk5ZOzuscneOkMiet3QZTQrVqMXP/xzLWsvII58xeQiUQZOvgRQjJJxa4WTNUz\nz5olUSTzXs6OUbVvM4rqctLpNN/+9p/w93//P1DI5bzWvJSv/vpvsPS1l4kNjeL4h38hEgjgMKpQ\nWc3Ma9lC7cZ1SKJIKhrNsdFDEZKRCKlwOPc6HEHIpBEFkcD4OMoH/SjTWRJVRYhyGWqHh0woQgaJ\nTEURpvlN2GprKG6op7ipAe0MtaRHabKhri7aLl9GJpOxZNMmrHn5+J1Ohjo6uHPpEmqVinpbMfIR\nJxpbHoV7Wiiur8NW/mSvezab5fvf/xv+8i//jHQ6zRtvfIk//dM/w6zWEvt/D+O908ZkkZWUVY/Z\nVgCJJJIgUlhcTN64D5lOg7RtDcOnzoNMRu2r+1GajJw/fZLRkWHqG5tYt3HztNm66/ptPHcfYK2r\npmj1MgZPnCPu82EqKaZm6wb8H5xCiMYo/MI+VDPoGIuCQMThJDA4TGBwhMlxB+6xMRR6HQt3bKN2\nxXI0YpLWI2cYuHuPdDpNXnUla15/jYK6mifOJ+MPErx0k/DAMNF4jEihBdua5TTNm/+ZW9IyiQTe\nrt6caX00V7c2lRRjn9eMtaZqWnDLZrM4h4dwOMfp7elGqVKxdsMmoicu4m/roHj5EmpfPzDNRCV7\nt5PslVaUa5agXDa9Tvg4EokEv/m1L3Pk3CmKbAX861u/YNGiJeSTxX2jnURbN/FRB0GXG8+Em7Gg\nH79CRNdcx8Zf+goNK1d8qramR+hra6P9xg10RiNqlYqRrm7yKitIyyQy6TTZVAr/yBgyQcRqs7Gs\nZStzV6x4gqDqGR7h7qnTKFUqVu7fi8k2s0Z+yhfA8d5HIAgU1dYQOH2FH58/yj+NdRAXszTaS/jv\nf/xt1rz68mdKx86Upg72D9Lxg5+QDoaZ+6tfpWjV0hk/K4oi77/7NrFolJdffxOj8bM5WIUdTvo+\nOkEsk6ZXmUVrNHDgwMtPOOM9D7q6Orh8+SJ6vYF9+w5g0hsYOHmW0JgDY3ER9bu3fWb5zM9lzZgZ\ngrGQShF1TRB1OImOu0j6gx+flErBeCjARCKKtamOXfsPPNECIUkSno4u+s9cIBYOg1GPUFRANB6d\nYp8C1M2Zy6I1a546U5IkieDZq8R7BjDMbcC6ec20bTJeP6Frd3L9mzIZ+sYaTCuXoDQ9HxX/wc2b\ndN27S+2cOSzfsBEAX+8AQ2cvYJ/bTOWGNVw6f5YJ1yixWOrpO/OFkA+MIhn1SHNq4bE/0/yFi1m2\nchUJzySjh0+ATEbVC7tmXSGLviCZd46CTI5yxzoUtbnAfe7cGX7zN34Vr8/L/MIy/o//9H+x9Y0v\nIDo9eH56EPfICH1igqxGhbW4GDGdmyXLZXJkcjkyuQyZTI5SpUJtMpDOZAi43YgyGUadjtqkDJPN\nhpBMkU0k8Msl7oz0kI7EMJssUzcgmVyOoSCf/MoK7HU1lDQ3Yy0rwV5o5tyR03TdvImQSlFeXUMm\nniCdTCKKIn2dHYhyOSuXrcAw7kWfn0f16wdQW6fXFHt7e/jt3/4Gd++2UlRUzHe+8zfs2LGbbCpF\n8BfHCF28jiseIVlRhKGyLOfvKpMzfugYqqv3Uek0WL75RSY6e0j5A5Rv34y5voab16/S+eA+xaWl\nbN+1dxrrPzwyxtDR02gsJorXrWL43CWyyST2uc1UrFtF5GYb0bvtGJfMx7Jm5hvbI0iSxP3r1+i8\neBlFLEl5QeFUBsmg1xBPZDBVlTMeCTIZ8KEzGFjdsg17cTHxeByncxyn04Fz3EF6xIl5dAJFKoOo\nkKOqLmf+vl1ULpxe93tePGqX8nR051r7yJG/7HOaKJjThNpoIJvNcvn4cTzOcaoaGigoL+falUtk\nMxmqFFr0Yx5UShXWpnqqX9z9RNpbSqVJ/+R90KhQf+XArBmVRxDSGf7bN77B94++h0at4bvf+zu+\n+c1fYXIygiRJiBNesn0jJO53MXj9Jp6BITKiQNyiR9FQTfXGtdSuXIGlrASprQcpFEG5cQWyGYJ0\nKpmk7fo1bp87RyaTJb+iDFEQcbZ3olCpqF+9Eq1cTtg1gU6rpaS2loUb1qP9RJuP3+nkztHjIJOx\nfO8e8opnFk8R02nG3j6E1DmAJp7iVNstvjtwh/F0jDydgT/8jd/lq9/61qwZwufBJ4Oxr7OHkWNn\nCHf3Ubx2JU1fe2PWIN/f28PlC+domjOXNes3PvcxhVgcMZFCzGZIB8MMHD1FPBRiTEojqZSsXrYS\nq8mMlMkiZTKI6QxSNvcamRxtZSm6uqoZS47t7fe5du0KRqORffsOYNAbGD57Cf/AIHqbjcYXdn2m\ngPy5DcYTzgCxCU9u9etwkpj08ejYcqUSQ0khxrIS5HkWrrXexuebxF5YyNYdu1GrVAT9fgLeSbwO\nB85rt4mOu0AuR1VVijzfilKlIq+g4OHDjq2wENMzhBwAYh09BC/cQGW3YX9p16z9lwDJMSfhq3fI\n+ALIFAoMC+dgWjofuebpNQ8hm+X0Bx8Q8vvYsGs3JZWVSKJIx9sHSUdjzP/Sa6gN+icG+ePX5dHr\nR8+jp84TGhimeNVSChbPJ5lIcup4Lh06d/4CVqxeS2R4FMeJcyh1Wqpf2jurJaMwPE72xGXIZlGs\nX4ZyUY4IMjk5ya9/7UtcvHUdk0bH7//W7/KN3/8DxBEnsY/O4RodoTXgJq1WIteoUWg0yDXq3EOl\nRCVXQjJFbMyBEIqiksBsNmMyGNEGougmAmT0GuJldjIFFiJihgGnAzQqykvKyIQiJP1B0uFwzllK\nlgvwKp0WlVGH3+dHrlZTUleH1mLCkJ+PrbwM54SbCa+HeXPmkTc8gZjOULp/B/qyJw0+BEHgH/7h\n7/iLv/ivpFIpXnn5Nf7wN38PWSxB2Oki3TOIvnuYcDCEv9hKxfYtFM5vprC5ESkaJ/Xzo0S6+5m0\naAnFIig1Gsq2rKd001q6Ozu4fuUSFmseex8jpz1COhyh770PEbNZ8uY2MdHeBUDl+tXY5zWT8QXw\nvHMEhdFA4Rv7n9o/LggCdy5dYri3B5PFwobde9Dr9IRGHQRHRrGX2lCXV6M25gwuHty+xfXz54lG\nwrneYd3HPd8ajZaSklJKi4oweUKMXb6O3zGes8GsKKN51zYKly5E/hlrfADJYIjJjm58PX1k02lk\nMjnmijJGg168kRBKpRJBEFixaRN5hYWcO32KkNOFcchJZVEpRqsF65xGSjc/SdDJXLiF8KAHeWUp\nqh3rcqWYpyDU2csHP/wR//f7/0o0Eedb3/oWX//6b1FQ8PHEVRJFMqNO7v7r2wSvt6JIpJBJEqJe\nRzbPTKneRKHJgtZqQd1Ui/al7cTjcSYnJpiccDPpdjM+OMjE0BByhYLy5iYKy8qwFxUTcU8QdLqw\n2u3EQ2FUGjVz16yhvLFhWrAITU5y68hRREFgyc7t2GepEYvRON5//DmJK7fp97j47lgbNyOTKOVy\nvvbyG/zBf/s2ZqMZ59lLaO02bEsW/JtXxp67D3Ie1CMO9AU26l4/gHEWHXVRFPngFz8nEg7zyutf\nfIL1PRvEdJrghRsk+oaAhx4Fvf0kwxECQpqMWklVdS22mbIEMllugpQVphZpSosJbU0lutpKVI+p\nGd67d5dbt65jNpvZt+8Aep2e0UvXmOzqwVRaQsMz/Otn+Z0+f8G4450PpYn+UcRsrr9YJpehL7Rj\nLC/BWFqCvsiOXKlk0uPh3KnjxONxqmtqMWq0+D0eQoEAkigiBEJkhh3IRAljaTHl61dTUFFOfoEd\nk9X6qdNp6Qkv3vePI1Mpsb+2D6X52fR6SRRJ9A0RvnEPIRpDrlFjWr4Qw7ympwbyoM/H6Q/eR6PV\nsuOVV9FotXi7ehi+cIWihfOpWLvyuUX9s4kkve98QDaVouGV/ehs+STicU4cPUIw4J8yzPA/6GTi\nyk00+XlUv7h7Sgv2kxAn/WQ+PIcUT6Bc1Ixi3VJkcjmiKPKd3/09vvvOP5MVRQ7s2M33fvCPqB0e\nUmeuklXIyVaVIEVjiLEEYjSOFE8gxhOEPJNEfLkJl0avw1RQgEKpRJQkRJkMURRzs1ghiyiKiKJI\nOBggHAyBToOpvBRBpyGtUhBLJYlGI0RjUdKRGLJsBqVaS3FVJQarFZ3JhMagJ41IX38/xvw85qrN\nyAWRkp1byFvwJNN0YKCP3/6tb3L7zi3yrXn83ptfY1Fp9VTbjkKUKBhwIUZjBMtsVOzZTtHyxbnr\nn86QOXgS0RtAuXYJ7lCA/rffR65WUbZ9M4rGai5cvoBGo2XvgZemKU2JgsDAoWPE3BMozWaS0QhK\nrZa6HVswlZbkWvA+OEHa5cG2ZyvaWUhJkJPSvH7mNK7RUfILC1m/cxda3ZM1WatVS0dHP+PjDpzO\ncbzeSeLhMBNDw4iCQHlNLau3bKGyqhqbreCJ/5Akirjvt9P+0QkSQ2M5273iYmo3rMO6eB6qwtnJ\nXs+CkMkQ6B/Efb+Tvtu3CQeDWEtLWLRrB609HUiiyLaXXkZvNHL10gWGDx1Dmc7StGAhepmcks3r\nyJvzsVCDlEqTOXEZcdSJzGJEtXsT8oLZzQAkUWT0ncP09RCsqN4AACAASURBVPbwn4+8xeDwIEql\nku3bd/Hmm1+mpWX7VH0/Ggxy5p13ibZ1UizI0frC5PkiKBNpknoNYr4ZIZ1mUqPAUWRCptci12rI\nIhEJBjEW2Nh04AC1zXOmNA1SiQRn3/o5QiZDYWUFCzasRzdDSSYaDHLz0IdkUikWtWyluG46iU2K\nxRFaO/EfPIbzdhs/mujmveAYIhKbV6zhv/7139DYlPsPeFvv47lxBwBLQx2lW9Y9M5PwSTzqXXff\nuIPn7oNcci6exFRdQfVLe2cdEwN9vVw6f5aGpmbWbdz8zONkvH78Jy6SDYVRFeSjLrbj7RsgMDSC\nKxkllGeged58FixZikylQq5WIVMpkalUuYdSgUwmQ8xkSI2MkxgcJTniQHpIzFEY9GhrK9HVVaEu\nttN69w6trbexWvPYu/cFdFotg6fOERgaIb+ulpptn8774HMZjK995weSqDNiLCvBWFaKoaRwmsTk\nQF8vVy9dQBRFlq9aTTwQZKi7G4VCgdliRe7xIQXC6I0majevp2jhvH8T/VxIJJl89yOEWBzbvha0\ns7CPZ4OYyRJ70E2k9UGO1GM2Yl61BF199RPnJUkSCCJSNkt3ayudt29TXlXF0tVrEdJpet8/QjaZ\nonH3dsoW1BLm+VYdj9KcuoJ86l/eh1yhIJFIcPLohwT8/injjIkrN/E/6MRQVkLl3u2z/vGkcDRn\n9ecPoairQLltHTKVEiGR5PR3/5b/9OPv4wj5qamo5Mf/+jYNWRWpS7ee3IlaRTSZwOVwkBSzKM0m\nKpcvIb+2GrnRgEyvRabXgVo1pcYmhWOIoTBiMIwYjNB99Srh0XEKTVaKS0tzgVoQcsFbFBE0atQF\nZtRrlpORy0gEQySDIaJeH/euXCadSFCjNKBMZVCU2FFVlKAxmdBZLahMRt47cYS//ckPSWcybFiw\nhF/b8zIWowmDvQBLaQnmslLUnQNkOvpwhgJQX0nTmy8jVyqRRJHsRxcQRsZRzGtAWNjI8MEjiNks\nyoJ8/GMO+gf7karL2PWVr2CfQYfZeeUGE61tJKMxlFYzhoIC6nZtRfNwlRDr7CN4/hq62iryd22a\n9vlHSCWTXDp+DL/HQ3FFBWu2bX+CHBaPx7ly5RJ+v5twOA7krA8LC4soLS0j35rHYEcHPrcbo8XC\nmpZt5BXMbkgy1NXNgyPHkIbHUQsiJSWllDQ3YZzfhL6h5pnZoZkgiiLXz5xh9MEDDBmJEks+IKEq\nK6bHNYYlP5+WF19CoVBw59CHdB09iVhip1Kpo8heRM1Le9A9RmSURBHhxn2yd9qRqVQot65G0VA1\n6/GjQ6O4jp2FEju3EhP86Ef/SEfHAwDs9kJee+0N3nzzyzQ1NTPa18et06cRBkZpDqQxSnJSWhW+\ncAhZMoVMEFFKElGbmVhlEXKZDI/DgQwZlY2NWEuK0edZ0VotU8+JZBIRieLq6hnvZYlolBsfHCYZ\nizFv4wYq5nzCwSoSI9vaSfLaHXy32vh51x1+GBggImSoLa/kT//8L9m+Y/fU9tlEkv6fvYdMLkdt\nMZGYmERfWkzFzq0oPoWaWEGBkXsHT+Lr6EFjMaE1GIk7XVTs3jYrR0UURQ699w7hUIiXv/DmUw1Z\nJEki1tFL+MptJEHAuHge5lWLCQ6PMXDyLOM+DxMFRmrrG2hp2fGp4oD4sFMmOThCcsjxcYlNp0Vb\nXU5vNMB95wh5Nhv79r2AWqGi96MTRN0TFC+aT/malc84wsf4XAbjTDwhBWMz04RFUeTu7Zs8aLuH\nSqVi09ZtGPQGTr9/EJPVyqoVqxm7fJ10NIrBXkDN1o1o856dfn4aJFHEd+QMKYcL88rFmJYvfPaH\nZoGQSBK984BYew+SKKIw6ABZrl4hCEiCCI+lmYd7eojHopTX1GLJzyc2MZkjC5QUUdJUi3r1CvSN\nNU8/6EOMnb+Cv6uXomWLKF6ZqysmkwlOHv0Iv89LfWMTa9ZtwHnqApHhUSyN9ZRund1iUkqlyRy7\niOhwIy8uQLVnMzK9loTTTd87h/iLgz/lVHcbGrWaP/mTP+Mr+19BlhWQ6bXEM2m6b97EO+ZArlBQ\ns2ghtUsWTxNmkSSJpNtD3OHKtRV9op6dyWQ4efgDQl4vKxcto7qwZCpQSw+Dtg6RBHJ0B7Yhz8+N\nhesXL9Df1UltRoEpmkKRb0HbXIfX6ebyzStcu3+X1r4uIok4Zr2B//Dm/8b+/QewlJVgKi6aqgll\nhx0kPzpP0OfDV2Cictsm8hrrcqzwi7enUqGKnesYfv8oqUCQ8h1bkBfaOPLDH5LqHaK6qoa69Wso\nWbPiCRGM0OAwfe9/RHjchamuCltjPVWb109tI8QTeN46hCSKFL15YFYd6VgkwsWjHxEJhahqaGT5\nxo1P1KTdbhdnzpwiHo9RWVmK1WqnpKSMkpLSJwK2KIp03L5N1727KBQKlq5fT03T9H7VRxAEgY6O\nB3Scu4DS4cGSyFBWWo7VZkPfUINhbsMTqb+nQZIkbp4/z0hfLwXFxWzYvQcpnaHrvUMI6QxCTRlD\nA/3UNDezYuMm0tEYd/6fnzDidZPKN5PvClC3YD6Nb7w8TXVO6B8le+YaUiaDcuk8FKsXzcgSliSJ\n8fePkXB7WPT114irDTx40MZbb/2U9957h0AgAMDSpct4440v05xRoL56DymVIdFUhW3beuxFxSTf\nPYZep0dvNkM8QaSmhEt97WSicRrmzkWjVJEMBknHp8v9KtVqVDodKr0uV4LR6VDpdEhyGV1Xr5NK\nJmhcvYq6lctRqB5OYrMC2autpG7cI/qgm4v9nfzVWBvDqSgmnZ7f/8P/k1/5+jemdZ64r9zAf7+T\nonUryZvTxPiZi0SGRtDkWanYs23WUtbjEAWB8J07jNzpQFeQT+nalYx+eAKtLZ+aV2c2jgG4d+c2\n91pvU9/YxPpN0wVapvafShM8f43EwAhyrYa8lnVoq8pJBkN0vfchbreTMZOKwsoK9u07MKPexPNC\nEgRS426Sg6MkhsYQE0kkScIx4WI0HUNXV8X2L34RpUJB9wdHSAZDVKxdRdHCp5MEH+FzGYyZhU2d\nTqe5eO4MjtERzBYLLTt2YbZYOffhYSadTuaUVpJ2TiCTySlZtojiJQv/TcpYjxC+3kqktR1tVTn5\ne57ek/a8yIYihG/eI+10g0KRqy8oFLlUiVKBTKlEplCQTKW4e+M6CpWSZVu2oNZoGTx7AUkQqK2q\nIBZNYt24CsO8Z2ulCuk0ve8cIhOLUf/i3ql2p1QyyanjR/FOeqipq2fduo2MHTlJwjOJffli7CuW\nzLpPSRDInruR8+C1mFDt34LcasZ/uw3vjVaO3L3G3546RCyRYPeuvXzjG7+JEAwRG3eBJFFQXs6c\ndWsxfIIslY0niPQMEO7uIx3IyS3KZDLMzfXkr1qK8rGWl0g4zIlD75NJp2nZu4/C4ielDg1jY0wc\nvoBMp0X7QgvueITzJ46RF0tTrzLilYs8IMHJ0ye5du0y2WxuIlhcVEzLxi38wR/9Z0pmqLtJyRTx\nt4+Q9gZwyDJoKkqp27weyTWJ5HAjDDmQF+ShenkHzsvXCfX0k79gLrZVSzn24Qf4fT4WNs/D6PKR\n8AXQWM1UtmxCX1hAKhTm/g//Gf/AEJamOqo2rqN46aInxl7gzGXiPYNY1i3HuGhm/9yA18ul48dI\nxuM0L17MghUrp/YhSRIdHQ+4ceNazj1sxSpaWjZMKXDNBufICDfOnyOTSlHd2MTS9eufSnpMJhO0\ntt6mp+0+Go+f4qREha0QvV6PypaHfk49+jkNswrmSJLE7UsXGeruxlZYxMY9e6bSt96ePobPXSKv\ntprBaICA18uqrS1U1dczcPg4wZFRvOUFeNra0XoCzFm7hnlffHVasBV9QTJHLyCFIk+tIydcEzje\nP0ZhYxXmlo8Z76lUihMnjvLWWz/l3LkziKKIRqlifVktS5avYM6ChagUCmqXLKZGbyF14SZyez4Z\nX4DR1ntMVBXS/ObLlNd9bA6STaZIBIMkAkESwRCJQJBUNEomkSSbTE5xQsSswHhvH6l4nLzioin2\nv1yhQK1QYe0bQzPmYdjl4K+c7Vz2OZABX9hzgP/yV999ou79COlwhIG33kdp1FP3+otTmZ6Ja7fx\n3+9AqddRsXvbrGRPgFQojPPyDQSfB0xWqndvY+LyDUJ9A5Tv2oq5ZuYsRGf7A25eu4LRaGLPCy/O\nqkGd9njxn7hIJhhCabehW7UIX8CHZ8yB+/Y9kqEwLp0cU00VX3jji1is/36exJIoknZ5SAyMkBgc\nZairC593ErVej6qiBKkgj+jwGDJJomD1Mqw1lajVGtRqNWq1GpVKjUaTe1arc45b/78JxuFQiDMn\njxMK5uQtN23dhkarZaS/n+tnTmOOZ7DrDGitFmq2bvrMOtGfRGJwFP/x8yjMRgpf2/eZ0mv/VvR1\ntHP3yhVKKqtYv3MnE/ce4Lhxm8blC4ndH0BMpjCvXYZp8bNnYNFxFwOHj6NQq7Avmk/Bwrko1GrS\nqRSnThxlcmKC6to61qxcw+jh42TCEUq3rMfaPHt/nyRJCDfvk731AJlWg2rPJmTFBYx/eJK4w0V/\nMsKf//zHdA8PTH1GIZdTXFxCdU0tFRWVlJdXUFFegV2jxxLPYAonUMhkyJUKDDVV6CtKCd7rIOUP\noNCoyV+xBMv8pqmbqts5ztmjH6HRatn14ksYHmuBsNtNOM/dIXXhJoJCznkpTGdvN66BQW4M9TI0\n4ZzadtGiJezYsYudO3ezYMGip068Eicvk7nTTigYJJVKUlRejuqxdjp5ngXVC1sJjTtxnruM1l5A\n5Qu7OH/uNI7RERqb57Bm/UYkQcB9s5XJtg5kchlFy5cwcvYivu4+LA21zH39JayfuHGlHC68h0+h\nKsjH/uqeGVdyHqeTKydPkEmnWbxmLY0LPmY4ZzIZLl06z8BAPzqdjq1bt1NaWvbcPIRoOMy1M6cJ\nTE5iybexdtu2ZxIgAwE/N25cY2x0BE0kTq3CQLlCi0qhQFNegm3P1mk8CkmSuHv1Cv0dHeQVFLBp\n774nCG6SJNH13mHiXh8VLZu4euViTtz/pZfJuD2MnbtC8cqluGVZ2t/6BfJghPqWzSx//ZXpZhKp\nNNmTVxBGxp9aR3YePQOTHuSlZRRuWYf8sXOWUmlG3z7Eu8cO8e6DmwxNugHIt+SxecVq1i5Ywuqt\nWylzhUgNjtIZ8aEbn6SsrBz7l19CUVc5428npNJMXrhG2h9EY8/JryrMRiS1mrbTZ/E7x7GXlVHe\n1EQ2kSSTSCBOeNHc6aK3r5ePXP0c9AyQFUWWVNXxR7/ym2z+xq/MOr4dp84T7h+aUoV7HP77nbiv\n3kSuVFK2fTOmx2wFJUkiMjqOr72L8KgDgNK5teSvXUs2Fmfg7ffR5OdR+9oLMx77EXtaq9ayorYR\ntUKJkE4jZjK5DEg6TTadJjviROgfIZVIEDdqCWrkxOPxqQlKJpPBlYoSzzPR2NiE0WDEaLFgtdnI\nKyjAasuRdz/Z/vpZIEkSKbeHOx8eZbKtHUUyl8oWBYF0JEpSpyK5sBHyZk61y2Qy1Go1//E//ofP\nfzB2Occ5f/okqVSKufMXsHzVGuTynPrR8Xd+TmxwhEpzPnkV5TTu3/VcerfPg2wwjOcXH4EgYn9l\n96fqF/73hCRJXDz6ERPj46zYtInKmloe/PQdtGo5aosN1fgkclHEtHwhphVPDyAAvq5e3Ndvk02m\nUGo1FC5ZiG1eM1lJ5MyJY0y4XVRW17B6yQrGDh9HTGeo2LcdY/nT6+RC5wDZ8zdALkPZshap1M7o\nzw8jpNOEiqwcPnUUh2eCBCLBaATHuIOJCfeM+5LLZBQVFFJRXU1lVTXl5RVYLRYU4Tgy9yQ6uRKr\n3U75ulUU1tdiMpkYGxri3s0b5BfY2b5//1RKym43MTTk5PRPf8bBf/4JV8f6CadzLWEajYaNGzez\nc+cetm/fSUnJ7N9RSqURXZNIrkmyD3rIXLtLVhIJSwIaq4X8FYuRldiRlxYiLykEi5GUP8jwwSPI\n5HJqXnuBu+1tdHW05/TSd+5+Il0cHnXQf+hoTjktEsVcWc7S3/5VdPlPBgQpK+B550OyoQj2l3ej\nLpo+8RwbHOTGubMArNy8mcq6+qn3gsEAp0+fIBAIUFRUTEvLdgwPNbefNxhDjvV/7/p1Bjo7UKnV\nLFy5iuqmpmeasTgcY1y/fpVAwI8aGQuyauyCHH19Nfk7Nj7hcXv/xg167rdhybexed907QD42FDF\nWFiIdkETN8+dxWqzsXnXHnp+9gvUJhONr7+Ic2SYK3/zA7KRKEVb17Ph1VemiwJJEsKNNrK325Ep\nlShb1kyrIwuJJJHLV5nsG0VXVkzJzi0otBrEYJjsRxcQAyEUVWUotq/lVttdfvSDv+PEqROkHo65\nxqoaltQ1syouo85SQPEX9lHqDIEooNq/FXn5k0z+dCiM6+gZ0oEQMoViyjhHkiTcg4PEUymstVU0\ntmxGV2RHY8un69RZDv7TP3K47SYjIR8AZUUl/PrGXWxfs5HK1/ajmGVhkZiYZOjgEbT2Ampe2Tfj\n/SQ8NILz9EVEQaBkw2rM9TU5xbqOblKh3PgxlBRSMH8O9asW4PXFGD97iVBPP+U7tmCuq562z9Hh\nIc6dPolarWZJaTXR/qFp22QTSdK9wwhePwkhS9huIWvSIVMqMOblkV9UjMmWz/2+LsIyWLJ4CXqt\nlqDPR9DnI5N6shVUZzA8EZytNhv6T2lo8jhEUSTh9hDtGyTeP0J0eJTQ4AiSTIZh6XyoLkMosJJW\nQDqdIZ1OkcmkSaXSfPObv/L5DsbdnR3cuJoTalizfiMNj9WoHty6RfvZc1iiacobG2h+ef+sOrlP\nQzabxekcp6ioGM3DWbeYyeA9eJyML0Beyzr0TXXP2MungyAIjI6OEAoF0ev1GI0mDAYjBoNhxpRf\nLBrl5C/eBWDHK68iBMP42+7hdUxAKoPOG8RgsZC3ehnmdcufOZiEdBrvgy4m29oRUmlUBj2FSxdi\nqq/h3JmTuJ1OyiurWDV3IY6jZ5ApFVS/uAftDGISj0McdZE5fhEpnUG5dgnJfDOuo2dQWcwol87F\nWlKM0WpFzArEhkaZvN/BcGcn7mCAiWgIv1zEk4rj8npwOMZwOscRH+sFfxa0Wh0alQqT2UxhUTEm\nkwmZTOLq1aukH5Iv8lVaNhRXs/uXvsqur/8yev3MY0YSBMShccTxCSSXB8kXfEiwE8i094FSgS/f\nQEKvofqXXkf3iZuomMkw+IsPSQdDlO/ciiMe5ua1K1jz8tmz/8DUCk+SJMJj47ju3CPscBIbG0dn\ntbL0d34N9Qx14Mjt+4Rv3sMwvwnrxlXT3u/raOfe1as5EYzt2yl6zIlmaGiACxfOkclkmD9/IStX\nrn4ieH6aYPwII/393Ll0kWwmg8FsZu7SpVTVNzy1W0EURXp6urhz5xaJWIyyUR+15nyKVizFumUN\nMpmM9tu36Wy9g8lqZfO+/ehmuU4Ag6fO4R8YombrJgbdDoa6u6mfNw9bNE1wcISGV19Ab7fhHx3j\n0vf+gVgsinrVIgprqtHrDegNBgwGQ85r22BA7fQinL3+sI48F8XqxU9kH2xWHe0/P0Z0YBh1vpXi\nRQvgSitSMoVyyVwUaxY/Mam4fOwYHx5+n9buDh50dUyt4GxqHatrGmnZt5+dynzMJhOql7Yjt+cm\n/gmnG9fx8wjJJNZF8yhYvZRMOErSM0n36XP4+wbRq9QUV1cz5pvkTHsrZ+/dZDDoBUCnVLGtYT4v\nfuWr1GeVqOUKyl/Zg8Y288JCkiRGDh8n7nRTtX8XhvKSGbcDiE94GPrFESKjDtCo0RYXolApsdbX\nYps/B7091z5kt5sYH3Ay8LODqK1mamewuHSOOzh9/ChyhYKtm1pwn7mIUq2mqmUjgUAA76QHb98A\n0t0uFOkMWbMBaWEThXU1FJeVU1hWhkarJZvNcvToh0xMuFmxYhWLF3/ccy9JErFIhIDX+zA4ewl4\nvSTj8SfORavXU1FbS1VDI3kFz8dpmA3ZYJiJ81dxnrmIIiNQ0NyAQq1GXWzPMbNrKlBapgxtPp/B\n2O0OcvPaFXq6OtFqdWzZvoOix2qB0XCYYz/5CZm+YRoXLGTuy/vRz2Cb9jy4cuUinZ0dKJVKqqpq\naGhoRN81TLJ/eNYb3meF3++nr6+bvr5eEjP4MQPodDqMRiMGg+nhswGj0UjA46HrTiulFZVs2b8f\nu91E3412XHfbiDnd0DOEVq2hYONqig/sfC6pumwyxWRbO977nYjZLGqTkYLF82gdHsDlGqesvIJl\n1Q24z19BZTRS/fJeVM+Y8IjeAJkj55CicRQLGgkpJAL3uzA31WFdPI9wVz+R3gGEZG6Wqispwjyn\nAWNd1bQe2Uwmg8vlZHzcQSgUIhIJE4lEiETCBNwTTPYNEA4GiKVTpFQKEpKAZ8JNLBYnlU5N1X/n\nNM+luqyChUojuyqbKZKr0FWUot23FUXpdKlLSZJyKcu+YYBcHb/QhrykkMyok+zEJMliG07fJLa5\njZRvWjft884zlwj1DZC/cC7p8iLOnTqBVqtj74svYTSakCSJ0MgYrjv3iE3mbp7WqkpKli3CUGj/\n5CnlrlcojOftD5Fr1RS+ceCJsokgCLTfvkVPWxtavZ4Nu3ZPMZ4FQeDWrRs8eNCGSqViw4bN1D22\nWn6EzxKMARKxGN1t9xjo6kIUBEwWC3OXLaeitvapQTmVStHaepvOtnvkdY1QrDXSsGsbHrOGjju3\nMVosbN67D/0zXHpSkQgdbx9EqdXS9OoLnP/oCCG/n0Vz5pNs733CpN7f1cudn/4cTyREtr4CSTe9\nNiyTyTBLcqr73egyIpQVkd24DH1ezmO7rr6CaDSb03M+dQmd04epqRbt3i0o5kyfuGfSac4dPEgs\nHEaUy7l55yZ9o8Pcf9BGOJm7DyjlCpYWVdAyfwm7f+93KNGbmLx0AyQJ+8bVWObmeCGSJNF15Rqj\nHR2klQqGIn4OH3qfBw+Z3Wq5gvXF1WwvrWODrRxFfRWiPTeJLmrZgPkpC4vIiIOxo6cwVpZTuXf7\njNuIgkB4eAxfexehwREifYM5/seSBTS8+QrqT4gb2e0m2t49QbCrZ8a09+TEBCeOHUEUBLbt2kOm\nf4SRW3eIm/VEFSBms2jcPgzjPgwmI5ZVSynbthFzXt60TpRLl87T09NNXV0DW7a0PFcgTcTjBB8G\n6IDXy6TLSSqZBMCSn09VQwOVdfXPHINPg6u1Dcfl62gFKKysIOvxTRF1VQV5aGsqqduz/vMXjBOJ\nhPSLtw/idjnJt9nYumPXNBm0i4cPM3z8LGUVFSx741U0kozA6Uuo7Db0jTVoaypRzKBr+0l4PBMc\nPvw+RqMRuVyRW6m6/dicfvLramj42huztnA8L1KpFENDA/T0dOPxTACg1Wqpr2+ktLSMRCJBLBbN\n9cZGo8RiMaLRKILwJKN8Ki0VClPR0MDW3dtobs71s4ZHHThv3CF66SbEEujrqyn/0iuYK8uea0Bm\nEwk8rffxdfYgZgXUZhPDmRgT6Tgl5eUstpfhu92GtsCWUzR6RilAisZzrU/eAPLKEibiUZL+wNT7\nSp0WU3M95uYG1Hkzu+c8DyRJItIzgO/abbKJJCqzCePS+VxsvUEiEqVaVGFUyXBOeIkEQpSbLNRt\n3kB+RTmpk5eRKRW5G2jZk21FQtcAmTPXkBcVoFy3FFmhDZlSQXbUSfLDs2AxMZLOKbg1ffEVVI+t\n2lL+IP6ObgLtXegK7RjXLefEsSMA7Nr3ArYCO8HBEVytbcR9uRRiXm01JUsXPXVCKUkSviOnSY25\nyN+xEV199dR74WCQG+fOEpicxGixsHH3HowP20FisRjnzp3C5XJhteaxbdsO8vJmXhl91mD8CLFo\nlO67dxns6UYSRSz5+cxduozympqnjkOPZ4LLZ84gu9IKwTCxojysyxay5YUDz22XN37jDq67bZQu\nX4KhrprT7x9EJpNRnQStwcCcL7/2Mcfg8g28bR1kMxmURTZUtZVkdGri8TjxWIxYLEoiFicRClHS\n58QUiJLWqHE0lZIyaDGb9axb30J+n4P4mWvE3BOkmqopfHUPhlnadYJeLxc++ABRFKlsbGTppk0I\nySSXvv1dTt68zDn3MIMux9T2RSYL6+YuYt8Xv8TWAwemMjjXTp7k3Z/9lFtdD+gZyvEwlEolGxvm\nsb9pMbu27UKn0ZLpG0IsLSRbWUzK60NXWoxt5VPImKLI4LuHSQWC1L52YFoWLBOP4+/qw9fZTSaa\nW02aKkqxNtQS6uglMeFBX1JMxa4nW5/MGrjxP/4FlclI3RsvPrFI8Pt8nPjoMOl0ms0t28k3mrnw\nve8TiIRRz2vAlpdPwUQIfTyN0V6AbcdGNA9X65IkISVSiKEwQijCwL379LXewarUsnDefIzrV6Cq\nmllQ5GkQBAG3Y4yR3j6coyOIgoBMJqOwrIzqhkZKq6tnNB96GiRJYuzydTwdXZjLSqnZsoH0mItw\ndx+hngHikQg7f/jtz18w/td/+rHkdnupqqll/aYt0764a2SY09/9O7QyBeu/+iVsZaV4PzgBovSx\nvKVMhqa8BH1DNdqayhmJV6IocujQQbzeSfbtO0BxcQnO9k5GfvY+/nCQiTmViBoVhYVFNDY2UVNT\nh/Y5C/6SJOFyOent7WFoaIBsNotMJqO8vIKmpmYqKqqeKbuZTObEKx4F51gsQsDvp/XCBeKxOHWL\n57J1216qqqqnPhcaHmXsJ++QGB0HqwnjmmWUrFiCpariuYJyOhLF09qGv7sPMSswOunGr1dR2NzI\nAn0ekb5BVGYT1uYGrE31qGZpp4GHghfHLyKOupDMBjxSBlVBPuY5DRiqyj+1eMDTIKTS+G+3EXrQ\nlevvNWi5d+smUjZL3cK5DJ+/jkGE+a++RNVr+5Ap6ggdKAAAIABJREFUFGQHx0ieuARyGdo9m1FW\nPPyTByOkf34UZKB+fQ+yh4YfUjpN/K2PkGJxog1lTPQPUbR8McUrlpBNJAn3DxHqHSDhmQRAqddj\n37GRU2dPkUgk2Lx1G8YsuO+2kQgEkclk5NXVULJ00bS68EyI9w0ROHUJTUUptn25Wb8kSfR3dHD/\nxnUEQaC6sYnFa9ZMpcBdLidnz54iHo9TU1PLxo1bnup681mCsRhLkHV5kOt1KIpsyBQKouEwXXdb\nGe7rQxJz+snzli+ntLJq1nEoCAIn3nkX11vvoxbBtmszG7/25efWIxbSadrffg8hnWH+G68wPj7G\nrQsX0AeilJus1L2wG9ND3oMkSUSGRvHde0BiIne9dEV2bIsXYHrMgUeSJBLxOInLtxFuPSAtiXib\nKxmIeijsdLKopBJtaRGpeXV4ruX6XO0bV2OZ1zTjObqGhwn6fDQtWTKVMRCcEyQ+OA1GPcO1hRz9\n73/Nre52bke8xIQMAGq1mnXrNhAOBGhtu4sk/X/kvXdwJGea5vfLzPKF8vDemwbQ3rK9ZxuSQ85w\nzM7O7NztxWq0GzqFIk46rULS8e5CIV0obkMXsad12o3VrZnd4dAMe7qbbO+9d/BAoapQBVcG5U1W\npv6obrDBRqPRJIfquXsiEI0GkFVZX2Z+7/e97/M+j4ooimzcuIW3Nu9gDxbsGj2aVV3kNSK5a/eQ\nKksxvLlz0c9ZpGcA/5kL2NtbqNy+afbzJyemCD7sJTI0gppXkHRaHG3NuDrbZ9tGFVnGf+oC0aER\ndHYbtQd2z7Y+Je/ewX3pNpU7NmNv+zwbE52Z4eihX5JKJdm0dTtmo4kLf/5XJMcncazsZu3GTai3\neshHougcNixLOyCTRZmJkZ+JoczEULOF8YlGowwN9qPRaGhb0olOW7jHTdvWoV/y5c0lMuk0vuFh\n3AP9BCcKGymNVkt1QyN1LS2UVlYuOo2dTad58NGvmOzrR7UWIZc5SSUSkM+jjcT5vT/6t69eMP6P\n/9d/UFvbl7Js5apnPqgsy/zyf/t3JHx+Vr5xgLZtWwh+dJR8Mo3z9W1oi52kh0ZJDoyQmyrsOgRR\nRF9XhbG5HkN99eyu7sGD+1y+fIHW1ja2bt1BPpkqCHskU9j3bWU8n6W/v4+xMW/B71jSUFdXR0tL\nG9XVNfOm3xKJOP39ffT39xGNFlpyrFYbbW3ttLS0zhJlvgp8IyOcPPRLxgI+Gpet4Lvf/cGcc1Fy\nMoGff0Lo5j3SigwtdZhLSihfuRRH4/y+zF9EZibKxM07hPoGGR0eJiJnsHe2sbSynrTPXyCRPLZi\ntHe0YKmdP7iq+TzymWvke4YQrEVI7Y0gCoBQ0MkWePxv4UsQ5vk5Amg1iPVV8+r5Po1sKML46Yv4\nT18gHgwRtOjQt9SgXu/BZXNQvHENTe8cRP94opBHx0gfPQeAYd9WpOoych8dRxmfRrt7I1Lb5z3c\nmTNXyT0cQOxsYWigH1EjUbVxLbGhUeKjPlRVQRBEzDWV2Fqb0FeV89nRXxGanqazsg5TLEU6GkUQ\nRFytTZSvWIphHv3r+aBkskz87JeomSyl338Tjc1CMh7n+tkzTIyNoTcYWLV5C9UNhfNVVZX79+9y\n/fpVANauXU9X19IXThyLCcZqTkb2T5DzjSN7/eSDT+vEa9BUlqGpqUBbXUFCVOm5cxvP4CCqquIs\nLaVz1WrKq6ufOZfh3l5unDuLmMlhHfaTjsVJtNfS/fpelizpXJRi3nRvP+4zF3C1NFG/YwvXzpzG\nfesOjlCCjp3bqNkx1wtXVVVS45ME7zwg5vYAoLNacS3vxNbaPKfdKj/sJfvpeWb6BohGQoRTaZQl\njaz+l/8NWqOJ9PgUgaMnkVNpHCu7ca1bvPF85vJt0pdvE4vHSLmsGGcS5MMzXBh3czIxyd2BXrwT\nAQDaGpr4rR/9hHfe/R4u7zT5q3dBktDsXI+q1ZA+chbBbML07j4E0+I2D0pOZvBnH5JPp2n6wTvk\nMxkigyNEBkfIxgqtbgaHHVdXO47WpmdEmJ6M5eSVmwTv3EcyGqndtxON2UTg40NkBA3NP3hndu5J\nxOMc/dUnxGNRlq9cTSYaY+TWbbJ9w1S1tbK8vYvE0TOQSqMrL0VXUTrXBlYjIVotiHYLaa3EuVvX\nSGtFtr/xJuWNDeQnpokfOYOazmBY1Y1h3YuJrS9CLBJhdHCQ0YF+ErHCM2IqKqKuuYXalhZsT9uk\n5vPMhEKEpiYJTU4Rnp4iGg6jyHmy/cMo8STGuioq16zEWVKKo6SEpSuXvHrBeGhwULXa5hc2v/oP\n79N78gzlbS3s/IP/itAnJ8gFw/P2WsozUVIDblKDbnKPjSUErQZDfQ1UlXHo6lkEScO7734fg15P\n8NAJMmPjWNevxLKya/Z1Eok4AwMDDA72zTb2m0wmmptbaGlpx2q14vWO0tvbOxu4NRoNDQ1NtLa2\nUVGx+NXTYnH97BmunDtFOJ7g2z/+CZ2dc4X5VTlP6MR5Yg/7CmlwhxkkCYPNRvmKbqxVlciZDHIm\nQz5dcIHJP/6/nH78fTpDKhRmZmCYQP8A6WQSraWI7rcOUrOsi3DPAOnHtU6NyYitrRl7ewv6LwQY\nVVXJ33yAfOXuV/rMYk0F2oPbFlzp5zMZhg59RrRvCCkrE8ulmQkHKamupXrDGoJ9g2iLTDQe3Du7\nqpc9ftJHzoKqoq0oRfWNI7XWo92z6fPX9Y2T+uUJBKeNcZ3AxPXbGO029I93zQaXE1tbM7aWBjQm\nE4qicOLIYXx371GiSlS4ShElCVdbM+Urls4uBhaLyLmrJB70YV23AsuqbkYHB7l18QK5TIaK2jpW\nb9kyS3DKZrOcO3eakZFhTCYTO3bsXpAl/jTmC8aqqpKfDiF7x8l5/ciBKXjM6EWS0FSWoq0qQ4kn\nyfnGUSLR2WNFkwFNdQVpm5mBST8efyENW1xeTueq1ZRVFdKIowMDXDtzGp1ez7aDb2CQFfr+8u/x\njbqZaqrA3trEpk1b59cU/sK5Pml1an/7IHqHneMffsDM+WvUNTax/p//9Ln9zJlwhODdh8z0DaEq\neSSDAWdXO46uDjRGA9lYnNFfHEJz9T4GJc+gkmHYZaJ+11a27CooO2VnogQOnyAbiWJpaXym9el5\nSI1NMP1//ilqJIZ+/zZK3nmd/OmryL3DJIoMeMos9D96iKTV8vqPCtkC+dQV8oOjCBYz2v1bQSOR\n+sWnqHkF4zt7kEoXz6GZvnUP/5mL6IqdCDot6ce9/ZJOi7WhFkdrM0VVFYuax0IPehm/cAVRkjCW\nlaCGQ9jWrp6VI02nUxw99AmRcIjqyiqS4UhhbhmbpFRrpMbmQrg/gCAImFd1o6urRLJZEB9/STYL\ngtlUGO9slk8++YhwOMTmzdtob++YPY98JEr8V6dQZmLoWhsw7djwtWTjVFVlenwc90A/vuFhck+I\noaWlOIpLHtefp8k/eUYo7KYdJSU4S0qwWW2Ert5GSaWp3bye0q5C3HplCVzzrc799x5w6v/+cwSD\nnjf/l/+R7OU7pD1jmLvasG1eu+CNkguGSQ0WArM8E2NkZIhgdIbGjetp2b6FjNdP/M4jDA01OF9/\n1r4OChdhamqSgYE+hoaGyGQKRX6NRjNLFCotLaOtrZ3GxuYvbYK9GOTzea6fOcYnH3yMvbSU//YP\n/+dZJvjs+SoKkdOXSPYNg8lAtrKYsNuLouSf86rPQqPToTEYyGeyDN24QdQzhlajYee//p8oX9pJ\nejpIuGeAaP8Q+cc3pamyHEdHK5aGujkTnxKagWSqQFxQAVUt6Ds//v7pn4MKijr78/zAKIrHj9Te\niGbnhnmvTz6bZfhXx0hOTOFsa6Zy03oidx6QGXVj37AeU3UFU3ce4L98HY3RQMOBPbOMz7xvnOTP\nDsHDITRL2zD8wQ8RHpc21GyO2F9/QNo7RshZRODBIySDgbI1y7G3NmNra8LwFDs1lUpx9qOPGLt4\nFYvRRFNrGyUdbZQv70ZnefnMSHZ8iqmPPkVjt2J7cxe3r1zGOzSERqtl2foNNLa3z45HKBTi5Mlj\nRCJhKioq2LFjz3PZ4vPhSTBW4gly3sLON+cbR02lZ/9GKnagralAU1OJprzkmWyFEkuQ8wWQvYFn\njs1oJXzJGfzpBBmriZLaWsqqq3lw/RoanY5tBw7OcjQy/gnGPzyK1+dhsNRM3lbEsmUrWL585YIl\nnqdbndrePkAkGOT4H/8JjE+z5fd/j/IXKCLJySSh+z2EH/aRz2QQNRoM5WXE/AEURaG4o5WW1V3c\n+fQs9y5cIJGXWfq9t1mxqbDrzqfSBI6eIjU+Oaf16bnn2z/M5JmLEEtii2cw1lRi/N4BBIMO+ci5\ngpxqaz3ya8sLQh65PLkjZwt8jIpStPu2gCSS+sWnKJEo+l0b0T6V0VkImZkooUd9DP3DR8ipDLbu\nDjQGPda6auzNDVhqqp+7eFkIMbeHseNnC+YmFcWUv7EfQZLIZrMcO/Irxsd8aBEwaHVoJIn6skqU\ni7cwJLPYsnm0liLs3z2AcQG1Q1VVOXHiM9zuETo7u3nttU3P/I2STJM4chp5YhpNVTnmfVu/Vq0I\nWZbxj7oZHRhg3OdDVRQEUcTudOIsKcVZWoKzpPQZL4RMNEbvx79CTqVp3L0dR2P9b04wjgcmOP3H\nf0o4GGTV7/wWVbJI4n4v+poKXAd2Lir1Co8L6fcfcOnnH1CcVWmva5ydyDQ2KyXf2b+oiyXLMl7v\nKP39fUQiYerrG2hpace5gA/y1w27Tc8f/Zv/g76eR6x+bSPf+dHvPCtkoKrMnL9G4kEfGpsV687X\nCLpHycYTaPR6NEYDGr0eSa9Ho9chGfSFn+v1SHrdnHFVFIVjf/JnBI6cxOB0sud//Ze4WgrMTCUn\nE3N7iPT0F9S1KBjJW1sasXe0Yix58SpdkWXkZGruVyKJnEqRjycx9brRK6Dfsgbt+uVzjs1ns4wc\nPk5ifBJHaxM12zfNnvt8Fm5j5y4j6rQ07N+NubwUNZMl82f/QO5OD7Q1YPz2XqgsJT7qI3roBPlH\ng8iVJUxlkqiiSNM7Bylbt/KZ+87jHuHi8eMkb96nqKiItW8epGrVii/VcgeFBdXULw6Tmw6jrlvK\n7d4HpBIJXGVlrN22HYutkIVQVZXe3kdcuXIJWZZZunQZq1eve2HP79OQx6cwTk0SvD9E/vHOCEA0\nG9HUVKKtLkdTXYFofjExcvb8VbWgbuULIPvGkf0TqDmZVCLBZCBAKJ8hYzOTrSlj07e+hat0LrM9\n7fYRPHqaaCLOfZvEDHlsNjubNm2hsvL55JwnrU6NO7fibGmi58oVbv8/f4OtoZZ9f/jfLyrlreRy\nRHoGGDtzkeD9R6iKStm6ldS/+Tp13U1MjEcYPXuRy7/4EFnJs/YH36V96+bHx8pMnLow2/pUuX8X\n2i8Yy6iqSuj6HUI37iLqdJTv2Yo2HCV74SZSfTWG/VshJ5M7dAolMIW0tA2pqbbQOpjKIHW2oNmy\nGkSR9JGz5N0+tMs60G9ateDnysYTzAwVUtDJyWkSnjEyU9OUrFlB1Y5NWOtrvxathtTkNOMXrtK6\ncz05mwtZznH86BH6H9xHSWeorKqmtLKS7oYWfH/5M3LhKGV2B8ayEswbV2PctHCL5s2b17l16waV\nlVW8Po/t6BOoOZnEiQvkhr1ITjtFB3cgLtLO9qU+bzJJKpHA6nAsuFh8gsTkNP2HjqIqCq0HX6dh\nafOrH4wz0Ri3/uYfGLh7F+fqZWxZu5HYpRtonXaK334dUa9DzclkB0bQ1lUvOFnIssyHH75PNDrD\nW2+9gzWnFtLY0yHsW9fPa8r+qqKkxIJ7xM8f/Zv3SMbjfPfHP2HF+mf9lVVVJXrlNvHbD5CKzBS/\nuRuN/eVMtZ9Ayef55R/+K2Ye9WFqqGP7H/weJe1zpTizM1EivQNEegeRH/fwGYpd2NtbkAz6z4Ns\nMoWcTM4G3nxmYW9mISdjfjiCRlERN6/CtGlNoQ9SEHAfPk48MIGjpZGaHZvnBMn5Uq/hgSG8p84j\nShK1e3dgfDSC3DtE1mEl1T9ENjRDqsKJqtOi7xlBU16CuGcTU496sdRW0/jG3jkTRTaT4erliwz2\n9UK/mzKrnTXf+84zY/OyiN95RPjCNSYFmSFtHkEU6Vq1mrZly2YDSiqV4vz5M4yOutHrDWzevJWG\nhmfdehZCzuMn/qtTmIxaUtk8mqpyNNXlaGsqEZ22r63Moubz5CeCs8E5OjRCZHIKR0MdZT98G8n2\nbPo+2T9C+OQF0Grw1BXzcHQIVVVpa+tg7dr185Iqn2516vx+wbjjs3/77wh7fSz5nR+wbNPGZ46Z\nD9MPehg7f4XcTBSjzcqTUShprCYr6dFazEx6vNw+/CmCJLL6rYO07tuDqNUU2O+XbxC+U5CPrNi/\nc1ZbXcnJTJ6+QGzQjdZqoXL/TnROe4G4+clJ8r5x9NvXo13SjJrOFHgMwcgsp0KzZTVSV+Heyl69\nS/bGfaTqcgxv7Jh3Y5JLJJkZHiUyNEwiMAkU3PAMTgfx/mGKqito+e13v1ZS5ROUlFgYH4/w2aFf\ncuviBbSShua2dpatXI0rnGDq7GVm3B4crmKctTXoWuow792y4AZrZGSIEyeOYbVaeeutd+b4188H\nVVFIXbxJ5l4votmI+cB2NIvYIPy6MePxMXj0BJJOy+7/4acv9ZBJ77333q/ptObgvWSykPKUMxn6\nD33K8P37UFHCuo2byF67h2g0UPzWHiSzscD4O32F9I37ZPuGkZx2pOcEmzt3bjEyMkxX11La2jrQ\nWIow1FVj7mhZVCvUqwSzWU9OBltxMb337uJ3u6moqn6mFUsQBAw1FQiiSHrEQ2poFEN15Zf6vIIo\n4qqsJDA8QmJqGn9vHyXV1XP6YiWDHnN1Jc6lSzCWlqDKMqnAJHGPl9jwKAnvGKmJSTKhMLlYHFQV\njdmEweXEVFZKUW011sZ67G3NOLs6KF7RTfHKZRjKS5EdFtQhL/nBUSLjAaZ6+vEcPUk8MIG9uYG6\n13c8o0duNut5cj89gdHlRGe3Mn3nAZGjp1Fv9xCPxwna9GREEKfCGHIKRXojltoqnP/s+0z29ZPP\nZKnbs33OTtc/5uPY0cNMjo9jiWeoszlpWL2KqjWLJ/DMBzmWwPfhEUZHhhhzmbAWu9iybz81TU2z\nr+v1evj008NMT09RWVnF/v0HKS2dn2/xPOTDM8QPnQJVpfw7exDWr0bf3lRYgJgMXyvfQRBFRIsZ\nbVU5+o5mLGuWYnE4EPxT5IZG0dZWIn7ByEHrciAaDWSGPThz0LptE9ORMD6fh8HBflyuYqxfcPXR\n6PUocp4ZjxdRkrBWVWAymfDff8hUcJqSpobZ1q/5oKoqgcs3GL92C43RQMu7b1K5dSPminLyqRS5\n6SCx8SmSgQmEZBqdJBEfHmX65j1C126TnpgmGZgATSEoJ9xeoo/60dosSEYDgcMnSHr9GCvLqHpj\nz+yuWRAEpOoy5J5h8p4xNE11iEUmxIZqlBEvCALag9uRmguqYPKQh8y564jWIoxv7EDQFXa02Xhi\nthfYf/k641dvEfP4kBNJiirLKV3RTc22jWQmp8mn0lRs3YjhS+o0vAgGg4b/9Gd/wY1zZzHo9GzY\ntp313SuQbj4i4w0QCQTQmM2UVlWhq6mgaN+2Be1lg8FpPvvsKJIksW/fG89c+/kgCALauioEvY7c\nsJdc/whSsfO5ceKbgsFmRWcxExocpmXr+n/9Msd+oztjVVEYPHoCz917jCdj1K9cTkMwBYpK8Vt7\n0JU/NjroGSR56jKitQglngRFQb+0HeOGlXMu6szMDB988HP0ej3vvvv9X2tN95vAbH1PUfjZ3/41\nj65dp7W1nV1vvkVVff28x8Tv9zJz/hqiyUjpd/Y/1+lnISiyzP2/e5+h+/eJRaOYjEY2/5MfU768\n+7nH5BJJYsOjhVW9yYjGbERjNKIxGRdlwq2q6mxQyI9NkPrZITKRGdyZODPBEAaHDVtjPTq7laLa\naopqqzFXliNqNbPjpCoK6akgibEAibEAycAE2cAkusv3Cu/x+kaKN6zBXFWBTpLIHj6DmsmiXbGE\npMuK59R5HG1N1O7YUvhMuRw3rl6hr+chgiDQXtuAOOTDYLPQ8e03Z52dvgzy+TwP/vSvmbx9j0RD\nBXU7ttC9Zu1s+kuWZa5fv8qDB/eQJInVq9e+UEt7PijpDLEPCrVG087XqN68/Cv1GX9ZpO88InXx\nJoJBT9EbO9HMQz56ojymcdhwvrGLh4P93Lp1HUVRWL/+NTo7u+d8/i+2OgHc+rO/xu33YlrRye53\nvj2vqpeSk/GcOsfM8CgGh436/bufIdy5nCYC7gly8QRyPEEunuD+lSv4r97EmM7htDuw1lZjeOwS\nlgvPkBz2gKKgKTJjbarH3t1ByZYN8xK8cv0jZI5fRCovwfD2bgRRnPXVfVKjV4IRUh98WiCM7t1E\nMp0mEZgg4R8nE/38Gko6LebyMopqKrE3NcyK9jyRvTSWllD/zvN9hdOpVKEFh0JQe7I7FwShkCl4\n8v2Tnz/+OwSBZDzO4Z//Hb0PHmG1O3j3t34bx+QMqf4REASyVhOR/iGcORV7ZzuWd/Y+sxibcy7p\nFB9//AGxWIxdu/a+dAYIIDvkIXH8AigKpq3r0Hd++danrwvTfQN0bFr5Ug/vy1fyvwK8l64RdnsI\nJmIYqsqonk6iynmce7bMBuJ8MELq3DUEnY6iN3ehZnMkj50nc68X2T+BefcmpMfpn0uXLpDPy6xf\nv3Cv5W8aRFFk87adhEIhAgE/V06eYMv+A5RUPCtlV9TdDnmFmUs3CH12luK39i64Cp33/TQaypZ2\nkkummMpnmHzYw7m/+k9s+vEPqVy9fN5jtGYTzu6OeX+3EPL5PPfv3ubBvbsY9AacLhfO4mJcazoQ\nfv4ZzmwGx57t2Jd2kPQFiHvHCD/oIfygB1GjwVRVQa61lvEBL0n/+CzJDEDvsFMcjCOuWMpkkRbZ\noEeymGclAKV39iC7x5A6mgn84mNEjUT5moK83sR4gAtnTxOLRrE7nLy24TXGT10kJ4o07Nz6lQJx\nbGaG6z//APnWXcRiB+t/54eUV38uJBEKhTh9+gShUBC73cH27TspLp5fsWshqIpC4th5lEgUw4ol\n6Nu/XsnXl4Fh+RIEnZbkmavEf3kc8/5taKvmyosWrepGyWSI3+0h8ulZlr65i4qKCk6c+IzLly8S\nDE6zceOW2QWLpNNRtXYV7jMXGLt6g4adWyltbSIZizEVinDt9Ck279s/p34sp1KMHD1JcmKKospy\n6vbuQDMP+UqUJHRWyxwbwS3LOjleV874/UeYMyqC1YKxthpnewv5VJqE18fUmStkpqbJAyVb1j+X\naa1tbSA/4kMeHCV36yG61d2zQVhVVTJTQWJ/8xHZwCSJqhJSR0/MHivpdVjrayiqKMdcWY6x2PlM\nyldVVSau3ACgdP2zLaRPEJqa4vShT8jL81vazgeVgm55LpcjFAwSCU9RUVvH9/e9hXyrl1QyhbbE\nhWXDSvr//kMM/mmsa1cWarkLBOJ8Ps+JE8eIxWKsXLn6SwViAF1TLaJpF/EjZ0ieuYISi2NYt/xr\n73h5GRS3vfyC4BtLU7uv3cF/4zaReJSU00JzSsAiabGuW4G5q9BQr2ZzxA+dREmmMO/ehLaiFNFs\nRNfeVKizjI6R7R1CMBrwxsPcuXub6uoa1qxZ97UMvKqqyB5/gRhQ4lo0iezrwtPpV6vVxnQoSCyZ\nQM3mmBobo7y6Zt6Vv7asGDkaI+Pxk0+mMNQ/2/P5IhicdqYe9mE1mjAu7WCqf4DA/UfYHU6sLzCU\nWCymJic5eewo7uEhdDo9iqIQDE4zPubDffkaMxMTiLEkcl4mVF2MrraSkhVd2Bsb0JpM5NMZUuMT\npMcnSUyF0JhNWBsbcC3vomLzBuyZPNrgDMY13VgO7GBm2E1kyP14J1GKYDQgVZQyff8hMyMeSpZ1\nYamr5tb1a1y+cI5sNkvX0uVs2b6Dyau3SExOUbVmxSyp7csgl81y6qMPUa/dx+5wsvq/+30clZ8L\nVTx8+GDWe3jJkk527dqzaFGMLyJ14Qa5ATfauipMj/Wg50vpf1PQlLiQHFayg6Nk+91oih1z0oiC\nIKCvqSQfT5Ie9ZGbmKZkWRdNLS2Mj4/j9Xrw+8eoqamdXWwbXU5mRr1EfWNYa6rQmozkpoIoosh0\nIoogipQ+bvtKhyMMf/IZ6VAYR1sTdXu2I+nmJzLNN06iKFJdW4s74Cek5LBrdCjRONlYnNKVSwsl\nl9dWo4oCuXQaRZYpqq2e9/UBpOpy5H53IRVt1BGbnGb6/iP8F68Q/cfD5Dx+kvYi8lUlWGqrcS1p\no2L9Kio3rMHR2oS5vBTt4xagLyI+6iN4+x5FdTWUrFo27/tnMxnOHjlMJpWisaOD4vLygpGCxYLO\naCzoNQgC2XyeVCZNPBEnEo0QDAUJz0SIxePIap72jjb2NXQjPxoERcW6fgWO7a8ROHcF+eINrLU1\nuH77bTSuhV2/Ll++wMjIMA0NjWzcuPkrzeGixYy2sYacx09uxIcyEyuksV9iDlcyWeTAJLlBN+m7\nPaQu3iR99Q45t4/8ZLCQpVVVBL0OQXrx65rN+lcvTR12e9Wrf/sxeVRG5ST2QIi2smosS1qx73ht\n9iIkTl0i2zOEfmk7ps1rnnmd7OAoydNXkFMpbk76CNQU8/b3v4/NtvBFfxHUfJ7c4Cjp2w9nBQ80\nVeUU7d+K8A3uuL9ITJqamuTjjz9Ao4JZkDAYjex48615a2NKTmb6o08fE9fWYX6OYtBC8F25zvid\n+9Rufo0+7wjDhz7DKGlY/91vU7Nx/Zd+WGQ5x52bN3l4/y6qqtLa3sHqtevR6nQkojF6PzlMaHCY\nnNmIMZPHPDJG0mrC01GNKopoNBocTidOVwnoQ4UnAAAgAElEQVQ2g5EqexFaZxmmp1SulLEJch+f\nAKsZ3fcOFHorQxGGf/UZuUSSstXLKVu9nHw6Te/ff4AgipTs3cqlSxeJhENYrFY2b9tBaVk5U4/6\nGD13EUtlBa0HF6cJ/jxcP3eW8U9PUyXoaX5jL9b1hZ14Mpnk3LnTeL0eDAYDW7Zsn6O89rLIPOgn\nefYqktOO5dt7Z+/bryqH+XUgNzpG4tOzqHkF8+5N6Frq5/xeVRRCx86RHvYUWhF3byGPyoUL5xgY\n6MNkMrN7997Z2vnTrU7N+3fR8zc/RzToGRZzpJNJtuw/gBkR92enyGeys9d+oft3oXEKBYMc+eQj\nUBRWFVeT9U8g6XXU7NiMrb6WfDaL+8PDZMKROVrNqqKQiURJh0KkgmHSwRDZIQ+am49QjHqSnU0g\niRgnQpijSbQtDVje3YfB5XypZ+1FspdQWPhdOn6cR3dvY7RasZYUk0wkSafn19OHx9wUgxGT2YzJ\nZCoYcITjVEViZOIZdBWlOLa/hsZuJeX1M/xv/wOSJNLwL36Kvml+b+Mn6O19xPnzZ3E6Xbz55tsv\nLUf5PCipNIkjZ5DHp9BUlhVan+bJhKhyvtBrPxkkPzFNfjJI/ql+eih0HQgGPfnQzKzu9OOBQbJb\nkYodSCXOQq262PFMFuCVbG269Md/pcZmkkTtJqL3emg2WCnt6sD1xq5Ztl+md4jkyUtIJS4s7zw/\n1arEEtz7s78m+KiPsqZG2v7JD9BWvhzB5QnUbJbMo0Eyd3tR4gkQBHTNdag5mZzbh1TiouiNhVMt\nXyfmmxBOnz7J4GA/TTV1THu8FNlsbH/jzXl3yHI0ztQvDqNmc7je2oO+4lnDhIWQTSR58Hfvo7MU\nseS73+LSyRMMffIpRlFi7dtvUbdt00sH5PGAn4vnzhCLRimyWNm4ZSsVj1tYlHwez/EzzIx4sNRU\nUv96Qeovffg0yft9RF0WxlorCQaniYTDs844JpOOZDKLyWzGbndgN5mpuNqLEQHjd/Zhaqz93CQ+\nGmPk0GdkojFKlnWi5hWm7j8kWWJnYGYaVVVpX9LJqrXr0Wq1pEIRej78BFGSWPKdt75UH/ETjI26\nufH//j3OqSjN69ZS9r03EbUaPJ5Rzp07TSqVorq6hi1btmN+juH6YpAbGyf+yxMIeh2W7+ybw2B+\nFYIxgOyfJH74FGpOnreup8p5gkdOkfEFkMxGzMuWYOpo5lF/L1evXkYURTZt2kJra8Hl7elWp9iI\nh8iQm+KtG7h06TyaWII6XREajYbqrRtxLuDd/QQvGqeR4SHOnjyOucjC5iXLmLp2C0XOU7q8i7K1\nK0lNTjH4s4+QEwkcSzuRczky4QiKPFcDQGs2YZ6eQT8ZRrusA0NzPcrVu0gOK8bv7JvthX8ZzCd7\n+TRCwSDnTxzj5vnzaA16qltb0Wq1j4Os+Qv/mjCbizCaTRiNptn2IlVRmLl4g8T9XorsZqTuTsxd\nbQiCgJJI4f7f/5jUWIDS775Byb7tC55vIODnyJFD6HQ63nrr24sibL0M5rQ+OWyYD2wHOY88OU1+\nYroQgKfDcwKsoNMilbrQlLqQSovRlLkQH/NvVDlPPhwhPx0ufE2FyAfDs9KdTyCaTY+DswOp2EnV\n+s5XLxif+/d/qooNDfScPUfJVIymtasp/fb+2RVLPjxD7P0jIAhY3t2/ICMuGAzy8UfvUxFMss5c\njCiKGFZ1YVizdNE7GCWRJHOvl8yDAdRsFkEjoVvSgn5ZR8HgW1FInrlCtmcIyW6l6M1dv5Y+ti9i\nvgkhFovy/vv/iNFoYEljC3137xa8XQ++MceU/QkyvgDTh04gGg1fitDlPnOB6d5+mnbvwN5Yx6WT\nJxj85ChGRFa/cYCGnVsXNc7ZbJab167OkqE6urpZsWrN7ApYyefxnDjLzPAoluoK6l/fNStGoMp5\ncp+cRPFPolmxBM3GleRlmUgkTHB6GiWfwjPqJxIJk4zHqRoIYA1GmaopZrq6GJ1Oh81ux2Z3YLPZ\nMev0RK/dhmSGTCaNdyJAvKECs9XKxi3bqHxsSajIMr0f/opkKETTnh04GutfauyeRiad5uSf/Dna\nh8M0r1xBzQ/fAbOBa9eu8PDhAyRJYu3a9c+QlF4W+ZkYsfePoGZzFH1r9zML01clGAPIU0Hin5xE\nTWcwbliJYeVcsQ4llyN2/S6Jh/2oORlRp8Pc3UbEZeHMpXNkMhm6upaybt0GcvEED/+x0OpUu34t\noyfOULKskzGfl4FjpyhyONn2z3+KtWZxxgKLGadbN65x7/YtKquq2bh6Pd6TZ8lEokhaDfmcTDYc\nITboRjLosXe3YywpxuhyYnA5Cv867WiMxkJf9vtHUcIzIEkIkojx23sRnS+f4VNyMoN//wFKNkvT\nD96Z1ZZPp1MMDw4y2N+H3+vB29uHTq9j+5tvsaR7Ka7ikkXfd0pOJnzyAulhD1qnndYfvUEkVzhW\nzeYI/u1HTJy/grSkmbZ/8V8v+LrxeIyPP/6ATCbDvn0HF+wt/ypQFYXUpVtk7vY8+0tRRFPiRCor\nRipxoikrRrRbXy4boaoFTe3p0OdBejqEkvg809D63u+/eq1NqsB796/fRPtwmLolHVR85+BskFBz\ncqFOHE9i3rVxwV3uE4WWeDzOxre/RXFXO7IvQM7tQ/aNo6kuX1DgIx+KkLp8i+Spy8j+SQSdFsOq\nLsx7NhdIAI+PFQQBbX317A45N+RBW181b7rj68R8dSu9Xk82m8Xr9VBRU4fDZifg8RCcmqS2sekZ\nsQON1YKo05Ie9pANTGFqbXypNKvBZmXqUS+ZWJzijlZqGhtJ6LVM9g8y1TeAURBxNC3s2DPm9XDi\nsyME/GPY7A527nmd1vYOJEkqMKCDIQIXrzIzPEpRVQUN+3bNUQUSRBGxoRp1ZIz8iA/BoEeqKMVk\nMuMqLqGjs5WKqno6u5fRrrVQGghhbKhF2LIGg9FIPq8QDoUIBacJ+McY9Y4ynkkyOTBIyB8gU1lC\n8/Ll7Nz9Ovan3I58l68TGfUU1LVWPF8p6EVQVZXrv/gY+epdyurqaPrJ94mqOT799DAejweHw8nr\nrx+kvn7hcXzh+2SzxH95AiWexLR9PbrG2mf+5v/PmvEXIZpNaBuqC60oT5jIVeWzYyBIEoaaSsyd\nrYhaLdnJaTJeP9Kon4bKGoK5FKN+H5OTEzQ0tyAiMOPxonfYyYYjJPzjSMk0OVQS5Q70TgellYvj\nOyxmnMorKglOTzPm8yLotHTt3Fbop09nMJeX4uxsp6i8BEmScDQ10PDWPmwNdZjLStFZLbMa+oIk\nIpaXIPcMgaJg2LtlXtvPxSB4t6DB7VrehbmuGp/Xw83rV7l84Rw+r4dEIk5iapqS0lK+90//GUu6\nupm4coNUKExRWekL54Z8MkXo8EkyvgD6qnJcb+zCWuogmcwWCIOfnmP60nUyFhM1v/sDDLbnb6Rk\nOcfRo4eJRmfYsGHjvJafXxcEQZhtq1MzWTQ1FeiXtGBYuwzT5tXou1rR1lWhKXYiGl++3U8QBESD\nHslpR1tdga61AcPyJeg7W9FWlyM57diaa16qZvyNBGPP0PB7Ex8dx1VcQvNPvo/uqebs1Llr5Dx+\n9F2tGJ7Sj54PfX29PHr0gMbGJpYvX4loMRfIXdEEOY+fbO8woq0I6akVpqqqyIFJUueuF5xapsOI\nNgvG9Ssw73wNbXU5wjytOIIgoKmpQJBEcsNesoOjhWD/JZWXFoPnTQglJaX09vYwMRFg667dJGNx\nxr0eopHIvFZ2cwhdiZcjdGmMBlLBENExP5bKcgxWKzUNDSQMWiYHBpnqH0SfV3E0PWtQkUmnuXzh\nHDeuXUHO5Vi6fCUbVq1FnYkS6h1g4uYd/BeuMf2gh3R4hqKKMur375pXHUjQFIwklIFRlCEPgsuO\n6LTNGSc1EkP59AJaowH7b71FZVMjDU3NdHR20b1sBU0tBUtLp6sYs9WKUOJAU1rMxjcO0r1sBdJT\n131m1Ivn4hWMDjtNe3YuiqDxPIxcvUHgw8OYLRa6f/+f4p4Jcvz4pySTSTo7u9m5czdFX8FLFZid\nCOXxqULb3+r529BepWAMIBoNBXvD0TFyIz7UVBpN3VxbUEGjQV9ZhrmrHclsJBeMoE5MU5FSERIp\nxiJBhsc8NC9bTtLjI+Yfx1ZdSTocwVRaQvcPvoN/3E/AM4qrrGzB/uMnWMw4CYJAdU0tnlE3Xs8o\nVrudhtUrKV66BEdLE5bqSuztLWSCYRLeMQRRxFxZPu9riWYjYnkxmqY6NA3PJ30tBDmZwnf8LOlc\njmBxERfPn2Ogr5eZSASb3UHXsmUYBAlRVVm+fgPNnV24T18gPDxCPDBBeNiNqdiF7jn3ojwTJfjJ\ncXLBCMbWBpx7tyJqtZjNehKJDKmz14jdvEckGce4bR1Va1c+91xVVeXs2VOMjflob1/CqlVrvhG2\ns6asGP2SZnQNNYUdsNn0ayXmCjotkt2KprL0pQlc30gwPv+v/v17ZGS6fve3KXrKszXbP0Lq6h2k\nYgfmvQunP9PpFMeOfYooCuzdu2+WXSloJLRNtYgWM/LoGNl+N0o8gbaqjJx7jNSpy6Rv3EeJRNGU\nl2DctAbT5jVoyopfeFEEQUBTWYZoMpIb8pAbcCNVlCB9hTriQnjehKDRaJAkidFRN4qisG7TZoIT\nk4x7PaRTKSpqa+dOZoKAvqaKjGeMjGcM0WREV7p4D2edpYjp3n7kVBpXa0GQoqa+nqRBy8TgENMD\nQ2izMs7mxtmg5R4e4vihQ0wNDmOTobOkEq1/iqk7D5gZHiU5MYWcSBb6hxtqKe5qp2Ld6gVl+gS9\nDrG6HKXPjTLkQawqR7CYC5NBLEXu8BnUaBztzg2IX8ioiKKIwWDAZndQVl5BbX0DrR2ddCxfgc0+\nNx2YSyYZOHIcFJWWA3u+Up045vPz4D/+JaKi0vXTnyA7rRw//ilarZadO/fQ1bUUUfzqqkipS7fI\n9g2jqanAvGvjcye2Vy0YA4h6HbqmuoJO9uhYgfla/yzzVZBEdGXFmLta0dhtKNEY9pyKaSLMjMdH\nv89NWXMz8lQQvd1K6fJuKjasQV9kxlVWhnugn4BnlNqmZrQvIGIudpwkjYbKqiqGBwfwjLqpqq7B\n9FS9XxAEimqqiA65ibk9GEuL0T1ntyjaLIiOL1cvTadT3Pnglwzfuo1XlJlKJ5Akidb2DjZs2syK\nVWuIh8IMPLg/K7Ua7Oln/M49ispKcTTWE/X6CPYNkM/msJSXzVmAZsenmP7kOPlEEsvKLmyb181e\nH7NZT+j8TVK3HhKanCTVWEnTvl1zPMCfhqqqXL58gb6+XsrLK9ixY/ei5Et/0/FKBuMHf/vhe+U7\nNlOz83O7s3wkSuLwaQRJpOit3YgvUI+6dOkCExPjrFu3gerquWbfgiCgKXGibapDHp9CHvWTvtND\nbsCNkkiira/GvH0DxnXLkL6EFKCm1IVkt5IdcM/bovF1QM3nMRm1JFO5eX/vchUzNDSA3z9Gc0sb\nTe3tTPh8BDweVFWddct5AkES0ddWkeofJj3sQV9VjmaRQUZXZCbmHyc65sdeX4vWVGinqK6rJ2nQ\nMTE0THBoGCmdIRNPcP4f3qfn6DHU0TEqDUVU2ZyQyqAxGrDUVOJsb6F01TIqN62jZGkn1vpajMWu\nRe0+BbMRodiB0u9GGfEiNtZQ5LISPXWd/IC7YDSx5qullIeOnSYVClG9YS2OhoVZoAshF41z64/+\nhOxMlNq3D1C5fjWffXaEZDLBzp17qK398q/9NDI9Q6Qv30J8wmdYQPj/VQzGUNhBaJvryfsnyHn8\n5KfDaBtr510gC6KIttiBqbMVfWkxJkHCGE+R6R9hYmQENSej5mTKVy3D8HgXbDKb0ep1+EZGCE9P\nU9fSsuBz/zLjZDAYcTidDA8O4PN6aGxqmcMGFrVaTOWlzPQNER/1Ym2qR/oKfepPkJdlvKNubl2/\nyuUTJ4hcvY0siZRve41Va9ezYdMWaurqMZnMRIJBLp84jlanY+uBg+RjcYaPn0bS62k9uBdncyPW\nqkrigQlmPF7CQyMYXQ70FgupYQ+ho6dRczL2reuxrOyau9gf8RA6dol0Ok3QYcK1pI3Srvk1BxRF\n4cKFc/T0PHpcnjnwn5UmxEJ4JYPx4I3b7y393R/ProZUOf95nXjna8+IAXwR4+MBLl26gMtVzObN\n8zswAYgGPbq2JtR8HiUYRtfaiHn3RgzLOr4yAUty2dGUusgNjZLtH0GyFiEVf3XdayWWIH3rAYlj\nF8jcfkQ2nkJyOZ5xzhFFEZPJxPDwEMlkgpaWNqrqGxgbdeN3u9FodRSXfWF3qNehKy0m2TdMetSH\nqaUecZEPgtZgIDQ4TD6bmyUyFQJyHSmTjonhEaYHhhi5eZvE1DRFRUV0bd5Ew9pVlK7opvK1NZSt\nWo69qQFzeSk6S9EzspaLhWi3IpiNBaenUT8Gp4X48csItiK0Bxa2YHwRJu4+YOpRL7ba6q/UvpVP\npen7i78hOOLGtKqbFb/1LjdvXsftHqa9fQlLl87f+/mykP2TJD47h6DVYvnWbqSihcsmr2owhkI6\nWtdcjzw5jezxI49PoWusee71FAQBjd2KuaMZW0sjJklLbHiU9MQUmWEP+XgSjd1KYmKKeGACTUYm\n5vUz3tNLeHgEJTRDsH+QqZ5+ph70MHn/IeN37hO4eYfpR71k0jmMLuei7lOrzY4kSnjcI4z5vCj5\nPFqdFr2hUH/UFpnRmAxEh9wkAxPYWpu+VOlDVVWmJie4d+cWF8+dYXCgn5lQCNvUDCUWG+t+/EO6\nN76G3eGYnV9z2SznjhwmnUqxYddubDYbA786Ri6dpmn39lmpW52liOL2VlRVYcbjY7pvgFTPINkH\n/QiShGvfNkwtc92ict4AqeMXyCkqkwYBRaOh6fUd84riKIrC2bOnGRjoo7i4hAMH3sBo/M2SKP4q\neCWDccnyzvcE6fMgkDp/nZx7rFBQf06t6wny+TzHjh0lnU6xe/deLJaFBREEUURbW4l+ZSe6xpqv\ntS2pUAsoIzfkITvgRjDo0ZQtPv37BKqqIvsnSF28QfLsNeTAJIJWg96oJznkJfugDyWVRip2zGrT\nAtjtDnw+L2NjPqqra7DbHVTU1uIbGcE3MgxAScVcj1KNtQhRr3tpQpfeZmVmxEPMH8DV2jz7sAmC\nQHVtHSmjlolwCLHYyfJvvcGWn/yIsq4lFFVVoLfbvhaXmKchljhBUVBGfDDsJScraA9uR5zHiGCx\nSE5NM3LyXEGr+MDeL33OSi6H//3DuG/cIlddxvqf/i7hSJjz589gtVrZtWvvS7ktPQ/5aJz4JydA\nlik6sH1R996rHIyhQNrSNdeTD0UKgju+cTRVZS8kS2osZuxd7ZQt62JiPEDG6yfRP0TSGyA6EyHq\nGyPmD6DLq0TG/IQ8PoRMFiWdIRuLk88WCEiiJKIxGBDkLFODbqZ7+lEVBdMignJpWTnJRAK/z8uY\nz0vvo4cM9PUSDoXI5/M46mpQM1niHh+5RBJLfe2iF3uxaJSeB/e5dP4sD+/dJTg9hV6no97qoi6p\nUGq2UNzUSNWWufajqqpy/dxZpgIB2pYto3lJJ+5T54iPT1CxYhklne1fGH8Ra3UV1poqopduEr91\nn3QsTvHBXVja5xKslFiC+MfH0GokYvWVhIPTlHZ34mx+Vjkrn89z+vQJhocHKSsrZ9++g/MagPzn\njFcyGCdTqfdkWUUUJbKDblKXbyO57Jhf3/rC1eKDB/cYHBygvX0JS5YsTPB6Gr8ucoBoMaOtqyI3\n4iE35AFAU1m2qPdTczLZviGSpy6Ruf0IJRxFKnEWyGQ7XqN08wrSiOSnw8jeAJl7fajJFKLTjqjX\nIQgCdrud/v5eIpEIra3t6A0GKuvqCHg9jLndZNJpyqvnEra0pcXkY3HSnrFFE7oEQUDUagmPuEFR\nsdXVzPlddW0drroaVmzZTE1j0zdCxhCqyiCWQBONoazoRFqkx+t8yOdyj3cLKZr37MTk+nJWmWo+\nT/DIaYYvXCZuMdD5O9/HUVLC0aOHyWYz7N79Onb7VxOlgafU6aJxTJvXoGtd3Gd/1YMxPF5AN9Wi\nxBLIHj+ZB/0ooRlEW9ELCZN6q4X6DWuZsuiIjniQsjnqlnZTvnk9Je2tlHV3ULliGYFElEyRkTXv\nfpv6jeupXLWc8uXdlC3tpLSzg6b1y0lnFRLjk4Vd4qM+lLyC0eV4rta6IAjU1tXT0taO0+VC0miI\nzswwNTHB6MgwD+/fI6zIyFNB0mPjGKwWTGXPZ01n0mmGBvq5dvkiN65eZjzgR8nnqW9sYmlzO5Vx\nGe1kEI0o4VrWRcW21545t+HeXnrv3J6tE08/7GXi3gMsFeXUb59f5UqV8yQu30KTSCEVmUmXOQmP\nj5ONJyiqKJt9j9S1u8j+SZw719F//xGCINK0Z/szZRJZljl58hijo24qKirZu3f/M97s/yXglQzG\nf/EXf/He9es3GLp7l9TRcyRSSWJrOslKjynoWu38Em/xGCdPHken07F79+uL8pT8JiCajGgbah4z\nQr0F6nxt5XMDUj4aJ3PzPokTF8kNjqKmM+ia6zBtXY9h3XI0JQWt2SKLkYzFir67FdFiJh98HJTv\n96HE4kgOG9biYkKhEGNjXpxOFw6HA73BQE1DI5P+MQKeUWKRGSpra2fTVk9kBzNef4HQZTSgW8Su\nyuiwE+ofJD4+SXFH65ydoyAI2Ox2dLpv7iETBAGxoRr78lYyleVfaQHgOXeJ6Jif8mVdlHS+vMY2\nFHYh4VMXGbtynUk5g3PXZrrWruXq1Uv4fF6WLl1Ge/uSL32OT79P4vh55LEJ9J2F9ozFfvbfhGAM\nj+eBhhokhw1lJorsGyf7cID8xDRCkQnRYn5+eUoUqW1pYZQMCa8fXSKNq6oK65JWdEVFWFxODEVF\njHlGCQenqWttfYZAZLGZkexOipe0Iel0JCYmmfH6mHrYhyLnMDmdz63N63Q6nK5i6hsa6Vq6jNq6\neiwWK4pakHsNIzPTP8TI9RuMxaNkVWU2pa0oCj6vh1s3rnHp/Fm8o26SyQQVVVUsX7matavWYgwE\nid95iJxIYGmoo2bfDmzNDc/s3CPBIJePH5utE8vRGCMnzqIxGGg5+Dqaedo+lXSG4OGTZDx+9JVl\nVP/oOzjamkhMTjPj9REaGEZvt6EzGEievIhoNJAuszE1NErl6uXYviD/mcvlOH78U3w+L9XVNezd\nuw+t9r+MGvEX8UoG40Qi8V4+q2C53kMuHMFdbmU4EWZwcICHD+9z794dhoYGGBsbIxicJh6PI8s5\nbt68TigUZOPGzZSVLVxX/qYhGvTomuuQPYU+5y8yQp9ORafOXUMOTBX6mpd1YN69Ef2SlmcmmCcT\npyCKaEpc6LvbkOxWlFAE2Tde2DGEZyhraaLXPcT09DTt7UsQRRGtTkdNUxPByQkCXg+hyUmq6utn\n06OzhK6BkUUTup44t0RGPUgaDZaqZ40qvmkIgoCl1P6VAkxoaISxazcxFbsWLWLyRaiqSvTiDUI3\n7zE6NYG8rI3NBw4wMTnBpUsXcDicXxtrNH31LtlHAwV5vz2bXup8f1OCMTy2G3TZ0XW2oCkrRokn\nC0G5dxjZ40cw6J8rziAIwv/X3n3G1pWmCX7/n3NzYs6ZFMlDiaSoTIoKpRxKpa7q6unGzLSnMY3x\nBsAfZuC1F9ixMW4DhmHAmDG8wHqx3vG60ZjpHk9Xd1V1lXIoSaWcA0XyMOecbs73+MMlWaJESqQS\nqdL7AwSSl+S9rw7fe55z3vA85BQU8mhsAH9nL1anD6PRiGl6lX1yWhoel5Oh3vj8blbe3CAyc5xk\nvR5HdhbpVavRm0z4RuNBabSxmWgojCU1+fk7ACQJq9VGZlY2peUKa6rXkpmbi+Sw4u/qw9XVQ2/A\nTbPaRKvaTMPD+7SpzTinJklITKKquoZtO3ehlK9G6xti8Py3BEbGMKWmkLfvA9I2rEU3zxB+OBye\nnSeu27ePxIREWr8+STQUYtWBPVjnKacYcXkY+/I04bEJLKsKST20G9lowGCzklpRhiTrcPX2M97a\nRuihitEfQl9dzkCLCjoDJXt3zV2BHQpx+vRxBgYGKCwsYt++g+j1r3e66l2yIoNxSUnJL1K6xskI\naeTurKfo6IH4/s+UVGw2O7Is43a7GR8fY3h4iO7urtmh2KysbLZuXXjrxnKSjAYMZXNXhOrzswm1\ndC4wFL0VQ0HOgvmunz5xSpKELi0ZY7WCLiWJ2FT8joHWbsyBEP2uSYyJCbM5e3V6PfmrVuGanGSw\nt5fh/n5yi4rQT5884gu6UmcXdFlKi56bJAXiBSTGmlrwjoySXrX6pRdhvU6vEmCCbjftx8+AJFF+\n5OBs+bml8txtwHn7Ad2D/UyWZFN7YD9Wh4MTJ44RjUY4dOjDly74MKe9ze34r9xBTnRg/8HeRS/A\nm/EuBeMZ0nTuX9PqVRgKctACQSJ9Q4Tbugm3doHBEN8V8dRFidFoJDE1lYbxIaJ9gzg8AWSDAVN2\nBpIkkZmbR39nJwM93SSlpZHwxPTB08dJ1umwZ2WSUbkavdmEd3QMV28/o4+biQSCWFKSFyw68SSd\nTkdCYhL5ikJ6VhZmb4BkowV7cSEulxNJkimvWE1t/XY2bNpCRlYWgd4Bek+dx93RjWwykbVtC9k7\nty64RUrTNG5fusjIwADl1Wspq6yi89xFvCOj5GxcR9rqZ/PUh0bG4luX3F7s69aQtGvrnMAqyTKO\nnCySigvxDY0SvBxPFOLJSCYW8JO1eSO2J4bcg8EgJ08eY3h4iJKSVezZs3/FjGQulxUZjP0tXb+Y\nOHstPsx6ZDc2h4OUlBSys3MoLi6homINNTXrWb26ksLCIjIzs0lKSsLhSKCubtuKnvh/ekVo8H4T\n4a6+6aHoIqy7aucMRT/PQidOSZLQpdqTFiIAACAASURBVEzfMWSkEnW6sbr8hBtbmWhtJ6esDMP0\nVitZlskrLsHv9TLY08NAdxc5hYWzqTOfXNAVHnrxgi5ZpyMWieDs7cNgsWB/zpzX2/KyAUaLxWg7\neY6A00XhzvpFp0p8mrexFeeVW4xNTdKbYaeoqpKKmnVcvnyJoaFBNmzYxKpVr1ZTNeb24v3mGsE7\nDUhGA46P96FLWPr+53cxGD9JttswlhXFc8ZHokT6hwl39hJqao8n7E9JmrP6OikpiUAkRKfPhWnC\nhXnCjWw0YJzOjJWenU1Xi8pATw8Fq1bNvi8WfO/pZOxZGWSsWY3BZsE3Oo6rr5/Rx02E/YHpoLy4\nCyRLVgaRSSdMOikoKKL2yIdUra0hL78Aq81GYGyC/jMXGX/QgBaJkrq2krwDu7BmP39NSqeq0nTv\nLqkZmdTu2cPoo0ZGGhpJyMmeN598oKuP8WPn0EJhEndsIWHT2gWf32Cx4NBkol19eC1G/HqJpKx0\nsuu+q5QXCPg5ceJrRkdHKCtT2LVrz2tZsPiuW5HBePQf//CLcDj63BzPM3PHDoeDtLR08vLyKS4u\nWdGBeMbMitCYy40WCGKqWY3twHZMq0ufO9f1tBedOGfuGIxrSjHlZRMYn8Df0UNE7cAW0TAU5SHJ\ncnzIrrCQWCzKQHc3fZ0dZOXlYZ7eVhBf0OWNL+jy+DAX5z+3jZbkZEYeN+EfnyS9suKVM9gEnC6G\n7j1Ab7VgeMH+8vm8TIDRYjG6L11lqquHlFXF5GxZuObr8/g7epg8d4VAJEyTOYY5NZntBw/R29vD\nrVs3SE9PZ+fO3S89PK1FogTuNOA7/S3R8Sn0WenYDu5E/5Lb6N71YDxDtpgxluTHazRrGtGhEcJd\n/QQft6BFovGgPD2nm52dS9dAH30RP+lhYGA0Pq2UmYbZasVis9Lb3s74yDCFZfH54xe+93Qytox0\n0isrMNps8Sx1ff2MNjQT9vmxZWUsuNBr9jkkCXthHu6OHjzdPZhTkjGnJBPx+Ri6fIOhb68Tdntw\nFBWQd2gPiWUlL3zOqfFxrp45jd5o5IMjRwg7XXSevYjBaqb8o4NzLhRi4TCua3dxXrmNJEmkHNiJ\n7QU1rzVNw3/2CgaDkYyffoImyyh76olO747x+XwcO/YVExPjVFSsYefOXe9FQo/FWJHB2Hnx1i8M\n2zZjKHg9dXFXIkmWMa4qxLxuTTzF5iKGsJ622BOnJEnoEh2kbFrL/aFe/MOjpIZiMOmaTZwwMyyn\nNxjo6+ykt72dtOwsrHZ7vDRaQS6BnviCLmIxjFkL56mVDXrCPj+uvn7MiYlY015y5XEsxvCDBjrO\nXsA9OMREazvW1BTMSYlLep6lBphoODxb5ceamkrJ/t3onpMoYyHB/iEmTl5Ak0A1xfDroH7/AQxm\nM6dOHUPT4NChI1itS9/Trmka4c5evMcvEO7oRTabsOzcgmXHZnSvkIL1+xKMZ0gmI4bCXIyVZUh6\nfbwKT88AoUfx7YCSxYzObiM7O4fm9jZG5Sh5OnO8CpvVgjEjlaTUVLxuN0O9PUTCIbLzCxb/3pNl\nbBlppFdWYEpw4B+fxNnXz0RLO6bEhBf2ZVmnw5qThbOlHXdXD7FwmP5z3+IfGcWUkkzuvg9I31iD\nfhE3IU/OE2/dG58nbvn6FLFwiFWH5u4QCPYNzi7U0iclkPrhHsz5L14DEu7qI/hIxaiUYFtXSVJR\nASmZKfh8ITweN8eOfcXU1CSVldWvXJP4+2ZFBuOo0/0Lae0a8Yd6gaWeOGVZhz7RQYNnDGnChWF0\nCt2UB2Np4WxgTcvMwuZIoLezg562NpJT03AkxufbTAW5BNq7CPQM4G/rQp/gQJfomPfvZE5OZLSh\nmYDTSfqaiiX/LX2jY7SdPMt4Sxt6k4nM6ko8Q8NMtLajN5mwZaYv+rmWcpzCPh+tx07jHhjCkZPN\nqg+2IcViRL2++D+3l4jLQ2TKTWTKSXhiisj4JOGxScKj44SGxwgNjxIcGGLq0g2IxRjJTmbAM0VZ\nVRWr1qzhwoXzjI2NUltbR2Hh0rdbRSed+M5eIXCnAS0UxrRuDfZDO9FnLr6yzkK+b8F4hmTQY8jN\nwlSlIFnNRMemiPQOEmpsJaR2YIpqOFJTaB8bwp9gJTumJ9DRg95hw5ieSmZeHgPdXQz29JCQnEJ2\nXtaSjpMky1jTUklboyDrZhY6tcdzrudkPneRl95qwZjgwNnagW9wGNloJKt+M9kf1GN6TqGFJ8Xn\niS8xMtBPWXU1ZVVVdJz5Bt/oGLmbN5CmxKdJYqEQzsu3cF6+Fa/stb6KlAM70S9yysN34Toxtxfb\nvm2zWRJtNhNDQ2McO/YHXC4XNTXrqa3dKs7vT1lqMF5yCUVFURKBfwAcgBH4b1VVvf6839E0TRsb\n8yzpdd5HL1PuLhaLcfLkMQZ6e8hs6SfREyKxopS8n35KasZ387sD3d1cO3eWWCzGlg92UVg2/WYN\nBHHffojnUTNoGuaCXBK2bcKQ/OwVfue5i4y3tlN6eD9JhfnPfH/e9oUjDNy5x/CDx2hajDSljLyt\nW+KLYkZGaTt5lrDPT0blavK31S5qCHyh46RpGjGvj4jbS9Tlxtc/RN+lq4QnnVgcDqJ6Hc6pSdLS\n07E5Epa+wESS0GoUrj2+jz0xkf2f/ojOznYuXDhPdnYOR478YGll2EIhArcfEXjQHK9glJ+Ndfum\nOYVOXtVKKqH4JmnRaHyrYVt3fM1GOIKmabQN9dFLmFWbNpIx5kYLR0jesw2rUoJzcpKzn/8eWZb5\n43/xM4Lhl5/n9E9M0X3hMp6REfRmM/n1taSUlTy3P0w0NBPx+khdV7nkdJmdajO3Ll4kJSOD3Ud/\nwOjDx/TduE1CXi5lRw4gSRKB7j6mLlwn6vVhSEkiaU/9knLUR4ZGcf/uJIaCHOxH984+rtdH+NWv\nfoPX62Hjxs2sX/9yUz7fd+npjjdbz1hRlF8AE6qq/ntFUcqB36iquvEFv6a9DyeEV/WyJ05N0xgZ\nGaa1uRn3iW8wjkziT7QR3raesooKVq0qw2KxMDo0xOVTJwkHg6zbWk959XfZz8Ljkziv3CbYNwiS\nhL1mNY5Na+es3vWNT9D42y+wZ2VS8cmRF7bL1TdA96UrBF1uTAkOCnduIyFv7lRF0O2m/cQ5fBMT\nJBbkUbJv1wsXxCQQZrh9gIjLHb+zdbqJujxEPV60aLyYe8jtYVRtxe/1ErGamdLD2OQEASmGrNOT\nnJJCenY2jqQk7MlJOJKScSQnY7JZkXS6+FC/TgZZN/1RBquZ8+fP4PN62fODjzFZLfzud/+Mpml8\n+ulPFl0kXdM0wi2d+K/dJeb1IztsWLZtwlDy/Ln7l/G+BOMnaeEI4d4Bwm3d+Ns6aXz4kEg4TOnq\n1chOLyTYSfnkANayYrpaWrh54RvyinLZuHMfpldYo6LFYow0NNF/8w6xSISkwnwKtm99pcIj8xns\n7eXq6VPIej37f/gpeP2ofziBwWJm9Y8/RifJOK/cxqfGF7k5Nq2N55de4qIqz8mLhNt7sH+8H0Ne\nfGvpxMQEly6dZnR0ktraraxdu+61/t++T95GME4EgqqqBhRFqQT+k6qq21/wayIYL8LrOHGGg0H6\nfvMF4w8fM0yEwfJcJIOe/PwCysrKcdjsXDtzBr/Xy+p166na/F0pM03TCHT24Lxyh6jbg2y1kFC7\nHmvFdxm2Wo+fxtnTR8UnR7BnzV97OhII0nftJmNqa3zuuqaKnI3rF0yaEA2F6Dh7AWdPH9aUFEoP\n71vwBOa+10DkQcMzQ4qy2YTOYccva/T39jDwqBFPOEi0OBe3zYRrZIQERwKF5eV0dLThdbmQNcjK\nzCLB8V0QNVutJKelkZSSSlJaGslpadgc8aH7W5cu0tnczOr1G6jatIkTJ76mv7+PHTt2UVGxuMQh\nkdEJ/N/eIjI4Ajod5g2VmNdXPpOL/HV5H4Pxk7RwhP4797nz+Zcke8MoOQUE27vAYCBh/w7sOzZz\nv7mBod5OJJ2J+n37SU5beorbJwVdbrovXsHVP4DOYCSvbhNpa5TXcqE1E4gBth86REpKKk2//ZKw\nP0D50UPoAyGmLt0g5vNjSEsheU89hkWu8dA0Db/fj8/nxTs8Suh3JwlaTEzWVuLz+/D7fUxNTWE0\nytTUbKay8vmpjN93rzUYK4ryF8BfPfXwn6uqekdRlCzgOPCXqqp++4LXEcF4EV7XiVOLRvGeuYyv\nuZ0JnUZzlp3RqQkATCYzudk5jPf0oEVjlFRUsHH7jjkrILVIFM/9x7jvPkKLRDFmpJG4fTPGrHTc\nA0OofzhOUmEBpYf3zX1dTWOyvZPeKzcI+/1Y01Ip+mAb1vQXn9y0WIzeKzcYedyEwWqh9NC+2YT2\nM4JDI4x9foqEjCSkVaviW31sFsa8bnqH+unu7sLd1gW9g0g6HZn1mzEnJzPa3Y3ZbKZ2z17yiovx\n+/3cvn0TVW0iHAySlpJKYW4B4YCfqfFxvO65fwODyURicjJjQ0Mkp6Wx5+NPaG5u5OrVyxQUFHLg\nwOEXnmhjgSCBG/cJPm4FTcNQko9l26aX2q60FO97MJ5x69YNHty5TUViOlWyDdfZbyESxVJahDE/\nB6dD5vrIIDqDgU07ds5O47wsTdMYV1vpu3qTSCiEIyebwp31S16s+KTBnh6unDmNRDwQZ+Tk0nrs\nNK6+fnLWVWN2+fG3dcX3CG9Zh33dmnmnfXp6uhkfH8Pn8+Lz+fB64x/9fh+xWAyA1M4hEocmGS7L\nxZsWv1jV6/XYbHb27NlBWtrL1WB+n7zxO2MARVGqgd8A/0ZV1VOL+JWlv4jwSrRolMljF/E1d2DK\ny4JdG1E72mlubsbr9RIJhxnr7sVmtrCqrIxN2+spUcrn7A8Mu72MXbyBu6kdgISqclK3b6Thq9O4\nB0fY+Oc/wTa9YjPgctN27jITHd3Ieh2F9ZvJ27h2ydug+u8+ov3CVWS9DuXQbtLL41svooEgPb/6\nnIjLQ+rHexmOBOjo6KCnp4dQKISmacj9I9g9IdJzsqn/8z+mq6eHh7duYzab2fvxUTKy52ZxGxkZ\n4eLFiwwMDCDLMhs2bGDTpk1osRgTo2OMj4wwPjLKxMgozqkp9Ho9H/3xT0An8+tf/xq9Xs9Pf/pT\n7AsUZ5/ha2zHee4aUX8AQ2oSiXvqML9kQXnh5USjUT777DOGhoY4cOAABSYbff/ld0RGJrBlp6O3\nWfHnZ3BzcpBQKMSa9TVs2r7tlffLBj1e2s59y3hb15z3RTQW4/jx44RCIQ4fPozN9vwV+H2dXZz/\n6hhIEvs+PkpOQT491+/QefkmDrOFRHRE/QEsORlkHNyJaZ6tcJqmceXKFe7cuTPncZ1Oh81m++6f\nzoDtzC2MiQ6SfvYD7A4Hdrsdo9Eo5oaX5o0PU68Bfg/8WFXVR4v8NXFnvAiv+y5Gi8XwnblCqK0L\nfVY69qN70PR6+vv7aG1toaOjjcH2DryTk8iyTGJyMqWVVVRv2Eh+YdFsubPgwDDOyzcJj00iGfRI\nORn0dXeRvlqh8INtjDY203/jDtFwmIScbAo+2IZ5katC5zPV3Uvn2QtEw2HyajeRUVPF1Jlv8bd3\nM5Hm4HHMidvtByAhIYGCvAIMfcNo406sqcmUHNzDg7t36Glrw5GYyPZDh3Ekzn9HomkaHR3t3Lx5\nDY/Hg9VqY8uWWkpLy+eceMKhENFoFKPJxNdff8nw8BB79uxn1arSeZ935rkDtx8RuPkAyaDHvHkt\nprUVr1TycanEnfF3XC4Xn3/+WzRN44c//DEml4+J4+fRIlFSLEb8vhDStg3caGvENTlJenY2dXv3\nYbG+/NYymB4x6uii59JVwi43lgQHg9EgwyPDoGlYExPZuXcfCWlpyEZjvCiM0TB7Ifv0HXF6Vjbu\n/kFavzyObnCctMxMdBYTCbXrsVXPnwdgpq6wqjaRlJRMbe1W7HYHVqsVk8k0p6/7bz4gcOshlh2b\nMa+teOa5RJ9anLcxZ/wFsBbonn5oSlXVH77g10QwXoQ30cm1WAzfuauEWjrRZ6ZhO7p3NgVmKBSi\nq6uDro521EcPGejqJhaNxvM/pySTW1JCQfEqMjIySU9LwzIyhff2Q6L+AGMdnUTSk7GXleAZHUVv\nMpFXv4XU8tLXcvXsGxun7cRZQl4viVY7FpeP0ViI2+YIaenJFBdXUFhYiN1kof3UOTzDIzhyssnb\nWc/Ny5cYHRggNTOT7QcPLWpRTiQS5sGD+zx4cJ9oNEJ6egb19dtnU43OuH//HrduXWfVqlL27Nm/\n4PNpsRj+izcJNrYiO2zYj+5FN88K9TdNnDjnam1t4cKFc6SnZ3D06CeE+4eYOP4NpmgY792m+OK9\ndRX0jQ4zMTWJwWpF2biBxIwMJJMR2WiYDpbxoBl/L0nEAgGi/gCxJ/5Fn/o86vXj7OtnpLMTj9eL\nJT2NxNxshgYH0MsyBXmFmI1GYpEIsUgUTQKv18NQ/wCapJGWnYPJYiGqxUCWkEanSC0txlG+iqTd\nW9EvcAEciUT45ptzdHV1kJ6ezsGDRxasK6yFIzh/9XvQNBJ/9um8+RJEn1qctzJM/RJEMF6EN9XJ\ntVgM3/lrhNQOdBmp8Uxo8+Sk9no83L91g8d37zI2PIzX68VgMZOYkY4tKQm93kC6xUZ2+zCmxjYY\nnSRSmEPCT45QsL0Ww3PuIIKBABMjI4wNDzM+PIxzcoKMnBzKq6pJzZx/IVjI66Ptd1/hvXgTnxah\ndW0h1ox0fvazPyES0RN0uWk9dpqA00lKaQnpG9dx5expXJOT5BWXsGX37iVvX/J43Ny8eZ329jYA\nysoUNm+uxWazMT4+zpdf/g6TycSPfvQTzOYFTmihMN7T3xLu7keXnoL9yB5k2/IUVRcnzmdduHCe\n1laVmpr1bNlSR6B3EF1PN+OP2og8aAajAamihLHBQUb6+0GSyMrPJzktbekXmpKEzmpGtsT/9Y+O\n0PjwAQaPj7y8fHQ6Pc7xcUYGB9BpElkZGVhNZohECXo8TI6MIMc0kpNTMNtsyHo9sl6HpNdjy0gn\n48hebJXlC7YrFApx9uwp+vv7yMnJZf/+Qxifs1sh+EjFd+km5o3VWOrmXykt+tTiLDUYv9+ZvN8T\nkixj3bMVZIlQUzueL8/Giw48Vf3FZrezbfde6nftYai3l5aGR3S3txGYcmJp6iIpFMXiD+MEJFnD\npEWxDo6SoMnon7jS1jQNj9PJ2Eg88I4PD+OcmPiuPZKEyWKht72d3vZ2UjMyKauuIreoeM4cncFk\nIllnZCwWpivsw9g/wv4f/YTk5GS6HnfQduIsYb+frHVrsZQU8M2xrwj4fChra1hbW/tSd+h2u4M9\ne/azenUl169fpbVVpaurg3XrNtDR0U40GmXHjl0LBuKYL4Dn2HmiI+Po87OxH9q5YGEQYXnU129n\neHiQhw/vk5eXT05+LukbyjFsq8N35Q7B+40YCvPJ/pd/ynB3N7fOnqXF66MgO5HVVWuRojG0YIhY\nKEQsGAJNmw22Oot5zueS6bt51s7ODm6da8eyuZJ9hz7C09qBf3ySVJORROcUDxsbaNVJbNhSR4Ij\ngbs3riEZFLYfPkxmfj6EI8SmX1cLhtCnJKF7TjrZQMDPyZPHGR0doaiomN279z334lSLxQg8aAKd\nDlP1s8UlhDdL3BmvIG/6ilPTNHzfXCfU1IYuLTl+h2yZfwg36vIQ7ujB9aiZsYYmpsbHicWihBNs\n6FYVYCgvxv24Bfu5m0QTbKR+cgBJKZ69+w36/bPPpTcYSEnPIDUzk7SsLFIzMjAYjYwMDNDa8IjB\nnh40TcNis1FaWUVJRQUms5mpSzdQT5yhkyCRRDsl1kSsDgcVu2ppPHcVLRolf3sdEbs1ntAkGmVd\nfT1llVWv5XjFYjFaWlRu376Bf/r/oyir2blz1/zHbMqF5+vzxJxujEoJ1t11b3V+eD7iLmZ+IyPD\nfPXVF5jNFn70ox+Tn5/B6KgbLRbD8+VZIgPDWOrWYd5Yjdft5uqZ00yOjZGSkcHWffuxvWDR3tOG\nhgY5fvxrZFnio48+Ji3t2YxzQ0ODnD59konhYXTBEDk5uWw/eJDM3KUv9vN6PZw48TWTk5OUlyvs\n2PHinNGhtm68py5hWlOGdXfdgj8n+tTiiGHqd9jb6OSapuG/eIPg49ZnAnJ0ykW4vYdQew/R0fH4\nL0gS+uwMpIJs+sN+2jrbZrf+pKanE/viHHT0MWw3gFKMcU0pVrs9Hngzs0jLyiIxJeW5JwK300nb\n4wY6VZVIOIxOr6cgIRn/1XuMhf3Etm/g8Ecf4+/po/vSNawWA/5QlJJ9uxjzubl35QqyTkfd3r3k\nFha99mMWDAa5f/8OLpeLDz7YM+8wX2R4DM+xb9D8AcwbqzDXrlsRK0/FiXNhM/P/hYVF/Omf/piZ\nLIExXwD3Px8j5vNj/2gPhoIcIpEIdy9fpqtFxWSxULdnL5m5i6v6NTU1yR/+8AXhcIgDBw6Tn1+w\n4M82P3rIr//+/yYcDrPj4CEOHf14yYUXpqYmOXHiazweD9XVNYtKValpGu7PThAdnSDhT44+d32D\n6FOLI4LxO+xtdXJN0/BfukmwoQVdahKGVYWE27uJjk/Ff0CS0OdlYVxViKE4H9n63d1zLBZjsKeH\nloZHjA4MYB+eJKG9n6nJKQI2E6k7atnxL37+UrVMQ8EgnapK6507eE5cxO/14q4p5ejPf05RaRmS\nJOHqHyDQ1YG1tJz2rg7UBw8wWSxsP3hoTvrPtync3Y/35EW0aAzrzi2YqsqXpR3zESfOhWmaxvHj\nXzEw0M9HHx0iO/u7vOKR4THcvz+FZNDj+PGH6BIdaJpGe1Mj965eBWDtllrKq6ufG+h8Ph9fffU5\nLpeLnTt3oyjPrk6eMdDdzdWzZwhHIoT0MqFYlKKiEnbv3rvo99PY2CgnTx7D7/ezeXMtNTXrF3VR\nGB4YxvP5aQzF+dg/3PXcnxV9anGWGozfSqEI4Bffx2T1r9vbSuovSRL6wly0YIhwVx+R/mG0YAhD\nQQ7mjdVY92zFXFmOPiP1mcxQkiSRkJREcblCsVJB6batpHiCpKVn4NKi+Dp76OjuJGvNakxLzLer\n0+tJSklh5NRFAuMTuPMzSC4uZKinh972NiRZIq2ggML1a7hy4Vs6mppwJCWx66OjJKW8XCWpVxVs\nasN7+jJIEraDOzApJcvSjoV8XwtFvA6SJJGbm0trawu9vd2EQvFV9LIsI9utyDYL4bZuIgPDGJUS\nJJ2OlPQMMnJyGezpob+zE7dziqz8/Hn3I4fDYU6ejA8Vb9iwierqmgXbMhOIJUnigw+PsKmunpGR\nYfr6ehgeHqSgoOiFAXlgoJ+TJ48RCoXYseMDqqoWrlP8NP/Fm8Scbmy7ty5Y5naG6FOLsyKrNiGC\n8aK8zU4uSRL6ghxkhw1jaRHW3XWY1pSiT09BWuRVuMFojFenkSQYHCWvbiMepxtfaydqeyv2glyS\nkhZf9CAajXLp73+Ju6EZa1kxH//NX1NUVk4sGmNseIiB7m7amxrp7+ygr6uH9Oxsdn54BOsLEia8\nCTN7iP2XbyOZTTiO7lmRJULFifP5jEYj6ekZjIwM0tbWTkdHO4mJiSQmJqJPT0Xz+gl39xPz+DBM\n1/222e0UrCplfGSYod5eultbGB0cwDkxQcDnQ9M0dHo958+fYXBwEEWpoK6ufsHA+GQg3n7wEJm5\nuej1ekpKSnG5nPT29tDb20NBQSFG4/wXuF1dnZw5cwpNi7F7917Kyha/ACs6MYX/8m30WelYahe+\nYJgh+tTiiGD8DnvbnVySJPTpKehSk5H0L7/QSJeaTLCxDSZdFP7Zp0QHhvG1daF2d+A3GcjJyX3h\nFXokEuGbzz7Dd/k29vQ0dvz3f4nZZsVitZJbVERxRQUGg4HJsTGCAR9ZBUXU79uPYRlWKs/uIb7f\niOyw4fhkP/r01LfejsUQJ84XczgS2LJlA263fzYhzsTEBJmZmVhXFRHuHSDSM4BsNqHPjKd2NRiN\nFJaVEY1EmBwdZWp8nLGhIfq7OmlvauLMH76k5fFjLAYTZcWlBP1+NE3DYDTOmQOeLxDPkGWZ4uIS\nQqEQPT1ddHZ2kJub/8we4ZaWZi5cOI8sSxw4cHjJZTz91+4SHZvEsn0zupQX74UXfWpx3ngJxZck\n5owX4V2eiwnce4z/6l3Mm6rRlxfT8+vPaW98zEhOMkk1a9i9e/+CKf/C4TBnjn9N6NS3JJltbPmr\nf4W1YP4VpNFIBL0cIiZZlmWB1EraQ7wY73KfeptmjtP4+DhXr37L0NAger2e9es3UVm0Cu/vT6EF\ngjg+OYA+Z+7aBE3TCAYCOCcmcE1Ncv/ObR4/eoBB1lFSVDJnCFuSJKwOBwlJydgcdjqam+cNxE8/\n/6NHD7hx4xomk4n9+w+R6UgEnY7HbSrXr1/FZDJz6NCHzySpeZGY14fzV58jJ9hJ+JOjr1TCVJhL\nLOB6h73LnVwLhXH+wxcQjZHwsx8S9XgZ/v0JOlWVrhQzcmEOu3fvIydn7gknFApx+tQx/JdukxXT\nUfXjT0iu2/Dc11qu4/TkHmJDQQ62gztW/B7id7lPvU1PHidN02htVbl58zp+v5+kpGTqSyqw3mhA\ntppx/PhDZNv8CW5aW1UuXDiPw+Hg6NEfopNlXJOTuKYmcU7EP7omJ2e3/un0+ucG4rnP3cK1UydJ\n7hunypGKNxBAxU9oVT4Hf/AJKS+xbsJ/7S6Bu4+xflC76IWHok8tjkj6ISwLyWjAvG4N/mt3CT5o\nwrKlhswfHkL+Usbe28vjvmGOH/+KTZtqqamJb/sJBoOcPHkMd0MzRZqe8vo6krasrPqomqYRc3uJ\njk/iv3Invoe4YhXWXbXLvodYeDMkSaK8vILCwmLu3LlJY+Njjt+9xhqjhdJJJ/Kpb7F/vO+Zv39f\nXy+XLl3AZDJx8OCHsyNBZouF6W3BmwAADCpJREFUjJy56wkCfj/uqSmsDsei9ixHp1zkdI/ygUdH\nx7iLJq8fXSRKJjrKsnRYO/vR7I5501cuRAuFCDa0IlnMGFfYwsP3kQjGwmtjqioncO8xwQfNmGpW\nY0hLIe3oPvjDGWxTUzz0hbl16zrDw4PU1dVz/vxZpnp6KXdHKa6oIOXAziVXeXpdNE1D8/mJjk8R\nnZgiOuEkNjFFdNKJFgrP/px5YzXm2poVsYdYeLNMJhP19TsoL6/gypVvaRweYnJkmLLJCXJTErHv\n+i4xxvj4GOfOnUaSJPbvP0Ry8vPvUs0WC+YF8kM/Keb24r/1kFBzO2gayYUFrNm3jbPND7CaLdTm\nlkJjG/7r9wk+bMa8sRpjZdmiLhSDjW1ooRDmLTVvrJ62sHhimHoF+T4M/wTuNOC/fg/zlhosm9cC\nEBoaZeyrM4QDQdQEPd0hD5IkoUWiVI4FKEhOI/XwbiwlCydDeNKrHqeYL0B0YioebKcDb3R8Ci30\n1KIUSUKXnIguJQk5JRF9dgaGvKz5n3SF+j70qbfhRcdJ0zRUtZlb166QdkclQZMp+pMfkru9Do/H\nzZdffo7P52Xv3v2UlCxcyWuxYh4vgTsN8YWRsRi6pATMW2owlBYiSRLRaBRZluPvo1CIwL1Ggg+a\n0MIR5AQ7li01GMqKFry41aJRXP/wJVogQMLPPl0wE998RJ9aHDFMLSwrU7VC4H4jwftNmNZWIJuM\nGLPSST2yl/Gvz1Lpi5FSWMyDwW6qJCt5yXbs1RWLDsQvI+bxEu4ZINwzQGRgBM0fmPsDkoSc6ECf\nl4kuJQldahK65CTkJIcYihaA+NB1RcVqioqKuJN1Ad/np2n65T/RMT7CcNiPz+elrq7+lQNxzOsn\ncLeB4ONWiEaREx2YN1VjLC+eE1jnLAozGrHUrsNUXUHgziOCDS14z15Bd+8x5tp1GIrynhnJCbd1\nE/N4MVUrSwrEwpsjgrHwWsXnjlfHh80eqVg2VQNgyskk9cM9jB87T07vOCWVG/A/bMKQlkzC1o2v\ntQ1aNEpkcJRITz/h7gGiE1Oz35PtNvRFeehSZu54k9AlJ77S1i7h/WE2W9h2+DBD6dl0/vL/w3ny\nIs61xVStW//cpB4vEvMHCN57TPCRihaJItttmDdXzyYbWQzZasa6YzOmmtUEbj0kpHbgPX4BfVY6\n5q3rMeTEV1prmkbgfiNIEqaa1S/dZuH1EsFYeO1M1Up82Ox+I+a1yuyKY1NeNimHdzFx/Bv8D5uQ\n9DqS9+9Afg3zVVGXh0h3f/zut38ILRyJf0Onw1CQg74gB0NBDnJSgpjvFV5Z1qZ1JEQ0+k99Q55b\nosgv4b92F8lkQjIbZz/KT3yNXvdM34sFQwTvPzHEbLNiqa/CuKb0pUdldAl2bHvrMa9fg//GfcId\nvfFUlwU5mOvWofmDRMcmMZYWokt0vI7DIbwGIhgLr51kNGJat4bAjfjdsXlj9ez3zAW5pBzaxdTF\n6yTUrceQsvgMXU/SwhEiA8Px4NszQHTKNfs9OSkB43Tw1edkisUpwhthqV1H9nRxlXBTO+EX/YJO\nh/xEoJaMxvi0SSiEbDVjrl2HqbL8tY3S6FKSsB/eRWRoFP/1+7NTNdL0sLRpfeVreR3h9RBnKeGN\nMK9VCN5vJHCvEVO1Mmc/rrkoj6yiP3qp542OTTJ28SrO5i60SBQAyaDHUJQ3ewcsrvaFt0GSJGwH\nd6L5g2jBIFogGK9zPP1Re+rjzOMxrw9t0gmahmQ2Ydm6AVNV+ZK2JS2FPisd+8f7iPQO4r9+n+jo\nOPrcLPQZKzNr3PtKBGPhjZCMxvjc1c0HBB+1YN746jWGw119eE9/i8UgIyc4Zoee9dkZYs5XWBaS\nJCFZzWBd2iIoTdPQgiEkg/6tLBKUJCn+XsnPJjI48twSicLyEMFYeGNMaysI3m8icH/m7vjlrvw1\nTSP4sBn/lTsgy6T8YA/e1GeLswvCu0KSJCTz0qqava7XnVnIJawsy5NhQXgvyCYjppoKtECQYEPL\nSz2HFo3iv3gD/+XbyBYTjh8ewFohsgUJgvD9IoKx8EaZalYjGY0E7jfOyWS1GLFgCM+xbwg+bkWX\nmoTjjz6crZojCILwfSKCsfBGzd4d+wMEHy/+7jg65cLz2QkivYMYCnNxfHrwhUXPBUEQ3lUiGAtv\nnGltBZLRQODe4u6OwwPDuH93kuiUC1PNamwf7lrx1ZEEQRBehQjGwhsnm02Y1i7u7jjY3I7ny7No\nwRDWXXVYt29atuIRgiAIb4s4ywlvRXzuePrueCY71hM0TcN//R6+c1eR9HrsR/diqixbhpYKgiC8\nfSIYC2+FbDZhqp7/7lgLR/CeukTgTgNyogPHHx3CkJ+9TC0VBEF4+0QwFt4aU00FkkFP8N7j2bvj\nmNeH+/PThNt70Odk4PjRIZGQQBCE944IxsJbI1vMmNZWEPMFCDa2EhmdwP3bE0RHxzFWrML+g32i\nnJsgCO8lkYFLeKtMNasJPmwmcPsRRO+jhSNY6tZj2lApqikJgvDeEnfGwlslW8yYqhW0QBA0Dduh\nDzBvrBKBWBCE95q4MxbeupmiEYbSQvTponKMIAiCCMbCWycZjVi2bljuZgiCIKwYYphaEARBEJaZ\nCMaCIAiCsMxEMBYEQRCEZSaCsSAIgiAsMxGMBUEQBGGZiWAsCIIgCMtMBGNBEARBWGYiGAuCIAjC\nMhPBWBAEQRCWmQjGgiAIgrDMRDAWBEEQhGX20rmpFUWpAK4DGaqqhl5fkwRBEATh/fJSd8aKoiQA\nfwsEXm9zBEEQBOH9s+RgrCiKBPwn4N8B/tfeIkEQBEF4zzx3mFpRlL8A/uqph7uBf1JV9aGiKACi\nKrwgCIIgvAJJ07Ql/YKiKK1A3/SXdcANVVV3veZ2CYIgCMJ7Y8nB+EmKonQCiljAJQiCIAgv71W3\nNr18JBcEQRAEAXjFO2NBEARBEF6dSPohCIIgCMtMBGNBEARBWGYiGAuCIAjCMhPBWBAEQRCW2Uvn\npn4RRVFk4P8C1gJB4L9WVbX9Tb3eu05RlLuAc/rLDlVV/2I527PSKIpSC/xvqqruVhSlFPglEAMa\ngP9GVVWxEpFnjtN64Cugdfrb/1FV1X9evtatDIqiGID/AhQCJuB/AZoQfWqOBY5TH/A10DL9Y6JP\nAYqi6ID/DJQT32X0r4nHvV+yyD71xoIx8AlgVFW1fvoE8bfTjwlPURTFDKCq6u7lbstKpCjKvwX+\nK8Az/dDfAX+tquolRVH+I/Ax8MVytW+lmOc4bQT+TlXVv1u+Vq1IPwVGVVX9M0VRkoEHwD1En3ra\nfMfpfwb+VvSpZ3wExFRV3a4oygfA/zr9+KL71Jscpt4GnARQVfUGsOkNvta7rgawKopySlGUc9MX\nL8J32oBP+S716gZVVS9Nf34C2LcsrVp5nj5OG4EjiqJcVBTl7xVFsS9f01aU3wJ/M/25DIQRfWo+\n8x0n0afmoarql8C/mv6yCJgENi6lT73JYJwAuJ74Ojo9dC08ywv876qqHiQ+vPGP4lh9R1XV3wOR\nJx56Mh+6B0h8uy1ameY5TjeA/05V1Q+ADuB/WpaGrTCqqnpVVfUoiuIgHnD+R+aeC0WfYt7j9D8A\nNxF9al6qqkYVRfkl8H8C/8gSz1Nv8oTvAhxPvpaqqrE3+HrvshbifzxUVW0FxoHsZW3RyvZkP3IA\nU8vVkBXuc1VV701//gWwfjkbs5IoipIPnAd+parqbxB9al5PHad/QvSp51JV9c8BBfh7wPzEt17Y\np95kML4CfAigKEod8PANvta77ufE59RRFCWH+KjC4LK2aGW7Nz0vA3AYuPS8H36PnVQUZfP053uB\n28vZmJVCUZRM4DTwb1VV/eX0w6JPPWWB4yT61DwURfkzRVH+3fSXfiAK3F5Kn3qTC7g+B/YrinJl\n+uufv8HXetf9P8D/qyjKzB/r52IUYV4zKxH/DfCfFUUxAo3AZ8vXpBVp5jj9a+A/KIoSJn5x9y+X\nr0kryl8THzL8G0VRZuZE/xL496JPzTHfcfor4P8QfeoZnwG/VBTlImAg3p+aWcJ5SuSmFgRBEIRl\nJhYJCYIgCMIyE8FYEARBEJaZCMaCIAiCsMxEMBYEQRCEZSaCsSAIgiAsMxGMBUEQBGGZiWAsCIIg\nCMvs/wchhMGU+9cu9QAAAABJRU5ErkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 29 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you pass a list of error styles to `tsplot`, it will compose them" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.tsplot(gammas[gammas.condition == \"pos\"], time=\"time\", unit=\"subj\", value=\"BOLD\",\n", - " err_style=[\"unit_traces\", \"ci_band\"]);" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 30 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, it's also possible to plot each individual observation with a point, rather than joining them. This is more useful for the gestalt it presents than as a quantitative visualization but you may prefer it." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "sns.tsplot(sines, err_style=\"unit_points\", color=\"mediumpurple\");" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n+/8BXf7ZHacenoQAAAAASUVORK5CYII=\n", - "text": [ - "" - ] - } - ], - "prompt_number": 31 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - } - ], - "metadata": {} - } - ] -} diff --git a/ipnbdoctest.py b/ipnbdoctest.py deleted file mode 100644 index d3bd143ef4..0000000000 --- a/ipnbdoctest.py +++ /dev/null @@ -1,342 +0,0 @@ -#!/usr/bin/env python -""" -simple example script for running and testing notebooks. - -Usage: `ipnbdoctest.py foo.ipynb [bar.ipynb [...]]` - -Each cell is submitted to the kernel, and the outputs are compared with those -stored in the notebook. - -From https://gist.github.com/minrk/2620735 - -""" -from __future__ import print_function -import os -import sys -import re -import difflib -import base64 - -from collections import defaultdict -from io import StringIO, BytesIO -from six.moves import queue -from six import string_types - -from IPython.kernel import KernelManager -from IPython.nbformat.current import reads, NotebookNode - -SKIP_COMPARE = ('traceback', 'latex', 'prompt_number') -IMAGE_OUTPUTS = ('png', 'svg', 'jpeg') - - -def sanitize(s): - """Sanitize a string for comparison. - - - Fix universal newlines - - Strip trailing newlines - - Normalize likely random values (memory addresses and UUIDs) - - """ - if not isinstance(s, string_types): - return s - - # Formalize newline: - s = s.replace('\r\n', '\n') - - # Ignore trailing newlines (but not space) - s = s.rstrip('\n') - - # Normalize hex addresses: - s = re.sub(r'0x[a-f0-9]+', '0xFFFFFFFFF', s) - - # Normalize UUIDs: - s = re.sub(r'[a-f0-9]{8}(\-[a-f0-9]{4}){3}\-[a-f0-9]{12}', 'U-U-I-D', s) - - return s - - -def consolidate_outputs(outputs): - """consolidate outputs into a summary dict (incomplete)""" - data = defaultdict(list) - data['stdout'] = '' - data['stderr'] = '' - - for out in outputs: - if out.type == 'stream': - data[out.stream] += out.text - elif out.type == 'pyerr': - data['pyerr'] = dict(ename=out.ename, evalue=out.evalue) - else: - for key in ('png', 'svg', 'latex', 'html', - 'javascript', 'text', 'jpeg',): - if key in out: - data[key].append(out[key]) - return data - - -def base64_to_array(data): - """Convert a base64 image to an array.""" - import numpy as np - from PIL import Image - try: - data = StringIO(base64.b64decode(data)) - except TypeError: - data = BytesIO(base64.b64decode(data)) - return np.array(Image.open(data)) / 255. - - -def image_diff(test, ref, key="image", prompt_num=None): - """Diff two base64-encoded images.""" - if test == ref: - return True, "" - - message = "Mismatch in %s output" % key - if prompt_num is not None: - message += " (#%d)" % prompt_num - - try: - test = base64_to_array(test) - ref = base64_to_array(ref) - if test.shape == ref.shape: - import numpy as np - diff = np.abs(test - ref).mean() * 100 - # TODO hardcode tol, make configurable later - if diff < 5: - return True, "" - message += ": %.3g%% difference" % diff - else: - message += ": Test image (%dx%d)" % test.shape[:2] - message += "; Ref image (%dx%d)" % ref.shape[:2] - except ImportError: - pass - return False, message - - -def compare_outputs(test, ref, prompt_num=None, skip_compare=SKIP_COMPARE): - """Test whether the stored outputs match the execution outputs.""" - match, message = True, "" - - # Iterate through the reference output fields - for key in ref: - - # Don't check everything - if key in skip_compare: - continue - - # Report when test output is missing a field - if key not in test: - match = False - msg = "Mismatch: '%s' field not in test output" % key - if prompt_num is not None: - msg += " (#%d)" % prompt_num - message += msg + "\n" - continue - - # Obtain the field values - test_value = test[key] - ref_value = ref[key] - - # Diff images seperately - if key in IMAGE_OUTPUTS: - - # As of June 2014, changes in IPython have broken the tests - # vs. the reference notebooks, and testing on IPython 1 - # doesn't work as conda doesn't package it for Python 3.4. - # To avoid rerunning the reference notebooks, I'm going to - # skip the image diffs for the time being until I find a - # better solution. - continue - mtch, msg = image_diff(test_value, ref_value, key, prompt_num) - match = match and mtch - message += msg - - else: - - # Clean up some randomness and check the match - test_value = sanitize(test_value) - ref_value = sanitize(ref_value) - if test_value == ref_value: - continue - - # Build a textual diff report - match = False - diff = difflib.context_diff(test_value.split("\n"), - ref_value.split("\n"), - "Test output", - "Reference output", - n=1, lineterm="") - message += "Mismatch in textual output" - if prompt_num is not None: - message += " (#%d)\n" % prompt_num - message += " " + "\n ".join(diff) + "\n" - - return match, message - - -def run_cell(shell, iopub, cell): - # print cell.input - shell.execute(cell.input) - # wait for finish, maximum 20s - shell.get_msg(timeout=30) - outs = [] - - while True: - try: - msg = iopub.get_msg(timeout=0.2) - except queue.Empty: - break - msg_type = msg['msg_type'] - if msg_type in ('status', 'pyin'): - continue - elif msg_type == 'clear_output': - outs = [] - continue - - content = msg['content'] - # print msg_type, content - out = NotebookNode(output_type=msg_type) - - if msg_type == 'stream': - out.stream = content['name'] - out.text = content['data'] - elif msg_type in ('display_data', 'pyout'): - out['metadata'] = content['metadata'] - for mime, data in content['data'].items(): - attr = mime.split('/')[-1].lower() - # this gets most right, but fix svg+html, plain - attr = attr.replace('+xml', '').replace('plain', 'text') - setattr(out, attr, data) - if msg_type == 'pyout': - out.prompt_number = content['execution_count'] - elif msg_type == 'pyerr': - out.ename = content['ename'] - out.evalue = content['evalue'] - out.traceback = content['traceback'] - else: - print("unhandled iopub msg:", msg_type) - - outs.append(out) - return outs - - -def test_notebook(nb): - """Main function to run tests at the level of one notebook.""" - # Boot up the kernel, assume inline plotting - km = KernelManager() - km.start_kernel(extra_arguments=["--matplotlib=inline", - "--colors=NoColor"], - stderr=open(os.devnull, 'w')) - - # Connect, allowing for older IPythons - try: - kc = km.client() - kc.start_channels() - iopub = kc.iopub_channel - except AttributeError: - # IPython 0.13 - kc = km - kc.start_channels() - iopub = kc.sub_channel - shell = kc.shell_channel - - # Initialize the result tracking - successes = 0 - failures = 0 - errors = 0 - fail_messages = [] - err_messages = [] - - # Iterate the notebook, testing only code cells - for ws in nb.worksheets: - for cell in ws.cells: - if cell.cell_type != 'code': - continue - - # Try and get the prompt number for easier reference - try: - prompt_num = cell.prompt_number - except AttributeError: - prompt_num = None - - # Try to execute the cell, catch errors from test execution - try: - outs = run_cell(shell, iopub, cell) - except Exception as e: - message = "Error while running cell:\n%s" % repr(e) - err_messages.append(message) - errors += 1 - sys.stdout.write("E") - continue - - errored = False - failed = False - - for out, ref in zip(outs, cell.outputs): - - # Now check for an error in the cell execution itself - bad_error = (out.output_type == "pyerr" - and not ref.output_type == "pyerr") - if bad_error: - message = "\nError in code cell" - if prompt_num is not None: - message = " %s (#%d)" % (message, prompt_num) - message = "%s:\n%s" % (message, "".join(out.traceback)) - err_messages.append(message) - errored = True - - # Otherwise check whether the stored and achived outputs match - else: - try: - match, message = compare_outputs(out, ref, prompt_num) - if not match: - failed = True - fail_messages.append(message) - - except Exception as e: - message = "Error while comparing output:\n%s" % repr(e) - err_messages.append(message) - errors += 1 - sys.stdout.write("E") - continue - - if failed: - failures += 1 - dot = "F" - elif errored: - errors += 1 - dot = "E" - else: - successes += 1 - dot = "." - print(dot, end="") - - print() - print(" %3i cells successfully replicated" % successes) - if failures: - print(" %3i cells mismatched output" % failures) - if errors: - print(" %3i cells failed to complete" % errors) - if failures: - print("Failures:") - print("-" * 20) - print("\n" + "\n".join(fail_messages) + "\n") - if errors: - print("Errors:") - print("-" * 20) - print("\n" + "\n".join(err_messages) + "\n") - kc.stop_channels() - km.shutdown_kernel() - del km - - return int(bool(failures + errors)) - -if __name__ == '__main__': - - status = 0 - for ipynb in sys.argv[1:]: - print("testing %s" % ipynb) - with open(ipynb) as f: - nb = reads(f.read(), 'json') - - status += test_notebook(nb) - sys.exit(status) From 1ceac69f2d28564d310cca9c0fa521ffcf7ac29e Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 24 Jun 2015 07:27:29 -0700 Subject: [PATCH 0051/1738] Don't test for overlaps with no tick labels --- seaborn/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/seaborn/utils.py b/seaborn/utils.py index 3a2841f06c..372d210012 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -462,6 +462,8 @@ def axis_ticklabels_overlap(labels): True if any of the labels overlap. """ + if not labels: + return False bboxes = [l.get_window_extent() for l in labels] overlaps = [b.count_overlaps(bboxes) for b in bboxes] return max(overlaps) > 1 From 5b99ef78b0982c03448b967dc6c98361f0244896 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 27 Jun 2015 14:43:04 -0700 Subject: [PATCH 0052/1738] Add new multiple bivariate KDE example --- examples/multiple_joint_kde.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 examples/multiple_joint_kde.py diff --git a/examples/multiple_joint_kde.py b/examples/multiple_joint_kde.py new file mode 100644 index 0000000000..7877d6b281 --- /dev/null +++ b/examples/multiple_joint_kde.py @@ -0,0 +1,31 @@ +""" +Multiple bivariate KDE plots +============================ + +_thumb: .6, .4 +""" +import seaborn as sns +import matplotlib.pyplot as plt + +sns.set(style="darkgrid") +iris = sns.load_dataset("iris") + +# Subset the iris dataset by species +setosa = iris.query("species == 'setosa'") +virginica = iris.query("species == 'virginica'") + +# Set up the figure +f, ax = plt.subplots(figsize=(8, 8)) +ax.set_aspect("equal") + +# Draw the two density plots +ax = sns.kdeplot(setosa.sepal_width, setosa.sepal_length, + cmap="Reds", shade=True, shade_lowest=False) +ax = sns.kdeplot(virginica.sepal_width, virginica.sepal_length, + cmap="Blues", shade=True, shade_lowest=False) + +# Add labels to the plot +red = sns.color_palette("Reds")[-2] +blue = sns.color_palette("Blues")[-2] +ax.text(2.5, 8.2, "virginica", size=16, color=blue) +ax.text(3.8, 4.5, "setosa", size=16, color=red) From a03c66c771ab17b9ad6290136cf14ab750c4050b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 27 Jun 2015 14:43:29 -0700 Subject: [PATCH 0053/1738] Remove defunct notebook tester --- examples/Makefile | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 examples/Makefile diff --git a/examples/Makefile b/examples/Makefile deleted file mode 100644 index 780a114267..0000000000 --- a/examples/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -test: - - python ipnbdoctest.py *.ipynb - -pat = 'Figure at 0x[a-f0-9]*>' -rep = 'Figure at 0xFFFFFFFFF>' -sed = sed s/$(pat)/$(rep)/ | sed s/}\n\r/}/ -dir = . - -hexstrip: - - cat $(dir)/aesthetics.ipynb | $(sed) > $(dir)/aesthetics_sed.ipynb - mv $(dir)/aesthetics_sed.ipynb $(dir)/aesthetics.ipynb - - cat $(dir)/linear_models.ipynb | $(sed) > $(dir)/linear_models_sed.ipynb - mv $(dir)/linear_models_sed.ipynb $(dir)/linear_models.ipynb - - cat $(dir)/plotting_distributions.ipynb | $(sed) > $(dir)/plotting_distributions_sed.ipynb - mv $(dir)/plotting_distributions_sed.ipynb $(dir)/plotting_distributions.ipynb - - cat $(dir)/timeseries_plots.ipynb | $(sed) > $(dir)/timeseries_plots_sed.ipynb - mv $(dir)/timeseries_plots_sed.ipynb $(dir)/timeseries_plots.ipynb From a80c508acc05375d3af34c9d0b0256054df4d149 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 27 Jun 2015 14:43:46 -0700 Subject: [PATCH 0054/1738] Revamp homepage --- doc/index.rst | 51 ++++++++++++++++++++------------------------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index ba8d1a57e9..02afaedbfc 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -61,7 +61,11 @@ Seaborn: statistical data visualization

- + +
+
+
+
Seaborn is a Python visualization library based on matplotlib. It provides a high-level interface for drawing attractive statistical graphics. @@ -69,32 +73,17 @@ high-level interface for drawing attractive statistical graphics. For a brief introduction to the ideas behind the package, you can read the :ref:`introductory notes `. More practical information is on the :ref:`installation page `. You may also want to browse the -:ref:`example gallery ` to get a sense for what kind of plots -you can make with seaborn. - -For a higher-level perspective on the various tools that are included in the -library, you can read through the :ref:`tutorial `. Much more detail -about individual tools can be found in the :ref:`API reference `, -which includes documentation for all the functions along with short examples of -how to use them. +:ref:`example gallery ` to get a sense for what you can do with seaborn and then check out the :ref:`tutorial ` and :ref:`API reference ` to find out how. -To check out the code or report a bug, please visit the `github repository +To see the code or report a bug, please visit the `github repository `_. General support issues are most at home on `stackoverflow `_, where there is a seaborn tag. .. raw:: html -
-
-
-

Documentation

-
-
-

Tutorial

-
-
-
+
+

Documentation

.. toctree:: :maxdepth: 1 @@ -104,21 +93,21 @@ To check out the code or report a bug, please visit the `github repository installing examples/index api + tutorial .. raw:: html
-
- -.. toctree:: - :maxdepth: 1 - - tutorial/aesthetics - tutorial/color_palettes - tutorial/distributions - tutorial/regression - tutorial/categorical - +
+

Features

+ +* Style functions: :ref:`API ` | :ref:`Tutorial ` +* Color palettes: :ref:`API ` | :ref:`Tutorial ` +* Distribution plots: :ref:`API ` | :ref:`Tutorial ` +* Regression plots: :ref:`API ` | :ref:`Tutorial ` +* Categorical plots: :ref:`API ` | :ref:`Tutorial ` +* Axis grid objects: :ref:`API ` | :ref:`Tutorial ` + .. raw:: html
From e7fdb886b7b56937f28a1bda55650869c7d9cc96 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 27 Jun 2015 14:44:14 -0700 Subject: [PATCH 0055/1738] Remove defunct notebook tester, part 2 --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 02282d12bf..1d71ae1874 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,6 @@ test: cp testing/matplotlibrc . nosetests --with-doctest - python ipnbdoctest.py examples/*.ipynb rm matplotlibrc From 99c8ed5f7469120329e080661581a9a776461df8 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 27 Jun 2015 14:44:34 -0700 Subject: [PATCH 0056/1738] Update README --- README.md | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index fa6e020840..1fa0ccefb1 100644 --- a/README.md +++ b/README.md @@ -30,22 +30,14 @@ Seaborn is a Python visualization library based on matplotlib. It provides a hig Documentation ------------- -Online documentation is available [here](http://stanford.edu/~mwaskom/software/seaborn/). +Online documentation is available [here](http://stanford.edu/~mwaskom/software/seaborn/). It includes a high-level tutorial, detailed API documentation, and other useful info. -There are docs for the development version [here](http://stanford.edu/~mwaskom/software/seaborn-dev/). These should more or less correspond with the github master branch, but they're not currently built automatically and thus may fall out of sync at times. Also, note that the API docs should always stay up to date, but the tutorials may lag behind. +There are docs for the development version [here](http://stanford.edu/~mwaskom/software/seaborn-dev/). These should more or less correspond with the github master branch, but they're not built automatically and thus may fall out of sync at times. Examples -------- -The documentation has an [example gallery](http://stanford.edu/~mwaskom/software/seaborn/examples/index.html) with short scripts showing how to use different parts of the package. You can also check out the example notebooks: - -- [Controlling figure aesthetics in seaborn](http://nbviewer.ipython.org/github/mwaskom/seaborn/blob/master/examples/aesthetics.ipynb) - -- [Graphical representations of linear models](http://nbviewer.ipython.org/github/mwaskom/seaborn/blob/master/examples/linear_models.ipynb) - -- [Visualizing distributions of data](http://nbviewer.ipython.org/github/mwaskom/seaborn/blob/master/examples/plotting_distributions.ipynb) - -- [Plotting statistical timeseries data](http://nbviewer.ipython.org/github/mwaskom/seaborn/blob/master/examples/timeseries_plots.ipynb) +The documentation has an [example gallery](http://stanford.edu/~mwaskom/software/seaborn/examples/index.html) with short scripts showing how to use different parts of the package. Citing ------ @@ -72,8 +64,6 @@ Dependencies - [statsmodels](http://statsmodels.sourceforge.net/) -- [patsy](http://patsy.readthedocs.org/en/latest/) - Installation ------------ @@ -93,9 +83,7 @@ Testing [![Build Status](https://travis-ci.org/mwaskom/seaborn.png?branch=master)](https://travis-ci.org/mwaskom/seaborn) To test seaborn, run `make test` in the source directory. This will run the -unit-test suite (using `nose`). It will also execute the example notebooks and -compare the outputs of each cell to the data in the stored versions. - +unit-test and doctest suite (using `nose`). Development ----------- From c5d4eb6f931d08ebdddf343ee0383dae2866e46b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 27 Jun 2015 17:19:52 -0700 Subject: [PATCH 0057/1738] Remove JointGrid from axis grid tutorial --- doc/tutorial/axis_grids.ipynb | 196 ++++------------------------------ 1 file changed, 22 insertions(+), 174 deletions(-) diff --git a/doc/tutorial/axis_grids.ipynb b/doc/tutorial/axis_grids.ipynb index 795d1b688d..2f2cb0496f 100644 --- a/doc/tutorial/axis_grids.ipynb +++ b/doc/tutorial/axis_grids.ipynb @@ -4,7 +4,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - ".. _axis_grids:\n", + ".. _grid_tutorial:\n", "\n", ".. currentmodule:: seaborn" ] @@ -24,7 +24,7 @@ "\n", "To use these features, your data has to be in a Pandas DataFrame and it must take the form of what Hadley Whickam calls `\"tidy\" data `_. In brief, that means your dataframe should be structured such that each column is a variable and each row is an observation.\n", "\n", - "For advanced use, you can use the objects discussed in this part of the tutorial directly, which will provide maximum flexibility. Some seaborn functions (such as :func:`lmplot`, :func:`factorplot`, :func:`pairplot`, and :func:`jointplot`) also use them behind the scenes. Unlike other seaborn functions that are \"Axes-level\" and draw onto specific (possibly already-existing) matplotlib ``Axes`` without otherwise manipulating the figure, these higher-level functions create a figure when called and are generally more strict about how it gets set up. In some cases, arguments either to those functions or to the constructor of the class they rely on will provide a different interface attributes like the figure size, as in the case of :func:`lmplot` where you can set the height and aspect ratio for each facet rather than the overall size of the figure. Any function that uses one of these objects will always return it after plotting, though, and most of these objects have convenience methods for changing how the plot, often in a more abstract and easy way." + "For advanced use, you can use the objects discussed in this part of the tutorial directly, which will provide maximum flexibility. Some seaborn functions (such as :func:`lmplot`, :func:`factorplot`, and :func:`pairplot`) also use them behind the scenes. Unlike other seaborn functions that are \"Axes-level\" and draw onto specific (possibly already-existing) matplotlib ``Axes`` without otherwise manipulating the figure, these higher-level functions create a figure when called and are generally more strict about how it gets set up. In some cases, arguments either to those functions or to the constructor of the class they rely on will provide a different interface attributes like the figure size, as in the case of :func:`lmplot` where you can set the height and aspect ratio for each facet rather than the overall size of the figure. Any function that uses one of these objects will always return it after plotting, though, and most of these objects have convenience methods for changing how the plot, often in a more abstract and easy way." ] }, { @@ -170,7 +170,7 @@ "source": [ "Note that ``margin_titles`` isn't formally supported by the matplotlib API, and may not work well in all cases. In particular, it currently can't be used with a legend that lies outside of the plot.\n", "\n", - "The size of the figure is set by providing the height of the facets and the aspect ratio:" + "The size of the figure is set by providing the height of *each* facet, along with the aspect ratio:" ] }, { @@ -201,17 +201,17 @@ "outputs": [], "source": [ "titanic = sns.load_dataset(\"titanic\")\n", - "titanic = titanic.sort(\"deck\")\n", + "titanic = titanic.assign(deck=titanic.deck.astype(object)).sort(\"deck\")\n", "g = sns.FacetGrid(titanic, col=\"class\", sharex=False,\n", " gridspec_kws={\"width_ratios\": [5, 3, 3]})\n", - "g.map(sns.boxplot, \"deck\", \"age\")" + "g.map(sns.boxplot, \"deck\", \"age\");" ] }, { "cell_type": "raw", "metadata": {}, "source": [ - "By default, the facets are plotted in the sorted order of the unique values for each variable, but you can specify an order:" + "The default ordering of the facets is derived from the information in the DataFrame. If the variable used to define facets has a categorical type, then the order of the categories is used. Otherwise, the facets will be in the order of appearence of the category levels. It is possible, however, to specify an ordering of any facet dimension with the appropriate ``*_order`` parameter:" ] }, { @@ -222,10 +222,10 @@ }, "outputs": [], "source": [ - "days = [\"Thur\", \"Fri\", \"Sat\", \"Sun\"]\n", - "g = sns.FacetGrid(tips, row=\"day\", hue=\"day\", palette=\"Greens_d\",\n", - " size=1.7, aspect=4, hue_order=days, row_order=days)\n", - "g.map(sns.distplot, \"total_bill\");" + "ordered_days = tips.day.value_counts().index\n", + "g = sns.FacetGrid(tips, row=\"day\", row_order=ordered_days,\n", + " size=1.7, aspect=4,)\n", + "g.map(sns.distplot, \"total_bill\", hist=False, rug=True);" ] }, { @@ -293,7 +293,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "Once you've drawn a plot using :meth:`FacetGrid.map` (which can be called multiple times), you may want to adjust some aspects of the plot. There are also a number of methods on the :class:`FacetGrid` object for manipulating the figure at a higher level of abstraction. The most general is :meth:`FacetGrid.set`, and there are other more specialized methods like :meth:`FacetGrid.set_axis_labels`. For example:" + "Once you've drawn a plot using :meth:`FacetGrid.map` (which can be called multiple times), you may want to adjust some aspects of the plot. There are also a number of methods on the :class:`FacetGrid` object for manipulating the figure at a higher level of abstraction. The most general is :meth:`FacetGrid.set`, and there are other more specialized methods like :meth:`FacetGrid.set_axis_labels`, which respects the fact that interior facets do not have axis labels. For example:" ] }, { @@ -316,7 +316,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "For even more customization, you can work directly with the underling matplotlib ``Figure`` and ``Axes`` objects, which are stored as member attributes at ``fig`` and ``axes`` (a two-dimensional array), respectively." + "For even more customization, you can work directly with the underling matplotlib ``Figure`` and ``Axes`` objects, which are stored as member attributes at ``fig`` and ``axes`` (a two-dimensional array), respectively. When making a figure without row or column faceting, you can also use the ``ax`` attribute to directly access the single axes." ] }, { @@ -452,7 +452,8 @@ " cmap = sns.light_palette(color, as_cmap=True)\n", " plt.hexbin(x, y, gridsize=15, cmap=cmap, **kwargs)\n", "\n", - "g = sns.FacetGrid(tips, hue=\"time\", col=\"time\", size=4)\n", + "with sns.axes_style(\"dark\"):\n", + " g = sns.FacetGrid(tips, hue=\"time\", col=\"time\", size=4)\n", "g.map(hexbin, \"total_bill\", \"tip\", extent=[0, 50, 0, 10]);" ] }, @@ -469,7 +470,7 @@ "\n", "It's important to understand the differences between a :class:`FacetGrid` and a :class:`PairGrid`. In the former, each facet shows the same relationship conditioned on different levels of other variables. In the latter, each plot shows a different relationship (although the upper and lower triangles will have mirrored plots). Using :class:`PairGrid` can give you a very quick, very high-level summary of interesting relationships in your dataset.\n", "\n", - "The basic usage of the class is very similar to :class:`FacetGrid`. First you initialize the grid, then you pass plotting function to a ``map`` method and it will be called on each subplot. There is also a companion function, :func:`pairplot` (see :ref:`below `), that trades off some flexibility for faster plotting.\n" + "The basic usage of the class is very similar to :class:`FacetGrid`. First you initialize the grid, then you pass plotting function to a ``map`` method and it will be called on each subplot. There is also a companion function, :func:`pairplot` that trades off some flexibility for faster plotting.\n" ] }, { @@ -482,7 +483,7 @@ "source": [ "iris = sns.load_dataset(\"iris\")\n", "g = sns.PairGrid(iris)\n", - "g.map(plt.scatter)" + "g.map(plt.scatter);" ] }, { @@ -502,7 +503,7 @@ "source": [ "g = sns.PairGrid(iris)\n", "g.map_diag(plt.hist)\n", - "g.map_offdiag(plt.scatter)" + "g.map_offdiag(plt.scatter);" ] }, { @@ -523,7 +524,7 @@ "g = sns.PairGrid(iris, hue=\"species\")\n", "g.map_diag(plt.hist)\n", "g.map_offdiag(plt.scatter)\n", - "g.add_legend()" + "g.add_legend();" ] }, { @@ -542,7 +543,7 @@ "outputs": [], "source": [ "g = sns.PairGrid(iris, vars=[\"sepal_length\", \"sepal_width\"], hue=\"species\")\n", - "g.map(plt.scatter)" + "g.map(plt.scatter);" ] }, { @@ -563,7 +564,7 @@ "g = sns.PairGrid(iris)\n", "g.map_upper(plt.scatter)\n", "g.map_lower(sns.kdeplot, cmap=\"Blues_d\")\n", - "g.map_diag(sns.kdeplot, lw=3, legend=False)" + "g.map_diag(sns.kdeplot, lw=3, legend=False);" ] }, { @@ -603,15 +604,13 @@ "source": [ "g = sns.PairGrid(tips, hue=\"size\", palette=\"GnBu_d\")\n", "g.map(plt.scatter, s=50, edgecolor=\"white\")\n", - "g.add_legend()" + "g.add_legend();" ] }, { "cell_type": "raw", "metadata": {}, "source": [ - ".. _pairplot:\n", - "\n", ":class:`PairGrid` is flexible, but to take a quick look at a dataset, it can be easier to use :func:`pairplot`. This function uses scatterplots and histograms by default, although a few other kinds will be added (currently, you can also plot regression plots on the off-diagonals and KDEs on the diagonal)." ] }, @@ -643,157 +642,6 @@ "source": [ "g = sns.pairplot(iris, hue=\"species\", palette=\"Set2\", diag_kind=\"kde\", size=2.5)" ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ".. _joint_grid:\n", - "\n", - "Plotting bivariate data with :class:`JointGrid` and :func:`jointplot`\n", - "---------------------------------------------------------------------\n", - "\n", - "The :class:`JointGrid` can be used when you want to plot the relationship between or joint distribution of two variables along with the marginal distribution of each variable. :class:`JointGrid` is supplemented by the :func:`jointplot` function, which will likely suffice for many exploratory cases (see :ref:`below `). It can be helpful to know how to the :class:`JointGrid` class works, though, to have the best understanding of how to use these two tools.\n", - "\n", - "Like :class:`FacetGrid`, initializing the object sets up the axes but does not plot anything:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "g = sns.JointGrid(\"total_bill\", \"tip\", tips)" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "The easiest way to use :class:`JointPlot` is to call :meth:`JointPlot.plot` with three arguments: a function to draw a bivariate plot, a function to draw a univariate plot, and a function to calculate a statistic that summarizes the relationship." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "g = sns.JointGrid(\"total_bill\", \"tip\", tips)\n", - "g.plot(sns.regplot, sns.distplot, stats.pearsonr);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "For more flexibility, you can use the separate methods :meth:`JointGrid.plot_joint`, :meth:`JointGrid.plot_marginals`, and :meth:`JointGrid.annotate`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "g = sns.JointGrid(\"total_bill\", \"tip\", tips)\n", - "g.plot_marginals(sns.distplot, kde=False, color=\".5\")\n", - "g.plot_joint(plt.scatter, color=\".5\", edgecolor=\"white\")\n", - "g.annotate(stats.spearmanr, template=\"{stat} = {val:.3f} (p = {p:.3g})\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "To control the presentation of the grid, use the ``size`` and ``ratio`` arguments. These control the size of the full figure (which is always square) and the ratio of the joint axes height to the marginal axes height:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "g = sns.JointGrid(\"total_bill\", \"tip\", tips, size=4, ratio=30)\n", - "color = \"#228833\"\n", - "g.plot_marginals(sns.rugplot, color=color, alpha=.7, lw=1)\n", - "g.plot_joint(plt.scatter, color=color, alpha=.7, marker=\".\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "The ``space`` keyword argument controls the amount of padding between the axes with the joint plot and the two marginal axes:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "g = sns.JointGrid(\"total_bill\", \"tip\", tips, space=0)\n", - "g.plot_marginals(sns.kdeplot, shade=True)\n", - "g.plot_joint(sns.kdeplot, shade=True, cmap=\"PuBu\", n_levels=40);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ".. _jointplot:\n", - "\n", - "The :func:`jointplot` function can draw a nice-looking plot with a single line of code:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.jointplot(\"total_bill\", \"tip\", tips);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "It can draw several different kinds of plots, with good defaults chosen for each:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.jointplot(\"total_bill\", \"tip\", tips, kind=\"hex\", color=\"#8855AA\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "In many cases, :func:`jointplot` should be sufficient for exploratory graphics, but it may easier to use :class:`JointGrid` directly when you need more flexibility than is offered by the canned styles of :func:`jointplot`." - ] } ], "metadata": { @@ -812,7 +660,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", - "version": "2.7.9" + "version": "2.7.10" } }, "nbformat": 4, From c9f8e12af26817b91fcb3145db334cc100ed4abf Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 27 Jun 2015 17:20:04 -0700 Subject: [PATCH 0058/1738] Assorted doc tweaks --- doc/_static/style.css | 10 ++++++++ doc/api.rst | 44 +++++++++++++++++++++----------- doc/introduction.rst | 4 +-- doc/releases/v0.6.0.txt | 2 +- doc/tutorial/categorical.ipynb | 8 +++--- doc/tutorial/distributions.ipynb | 6 ++--- doc/tutorial/regression.ipynb | 10 ++++---- examples/scatterplot_matrix.py | 2 +- 8 files changed, 55 insertions(+), 31 deletions(-) diff --git a/doc/_static/style.css b/doc/_static/style.css index bc579f4e06..f168489b34 100644 --- a/doc/_static/style.css +++ b/doc/_static/style.css @@ -17,3 +17,13 @@ code { border-color: #adb8cb !important; color: #2c3e50 !important; } + +.headerlink:before { + + display: block; + context: " "; + height: 200px; /*same height as header*/ + margin-top: -200px; /*same height as header*/ + visibility: hidden; + +} diff --git a/doc/api.rst b/doc/api.rst index 54f972b81c..3290dc4ba0 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -5,6 +5,22 @@ API reference ============= +.. _distribution_api: + +Distribution plots +------------------ + +.. autosummary:: + :toctree: generated/ + + jointplot + pairplot + distplot + kdeplot + rugplot + +.. _regression_api: + Regression plots ---------------- @@ -13,11 +29,12 @@ Regression plots lmplot regplot - pairplot - interactplot residplot + interactplot coefplot +.. _categorical_api: + Categorical plots ----------------- @@ -25,23 +42,14 @@ Categorical plots :toctree: generated/ factorplot - pointplot - barplot - countplot boxplot violinplot stripplot + pointplot + barplot + countplot -Distribution plots ------------------- - -.. autosummary:: - :toctree: generated/ - - jointplot - distplot - kdeplot - rugplot +.. _matrix_api: Matrix plots ------------ @@ -68,6 +76,8 @@ Miscellaneous plots palplot +.. _grid_api: + Axis grids ---------- @@ -78,6 +88,8 @@ Axis grids PairGrid JointGrid +.. _style_api: + Style frontend -------------- @@ -93,6 +105,8 @@ Style frontend reset_defaults reset_orig +.. _palette_api: + Color palettes -------------- diff --git a/doc/introduction.rst b/doc/introduction.rst index d6680919c1..761c5d22b7 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -15,7 +15,7 @@ Some of the features that seaborn offers are - Several :ref:`built-in themes ` that improve on the default matplotlib aesthetics - Tools for choosing :ref:`color palettes ` to make beautiful plots that reveal patterns in your data -- Functions for visualizing :ref:`univariate ` and :ref:`bivariate ` distributions or for :ref:`comparing ` them between subsets of data +- Functions for visualizing :ref:`univariate ` and :ref:`bivariate ` distributions or for :ref:`comparing ` them between subsets of data - Tools that fit and visualize :ref:`linear regression ` models for different kinds of :ref:`independent ` and :ref:`dependent ` variables - Functions that visualize :ref:`matrices of data ` and use clustering algorithms to :ref:`discover structure ` in those matrices - A function to plot :ref:`statistical timeseries ` data with flexible estimation and :ref:`representation ` of uncertainty around the estimate @@ -25,7 +25,7 @@ Seaborn aims to make visualization a central part of exploring and understanding data. The plotting functions operate on dataframes and arrays containing a whole dataset and internally perform the necessary aggregation and statistical model-fitting to produce informative plots. If matplotlib "tries to -make easy things easy and hard things possible", seaborn aims to make a +make easy things easy and hard things possible", seaborn tries to make a well-defined set of hard things easy too. The plotting functions try to do something useful when called with a minimal diff --git a/doc/releases/v0.6.0.txt b/doc/releases/v0.6.0.txt index b4ebbe379b..c36c21042b 100644 --- a/doc/releases/v0.6.0.txt +++ b/doc/releases/v0.6.0.txt @@ -53,7 +53,7 @@ Other additions and changes - :func:`jointplot` now passes additional keyword arguments to the function used to draw the plot on the joint axes. -- Changed the default ``linewidths`` in :func:`heatmap` and :`clustermap` to 0 so that larger matrices plot correctly. This parameter still exists and can be used to get the old effect of lines demarcating each cell in the heatmap (the old default ``linewidths`` was 0.5). +- Changed the default ``linewidths`` in :func:`heatmap` and :func:`clustermap` to 0 so that larger matrices plot correctly. This parameter still exists and can be used to get the old effect of lines demarcating each cell in the heatmap (the old default ``linewidths`` was 0.5). - :func:`heatmap` and :func:`clustermap` now automatically use a mask for missing values, which previously were shown with the "under" value of the colormap per default `plt.pcolormesh` behavior. diff --git a/doc/tutorial/categorical.ipynb b/doc/tutorial/categorical.ipynb index 2a44b27326..07e6f5abfe 100644 --- a/doc/tutorial/categorical.ipynb +++ b/doc/tutorial/categorical.ipynb @@ -4,9 +4,9 @@ "cell_type": "raw", "metadata": {}, "source": [ - ".. currentmodule:: seaborn\n", + ".. _categorical_tutorial:\n", "\n", - ".. _categorical_tutorial::" + ".. currentmodule:: seaborn" ] }, { @@ -97,7 +97,7 @@ "Distributions of observations within categories\n", "-----------------------------------------------\n", "\n", - "The first set of functions shows the full distribution of the quantitative variable within each level of the categorical variable(s). These generalize some of the approaches we discussed in the :ref:`chapter ` to the case where we want to quickly compare across several distributions.\n", + "The first set of functions shows the full distribution of the quantitative variable within each level of the categorical variable(s). These generalize some of the approaches we discussed in the :ref:`chapter ` to the case where we want to quickly compare across several distributions.\n", "\n", "Categorical scatterplots\n", "^^^^^^^^^^^^^^^^^^^^^^^^\n", @@ -198,7 +198,7 @@ "Violinplots\n", "^^^^^^^^^^^\n", "\n", - "A different approach is a :func:`violinplot`, which combines a boxplot with the kernel density estimation procedure described in the :ref:`distributions tutorial `_:" + "A different approach is a :func:`violinplot`, which combines a boxplot with the kernel density estimation procedure described in the :ref:`distributions tutorial `_:" ] }, { diff --git a/doc/tutorial/distributions.ipynb b/doc/tutorial/distributions.ipynb index 53c8561fb5..524430e163 100644 --- a/doc/tutorial/distributions.ipynb +++ b/doc/tutorial/distributions.ipynb @@ -4,7 +4,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - ".. _distributions_tutorial::\n", + ".. _distribution_tutorial:\n", "\n", ".. currentmodule:: seaborn" ] @@ -226,7 +226,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "The bandwidth (``bw``) parameter of the KDE controls how tightly the estimation is fit to the data, much like the bin size in a histogram. It corresponds to the width of the kernels we plotted above. The default value tries to guess a good value using a common reference rule, but it may be helpful to try larger or smaller values:" + "The bandwidth (``bw``) parameter of the KDE controls how tightly the estimation is fit to the data, much like the bin size in a histogram. It corresponds to the width of the kernels we plotted above. The default behavior tries to guess a good value using a common reference rule, but it may be helpful to try larger or smaller values:" ] }, { @@ -397,7 +397,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "If you wish to show the bivariate density more continuously, you can simple increase the number of contour levels:" + "If you wish to show the bivariate density more continuously, you can simply increase the number of contour levels:" ] }, { diff --git a/doc/tutorial/regression.ipynb b/doc/tutorial/regression.ipynb index 543e0d56af..f66077727b 100644 --- a/doc/tutorial/regression.ipynb +++ b/doc/tutorial/regression.ipynb @@ -4,9 +4,9 @@ "cell_type": "raw", "metadata": {}, "source": [ - ".. currentmodule:: seaborn\n", + ".. _regression_tutorial:\n", "\n", - ".. _regression_tutorial::" + ".. currentmodule:: seaborn" ] }, { @@ -20,7 +20,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "Many datasets contain multiple quantitative variables, and the goal of an analysis is often to relate those variables to each other. We :ref:`previously discussed ` functions that can accomplish this by showing the joint distribution of two variables. It can be very helpful, though, to use statistical models to estimate a simple relationship between two noisy sets of observations. The functions discussed in this chapter will do so through the common framework of linear regression.\n", + "Many datasets contain multiple quantitative variables, and the goal of an analysis is often to relate those variables to each other. We :ref:`previously discussed ` functions that can accomplish this by showing the joint distribution of two variables. It can be very helpful, though, to use statistical models to estimate a simple relationship between two noisy sets of observations. The functions discussed in this chapter will do so through the common framework of linear regression.\n", "\n", "In the spirit of Tukey, the regression plots in seaborn are primarily intended to add a visual guide that helps to emphasize patterns in a dataset during exploratory data analyses. That is to say that seaborn is not itself a package for statistical analysis. To obtain quantitative measures related to the fit of regression models, you should use `statsmodels `_. The goal of seaborn, however, is to make exploring a dataset through visualization quick and easy, as doing so is just as (if not more) important than exploring a dataset through tables of statistics." ] @@ -516,7 +516,7 @@ "Plotting a regression in other contexts\n", "---------------------------------------\n", "\n", - "A few other seaborn functions use :func:`regplot` in the context of a larger, more complex plot. The first is the :func:`jointplot` function that we introduced in the :ref:`distributions tutorial `. In addition to the plot styles previously discussed, :func:`jointplot` can use :func:`regplot` to show the linear regression fit on the joint axes by passing ``kind=\"reg\"``:" + "A few other seaborn functions use :func:`regplot` in the context of a larger, more complex plot. The first is the :func:`jointplot` function that we introduced in the :ref:`distributions tutorial `. In addition to the plot styles previously discussed, :func:`jointplot` can use :func:`regplot` to show the linear regression fit on the joint axes by passing ``kind=\"reg\"``:" ] }, { @@ -590,4 +590,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file diff --git a/examples/scatterplot_matrix.py b/examples/scatterplot_matrix.py index 61162150d9..ebafe56274 100644 --- a/examples/scatterplot_matrix.py +++ b/examples/scatterplot_matrix.py @@ -8,4 +8,4 @@ sns.set() df = sns.load_dataset("iris") -sns.pairplot(df, hue="species", size=2.5) +sns.pairplot(df, hue="species") From 62ca4ae177b3f4f561914e45e4a38c2c6fc747cb Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 27 Jun 2015 19:01:20 -0700 Subject: [PATCH 0059/1738] Add examples for clustermap --- seaborn/matrix.py | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index ed700dc37b..a5563aa6fc 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -1059,6 +1059,73 @@ def clustermap(data, pivot_kws=None, method='average', metric='euclidean', Column indices, use: ``clustergrid.dendrogram_col.reordered_ind`` + Examples + -------- + + Plot a clustered heatmap: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set() + >>> flights = sns.load_dataset("flights") + >>> flights = flights.pivot("month", "year", "passengers") + >>> g = sns.clustermap(flights) + + Don't cluster one of the axes: + + .. plot:: + :context: close-figs + + >>> g = sns.clustermap(flights, col_cluster=False) + + Use a different colormap and add lines to separate the cells: + + .. plot:: + :context: close-figs + + >>> cmap = sns.cubehelix_palette(as_cmap=True, rot=-.3, light=1) + >>> g = sns.clustermap(flights, cmap=cmap, linewidths=.5) + + Use a different figure size: + + .. plot:: + :context: close-figs + + >>> g = sns.clustermap(flights, cmap=cmap, figsize=(7, 5)) + + Standardize the data across the columns: + + .. plot:: + :context: close-figs + + >>> g = sns.clustermap(flights, standard_scale=1) + + Normalize the data across the rows: + + .. plot:: + :context: close-figs + + >>> g = sns.clustermap(flights, z_score=0) + + Use a different clustering method: + + .. plot:: + :context: close-figs + + >>> g = sns.clustermap(flights, method="single", metric="cosine") + + Add colored labels on one of the axes: + + .. plot:: + :context: close-figs + + >>> season_colors = (sns.color_palette("BuPu", 3) + + ... sns.color_palette("RdPu", 3) + + ... sns.color_palette("YlGn", 3) + + ... sns.color_palette("OrRd", 3)) + >>> g = sns.clustermap(flights, row_colors=season_colors) + """ plotter = ClusterGrid(data, pivot_kws=pivot_kws, figsize=figsize, row_colors=row_colors, col_colors=col_colors, From 30c0cb18e2bb99b6e974fba47a878b48abc1f782 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 27 Jun 2015 19:13:02 -0700 Subject: [PATCH 0060/1738] Clean generated API files --- doc/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/Makefile b/doc/Makefile index a0df55eeeb..9d1e410d9d 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -44,6 +44,7 @@ clean: -rm -rf example_thumbs/* -rm -rf tutorial/*_files/ -rm -rf tutorial/*.rst + -rm -rf generated/* notebooks: From faefd76ceff9109789135bb9bfb2c8840c7525f9 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 27 Jun 2015 19:13:13 -0700 Subject: [PATCH 0061/1738] Fix barplot timeseries order --- examples/timeseries_of_barplots.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/timeseries_of_barplots.py b/examples/timeseries_of_barplots.py index 722cf00937..528b9f19ad 100644 --- a/examples/timeseries_of_barplots.py +++ b/examples/timeseries_of_barplots.py @@ -15,6 +15,6 @@ years = np.arange(2000, 2015) # Draw a count plot to show the number of planets discovered each year -g = sns.factorplot(x="year", data=planets, palette="BuPu", kind="count", - size=6, aspect=1.5, x_order=years) +g = sns.factorplot(x="year", data=planets, kind="count", + palette="BuPu", size=6, aspect=1.5, order=years) g.set_xticklabels(step=2) From b152ca1ea79b37548c81b2a5bee4e4a22542a080 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 27 Jun 2015 19:23:37 -0700 Subject: [PATCH 0062/1738] Remove dataset_exploration tutorial --- doc/tutorial/Makefile | 1 - doc/tutorial/dataset_exploration.ipynb | 457 ------------------------- 2 files changed, 458 deletions(-) delete mode 100644 doc/tutorial/dataset_exploration.ipynb diff --git a/doc/tutorial/Makefile b/doc/tutorial/Makefile index 1de4491689..1dd93da344 100644 --- a/doc/tutorial/Makefile +++ b/doc/tutorial/Makefile @@ -5,5 +5,4 @@ notebooks: tools/nb_to_doc.py distributions tools/nb_to_doc.py regression tools/nb_to_doc.py categorical - tools/nb_to_doc.py dataset_exploration tools/nb_to_doc.py axis_grids diff --git a/doc/tutorial/dataset_exploration.ipynb b/doc/tutorial/dataset_exploration.ipynb deleted file mode 100644 index 6194d05156..0000000000 --- a/doc/tutorial/dataset_exploration.ipynb +++ /dev/null @@ -1,457 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ".. _dataset_exploration:\n", - "\n", - ".. currentmodule:: seaborn" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visual dataset exploration" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "import seaborn as sns\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "iris = sns.load_dataset(\"iris\")\n", - "flights = sns.load_dataset(\"flights\")\n", - "networks = sns.load_dataset(\"brain_networks\", index_col=0, header=[0, 1, 2])" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ".. _heatmap:\n", - "\n", - "Visualizing matrices with :func:`heatmap`\n", - "-----------------------------------------\n", - "\n", - "Often the easiest thing to do to visualize a reasonably large table of data is to encode the value in each cell with a color and plot a heatmap. This can be accomplished with the :func:`heatmap` function. Note that unlike many other seaborn functions, :func:`heatmap` expects the input data to be a table of values with one variable in the rows and one variable in the columns. Your dataset may be in tidy format, as with the ``flights`` example." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "flights.head()" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Fortunately, it's easy to pivot a dataframe out into rectangular format. Note that this sorts the index by default, so if your index has an ordering that doesn't correspond to its alphabetical order, you may need to reorder things after the pivot operation. It's also possible to collapse over a third variable (using an aggregation like a mean or sum) with the ``pivot_table`` function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "flights_rect = flights.pivot(\"month\", \"year\", \"passengers\")\n", - "flights_rect = flights_rect.ix[flights.month.iloc[:12]]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "flights_rect.head()" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Data in this format can be passed to :func:`heatmap` to produce an easily-interpretable visualization." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.heatmap(flights_rect);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "If you do particularly care about the precise numeric values, you can annotate each cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.heatmap(flights_rect, annot=True, fmt=\"d\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Because the color is an encoding of the data, it's very important to use an appropriate colormap. (See the :ref:`palette tutorial ` for more information about different kinds of color palettes and how to choose one that is appropriate for your data). :func:`heatmap` tries to choose good defaults for your data based off some heuristics about it. For example, if your dataset spans 0, it is assumed that a diverging colormap is more appropriate." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "network_corr = networks.iloc[:, :12].corr()\n", - "sns.heatmap(network_corr);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Note that this might not always be the case! For example, you might be plotting temperature over time in several cities, for which 0 isn't really a meaningful midpoint value. A sequential colormap would be better in that case.\n", - "\n", - "When the data is inferred to be diverging, setting the anchor points preserves symmetry around the midpoint (the defaults depend on the extreme values or values near the midpoints if ``robust`` is ``True``)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.heatmap(network_corr, vmax=.8, square=True);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "There are other uses for diverging colormaps where the midpoint is not 0. For example, you might want to be plotting change \n", - "relative to a specific comparison value. To set the midpoint, pass a value to ``center``, which will imply that the colormap should be diverging." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sns.heatmap(flights_rect, center=flights_rect.loc[\"January\", 1955]);" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ":func:`heatmap` is an Axes-level function, so you can use it in the context of a more complex figure. Plotting the colorbar is optional, and it can also be drawn in a specific existing Axes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "f = plt.figure(figsize=(7, 9))\n", - "gs = plt.GridSpec(15, 1)\n", - "hist_ax = f.add_subplot(gs[:5])\n", - "\n", - "yearly_flights = flights_rect.sum(axis=0)\n", - "hist_ax.bar(range(12), yearly_flights, 1, ec=\"w\", lw=2, color=\".3\")\n", - "hist_ax.set(xticks=[], ylabel=\"flights\")\n", - "\n", - "map_ax = f.add_subplot(gs[5:-2])\n", - "bar_ax = f.add_subplot(gs[-1])\n", - "sns.heatmap(flights_rect, cmap=\"BuGn\", ax=map_ax,\n", - " cbar_ax=bar_ax, cbar_kws={\"orientation\": \"horizontal\"})\n", - "bar_ax.set(xlabel=\"flights\");" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - ".. _clustermap:\n", - "\n", - "Visualizing clustered matrices with :func:`clustermap`\n", - "------------------------------------------------------\n", - "\n", - "Beyond the :func:`heatmap`, you may also be curious how the rows and columns of your rectangular dataset are related to each other. Enter the :func:`clustermap`, which will reorganize the heatmap so that similar entries on the rows and columns are plotted closer together. This can help you discover structure in the dataset.\n", - "\n", - "Let's take a look at the flights data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "cg = sns.clustermap(flights_rect)" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "By default, both the columns and the rows are clustered. For some datasets, though, you may want to preserve the original ordering:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "cg = sns.clustermap(flights_rect, col_cluster=False)" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "This is a little skewed because the number of flights increase by year, so let's ``standard_scale`` the data (i.e. divide all the columns by the maximum so we can compare year-to-year on the same scale). We provide ``1`` to indicate that we want to standard scale the columns, but we could also scale the rows, as in the next example." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "cg = sns.clustermap(flights_rect, standard_scale=1)" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "You could also scale the rows by setting ``standard_scale=0``, to see how the different years cluster together if all the months are normalized across all years." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "cg = sns.clustermap(flights_rect, standard_scale=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We could also normalize the rows by their $Z$-score, which subtracts the mean and divides by the standard deviation of each column, thus standardizing them to have 0 mean and a variance of 1. This is helpful for easily seeing which values are greater than the mean, and which are smaller." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "cg = sns.clustermap(flights_rect, z_score=1)" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Next, you may want a quick way to look at how different years cluster together. Let's annotate the columns by coloring by increasing year." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "col_colors = sns.color_palette('Greens', n_colors=flights_rect.shape[1])\n", - "cg = sns.clustermap(flights_rect, standard_scale=True, col_colors=col_colors)" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Now we see that the first few years of the dataset, 1949, 1950, 1951 and 1953 all cluster together on the right, and we can see this easily because the lighter greens come together.\n", - "\n", - "We can also color the months by seasons, to see which seasons cluster together." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "col_colors = sns.color_palette('Greens', n_colors=flights_rect.shape[1])\n", - "\n", - "season_colors = {'Winter': sns.color_palette('PuBu', n_colors=3),\n", - " 'Spring': sns.color_palette('YlGn', n_colors=3),\n", - " 'Summer': sns.color_palette('YlOrBr', n_colors=3),\n", - " 'Fall': sns.color_palette('OrRd', n_colors=3)}\n", - "\n", - "month_colors = {'January': season_colors['Winter'][1],\n", - " 'February': season_colors['Winter'][2],\n", - " 'March': season_colors['Spring'][0],\n", - " 'April': season_colors['Spring'][1],\n", - " 'May': season_colors['Spring'][2],\n", - " 'June': season_colors['Summer'][0],\n", - " 'July': season_colors['Summer'][1],\n", - " 'August': season_colors['Summer'][2],\n", - " 'September': season_colors['Fall'][0],\n", - " 'October': season_colors['Fall'][1],\n", - " 'November': season_colors['Fall'][2],\n", - " 'December': season_colors['Winter'][0]}\n", - "row_colors = pd.Series(flights_rect.index).map(month_colors)\n", - "\n", - "cg = sns.clustermap(flights_rect, standard_scale=True, col_colors=col_colors, row_colors=row_colors)" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Here, it's easy to see that the summer months all cluster together (along with September).\n", - "\n", - "If you like, you can also provide data in long-form, like many other seaborn functions expect. In this case, pass a dictionary of keyword arguments for ``DataFrame.pivot`` and the data will be reshaped behind the scenes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "pivot_kws = dict(index=\"month\", columns=\"year\", values=\"passengers\")\n", - "cg = sns.clustermap(flights, pivot_kws=pivot_kws, standard_scale=1)" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "The data given to cluster must have no ``NaN``s. However, you can mask the data you're plotting, so the visualization doesn't show the ``NaN``s." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "data2d = np.random.randn(32).reshape(4, 8)\n", - "data2d[:2, :2] += 2\n", - "mask = data2d > 1\n", - "sns.clustermap(data2d, mask=mask)" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "The function returns an object of type :class:`ClusterGrid`, which exposes some methods that are useful for postprocessing. For example, if you want to save the figure, you should call the ``savefig`` method on this object rather than ``plt.savefig``, or else the dendrograms will be chopped of.\n", - "\n", - "You can also do useful things like access the mapping between row and column indices in the clustered matrix and those in the original data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "cg.dendrogram_col.reordered_ind" - ] - } - ], - "metadata": {}, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file From b5791da57a6bc74a1c87a976d942630a3b61e985 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 27 Jun 2015 20:01:12 -0700 Subject: [PATCH 0063/1738] Update the contributor guidelines --- CONTRIBUTING.md | 64 ++++++++++++++++++------------------------------- 1 file changed, 23 insertions(+), 41 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6bf695a7c4..909913222e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,56 +1,38 @@ -Contributing code -================= +Contributing to seaborn +======================= -To contribute code to seaborn, it's best to follow the usual github workflow: +General support +--------------- -- Fork the [main seaborn repository](https://github.com/mwaskom/seaborn) -- Create a feature branch with `git checkout -b ` -- Add some new code -- Push to your fork with `git push origin ` -- Open a pull-request on the main repository +General support questions are most at home on [StackOverflow](http://stackoverflow.com/), where they will be seen by more people and are more easily searchable. StackOverflow has a `seaborn` tag, which will bring the question to the attention of people who might be able to answer. -Here are some further notes on specific aspects of seaborn development that are good to know about. +Reporting bugs +-------------- -#### Getting in touch +If you think you have encountered a bug in seaborn, please report it on the [Github issue tracker](https://github.com/mwaskom/seaborn/issues/new). It will be most helpful to include a reproducible script with one of the example datasets (accessed through `load_dataset()`) or using some randomly-generated data. -In general, it can't hurt to get in touch by opening an issue before you start your work. Because seaborn is relatively young, there are a lot of things that I have partially-formed thoughts on, but haven't gotten a chance to fully implement yet. I very much appreciate help, but I'll be more likely to merge in changes that fit into my plans for the package (which might only exist inside my head). So, giving me a heads up about what you have in mind will save time for everyone. +It is difficult debug any issues without knowing the versions of seaborn and matplotlib you are using, as well as what matplotlib backend you are using to draw the plots, so please include those in your bug report. -#### Where to branch +Fixing bugs +----------- -For any new features, or enhancements to existing features, you should branch off `master`. The main repo also has branches corresponding to each point release (e.g. `v0.2`). If you are fixing a bug, it might be better to branch from there so the fix can be included in an incremental release. This will probably get sorted out in the issue reporting the bug. +If you know how to fix a bug you have encountered or see on the issue tracker, that is very appreciated. Please submit a [pull request](https://help.github.com/articles/using-pull-requests/) on the main seaborn repository with the fix. The presence of a bug implies a lack of coverage in the tests, so when fixing a bug, it is best to add a test that fails before the fix and passes after to make sure it does not reappear. See the section on testing below. But if there is an obvious fix and you're not sure how to write a test, don't let that stop you. -#### Working on a Pull Request +Documentation issues +-------------------- -Since seaborn is a plotting package, it's most useful to be able to see the new feature or the consequences of changes your contribution will make. When you open the pull request, including a link to an example notebook (through [nbviewer](http://nbviewer.ipython.org/)) or at least a static screenshot is very helpful. +If you see something wrong or confusing in the documentation, please report it with an issue or fix it and open a pull request. -#### Testing and documentation +New features +------------ -Currently, seaborn uses the notebooks in `examples/` for both documentation and testing. This is proving to be a somewhat problematic solution, and I am worried about many incremental changes to these notebooks producing a large and unwieldy repository. Please try to hold off committing changes to the notebooks until the feature is ready to go. In the meantime, it might be useful to discuss changes in the context of the example notebooks, but please edit them without committing and share via nbviewer from a gist/dropbox link/etc. +If you'd like to add a new feature to seaborn, it's best to open an issue to discuss it first. Given the nature of seaborn's goals and approach, it can be hard to write a substantial contribution that is consistent with the rest of the package, and I often lack the bandwidth to help. Also, every new feature represents a new commitment for support. For these reasons, I'm somewhat averse to large feature contributions. Smaller or well-targeted enhancements can be helpful and should be submitted through the normal pull-request workflow. Please include tests for any new features and make sure your changes don't break any existing tests. -The formal unit-test coverage of the package is quite poor, as the focus has been on using the example notebooks for testing. Going forward, this should change. Please include unit-tests that at least touch the various branches through the functions to ward off errors; in cases where it's possible to programmatically check the outputs of the functions, please do so. +Testing seaborn +--------------- -Once you're ready to update the docs, it's good to add a little narrative information about what a feature does and what kind of visualization problems it can be useful for. Then, provide an example or two showing the function in action. The existing docs should be a good guide here. +Seaborn is primarily tested through a `nose` unit-test suite that interacts with the private objects that actually draw the plots behind the function interface. The basic approach here is to test the numeric information going into and coming out of the matplotlib functions. Currently, there is a general assumption that matplotlib is drawing things properly, and tests are run against the data that ends up in the matplotlib objects but not against the images themselves. See the existing tests for examples of how this works. -If you're unsure where in the documentation your feature should be discussed, please feel free to ask. +To execute the test suite and doctests, run `make test` in the root source directory. You can also build a test coverage report with `make coverage`. -After adding your changes but before committing, please perform the following to steps: - -- Restart the notebook kernel and "run all" cells so you can be certain the notebook executes and the cell numbers are in the right order - -- Run `make hexstrip` to remove the random hex memory identifiers that are stored in the notebook, for a cleaner commit - -- Use `git diff` to make sure your changes didn't result in a cascading change to lots of figures - -Useful commands to know about for testing: - -- `make test` runs the full test suite (unit-tests and notebooks) - -- `nosetests` runs the unit-test suite in isolation - -- `python examples/ipnbdoctest.py examples/.ipynb` can be used to test a specific notebook - -- `make coverage` will run the unit-test suite and produce a coverage report - -- `make lint` will run `pep8` and `pyflakes` over the codebase. Doing so requires [this](https://github.com/dcramer/pyflakes) fork of pyflakes, which can be installed with `pip install https://github.com/dcramer/pyflakes/tarball/master` - -Functions should be documented with the [numpy](https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt) standard. Current functions usually don't have examples, but it would be more useful if they did. +The `make lint` command will run `pep8` and `pyflakes` over the codebase to check for style issues. Doing so requires [this](https://github.com/dcramer/pyflakes) fork of pyflakes, which can be installed with `pip install https://github.com/dcramer/pyflakes/tarball/master`. Is also currently requires `pep8` 1.5 or older, as the rules got stricter and the codebase has not been updated. This is part of the Travis build, and the build will fail if there are issues, so please do this before submitting a pull request. From 34628d2ab88429a79c85354188c8578b75e254a3 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 27 Jun 2015 20:07:16 -0700 Subject: [PATCH 0064/1738] Remove defunct axes_style key in tutorial --- doc/tutorial/aesthetics.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorial/aesthetics.ipynb b/doc/tutorial/aesthetics.ipynb index a5454846df..abdc95b6f5 100644 --- a/doc/tutorial/aesthetics.ipynb +++ b/doc/tutorial/aesthetics.ipynb @@ -336,7 +336,7 @@ }, "outputs": [], "source": [ - "sns.set_style(\"darkgrid\", {\"grid.linewidth\": .5, \"axes.facecolor\": \".9\"})\n", + "sns.set_style(\"darkgrid\", {\"axes.facecolor\": \".9\"})\n", "sinplot()" ] }, @@ -465,4 +465,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} From dce9ee96822858ebb8d17ca588070361a26b2fe3 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 28 Jun 2015 09:54:34 -0700 Subject: [PATCH 0065/1738] Remove IPython 3.1 bug note as they released 3.2 --- doc/installing.rst | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/doc/installing.rst b/doc/installing.rst index cd2eaecd59..89227f558a 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -109,17 +109,10 @@ There is a `bug `_ in the matplotlib OSX backend that causes unavoidable problems with some of the seaborn functions (particularly those that draw multi-panel figures). If you encounter this, you will want to try a `different backend -`_. +`_. In particular, this bug affects any multi-panel figure that internally calls the matplotlib ``tight_layout`` function. An unfortunate consequence of how the matplotlib marker styles work is that line-art markers (e.g. ``"+"``) or markers with ``facecolor`` set to ``"none"`` will be invisible when the default seaborn style is in effect. This can be changed by using a different ``markeredgewidth`` (aliased to ``mew``) either in the function call or globally in the `rcParams`. - -Some changes to the inline plotting backend in IPython 3.0 interfere with the -seaborn style (you'll see plots that appear to have a white axes background and -invisible spines). The solution to this is to call ``%matplotlib inline`` in a -cell *before* importing seaborn, and not to call ``%matplotlib inline`` after -seaborn is imported. I believe these changes have been reverted, so this should -stop being a problem with the release of IPython 3.2. From 4ef72b346ea0d197d513de76d8be89c5742b87a4 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 28 Jun 2015 09:57:45 -0700 Subject: [PATCH 0066/1738] Sphinx formatting --- doc/installing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/installing.rst b/doc/installing.rst index 89227f558a..0127a38fb9 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -68,7 +68,7 @@ Seaborn will apply its default style parameters to the global matplotlib style dictionary when you import it. This will change the look of all plots, including those created by using matplotlib functions directly. To avoid this behavior and use the default matplotlib aesthetics (along with any -customization in your `matplotlibrc`), you can import the `seaborn.apionly` +customization in your ``matplotlibrc``), you can import the ``seaborn.apionly`` namespace. Seaborn has several other pre-packaged styles along with high-level :ref:`tools From b7ade9427be14bc1d064a241c481de9c0b05543e Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 28 Jun 2015 13:35:08 -0700 Subject: [PATCH 0067/1738] Avoid crash in clustermap with matplotlib OSX backend --- seaborn/utils.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/seaborn/utils.py b/seaborn/utils.py index 372d210012..a4243ca002 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -464,9 +464,13 @@ def axis_ticklabels_overlap(labels): """ if not labels: return False - bboxes = [l.get_window_extent() for l in labels] - overlaps = [b.count_overlaps(bboxes) for b in bboxes] - return max(overlaps) > 1 + try: + bboxes = [l.get_window_extent() for l in labels] + overlaps = [b.count_overlaps(bboxes) for b in bboxes] + return max(overlaps) > 1 + except RuntimeError: + # Issue on macosx backend rasies an error in the above code + return False def axes_ticklabels_overlap(ax): From d348ad0f6456534a009cc667218ac398f27a7404 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 28 Jun 2015 13:55:35 -0700 Subject: [PATCH 0068/1738] Draw margin titles with ax.annotate instead of f.text This should keep the titles tighter to the axes when the figure layout changes, such as adding a legend. This PR therefore fixes #303. Also added a background to the text, which should prevent the issue where adding multiple titles "stacks" the row titles (#509). --- seaborn/axisgrid.py | 24 ++++++------------------ seaborn/tests/test_axisgrid.py | 4 ++++ 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 3056e35728..6c6ce6e567 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -205,9 +205,7 @@ def _get_palette(self, data, hue, hue_order, palette): margin_titles : bool, optional If ``True``, the titles for the row variable are drawn to the right of the last column. This option is experimental and may not work in all - cases. If you call ``map`` multiple times when using this option, the - titles will stack; to avoid this, remove figure texts before the final - call to ``map``. See ``set_titles`` for more information.\ + cases.\ """), ) @@ -905,15 +903,6 @@ def set_titles(self, template=None, row_template=None, col_template=None, self: object Returns self. - Note - ---- - - When using margin titles for the row facets, calling this directly - will add titles on top of the existing titles (because the margin - titles aren't really "titles", just figure texts). To avoid that, - you should remove the existing titles first by doing, e.g., - ``plt.setp(fig.texts, text="")``. - """ args = dict(row_var=self._row_var, col_var=self._col_var) kwargs["size"] = kwargs.pop("size", mpl.rcParams["axes.labelsize"]) @@ -938,12 +927,11 @@ def set_titles(self, template=None, row_template=None, col_template=None, ax = self.axes[i, -1] args.update(dict(row_name=row_name)) title = row_template.format(**args) - trans = self.fig.transFigure.inverted() - bbox = ax.bbox.transformed(trans) - x = bbox.xmax + 0.01 - y = bbox.ymax - (bbox.height / 2) - self.fig.text(x, y, title, rotation=270, - ha="left", va="center", **kwargs) + bgcolor = self.fig.get_facecolor() + ax.annotate(title, xy=(1.02, .5), xycoords="axes fraction", + rotation=270, ha="left", va="center", + backgroundcolor=bgcolor, **kwargs) + if self.col_names is not None: # Draw the column titles as normal titles for j, col_name in enumerate(self.col_names): diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index a69877f8f8..1cac82addf 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -462,6 +462,10 @@ def test_set_titles_margin_titles(self): nt.assert_equal(g.axes[0, 1].get_title(), "b = n") nt.assert_equal(g.axes[1, 0].get_title(), "") + # Test the row "titles" + nt.assert_equal(g.axes[0, 1].texts[0].get_text(), "a = a") + nt.assert_equal(g.axes[1, 1].texts[0].get_text(), "a = b") + # Test a provided title g.set_titles(col_template="{col_var} == {col_name}") nt.assert_equal(g.axes[0, 0].get_title(), "b == m") From 7a2a3b7fb8ee836e5a77686a6a1114dcc9dd599b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 28 Jun 2015 14:02:45 -0700 Subject: [PATCH 0069/1738] Default to tight bbox_inches when saving Grid figures --- seaborn/axisgrid.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 6c6ce6e567..9007bd485d 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -29,6 +29,8 @@ def set(self, **kwargs): def savefig(self, *args, **kwargs): """Save the figure.""" + kwargs = kwargs.copy() + kwargs.setdefault("bbox_inches", "tight") self.fig.savefig(*args, **kwargs) def add_legend(self, legend_data=None, title=None, label_order=None, From b556bc9f17d2748b7bcee60040a5dd33e1a2ebba Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 28 Jun 2015 14:02:50 -0700 Subject: [PATCH 0070/1738] Update release notes --- doc/releases/v0.6.0.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/releases/v0.6.0.txt b/doc/releases/v0.6.0.txt index c36c21042b..f96e3ae1a8 100644 --- a/doc/releases/v0.6.0.txt +++ b/doc/releases/v0.6.0.txt @@ -79,7 +79,7 @@ Other additions and changes - The various property dictionaries that can be passed to ``plt.boxplot`` are now applied after the seaborn restyling to allow for full customizability. -- Added a ``savefig`` method to :class:`JointGrid` that defaults to a tight bounding box to make it easier to save figures using this class. +- Added a ``savefig`` method to :class:`JointGrid` that defaults to a tight bounding box to make it easier to save figures using this class, and set a tight bbox as the default for the ``savefig`` method on other Grid objects. - You can now pass an integer to the ``xticklabels`` and ``yticklabels`` parameter of :func:`heatmap` (and, by extension, :func:`clustermap`). This will make the plot use the ticklabels inferred from the data, but only plot every ``n`` label, where ``n`` is the number you pass. This can help when visualizing larger matrices with some sensible ordering to the rows or columns of the dataframe. @@ -97,3 +97,5 @@ Bug fixes - Fixed a bug in :class:`PairGrid` where the ``hue_order`` parameter was ignored. - Fixed two bugs in :func:`despine` that caused errors when trying to trim the spines on plots that had inverted axes or no ticks. + +- Improved support for the ``margin_titles`` option in :class:`FacetGrid`, which can now be used with a legend. From 2ea0fa592e3fe40bc50ed3f7dc6c03dd2566bb9d Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 28 Jun 2015 15:16:49 -0700 Subject: [PATCH 0071/1738] Validate violinplot inner param (closes #558) --- seaborn/categorical.py | 9 +++++++++ seaborn/tests/test_categorical.py | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 812ac3ff05..3c37664fa0 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -534,7 +534,16 @@ def __init__(self, x, y, hue, data, order, hue_order, self.gridsize = gridsize self.width = width + + if inner is not None: + if not any([inner.startswith("quart"), + inner.startswith("box"), + inner.startswith("stick"), + inner.startswith("point")]): + err = "Inner style '{}' not recognized".format(inner) + raise ValueError(err) self.inner = inner + if split and self.hue_names is not None and len(self.hue_names) != 2: raise ValueError("Cannot use `split` with more than 2 hue levels.") self.split = split diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 66172dde6e..8dee4fed64 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1237,6 +1237,13 @@ def test_draw_sticks(self): npt.assert_array_equal(x, [val, val]) plt.close("all") + def test_validate_inner(self): + + kws = self.default_kws.copy() + kws.update(dict(inner="bad_inner")) + with nt.assert_raises(ValueError): + cat._ViolinPlotter(**kws) + def test_draw_violinplots(self): kws = self.default_kws.copy() From c28a12c6d22d387849ea2a82d53fe7ce6bd9dc2c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 28 Jun 2015 15:46:50 -0700 Subject: [PATCH 0072/1738] Fix col_wrap axes with missing levels --- seaborn/axisgrid.py | 2 +- seaborn/tests/test_axisgrid.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 9007bd485d..f5343fb017 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -311,7 +311,7 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, if gridspec_kws: warnings.warn("`gridspec_kws` ignored when using `col_wrap`") - n_axes = len(data[col].unique()) + n_axes = len(col_names) fig = plt.figure(figsize=figsize) axes = np.empty(n_axes, object) axes[0] = fig.add_subplot(nrow, ncol, 1, **subplot_kws) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 1cac82addf..9843737031 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -107,6 +107,14 @@ def test_col_wrap(self): with nt.assert_raises(ValueError): g = ag.FacetGrid(self.df, row="b", col="d", col_wrap=4) + df = self.df.copy() + df.loc[df.d == "j"] = np.nan + g_missing = ag.FacetGrid(df, col="d") + nt.assert_equal(g_missing.axes.shape, (1, 9)) + + g_missing_wrap = ag.FacetGrid(df, col="d", col_wrap=4) + nt.assert_equal(g_missing_wrap.axes.shape, (9,)) + plt.close("all") def test_normal_axes(self): From 4b9ba5582b0d7b62880bf57bf5be47bd924f19f3 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 28 Jun 2015 15:51:48 -0700 Subject: [PATCH 0073/1738] Pass string labels to the factorplot legend --- seaborn/categorical.py | 1 + 1 file changed, 1 insertion(+) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 3c37664fa0..7d58bb19e2 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2721,6 +2721,7 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, g.set_axis_labels(y_var="count") if legend and (hue is not None) and (hue not in [x, row, col]): + hue_order = list(map(str, hue_order)) g.add_legend(title=hue, label_order=hue_order) return g From bdef81e0ea6bad8dab250f09936cbd21c3a892bf Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 28 Jun 2015 20:42:47 -0700 Subject: [PATCH 0074/1738] Finalize release notes --- doc/releases/v0.6.0.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/releases/v0.6.0.txt b/doc/releases/v0.6.0.txt index f96e3ae1a8..b10a6e46c8 100644 --- a/doc/releases/v0.6.0.txt +++ b/doc/releases/v0.6.0.txt @@ -1,17 +1,17 @@ -v0.6.0 (Unreleased) -------------------- +v0.6.0 (June 2015) +------------------ -This is a major release from 0.5. The main objective of this release is to unify the API for categorical plots, which means that there are some relatively large API changes in some of the older functions. See below for details of those changes, which may break code written for older versions of seaborn. There are also some new functions (:func:`stripplot`, and :func:`countplot`), enhancements to existing functions, and bug fixes. +This is a major release from 0.5. The main objective of this release was to unify the API for categorical plots, which means that there are some relatively large API changes in some of the older functions. See below for details of those changes, which may break code written for older versions of seaborn. There are also some new functions (:func:`stripplot`, and :func:`countplot`), numerous enhancements to existing functions, and bug fixes. -Additionally, the structure of the docs is changing in version 0.6. The API docs page for each function will have numerous examples with embedded plots showing how to use the various options. These pages should be considered the most comprehensive resource for examples, as the tutorial pages are going to be streamlined to provide a higher-level introduction. +Additionally, the documentation has been completely revamped and expanded for the 0.6 release. Now, the API docs page for each function has multiple examples with embedded plots showing how to use the various options. These pages should be considered the most comprehensive resource for examples, and the tutorial pages are now streamlined and oriented towards a higher-level overview of the various features. Changes and updates to categorical plots ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In version 0.6, the "categorical" plots have been unified with a common API. This new category of functions groups together plots that show the relationship between one numeric variable and one or two categorical variables. This includes plots that show distribution of the numeric variable in each bin (:func:`boxplot`, :func:`violinplot`, and :func:`stripplot`) and plots that apply a statistical estimation within each bin (:func:`pointplot`, :func:`barplot`, and :func:`countplot`). +In version 0.6, the "categorical" plots have been unified with a common API. This new category of functions groups together plots that show the relationship between one numeric variable and one or two categorical variables. This includes plots that show distribution of the numeric variable in each bin (:func:`boxplot`, :func:`violinplot`, and :func:`stripplot`) and plots that apply a statistical estimation within each bin (:func:`pointplot`, :func:`barplot`, and :func:`countplot`). There is a new :ref:`tutorial chapter ` that introduces these functions. -These functions now each accept the same formats of input data and can be invoked in the same way. They can plot using long- or wide-form data, and can be drawn vertically or horizontally. When long-form data is used, the orientation of the plots is inferred from the types of the input data. Additionally, all functions natively take a ``hue`` variable to add a second layer of categorization. +The categorical functions now each accept the same formats of input data and can be invoked in the same way. They can plot using long- or wide-form data, and can be drawn vertically or horizontally. When long-form data is used, the orientation of the plots is inferred from the types of the input data. Additionally, all functions natively take a ``hue`` variable to add a second layer of categorization. With the (in some cases new) API, these functions can all be drawn correctly by :class:`FacetGrid`. However, :func:`factorplot` can also now create faceted verisons of any of these kinds of plots, so in most cases it will be unnecessary to use :class:`FacetGrid` directly. By default, :func:`factorplot` draws a point plot, but this is controlled by the ``kind`` parameter. @@ -43,7 +43,7 @@ New plotting functions Other additions and changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- The :func:`corrplot` and underlying :func:`symmatplot` have been deprecated in favor of :func:`heatmap`, which is much more flexible and robust. These two functions are still available in version 0.6, but they will be removed in a future version. +- The :func:`corrplot` and underlying :func:`symmatplot` functions have been deprecated in favor of :func:`heatmap`, which is much more flexible and robust. These two functions are still available in version 0.6, but they will be removed in a future version. - Added the :func:`set_color_codes` function and the ``color_codes`` argument to :func:`set` and :func:`set_palette`. This changes the interpretation of shorthand color codes (i.e. "b", "g", k", etc.) within matplotlib to use the values from one of the named seaborn palettes (i.e. "deep", "muted", etc.). That makes it easier to have a more uniform look when using matplotlib functions directly with seaborn imported. This could be disruptive to existing plots, so it does not happen by default. It is possible this could change in the future. From 2138b69c3324b265f1da8a97eb7f6b980306e158 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 28 Jun 2015 20:44:28 -0700 Subject: [PATCH 0075/1738] Increment version to 0.6.0 --- seaborn/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/__init__.py b/seaborn/__init__.py index 8a6b3ae55d..da1b73811f 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -12,4 +12,4 @@ from .crayons import crayons set() -__version__ = "0.6.dev" +__version__ = "0.6.0" diff --git a/setup.py b/setup.py index d310fcf2ac..ad71c335af 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ URL = 'http://stanford.edu/~mwaskom/software/seaborn/' LICENSE = 'BSD (3-clause)' DOWNLOAD_URL = 'https://github.com/mwaskom/seaborn/' -VERSION = '0.6.dev' +VERSION = '0.6.0' try: from setuptools import setup From fed4d419473035b90775ea9e92d4495196369ae6 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 28 Jun 2015 21:57:58 -0700 Subject: [PATCH 0076/1738] Set the figure facecolor to white in the default style --- doc/releases/v0.6.0.txt | 2 ++ seaborn/rcmod.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/doc/releases/v0.6.0.txt b/doc/releases/v0.6.0.txt index b10a6e46c8..0cfce7bec5 100644 --- a/doc/releases/v0.6.0.txt +++ b/doc/releases/v0.6.0.txt @@ -83,6 +83,8 @@ Other additions and changes - You can now pass an integer to the ``xticklabels`` and ``yticklabels`` parameter of :func:`heatmap` (and, by extension, :func:`clustermap`). This will make the plot use the ticklabels inferred from the data, but only plot every ``n`` label, where ``n`` is the number you pass. This can help when visualizing larger matrices with some sensible ordering to the rows or columns of the dataframe. +- Added `"figure.facecolor"` to the style parameters and set the default to white. + - The :func:`load_dataset` function now caches datasets locally after downloading them, and uses the local copy on subsequent calls. Bug fixes diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index 85bdba8015..c2f16d3c59 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -6,6 +6,7 @@ _style_keys = ( + "axes.facecolor", "axes.edgecolor", "axes.grid", @@ -13,6 +14,8 @@ "axes.linewidth", "axes.labelcolor", + "figure.facecolor", + "grid.color", "grid.linestyle", @@ -188,6 +191,7 @@ def axes_style(style=None, rc=None): # Common parameters style_dict = { + "figure.facecolor": "white", "text.color": dark_gray, "axes.labelcolor": dark_gray, "legend.frameon": False, From 9664247bbe209dee807d3dff5b8c259236e5a66b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 29 Jun 2015 07:51:08 -0700 Subject: [PATCH 0077/1738] Use pandas assert in load_dataset test --- seaborn/tests/test_utils.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index 52fc647645..3a574305b8 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -6,10 +6,11 @@ import numpy as np import pandas as pd import matplotlib.pyplot as plt -from numpy.testing import assert_array_equal import nose import nose.tools as nt from nose.tools import assert_equal, raises +import numpy.testing as npt +import pandas.util.testing as pdt from distutils.version import LooseVersion pandas_has_categoricals = LooseVersion(pd.__version__) >= "0.15" @@ -71,7 +72,7 @@ def test_ci_to_errsize(): [.25, 0]]) test_errsize = utils.ci_to_errsize(cis, heights) - assert_array_equal(actual_errsize, test_errsize) + npt.assert_array_equal(actual_errsize, test_errsize) def test_desaturate(): @@ -336,7 +337,7 @@ def check_load_cached_dataset(name): # use cached version ds2 = load_dataset(name, cache=True, data_home=tmpdir) - assert_array_equal(ds, ds2) + pdt.assert_frame_equal(ds, ds2) finally: shutil.rmtree(tmpdir) From eb2d29fec3713332779b12e5fa6936b34841e743 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 29 Jun 2015 08:56:26 -0700 Subject: [PATCH 0078/1738] Increment version back to dev --- README.md | 3 +-- seaborn/__init__.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1fa0ccefb1..c0cba69a30 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,7 @@ The documentation has an [example gallery](http://stanford.edu/~mwaskom/software Citing ------ -Seaborn can be cited using a DOI provided through Zenodo: [![DOI](https://zenodo.org/badge/doi/10.5072/zenodo.12710.png)](http://dx.doi.org/10.5072/zenodo.12710) - +Seaborn can be cited using a DOI provided through Zenodo: [![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.19108.svg)](http://dx.doi.org/10.5281/zenodo.19108) Dependencies ------------ diff --git a/seaborn/__init__.py b/seaborn/__init__.py index da1b73811f..93d42bb44e 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -12,4 +12,4 @@ from .crayons import crayons set() -__version__ = "0.6.0" +__version__ = "0.7.dev" diff --git a/setup.py b/setup.py index ad71c335af..6c0872fd10 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ URL = 'http://stanford.edu/~mwaskom/software/seaborn/' LICENSE = 'BSD (3-clause)' DOWNLOAD_URL = 'https://github.com/mwaskom/seaborn/' -VERSION = '0.6.0' +VERSION = '0.7.0.dev' try: from setuptools import setup From c7c07d1f46b46b66c3a8de4268b9f25ae9ba39be Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 30 Jun 2015 07:08:04 -0700 Subject: [PATCH 0079/1738] Fix aesthetics tutorial (closes #616) --- doc/tutorial/aesthetics.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/tutorial/aesthetics.ipynb b/doc/tutorial/aesthetics.ipynb index abdc95b6f5..d2b754c96d 100644 --- a/doc/tutorial/aesthetics.ipynb +++ b/doc/tutorial/aesthetics.ipynb @@ -269,7 +269,7 @@ "outputs": [], "source": [ "sns.set_style(\"whitegrid\")\n", - "sns.boxplot(data=data, color=\"deep\")\n", + "sns.boxplot(data=data, palette=\"deep\")\n", "sns.despine(left=True)" ] }, @@ -460,9 +460,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", - "version": "2.7.9" + "version": "2.7.10" } }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file From f105999d9a47a12fbe46c00d2579e3bb03985127 Mon Sep 17 00:00:00 2001 From: Marcel Martin Date: Thu, 2 Jul 2015 12:52:14 +0200 Subject: [PATCH 0080/1738] Compute linkage correctly when fastcluster is not installed. When fastcluster is not installed, _calculate_linkage_scipy is used to compute linkages. It contained an erroneous call to squareform(). distance.pdist() already returns a condensed distance matrix that is required as input for the linkage() function. Converting it to a matrix with squareform() is possible and linkage() will accept the resulting matrix, but consider it to be a matrix with vectors of observations and run distance.pdist() again on it. Thus, we got a linkage based on distances between distances. --- seaborn/matrix.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index a5563aa6fc..71277996f2 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -522,8 +522,7 @@ def _calculate_linkage_scipy(self): UserWarning('This will be slow... (gentle suggestion: ' '"pip install fastcluster")') - pairwise_dists = distance.squareform( - distance.pdist(self.array, metric=self.metric)) + pairwise_dists = distance.pdist(self.array, metric=self.metric) linkage = hierarchy.linkage(pairwise_dists, method=self.method) del pairwise_dists return linkage From 3ae80919ec8b1a35741f8ae06e3a2cc07dcb0fac Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 2 Jul 2015 09:22:47 -0700 Subject: [PATCH 0081/1738] Fix categorical plot data parameter docs c.f. #620 --- seaborn/categorical.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 7d58bb19e2..14805dbc5e 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1466,7 +1466,7 @@ def plot(self, ax): x, y, hue : names of variables in ``data`` Inputs for plotting long-form data. See examples for interpretation.\ """), - data=dedent("""\ + categorical_data=dedent("""\ data : DataFrame, array, or list of arrays, optional Dataset for plotting. If ``x`` and ``y`` are absent, this is interpreted as wide-form. Otherwise it is expected to be long-form.\ @@ -1638,7 +1638,7 @@ def boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, Parameters ---------- {input_params} - {data} + {categorical_data} {order_vars} {orient} {color} @@ -1821,7 +1821,7 @@ def violinplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, Parameters ---------- {input_params} - {data} + {categorical_data} {order_vars} bw : {{'scott', 'silverman', float}}, optional Either the name of a reference rule or the scale factor to use when @@ -2023,7 +2023,7 @@ def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, Parameters ---------- {input_params} - {data} + {categorical_data} {order_vars} jitter : float, ``True``/``1`` is special-cased, optional Amount of jitter (only along the categorical axis) to apply. This @@ -2219,7 +2219,7 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, Parameters ---------- {input_params} - {data} + {categorical_data} {order_vars} {stat_api_params} {orient} @@ -2386,7 +2386,7 @@ def pointplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, Parameters ---------- {input_params} - {data} + {categorical_data} {order_vars} {stat_api_params} markers : string or list of strings, optional @@ -2564,7 +2564,7 @@ def countplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, Parameters ---------- {input_params} - {data} + {categorical_data} {order_vars} {orient} {color} From bbba2f728d1f40adec9f2edbf4491d89718f2a87 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 5 Jul 2015 12:09:57 -0700 Subject: [PATCH 0082/1738] Sort numerical category levels by default Adds an additional rule when determining category order in categorical plots. Now, when numeric variables are used in a categorical role, the default behavior is to sort the unique levels of the variable (i.e they will be in proper numerical order). This can still be overriden by the appropriate ``{*_}order`` parameter, and variables with a ``category`` dtype will still follow the category order even if the levels are strictly numerical. --- doc/releases/v0.6.1.txt | 6 ++++++ seaborn/tests/test_utils.py | 10 ++++++++++ seaborn/utils.py | 4 ++++ 3 files changed, 20 insertions(+) create mode 100644 doc/releases/v0.6.1.txt diff --git a/doc/releases/v0.6.1.txt b/doc/releases/v0.6.1.txt new file mode 100644 index 0000000000..d940c58153 --- /dev/null +++ b/doc/releases/v0.6.1.txt @@ -0,0 +1,6 @@ + +v0.6.1 (Unreleased) +------------------- + +- Added an additional rule when determining category order in categorical plots. Now, when numeric variables are used in a categorical role, the default behavior is to sort the unique levels of the variable (i.e they will be in proper numerical order). This can still be overriden by the appropriate ``{*_}order`` parameter, and variables with a ``category`` dtype will still follow the category order even if the levels are strictly numerical. + diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index 3a574305b8..958a70d7fa 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -286,6 +286,7 @@ def test_ticklabels_overlap(): def test_categorical_order(): x = ["a", "c", "c", "b", "a", "d"] + y = [3, 2, 5, 1, 4] order = ["a", "b", "c", "d"] out = utils.categorical_order(x) @@ -303,6 +304,15 @@ def test_categorical_order(): out = utils.categorical_order(pd.Series(x)) nt.assert_equal(out, ["a", "c", "b", "d"]) + out = utils.categorical_order(y) + nt.assert_equal(out, [1, 2, 3, 4, 5]) + + out = utils.categorical_order(np.array(y)) + nt.assert_equal(out, [1, 2, 3, 4, 5]) + + out = utils.categorical_order(pd.Series(y)) + nt.assert_equal(out, [1, 2, 3, 4, 5]) + if pandas_has_categoricals: x = pd.Categorical(x, order) out = utils.categorical_order(x) diff --git a/seaborn/utils.py b/seaborn/utils.py index a4243ca002..b702810f2c 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -520,5 +520,9 @@ def categorical_order(values, order=None): order = values.unique() except AttributeError: order = pd.unique(values) + try: + order = np.sort(order.astype(np.float)) + except (ValueError, TypeError): + order = order order = filter(pd.notnull, order) return list(order) From aa297f9610501b9c97a1d368221b5abb46c2cc4f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 5 Jul 2015 12:36:29 -0700 Subject: [PATCH 0083/1738] Ensure that order objects have the right type --- seaborn/tests/test_axisgrid.py | 3 ++- seaborn/utils.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 9843737031..6bbbacbe90 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -18,6 +18,7 @@ from ..distributions import kdeplot from ..categorical import pointplot from ..linearmodels import pairplot +from ..utils import categorical_order rs = np.random.RandomState(0) @@ -255,7 +256,7 @@ def test_get_boolean_legend_data(self): nt.assert_equal(g1._legend.get_title().get_text(), "b_bool") - b_levels = list(map(str, self.df.b_bool.unique())) + b_levels = list(map(str, categorical_order(self.df.b_bool))) lines = g1._legend.get_lines() nt.assert_equal(len(lines), len(b_levels)) diff --git a/seaborn/utils.py b/seaborn/utils.py index b702810f2c..ef0adb391f 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -521,7 +521,8 @@ def categorical_order(values, order=None): except AttributeError: order = pd.unique(values) try: - order = np.sort(order.astype(np.float)) + np.asarray(values).astype(np.float) + order = np.sort(order) except (ValueError, TypeError): order = order order = filter(pd.notnull, order) From eb9ff041a9370cb5f4e7d9b6bc7ae3a18bf9e555 Mon Sep 17 00:00:00 2001 From: Samuel St-Jean Date: Mon, 6 Jul 2015 16:54:29 -0400 Subject: [PATCH 0084/1738] Fixed a typo --- seaborn/categorical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 14805dbc5e..82c43da9cd 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -141,7 +141,7 @@ def establish_variables(self, x=None, y=None, hue=None, data=None, # Validate the inputs for input in [x, y, hue, units]: if isinstance(input, string_types): - err = "Could not interperet input '{}'".format(input) + err = "Could not interpret input '{}'".format(input) raise ValueError(err) # Figure out the plotting orientation From 6c37cc4a8e443871506f18bcd8cb255754dd9fec Mon Sep 17 00:00:00 2001 From: Kyle Meyer Date: Mon, 6 Jul 2015 18:32:43 -0400 Subject: [PATCH 0085/1738] factorplot: Extend input check when kind="count" When kind is "count" for factorplot, raise ValueError if both x and y are given. Otherwise, x_ and _y will not be defined, leading to an UnboundLocalError. --- seaborn/categorical.py | 2 ++ seaborn/tests/test_categorical.py | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 82c43da9cd..9d430b9302 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2666,6 +2666,8 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, x_, y_, orient = y, y, "h" elif y is None and x is not None: x_, y_, orient = x, x, "v" + else: + raise ValueError("Either `x` or `y` must be None for count plots") else: x_, y_ = x, y diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 8dee4fed64..ca8c4b57ef 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -2134,6 +2134,11 @@ def test_bad_plot_kind_error(self): with nt.assert_raises(ValueError): cat.factorplot("g", "y", data=self.df, kind="not_a_kind") + def test_count_x_and_y(self): + + with nt.assert_raises(ValueError): + cat.factorplot("g", "y", data=self.df, kind="count") + def test_plot_colors(self): ax = cat.barplot("g", "y", data=self.df) From 539c8de448068f1354e71ae0b68f68b3ef01ec23 Mon Sep 17 00:00:00 2001 From: Kyle Meyer Date: Mon, 6 Jul 2015 18:33:17 -0400 Subject: [PATCH 0086/1738] Convert two tests to almost_equal variant Prevent the tests below from failing with very close values: ====================================================================== FAIL: seaborn.tests.test_categorical.TestCategoricalStatPlotter.test_nested_stats_with_missing_data ---------------------------------------------------------------------- [...] Items are not equal: ACTUAL: -0.018721526986487352 DESIRED: -0.018721526986487342 ====================================================================== FAIL: seaborn.tests.test_categorical.TestCategoricalStatPlotter.test_single_layer_stats_with_missing_data ---------------------------------------------------------------------- [...] AssertionError: Items are not equal: ACTUAL: 0.082012970747837352 DESIRED: 0.08201297074783731 --- seaborn/tests/test_categorical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 8dee4fed64..16afe4faf6 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -513,7 +513,7 @@ def test_single_layer_stats_with_missing_data(self): sem = stats.sem(y[g == "b"]) half_ci = stats.norm.ppf(.975) * sem ci = mean - half_ci, mean + half_ci - npt.assert_equal(p.statistic[1], mean) + npt.assert_almost_equal(p.statistic[1], mean) npt.assert_array_almost_equal(p.confint[1], ci, 2) npt.assert_equal(p.statistic[2], np.nan) @@ -588,7 +588,7 @@ def test_nested_stats_with_missing_data(self): sem = stats.sem(y[(g == "b") & (h == "x")]) half_ci = stats.norm.ppf(.975) * sem ci = mean - half_ci, mean + half_ci - npt.assert_equal(p.statistic[1, 2], mean) + npt.assert_almost_equal(p.statistic[1, 2], mean) npt.assert_array_almost_equal(p.confint[1, 2], ci, 2) npt.assert_array_equal(p.statistic[:, 0], [np.nan] * 4) From 233aa880548f1e5d8dfa50a54ca4dac454ebbeef Mon Sep 17 00:00:00 2001 From: Kyle Meyer Date: Mon, 6 Jul 2015 18:33:17 -0400 Subject: [PATCH 0087/1738] Convert two tests to almost_equal variant Prevent the tests below from failing with very close values: ====================================================================== FAIL: seaborn.tests.test_categorical.TestCategoricalStatPlotter.test_nested_stats_with_missing_data ---------------------------------------------------------------------- [...] Items are not equal: ACTUAL: -0.018721526986487352 DESIRED: -0.018721526986487342 ====================================================================== FAIL: seaborn.tests.test_categorical.TestCategoricalStatPlotter.test_single_layer_stats_with_missing_data ---------------------------------------------------------------------- [...] AssertionError: Items are not equal: ACTUAL: 0.082012970747837352 DESIRED: 0.08201297074783731 --- seaborn/tests/test_categorical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 8dee4fed64..16afe4faf6 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -513,7 +513,7 @@ def test_single_layer_stats_with_missing_data(self): sem = stats.sem(y[g == "b"]) half_ci = stats.norm.ppf(.975) * sem ci = mean - half_ci, mean + half_ci - npt.assert_equal(p.statistic[1], mean) + npt.assert_almost_equal(p.statistic[1], mean) npt.assert_array_almost_equal(p.confint[1], ci, 2) npt.assert_equal(p.statistic[2], np.nan) @@ -588,7 +588,7 @@ def test_nested_stats_with_missing_data(self): sem = stats.sem(y[(g == "b") & (h == "x")]) half_ci = stats.norm.ppf(.975) * sem ci = mean - half_ci, mean + half_ci - npt.assert_equal(p.statistic[1, 2], mean) + npt.assert_almost_equal(p.statistic[1, 2], mean) npt.assert_array_almost_equal(p.confint[1, 2], ci, 2) npt.assert_array_equal(p.statistic[:, 0], [np.nan] * 4) From 97a00c787703b74a0a5e479c820a5e25254dff63 Mon Sep 17 00:00:00 2001 From: Marcel Martin Date: Thu, 9 Jul 2015 09:57:15 +0200 Subject: [PATCH 0088/1738] Fix tests failing due to extraneous use of distance.squareform. The tests were also written under the assumption that hierarchy.linkage can be fed with a distance matrix, which is not correct. --- seaborn/tests/test_matrix.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index d13579af6d..81e3c3adf3 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -358,8 +358,7 @@ class TestDendrogram(object): metric='euclidean', method='single') except ImportError: - x_norm_distances = distance.squareform( - distance.pdist(x_norm.T, metric='euclidean')) + x_norm_distances = distance.pdist(x_norm.T, metric='euclidean') x_norm_linkage = hierarchy.linkage(x_norm_distances, method='single') x_norm_dendrogram = hierarchy.dendrogram(x_norm_linkage, no_plot=True, color_list=['k'], @@ -499,9 +498,7 @@ def test_linkage_scipy(self): from scipy.spatial import distance from scipy.cluster import hierarchy - dists = distance.squareform(distance.pdist(self.x_norm.T, - metric=self.default_kws[ - 'metric'])) + dists = distance.pdist(self.x_norm.T, metric=self.default_kws['metric']) linkage = hierarchy.linkage(dists, method=self.default_kws['method']) npt.assert_array_equal(scipy_linkage, linkage) From 55c1c54c0df6fe956b39275b9968ddbdd36f83ab Mon Sep 17 00:00:00 2001 From: Marcel Martin Date: Thu, 9 Jul 2015 10:32:36 +0200 Subject: [PATCH 0089/1738] Fix Travis failing due to a line being one character too long (someone *really* likes PEP8) --- seaborn/tests/test_matrix.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 81e3c3adf3..a64312c613 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -498,7 +498,8 @@ def test_linkage_scipy(self): from scipy.spatial import distance from scipy.cluster import hierarchy - dists = distance.pdist(self.x_norm.T, metric=self.default_kws['metric']) + dists = distance.pdist(self.x_norm.T, + metric=self.default_kws['metric']) linkage = hierarchy.linkage(dists, method=self.default_kws['method']) npt.assert_array_equal(scipy_linkage, linkage) From 43c9f79ec5471ea3988851a15d56ff6c3844d65a Mon Sep 17 00:00:00 2001 From: Marcel Martin Date: Thu, 9 Jul 2015 10:32:57 +0200 Subject: [PATCH 0090/1738] Remove all remaining uses of distance.squareform. The tests in which they occurred did not fail for the following reasons: * In test_custom_linkage, _DendrogramPlotter just uses the passed linkage and does not compute its own. * In TestClustermap, the linkage itself is never used directly. --- seaborn/tests/test_matrix.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index a64312c613..5af444e71d 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -467,8 +467,7 @@ def test_custom_linkage(self): linkage = fastcluster.linkage_vector(self.x_norm, method='single', metric='euclidean') except ImportError: - d = distance.squareform(distance.pdist(self.x_norm, - metric='euclidean')) + d = distance.pdist(self.x_norm, metric='euclidean') linkage = hierarchy.linkage(d, method='single') dendrogram = hierarchy.dendrogram(linkage, no_plot=True, color_list=['k'], @@ -597,8 +596,7 @@ class TestClustermap(object): metric='euclidean', method='single') except ImportError: - x_norm_distances = distance.squareform( - distance.pdist(x_norm.T, metric='euclidean')) + x_norm_distances = distance.pdist(x_norm.T, metric='euclidean') x_norm_linkage = hierarchy.linkage(x_norm_distances, method='single') x_norm_dendrogram = hierarchy.dendrogram(x_norm_linkage, no_plot=True, color_list=['k'], From 62c957fc1bef04c5ec5f51715b2a4892fef56d0e Mon Sep 17 00:00:00 2001 From: Pete Bachant Date: Thu, 9 Jul 2015 18:22:39 -0400 Subject: [PATCH 0091/1738] Add font.size to font_keys in rcmod --- seaborn/rcmod.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index c2f16d3c59..0f7a516599 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -365,6 +365,7 @@ def plotting_context(context=None, font_scale=1, rc=None): base_context = { "figure.figsize": np.array([8, 5.5]), + "font.size": 12, "axes.labelsize": 11, "axes.titlesize": 12, "xtick.labelsize": 10, @@ -392,7 +393,7 @@ def plotting_context(context=None, font_scale=1, rc=None): # Now independently scale the fonts font_keys = ["axes.labelsize", "axes.titlesize", "legend.fontsize", - "xtick.labelsize", "ytick.labelsize"] + "xtick.labelsize", "ytick.labelsize", "font.size"] font_dict = {k: context_dict[k] * font_scale for k in font_keys} context_dict.update(font_dict) From 2a1a2dae6f6a2b883eee3d95d1b83f9ac19f1194 Mon Sep 17 00:00:00 2001 From: Pete Bachant Date: Thu, 9 Jul 2015 18:23:36 -0400 Subject: [PATCH 0092/1738] Add setting font.size to tests --- seaborn/tests/test_rcmod.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/tests/test_rcmod.py b/seaborn/tests/test_rcmod.py index f338ed5ac4..b354ed695c 100644 --- a/seaborn/tests/test_rcmod.py +++ b/seaborn/tests/test_rcmod.py @@ -133,7 +133,7 @@ def test_font_scale(self): notebook_big = rcmod.plotting_context("notebook", 2) font_keys = ["axes.labelsize", "axes.titlesize", "legend.fontsize", - "xtick.labelsize", "ytick.labelsize"] + "xtick.labelsize", "ytick.labelsize", "font.size"] for k in font_keys: nt.assert_equal(notebook_ref[k] * 2, notebook_big[k]) From d19fff8548feedb066e0f6f6239ea477c422efa3 Mon Sep 17 00:00:00 2001 From: Pete Bachant Date: Thu, 9 Jul 2015 18:31:30 -0400 Subject: [PATCH 0093/1738] Add font.size to rcmod._style_keys --- seaborn/rcmod.py | 1 + 1 file changed, 1 insertion(+) diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index 0f7a516599..50360a0ff2 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -44,6 +44,7 @@ _context_keys = ( "figure.figsize", + "font.size", "axes.labelsize", "axes.titlesize", "xtick.labelsize", From 9fd52e4a42cf2e9fe78e6b029997438e33abc9af Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 10 Jul 2015 08:19:06 -0700 Subject: [PATCH 0094/1738] Fix error in regression tutorial --- doc/tutorial/regression.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorial/regression.ipynb b/doc/tutorial/regression.ipynb index f66077727b..475ba14e96 100644 --- a/doc/tutorial/regression.ipynb +++ b/doc/tutorial/regression.ipynb @@ -328,7 +328,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "Note that the logistic regression estimate is considerably more computationally intensive (this is true of robust regression as well) than simple regression, and as the confidence interval around the regression line is computed using a bootstrap procedure, you may wish to turn this off for faster iteration (using ``ci=False``).\n", + "Note that the logistic regression estimate is considerably more computationally intensive (this is true of robust regression as well) than simple regression, and as the confidence interval around the regression line is computed using a bootstrap procedure, you may wish to turn this off for faster iteration (using ``ci=None``).\n", "\n", "An altogether different approach is to fit a nonparametric regression using a `lowess smoother `_. This approach has the fewest assumptions, although it is computationally intensive and so currently confidence intervals are not computed at all:" ] @@ -590,4 +590,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} From d4d85d895b5e3a967841ae4694c3c112f4f2c68e Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 10 Jul 2015 10:05:43 -0700 Subject: [PATCH 0095/1738] Update release notes --- doc/releases/v0.6.1.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/releases/v0.6.1.txt b/doc/releases/v0.6.1.txt index d940c58153..611d83ad93 100644 --- a/doc/releases/v0.6.1.txt +++ b/doc/releases/v0.6.1.txt @@ -4,3 +4,4 @@ v0.6.1 (Unreleased) - Added an additional rule when determining category order in categorical plots. Now, when numeric variables are used in a categorical role, the default behavior is to sort the unique levels of the variable (i.e they will be in proper numerical order). This can still be overriden by the appropriate ``{*_}order`` parameter, and variables with a ``category`` dtype will still follow the category order even if the levels are strictly numerical. +- Fixed a bug in :func:`clustermap` when ``fastcluster`` is not installed. From 0a048311e713da385d17e511a0284546cfa2b7c5 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 10 Jul 2015 10:07:26 -0700 Subject: [PATCH 0096/1738] Update release notes --- doc/releases/v0.6.1.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.6.1.txt b/doc/releases/v0.6.1.txt index 611d83ad93..5353349f40 100644 --- a/doc/releases/v0.6.1.txt +++ b/doc/releases/v0.6.1.txt @@ -4,4 +4,6 @@ v0.6.1 (Unreleased) - Added an additional rule when determining category order in categorical plots. Now, when numeric variables are used in a categorical role, the default behavior is to sort the unique levels of the variable (i.e they will be in proper numerical order). This can still be overriden by the appropriate ``{*_}order`` parameter, and variables with a ``category`` dtype will still follow the category order even if the levels are strictly numerical. +- Added ``font.size`` to the plotting context definition so that the default output from ``plt.text`` will be scaled appropriately. + - Fixed a bug in :func:`clustermap` when ``fastcluster`` is not installed. From 5b58fef61ff9a05c3c3da741758a4c01decf1007 Mon Sep 17 00:00:00 2001 From: Olga Botvinnik Date: Thu, 16 Jul 2015 14:40:19 -0700 Subject: [PATCH 0097/1738] Divide by standard dev instead of variance for z-score --- seaborn/matrix.py | 2 +- seaborn/tests/test_matrix.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 71277996f2..0a6c5f51eb 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -763,7 +763,7 @@ def z_score(data2d, axis=1): else: z_scored = data2d.T - z_scored = (z_scored - z_scored.mean()) / z_scored.var() + z_scored = (z_scored - z_scored.mean()) / z_scored.std() if axis == 1: return z_scored diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 5af444e71d..5bd5504b02 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -697,7 +697,7 @@ def test_colors_input_custom_cmap(self): def test_z_score(self): df = self.df_norm.copy() - df = (df - df.mean()) / df.var() + df = (df - df.mean()) / df.std() kws = self.default_kws.copy() kws['z_score'] = 1 @@ -709,7 +709,7 @@ def test_z_score(self): def test_z_score_axis0(self): df = self.df_norm.copy() df = df.T - df = (df - df.mean()) / df.var() + df = (df - df.mean()) / df.std() df = df.T kws = self.default_kws.copy() kws['z_score'] = 0 From 106a5ea437df3bdbac77fad64577b2c37908a66b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 16 Jul 2015 19:43:24 -0700 Subject: [PATCH 0098/1738] Update release notes --- doc/releases/v0.6.1.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.6.1.txt b/doc/releases/v0.6.1.txt index 5353349f40..5ae22e970d 100644 --- a/doc/releases/v0.6.1.txt +++ b/doc/releases/v0.6.1.txt @@ -7,3 +7,5 @@ v0.6.1 (Unreleased) - Added ``font.size`` to the plotting context definition so that the default output from ``plt.text`` will be scaled appropriately. - Fixed a bug in :func:`clustermap` when ``fastcluster`` is not installed. + +- Fixed a bug in the zscore calculation in :func:`clustermap`. From 7394ebd451619f5264d4e025b73b382a5112254c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 19 Jul 2015 14:53:46 -0700 Subject: [PATCH 0099/1738] Fix marginal ticks example (closes #641 --- examples/marginal_ticks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/marginal_ticks.py b/examples/marginal_ticks.py index 5f14bf9aed..17938e1480 100644 --- a/examples/marginal_ticks.py +++ b/examples/marginal_ticks.py @@ -18,4 +18,4 @@ # Use JointGrid directly to draw a custom plot grid = sns.JointGrid(x, y, space=0, size=6, ratio=50) grid.plot_joint(plt.scatter, color="g") -grid.plot_marginals(sns.rugplot, color="g") +grid.plot_marginals(sns.rugplot, height=1, color="g") From b3eed79aeadd3be9407693f737ca52488e79c0cf Mon Sep 17 00:00:00 2001 From: drewokane Date: Thu, 30 Jul 2015 11:44:44 -0700 Subject: [PATCH 0100/1738] Add letter value plot. Added letter value plot class and plotting function. In addition, added documentation and examples. --- seaborn/categorical.py | 418 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 418 insertions(+) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 9d430b9302..f0871856ce 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -6,6 +6,8 @@ import pandas as pd from pandas.core.series import remove_na import matplotlib as mpl +from matplotlib.collections import PatchCollection +import matplotlib.patches as Patches import matplotlib.pyplot as plt import warnings @@ -1438,6 +1440,229 @@ def plot(self, ax): if self.orient == "h": ax.invert_yaxis() +class _LVPlotter(_CategoricalPlotter): + + def __init__(self, x, y, hue, data, order, hue_order, + orient, color, palette, saturation, + width, k_depth, linewidth, box_widths): + + if width is None: + width=.8 + self.width = width + + if saturation is None: + saturation = .75 + self.saturation = saturation + + if k_depth is None: + k_depth = 'proportion' + self.k_depth = k_depth + + if linewidth is None: + linewidth = mpl.rcParams["lines.linewidth"] + self.linewidth = linewidth + + if box_widths is None: + box_widths = 'linear' + self.box_widths = box_widths + + self.establish_variables(x, y, hue, data, orient, order, hue_order) + self.establish_colors(color, palette, saturation) + + def _lv_box_ends(self, vals, k_depth='proportion', p=None): + """Get the number of data points and calculate `depth` of + letter-value plot.""" + vals = np.asarray(vals) + vals = vals[np.isfinite(vals)] + n = len(vals) + # If p is not set, calculate it so that 8 points are outliers + if not p: + p = 8./n + # Select the depth, i.e. number of boxes to draw, based on the method + k_dict = {'proportion': (np.log2(n)) - int(np.log2(n*p)) + 1, + 'tukey': (np.log2(n)) - 3, + 'trustworthy': (np.log2(n) - np.log2(2*stats.norm.ppf((1-p))**2)) + 1} + k = k_dict[k_depth] + try: + k = int(k) + except ValueError: + k = 1 + # If the number happens to be less than 1, set k to 1 + if k < 1.: + k = 1 + # Calculate the upper box ends + upper = [100*(1 - 0.5**(i+2)) for i in range(k, -1, -1)] + # Calculate the lower box ends + lower = [100*(0.5**(i+2)) for i in range(k, -1, -1)] + # Stitch the box ends together + percentile_ends = [(i, j) for i, j in zip(lower, upper)] + box_ends = [np.percentile(vals, q) for q in percentile_ends] + return box_ends, k + + def _lv_outliers(self, vals, k): + """Find the outliers based on the letter value depth.""" + perc_ends = (100*(0.5**(k+2)), 100*(1 - 0.5**(k+2))) + edges = np.percentile(vals, perc_ends) + lower_out = vals[np.where(vals < edges[0])[0]] + upper_out = vals[np.where(vals > edges[1])[0]] + return np.concatenate((lower_out, upper_out)) + + def _lvplot(self, box_data, positions, + color=[255. / 256., 185. / 256., 0.], + vert='v', widths=1, k_depth='proportion', + ax=None, p=None, box_widths='linear', + **kws): + + x = positions[0] + box_data = np.asarray(box_data) + + # If we only have one data point, plot a line + if len(box_data) == 1: + ys = [box_data[0], box_data[0]] + xs = [x - widths / 2, x + widths / 2] + if vert: + xx, yy = ys, xs + else: + xx, yy = xs, ys + ax.plot(xx, yy, **kws) + else: + # Get the number of data points and calculate "depth" of + # letter-value plot + box_ends, k = self._lv_box_ends(box_data, k_depth=k_depth, p=p) + + # Dictionary of functions for computing the width of the boxes + width_functions = {'linear' : lambda h, i, k: (i + 1.) / k, + 'exponential' : lambda h, i, k: 2**(-k+i-1), + 'area' : lambda h, i, k: (1 - 2**(-k+i-2)) / h} + + # Anonymous functions for calculating the width and height + # of the letter value boxes + width = width_functions[box_widths] + height = lambda b: b[1] - b[0] + + # Functions to construct the letter value boxes + def vert_perc_box(x, b, i, k, w): + rect = Patches.Rectangle((x - widths*w / 2, b[0]), + widths*w, + height(b), fill=True) + return rect + + def horz_perc_box(x, b, i, k, w): + rect = Patches.Rectangle((b[0], x - widths*w / 2), + height(b), widths*w, + fill=True) + return rect + + # Scale the width of the boxes so the biggest starts at 1 + w_area = np.array([width(height(b), i, k) for i, b in enumerate(box_ends)]) + w_area = w_area / np.max(w_area) + + # Calculate the medians + y = np.median(box_data) + w = (widths + .1) + + # Calculate the outliers and plot + outliers = self._lv_outliers(box_data, k) + + if vert: + boxes = [vert_perc_box(x, b[0], i, k, b[1]) + for i, b in enumerate(zip(box_ends, w_area))] + + # Plot the medians + ax.plot([x - w / 2, x + w / 2], [y, y], c='k', alpha=.45, **kws) + + ax.scatter(np.repeat(x, len(outliers)), outliers, + marker=r"$\ast$", c=color) + else: + boxes = [horz_perc_box(x, b[0], i, k, b[1]) + for i, b in enumerate(zip(box_ends, w_area))] + + # Plot the medians + ax.plot([y, y], [x - w / 2, x + w / 2], c='k', alpha=.45, **kws) + + ax.scatter(outliers, np.repeat(x, len(outliers)), + marker=r"$\ast$", c=color) + + # Construct a color map from the input color + rgb = [[1, 1, 1], list(color)] + cmap = mpl.colors.LinearSegmentedColormap.from_list('new_map', rgb) + collection = PatchCollection(boxes, cmap=cmap) + + # Set the color gradation + collection.set_array(np.array(np.linspace(0, 1, len(boxes)))) + + # Plot the boxes + ax.add_collection(collection) + + def draw_letter_value_plot(self, ax, kws): + """Use matplotlib to draw a letter value plot on an Axes.""" + vert = self.orient == "v" + + for i, group_data in enumerate(self.plot_data): + + if self.plot_hues is None: + + # Handle case where there is data at this level + if group_data.size == 0: + continue + + # Draw a single box or a set of boxes + # with a single level of grouping + box_data = remove_na(group_data) + + # Handle case where there is no non-null data + if box_data.size == 0: + continue + + color = self.colors[i] + + artist_dict = self._lvplot(box_data, + positions=[i], + color=color, + vert=vert, + widths=self.width, + k_depth=self.k_depth, + ax=ax, + **kws) + + else: + # Draw nested groups of boxes + offsets = self.hue_offsets + for j, hue_level in enumerate(self.hue_names): + hue_mask = self.plot_hues[i] == hue_level + + # Add a legend for this hue level + if not i: + self.add_legend_data(ax, self.colors[j], hue_level) + + # Handle case where there is data at this level + if group_data.size == 0: + continue + + box_data = remove_na(group_data[hue_mask]) + + # Handle case where there is no non-null data + if box_data.size == 0: + continue + + color = self.colors[j] + center = i + offsets[j] + artist_dict = self._lvplot(box_data, + positions=[center], + color=color, + vert=vert, + widths=self.nested_width, + k_depth=self.k_depth, + ax=ax, + **kws) + + def plot(self, ax, boxplot_kws): + """Make the plot.""" + self.draw_letter_value_plot(ax, boxplot_kws) + self.annotate_axes(ax) + if self.orient == "h": + ax.invert_yaxis() + _categorical_docs = dict( # Shared narrative docs @@ -1562,6 +1787,9 @@ def plot(self, ax): factorplot=dedent("""\ factorplot : Combine categorical plots and a class:`FacetGrid`.\ """), + lettervalueplot=dedent("""\ + lv + """), ) _categorical_docs.update(_facet_docs) @@ -2870,3 +3098,193 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, """).format(**_categorical_docs) + +def lettervalueplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, + orient=None, color=None, palette=None, saturation=None, + width=None, k_depth=None, linewidth=None, box_widths=None, + ax=None, **kwargs): + + # Try to handle broken backwards-compatability + # This should help with the lack of a smooth deprecation, + # but won't catch everything + warn = False + if isinstance(x, pd.DataFrame): + data = x + x = None + warn = True + + if "vals" in kwargs: + x = kwargs.pop("vals") + warn = True + + if "groupby" in kwargs: + y = x + x = kwargs.pop("groupby") + warn = True + + if "vert" in kwargs: + vert = kwargs.pop("vert", True) + if not vert: + x, y = y, x + orient = "v" if vert else "h" + warn = True + + if "names" in kwargs: + kwargs.pop("names") + warn = True + + if "join_rm" in kwargs: + kwargs.pop("join_rm") + warn = True + + msg = ("The boxplot API has been changed. Attempting to adjust your " + "arguments for the new API (which might not work). Please update " + "your code. See the version 0.6 release notes for more info.") + + if warn: + warnings.warn(msg, UserWarning) + + plotter = _LVPlotter(x, y, hue, data, order, hue_order, + orient, color, palette, saturation, + width, k_depth, linewidth, box_widths) + + if ax is None: + ax = plt.gca() + + plotter.plot(ax, kwargs) + return ax + +lettervalueplot.__doc__ = dedent("""\ + Create a letter value plot + + Letter value (LV) plots are non-parametric estimates of the distribution of + a dataset, similar to boxplots. LV plots are also similar to violin plots + but without the need to fit a kernel density estimate. Thus, LV plots are fast + to generate, directly interpretable in terms of the distribution of data, and + easy to understand. For a more extensive explanation of letter value plots + and their properties, see Hadley Wickham's excellent paper on the topic: + + http://vita.had.co.nz/papers/letter-value-plot.html + + {main_api_narrative} + + Parameters + ---------- + {input_params} + {categorical_data} + {order_vars} + {orient} + {color} + {palette} + {saturation} + {width} + k_depth : "proportion" | "tukey" | "trustworthy", optional + The number of boxes, and by extension number of percentiles, to draw. + All methods are detailed in Wickham's paper. Each makes different + assumptions about the number of outliers and leverages different + statistical properties. + {linewidth} + box_widths : "linear" | "exponential" | "area" + Method to use for the width of the letter value boxes. All give similar + results visually. "linear" reduces the width by a constant linear factor, + "exponential" uses the proportion of data not covered, "area" is + proportional to the percentage of data covered. + p : float, optional + Proportion of data believed to be outliers. Is used in conjuction with + k_depth to determine the number of percentiles to draw. Defaults to 8 + outliers. + {ax_in} + kwargs : key, value mappings + Other keyword arguments are passed through to ``plt.boxplot`` at draw + time. + + Returns + ------- + {ax_out} + + See Also + -------- + {violinplot} + {boxplot} + + Examples + -------- + + Draw a single horizontal boxplot: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns + >>> sns.set_style("whitegrid") + >>> tips = sns.load_dataset("tips") + >>> ax = sns.lettervalueplot(x=tips["total_bill"]) + + Draw a vertical letter value plot grouped by a categorical variable: + + .. plot:: + :context: close-figs + + >>> ax = sns.lettervalueplot(x="day", y="total_bill", data=tips) + + Draw a letter value plot with nested grouping by two categorical variables: + + .. plot:: + :context: close-figs + + >>> ax = sns.lettervalueplot(x="day", y="total_bill", hue="smoker", + ... data=tips, palette="Set3") + + Draw a letter value plot with nested grouping when some bins are empty: + + .. plot:: + :context: close-figs + + >>> ax = sns.lettervalueplot(x="day", y="total_bill", hue="time", + ... data=tips, linewidth=2.5) + + Control box order by sorting the input data: + + .. plot:: + :context: close-figs + + >>> ax = sns.lettervalueplot(x="size", y="tip", data=tips.sort("size")) + + Control box order by passing an explicit order: + + .. plot:: + :context: close-figs + + >>> ax = sns.lettervalueplot(x="size", y="tip", data=tips, + ... order=np.arange(1, 7), palette="Blues_d") + + Draw a letter value plot for each numeric variable in a DataFrame: + + .. plot:: + :context: close-figs + + >>> iris = sns.load_dataset("iris") + >>> ax = sns.lettervalueplot(data=iris, orient="h", palette="Set2") + + Use :func:`stripplot` to show the datapoints on top of the boxes: + + .. plot:: + :context: close-figs + + >>> ax = sns.lettervalueplot(x="day", y="total_bill", data=tips) + >>> ax = sns.stripplot(x="day", y="total_bill", data=tips, + ... size=4, jitter=True, edgecolor="gray") + + Draw a letter value plot on to a :class:`FacetGrid` to group within an additional + categorical variable: + + .. plot:: + :context: close-figs + + >>> g = sns.FacetGrid(tips, col="time", size=4, aspect=.7) + >>> (g.map(sns.lettervalueplot, "sex", "total_bill", "smoker") + ... .despine(left=True) + ... .add_legend(title="smoker")) #doctest: +ELLIPSIS + + + """).format(**_categorical_docs) From c3e368dd8223bff202553402455ee6cb3eafc378 Mon Sep 17 00:00:00 2001 From: drewokane Date: Thu, 30 Jul 2015 12:48:28 -0700 Subject: [PATCH 0101/1738] Updated letter value plot docs. --- seaborn/categorical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index f0871856ce..e10476701a 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1788,7 +1788,7 @@ def plot(self, ax, boxplot_kws): factorplot : Combine categorical plots and a class:`FacetGrid`.\ """), lettervalueplot=dedent("""\ - lv + lettervalueplot : An extension of the boxplot for long-tailed and large data sets. """), ) From e21840ec36f54a0246619dd4ea66d0ac3e20d3f5 Mon Sep 17 00:00:00 2001 From: drewokane Date: Thu, 30 Jul 2015 12:51:26 -0700 Subject: [PATCH 0102/1738] Spelling fix. --- seaborn/categorical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index e10476701a..ea921238c9 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -3137,7 +3137,7 @@ def lettervalueplot(x=None, y=None, hue=None, data=None, order=None, hue_order=N kwargs.pop("join_rm") warn = True - msg = ("The boxplot API has been changed. Attempting to adjust your " + msg = ("The Seaborn categorical API has been changed. Attempting to adjust your " "arguments for the new API (which might not work). Please update " "your code. See the version 0.6 release notes for more info.") @@ -3210,7 +3210,7 @@ def lettervalueplot(x=None, y=None, hue=None, data=None, order=None, hue_order=N Examples -------- - Draw a single horizontal boxplot: + Draw a single horizontal letter value plot: .. plot:: :context: close-figs From 3b057dc8f0042a1a3627a5f68a0f6c8eb93b9d90 Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Fri, 31 Jul 2015 10:30:31 -0700 Subject: [PATCH 0103/1738] Fixed bug with box_widths. Wasn't passing keyword to _lvplot. --- seaborn/categorical.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index ea921238c9..2b886aaf55 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1507,10 +1507,17 @@ def _lv_outliers(self, vals, k): upper_out = vals[np.where(vals > edges[1])[0]] return np.concatenate((lower_out, upper_out)) + def _width_functions(width_func): + # Dictionary of functions for computing the width of the boxes + width_functions = {'linear' : lambda h, i, k: (i + 1.) / k, + 'exponential' : lambda h, i, k: 2**(-k+i-1), + 'area' : lambda h, i, k: (1 - 2**(-k+i-2)) / h} + return width_functions[width_func] + def _lvplot(self, box_data, positions, color=[255. / 256., 185. / 256., 0.], vert='v', widths=1, k_depth='proportion', - ax=None, p=None, box_widths='linear', + ax=None, p=None, box_widths='exponential', **kws): x = positions[0] @@ -1530,14 +1537,9 @@ def _lvplot(self, box_data, positions, # letter-value plot box_ends, k = self._lv_box_ends(box_data, k_depth=k_depth, p=p) - # Dictionary of functions for computing the width of the boxes - width_functions = {'linear' : lambda h, i, k: (i + 1.) / k, - 'exponential' : lambda h, i, k: 2**(-k+i-1), - 'area' : lambda h, i, k: (1 - 2**(-k+i-2)) / h} - # Anonymous functions for calculating the width and height # of the letter value boxes - width = width_functions[box_widths] + width = self._width_functions(box_widths) height = lambda b: b[1] - b[0] # Functions to construct the letter value boxes @@ -1623,6 +1625,7 @@ def draw_letter_value_plot(self, ax, kws): widths=self.width, k_depth=self.k_depth, ax=ax, + box_widths=self.box_widths, **kws) else: @@ -1654,6 +1657,7 @@ def draw_letter_value_plot(self, ax, kws): widths=self.nested_width, k_depth=self.k_depth, ax=ax, + box_widths=self.box_widths, **kws) def plot(self, ax, boxplot_kws): From 567b276f36d3040c21612231a74da8d3a2aff9fd Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Fri, 31 Jul 2015 10:34:55 -0700 Subject: [PATCH 0104/1738] Made median bar same width as boxes. --- seaborn/categorical.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 2b886aaf55..076eee07c4 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1561,7 +1561,6 @@ def horz_perc_box(x, b, i, k, w): # Calculate the medians y = np.median(box_data) - w = (widths + .1) # Calculate the outliers and plot outliers = self._lv_outliers(box_data, k) @@ -1571,7 +1570,7 @@ def horz_perc_box(x, b, i, k, w): for i, b in enumerate(zip(box_ends, w_area))] # Plot the medians - ax.plot([x - w / 2, x + w / 2], [y, y], c='k', alpha=.45, **kws) + ax.plot([x - widths / 2, x + widths / 2], [y, y], c='k', alpha=.45, **kws) ax.scatter(np.repeat(x, len(outliers)), outliers, marker=r"$\ast$", c=color) @@ -1580,7 +1579,7 @@ def horz_perc_box(x, b, i, k, w): for i, b in enumerate(zip(box_ends, w_area))] # Plot the medians - ax.plot([y, y], [x - w / 2, x + w / 2], c='k', alpha=.45, **kws) + ax.plot([y, y], [x - widths / 2, x + widths / 2], c='k', alpha=.45, **kws) ax.scatter(outliers, np.repeat(x, len(outliers)), marker=r"$\ast$", c=color) From 7017de18f3f6b36b4274a56e824e775fa9de8396 Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Fri, 31 Jul 2015 10:37:08 -0700 Subject: [PATCH 0105/1738] Changed outliers to d. More consistent with Seaborn boxplot. --- seaborn/categorical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 076eee07c4..a62b865f80 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1573,7 +1573,7 @@ def horz_perc_box(x, b, i, k, w): ax.plot([x - widths / 2, x + widths / 2], [y, y], c='k', alpha=.45, **kws) ax.scatter(np.repeat(x, len(outliers)), outliers, - marker=r"$\ast$", c=color) + marker='d', c=color) else: boxes = [horz_perc_box(x, b[0], i, k, b[1]) for i, b in enumerate(zip(box_ends, w_area))] @@ -1582,7 +1582,7 @@ def horz_perc_box(x, b, i, k, w): ax.plot([y, y], [x - widths / 2, x + widths / 2], c='k', alpha=.45, **kws) ax.scatter(outliers, np.repeat(x, len(outliers)), - marker=r"$\ast$", c=color) + marker='d', c=color) # Construct a color map from the input color rgb = [[1, 1, 1], list(color)] From 006ef5d6525bd504713cfa69d65a6903225d4e28 Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Sat, 1 Aug 2015 12:01:37 -0700 Subject: [PATCH 0106/1738] Changed p to outlier_prop. Minor fixes. --- seaborn/categorical.py | 97 ++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 65 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index a62b865f80..d82f91be15 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1444,7 +1444,7 @@ class _LVPlotter(_CategoricalPlotter): def __init__(self, x, y, hue, data, order, hue_order, orient, color, palette, saturation, - width, k_depth, linewidth, box_widths): + width, k_depth, linewidth, scale, outlier_prop): if width is None: width=.8 @@ -1462,22 +1462,26 @@ def __init__(self, x, y, hue, data, order, hue_order, linewidth = mpl.rcParams["lines.linewidth"] self.linewidth = linewidth - if box_widths is None: - box_widths = 'linear' - self.box_widths = box_widths + if scale is None: + scale = 'linear' + self.scale = scale + + self.outlier_prop = outlier_prop self.establish_variables(x, y, hue, data, orient, order, hue_order) self.establish_colors(color, palette, saturation) - def _lv_box_ends(self, vals, k_depth='proportion', p=None): + def _lv_box_ends(self, vals, k_depth='proportion', outlier_prop=None): """Get the number of data points and calculate `depth` of letter-value plot.""" vals = np.asarray(vals) vals = vals[np.isfinite(vals)] n = len(vals) # If p is not set, calculate it so that 8 points are outliers - if not p: - p = 8./n + if not outlier_prop: + p = 8./n + else: + p = outlier_prop # Select the depth, i.e. number of boxes to draw, based on the method k_dict = {'proportion': (np.log2(n)) - int(np.log2(n*p)) + 1, 'tukey': (np.log2(n)) - 3, @@ -1487,9 +1491,9 @@ def _lv_box_ends(self, vals, k_depth='proportion', p=None): k = int(k) except ValueError: k = 1 - # If the number happens to be less than 1, set k to 1 - if k < 1.: - k = 1 + # If the number happens to be less than 0, set k to 0 + if k < 0.: + k = 0 # Calculate the upper box ends upper = [100*(1 - 0.5**(i+2)) for i in range(k, -1, -1)] # Calculate the lower box ends @@ -1507,7 +1511,7 @@ def _lv_outliers(self, vals, k): upper_out = vals[np.where(vals > edges[1])[0]] return np.concatenate((lower_out, upper_out)) - def _width_functions(width_func): + def _width_functions(self, width_func): # Dictionary of functions for computing the width of the boxes width_functions = {'linear' : lambda h, i, k: (i + 1.) / k, 'exponential' : lambda h, i, k: 2**(-k+i-1), @@ -1517,7 +1521,7 @@ def _width_functions(width_func): def _lvplot(self, box_data, positions, color=[255. / 256., 185. / 256., 0.], vert='v', widths=1, k_depth='proportion', - ax=None, p=None, box_widths='exponential', + ax=None, outlier_prop=None, scale='exponential', **kws): x = positions[0] @@ -1535,11 +1539,12 @@ def _lvplot(self, box_data, positions, else: # Get the number of data points and calculate "depth" of # letter-value plot - box_ends, k = self._lv_box_ends(box_data, k_depth=k_depth, p=p) + box_ends, k = self._lv_box_ends(box_data, k_depth=k_depth, + outlier_prop=outlier_prop) # Anonymous functions for calculating the width and height # of the letter value boxes - width = self._width_functions(box_widths) + width = self._width_functions(scale) height = lambda b: b[1] - b[0] # Functions to construct the letter value boxes @@ -1573,7 +1578,7 @@ def horz_perc_box(x, b, i, k, w): ax.plot([x - widths / 2, x + widths / 2], [y, y], c='k', alpha=.45, **kws) ax.scatter(np.repeat(x, len(outliers)), outliers, - marker='d', c=color) + marker='d', c=color, **kws) else: boxes = [horz_perc_box(x, b[0], i, k, b[1]) for i, b in enumerate(zip(box_ends, w_area))] @@ -1582,7 +1587,7 @@ def horz_perc_box(x, b, i, k, w): ax.plot([y, y], [x - widths / 2, x + widths / 2], c='k', alpha=.45, **kws) ax.scatter(outliers, np.repeat(x, len(outliers)), - marker='d', c=color) + marker='d', c=color, **kws) # Construct a color map from the input color rgb = [[1, 1, 1], list(color)] @@ -1624,7 +1629,8 @@ def draw_letter_value_plot(self, ax, kws): widths=self.width, k_depth=self.k_depth, ax=ax, - box_widths=self.box_widths, + scale=self.scale, + outlier_prop=self.outlier_prop, **kws) else: @@ -1656,7 +1662,8 @@ def draw_letter_value_plot(self, ax, kws): widths=self.nested_width, k_depth=self.k_depth, ax=ax, - box_widths=self.box_widths, + scale=self.scale, + outlier_prop=self.outlier_prop, **kws) def plot(self, ax, boxplot_kws): @@ -3104,52 +3111,12 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, def lettervalueplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, orient=None, color=None, palette=None, saturation=None, - width=None, k_depth=None, linewidth=None, box_widths=None, - ax=None, **kwargs): - - # Try to handle broken backwards-compatability - # This should help with the lack of a smooth deprecation, - # but won't catch everything - warn = False - if isinstance(x, pd.DataFrame): - data = x - x = None - warn = True - - if "vals" in kwargs: - x = kwargs.pop("vals") - warn = True - - if "groupby" in kwargs: - y = x - x = kwargs.pop("groupby") - warn = True - - if "vert" in kwargs: - vert = kwargs.pop("vert", True) - if not vert: - x, y = y, x - orient = "v" if vert else "h" - warn = True - - if "names" in kwargs: - kwargs.pop("names") - warn = True - - if "join_rm" in kwargs: - kwargs.pop("join_rm") - warn = True - - msg = ("The Seaborn categorical API has been changed. Attempting to adjust your " - "arguments for the new API (which might not work). Please update " - "your code. See the version 0.6 release notes for more info.") - - if warn: - warnings.warn(msg, UserWarning) + width=None, k_depth=None, linewidth=None, scale=None, + outlier_prop=None, ax=None, **kwargs): plotter = _LVPlotter(x, y, hue, data, order, hue_order, orient, color, palette, saturation, - width, k_depth, linewidth, box_widths) + width, k_depth, linewidth, scale, outlier_prop) if ax is None: ax = plt.gca() @@ -3187,19 +3154,19 @@ def lettervalueplot(x=None, y=None, hue=None, data=None, order=None, hue_order=N assumptions about the number of outliers and leverages different statistical properties. {linewidth} - box_widths : "linear" | "exponential" | "area" + scale : "linear" | "exonential" | "area" Method to use for the width of the letter value boxes. All give similar results visually. "linear" reduces the width by a constant linear factor, "exponential" uses the proportion of data not covered, "area" is proportional to the percentage of data covered. - p : float, optional + outlier_prop : float, optional Proportion of data believed to be outliers. Is used in conjuction with k_depth to determine the number of percentiles to draw. Defaults to 8 outliers. {ax_in} kwargs : key, value mappings - Other keyword arguments are passed through to ``plt.boxplot`` at draw - time. + Other keyword arguments are passed through to ``plt.plot`` and + ``plt.scatter`` at draw time. Returns ------- From 238b617afd00a72d017405ead65fef3fb59390fd Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Sat, 1 Aug 2015 12:03:45 -0700 Subject: [PATCH 0107/1738] Changed name of plotting function to lvplot. --- seaborn/categorical.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index d82f91be15..a20612595d 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1797,8 +1797,8 @@ def plot(self, ax, boxplot_kws): factorplot=dedent("""\ factorplot : Combine categorical plots and a class:`FacetGrid`.\ """), - lettervalueplot=dedent("""\ - lettervalueplot : An extension of the boxplot for long-tailed and large data sets. + lvplot=dedent("""\ + lvplot : An extension of the boxplot for long-tailed and large data sets. """), ) @@ -3109,7 +3109,7 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, """).format(**_categorical_docs) -def lettervalueplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, +def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, orient=None, color=None, palette=None, saturation=None, width=None, k_depth=None, linewidth=None, scale=None, outlier_prop=None, ax=None, **kwargs): @@ -3124,7 +3124,7 @@ def lettervalueplot(x=None, y=None, hue=None, data=None, order=None, hue_order=N plotter.plot(ax, kwargs) return ax -lettervalueplot.__doc__ = dedent("""\ +lvplot.__doc__ = dedent("""\ Create a letter value plot Letter value (LV) plots are non-parametric estimates of the distribution of @@ -3188,21 +3188,21 @@ def lettervalueplot(x=None, y=None, hue=None, data=None, order=None, hue_order=N >>> import seaborn as sns >>> sns.set_style("whitegrid") >>> tips = sns.load_dataset("tips") - >>> ax = sns.lettervalueplot(x=tips["total_bill"]) + >>> ax = sns.lvplot(x=tips["total_bill"]) Draw a vertical letter value plot grouped by a categorical variable: .. plot:: :context: close-figs - >>> ax = sns.lettervalueplot(x="day", y="total_bill", data=tips) + >>> ax = sns.lvplot(x="day", y="total_bill", data=tips) Draw a letter value plot with nested grouping by two categorical variables: .. plot:: :context: close-figs - >>> ax = sns.lettervalueplot(x="day", y="total_bill", hue="smoker", + >>> ax = sns.lvplot(x="day", y="total_bill", hue="smoker", ... data=tips, palette="Set3") Draw a letter value plot with nested grouping when some bins are empty: @@ -3210,7 +3210,7 @@ def lettervalueplot(x=None, y=None, hue=None, data=None, order=None, hue_order=N .. plot:: :context: close-figs - >>> ax = sns.lettervalueplot(x="day", y="total_bill", hue="time", + >>> ax = sns.lvplot(x="day", y="total_bill", hue="time", ... data=tips, linewidth=2.5) Control box order by sorting the input data: @@ -3218,14 +3218,14 @@ def lettervalueplot(x=None, y=None, hue=None, data=None, order=None, hue_order=N .. plot:: :context: close-figs - >>> ax = sns.lettervalueplot(x="size", y="tip", data=tips.sort("size")) + >>> ax = sns.lvplot(x="size", y="tip", data=tips.sort("size")) Control box order by passing an explicit order: .. plot:: :context: close-figs - >>> ax = sns.lettervalueplot(x="size", y="tip", data=tips, + >>> ax = sns.lvplot(x="size", y="tip", data=tips, ... order=np.arange(1, 7), palette="Blues_d") Draw a letter value plot for each numeric variable in a DataFrame: @@ -3234,14 +3234,14 @@ def lettervalueplot(x=None, y=None, hue=None, data=None, order=None, hue_order=N :context: close-figs >>> iris = sns.load_dataset("iris") - >>> ax = sns.lettervalueplot(data=iris, orient="h", palette="Set2") + >>> ax = sns.lvplot(data=iris, orient="h", palette="Set2") Use :func:`stripplot` to show the datapoints on top of the boxes: .. plot:: :context: close-figs - >>> ax = sns.lettervalueplot(x="day", y="total_bill", data=tips) + >>> ax = sns.lvplot(x="day", y="total_bill", data=tips) >>> ax = sns.stripplot(x="day", y="total_bill", data=tips, ... size=4, jitter=True, edgecolor="gray") @@ -3252,7 +3252,7 @@ def lettervalueplot(x=None, y=None, hue=None, data=None, order=None, hue_order=N :context: close-figs >>> g = sns.FacetGrid(tips, col="time", size=4, aspect=.7) - >>> (g.map(sns.lettervalueplot, "sex", "total_bill", "smoker") + >>> (g.map(sns.lvplot, "sex", "total_bill", "smoker") ... .despine(left=True) ... .add_legend(title="smoker")) #doctest: +ELLIPSIS From 9d6cf407ea7d0f7e859806806e36a33d0ff2ee58 Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Sat, 1 Aug 2015 13:02:50 -0700 Subject: [PATCH 0108/1738] PEP8 fixes. --- seaborn/categorical.py | 105 +++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 47 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index a20612595d..3dab6516c0 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1440,6 +1440,7 @@ def plot(self, ax): if self.orient == "h": ax.invert_yaxis() + class _LVPlotter(_CategoricalPlotter): def __init__(self, x, y, hue, data, order, hue_order, @@ -1447,7 +1448,7 @@ def __init__(self, x, y, hue, data, order, hue_order, width, k_depth, linewidth, scale, outlier_prop): if width is None: - width=.8 + width = .8 self.width = width if saturation is None: @@ -1485,7 +1486,8 @@ def _lv_box_ends(self, vals, k_depth='proportion', outlier_prop=None): # Select the depth, i.e. number of boxes to draw, based on the method k_dict = {'proportion': (np.log2(n)) - int(np.log2(n*p)) + 1, 'tukey': (np.log2(n)) - 3, - 'trustworthy': (np.log2(n) - np.log2(2*stats.norm.ppf((1-p))**2)) + 1} + 'trustworthy': (np.log2(n) - + np.log2(2*stats.norm.ppf((1-p))**2)) + 1} k = k_dict[k_depth] try: k = int(k) @@ -1513,9 +1515,9 @@ def _lv_outliers(self, vals, k): def _width_functions(self, width_func): # Dictionary of functions for computing the width of the boxes - width_functions = {'linear' : lambda h, i, k: (i + 1.) / k, - 'exponential' : lambda h, i, k: 2**(-k+i-1), - 'area' : lambda h, i, k: (1 - 2**(-k+i-2)) / h} + width_functions = {'linear': lambda h, i, k: (i + 1.) / k, + 'exponential': lambda h, i, k: 2**(-k+i-1), + 'area': lambda h, i, k: (1 - 2**(-k+i-2)) / h} return width_functions[width_func] def _lvplot(self, box_data, positions, @@ -1545,23 +1547,27 @@ def _lvplot(self, box_data, positions, # Anonymous functions for calculating the width and height # of the letter value boxes width = self._width_functions(scale) - height = lambda b: b[1] - b[0] + + # Function to find height of boxes + def height(b): + return b[1] - b[0] # Functions to construct the letter value boxes def vert_perc_box(x, b, i, k, w): rect = Patches.Rectangle((x - widths*w / 2, b[0]), - widths*w, - height(b), fill=True) + widths*w, + height(b), fill=True) return rect def horz_perc_box(x, b, i, k, w): rect = Patches.Rectangle((b[0], x - widths*w / 2), - height(b), widths*w, - fill=True) + height(b), widths*w, + fill=True) return rect # Scale the width of the boxes so the biggest starts at 1 - w_area = np.array([width(height(b), i, k) for i, b in enumerate(box_ends)]) + w_area = np.array([width(height(b), i, k) + for i, b in enumerate(box_ends)]) w_area = w_area / np.max(w_area) # Calculate the medians @@ -1572,19 +1578,21 @@ def horz_perc_box(x, b, i, k, w): if vert: boxes = [vert_perc_box(x, b[0], i, k, b[1]) - for i, b in enumerate(zip(box_ends, w_area))] + for i, b in enumerate(zip(box_ends, w_area))] # Plot the medians - ax.plot([x - widths / 2, x + widths / 2], [y, y], c='k', alpha=.45, **kws) + ax.plot([x - widths / 2, x + widths / 2], [y, y], + c='k', alpha=.45, **kws) ax.scatter(np.repeat(x, len(outliers)), outliers, marker='d', c=color, **kws) else: boxes = [horz_perc_box(x, b[0], i, k, b[1]) - for i, b in enumerate(zip(box_ends, w_area))] + for i, b in enumerate(zip(box_ends, w_area))] # Plot the medians - ax.plot([y, y], [x - widths / 2, x + widths / 2], c='k', alpha=.45, **kws) + ax.plot([y, y], [x - widths / 2, x + widths / 2], + c='k', alpha=.45, **kws) ax.scatter(outliers, np.repeat(x, len(outliers)), marker='d', c=color, **kws) @@ -1623,15 +1631,15 @@ def draw_letter_value_plot(self, ax, kws): color = self.colors[i] artist_dict = self._lvplot(box_data, - positions=[i], - color=color, - vert=vert, - widths=self.width, - k_depth=self.k_depth, - ax=ax, - scale=self.scale, - outlier_prop=self.outlier_prop, - **kws) + positions=[i], + color=color, + vert=vert, + widths=self.width, + k_depth=self.k_depth, + ax=ax, + scale=self.scale, + outlier_prop=self.outlier_prop, + **kws) else: # Draw nested groups of boxes @@ -1655,16 +1663,17 @@ def draw_letter_value_plot(self, ax, kws): color = self.colors[j] center = i + offsets[j] + artist_dict = self._lvplot(box_data, - positions=[center], - color=color, - vert=vert, - widths=self.nested_width, - k_depth=self.k_depth, - ax=ax, - scale=self.scale, - outlier_prop=self.outlier_prop, - **kws) + positions=[center], + color=color, + vert=vert, + widths=self.nested_width, + k_depth=self.k_depth, + ax=ax, + scale=self.scale, + outlier_prop=self.outlier_prop, + **kws) def plot(self, ax, boxplot_kws): """Make the plot.""" @@ -3109,14 +3118,15 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, """).format(**_categorical_docs) + def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, - orient=None, color=None, palette=None, saturation=None, - width=None, k_depth=None, linewidth=None, scale=None, - outlier_prop=None, ax=None, **kwargs): + orient=None, color=None, palette=None, saturation=None, + width=None, k_depth=None, linewidth=None, scale=None, + outlier_prop=None, ax=None, **kwargs): plotter = _LVPlotter(x, y, hue, data, order, hue_order, - orient, color, palette, saturation, - width, k_depth, linewidth, scale, outlier_prop) + orient, color, palette, saturation, + width, k_depth, linewidth, scale, outlier_prop) if ax is None: ax = plt.gca() @@ -3129,10 +3139,11 @@ def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, Letter value (LV) plots are non-parametric estimates of the distribution of a dataset, similar to boxplots. LV plots are also similar to violin plots - but without the need to fit a kernel density estimate. Thus, LV plots are fast - to generate, directly interpretable in terms of the distribution of data, and - easy to understand. For a more extensive explanation of letter value plots - and their properties, see Hadley Wickham's excellent paper on the topic: + but without the need to fit a kernel density estimate. Thus, LV plots are + fast to generate, directly interpretable in terms of the distribution of + data, and easy to understand. For a more extensive explanation of letter + value plots and their properties, see Hadley Wickham's excellent paper on + the topic: http://vita.had.co.nz/papers/letter-value-plot.html @@ -3156,9 +3167,9 @@ def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, {linewidth} scale : "linear" | "exonential" | "area" Method to use for the width of the letter value boxes. All give similar - results visually. "linear" reduces the width by a constant linear factor, - "exponential" uses the proportion of data not covered, "area" is - proportional to the percentage of data covered. + results visually. "linear" reduces the width by a constant linear + factor, "exponential" uses the proportion of data not covered, "area" + is proportional to the percentage of data covered. outlier_prop : float, optional Proportion of data believed to be outliers. Is used in conjuction with k_depth to determine the number of percentiles to draw. Defaults to 8 @@ -3245,8 +3256,8 @@ def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.stripplot(x="day", y="total_bill", data=tips, ... size=4, jitter=True, edgecolor="gray") - Draw a letter value plot on to a :class:`FacetGrid` to group within an additional - categorical variable: + Draw a letter value plot on to a :class:`FacetGrid` to group within an + additional categorical variable: .. plot:: :context: close-figs From f451e00368a48cf5ac152508931d2c3c041f900a Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Sat, 1 Aug 2015 14:19:23 -0700 Subject: [PATCH 0109/1738] Fixed division by zero error. --- seaborn/categorical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 3dab6516c0..4cdcaba08e 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1494,8 +1494,8 @@ def _lv_box_ends(self, vals, k_depth='proportion', outlier_prop=None): except ValueError: k = 1 # If the number happens to be less than 0, set k to 0 - if k < 0.: - k = 0 + if k < 1.: + k = 1 # Calculate the upper box ends upper = [100*(1 - 0.5**(i+2)) for i in range(k, -1, -1)] # Calculate the lower box ends From 6556b3ee1a513ba83fa623f40e3a8c1616866ed6 Mon Sep 17 00:00:00 2001 From: Gabe Fernando Date: Thu, 6 Aug 2015 07:43:31 -0400 Subject: [PATCH 0110/1738] DOC: correct some typos --- doc/tutorial/categorical.ipynb | 6 +++--- doc/tutorial/regression.ipynb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/tutorial/categorical.ipynb b/doc/tutorial/categorical.ipynb index 07e6f5abfe..630cdf769d 100644 --- a/doc/tutorial/categorical.ipynb +++ b/doc/tutorial/categorical.ipynb @@ -120,7 +120,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "It's also possible to add a nested categorical variable with the ``hue`` paramater. Above the color and position on the categorical axis are redundent, but now each provides information about one of the two variables:" + "It's also possible to add a nested categorical variable with the ``hue`` parameter. Above the color and position on the categorical axis are redundant, but now each provides information about one of the two variables:" ] }, { @@ -198,7 +198,7 @@ "Violinplots\n", "^^^^^^^^^^^\n", "\n", - "A different approach is a :func:`violinplot`, which combines a boxplot with the kernel density estimation procedure described in the :ref:`distributions tutorial `_:" + "A different approach is a :func:`violinplot`, which combines a boxplot with the kernel density estimation procedure described in the :ref:`distributions tutorial `:" ] }, { @@ -511,7 +511,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "Any kind of plot can be drawn. Because of the way :class:`FacetGrid` works, to change the size and shape of the figure you need to specify the ``size`` and ``aspect`` arguments, which apply to a single facet:" + "Any kind of plot can be drawn. Because of the way :class:`FacetGrid` works, to change the size and shape of the figure you need to specify the ``size`` and ``aspect`` arguments, which apply to each facet:" ] }, { diff --git a/doc/tutorial/regression.ipynb b/doc/tutorial/regression.ipynb index 475ba14e96..5eeb278e89 100644 --- a/doc/tutorial/regression.ipynb +++ b/doc/tutorial/regression.ipynb @@ -590,4 +590,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file From 6a83ad9c1d57958424a2556e9f114f4607638660 Mon Sep 17 00:00:00 2001 From: Saulius Lukauskas Date: Fri, 21 Aug 2015 19:17:04 +0300 Subject: [PATCH 0111/1738] changed matrix to work faster when no ticks are provided --- seaborn/matrix.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index a5563aa6fc..98ba25e7bb 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -111,19 +111,19 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, if isinstance(xticklabels, int) and xticklabels > 1: xtickevery = xticklabels xticklabels = _index_to_ticklabels(data.columns) - elif isinstance(xticklabels, bool) and xticklabels: + elif xticklabels is True: xticklabels = _index_to_ticklabels(data.columns) - elif isinstance(xticklabels, bool) and not xticklabels: - xticklabels = ['' for _ in range(data.shape[1])] + elif xticklabels is False: + xticklabels = [] ytickevery = 1 if isinstance(yticklabels, int) and yticklabels > 1: ytickevery = yticklabels yticklabels = _index_to_ticklabels(data.index) - elif isinstance(yticklabels, bool) and yticklabels: + elif yticklabels is True: yticklabels = _index_to_ticklabels(data.index) - elif isinstance(yticklabels, bool) and not yticklabels: - yticklabels = ['' for _ in range(data.shape[0])] + elif yticklabels is False: + yticklabels = [] else: yticklabels = yticklabels[::-1] From bd092542af770b7f9f8893436dc37527292295eb Mon Sep 17 00:00:00 2001 From: Gregory Hitz Date: Fri, 21 Aug 2015 18:24:12 +0200 Subject: [PATCH 0112/1738] changed handling of annot_kws so that standard values can be overwritten --- seaborn/matrix.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 0a6c5f51eb..2c1370c734 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -201,8 +201,9 @@ def _annotate_heatmap(self, ax, mesh): _, l, _ = colorsys.rgb_to_hls(*color[:3]) text_color = ".15" if l > .5 else "w" val = ("{:" + self.fmt + "}").format(val) - ax.text(x, y, val, color=text_color, - ha="center", va="center", **self.annot_kws) + text_kwargs = dict(color=text_color, ha="center", va="center") + text_kwargs.update(self.annot_kws) + ax.text(x, y, val, **text_kwargs) def plot(self, ax, cax, kws): """Draw the heatmap on the provided Axes.""" From 7d2fbfbf51882fe404a3aa838cf927d7215fb1e5 Mon Sep 17 00:00:00 2001 From: Saulius Lukauskas Date: Sat, 22 Aug 2015 19:27:02 +0300 Subject: [PATCH 0113/1738] changed the labelsoff test to request [] --- seaborn/tests/test_matrix.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index d13579af6d..97dfaace04 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -185,10 +185,8 @@ def test_tickabels_off(self): kws['xticklabels'] = False kws['yticklabels'] = False p = mat._HeatMapper(self.df_norm, **kws) - nt.assert_equal(p.xticklabels, ['' for _ in range( - self.df_norm.shape[1])]) - nt.assert_equal(p.yticklabels, ['' for _ in range( - self.df_norm.shape[0])]) + nt.assert_equal(p.xticklabels, []) + nt.assert_equal(p.yticklabels, []) def test_custom_ticklabels(self): kws = self.default_kws.copy() From 5969eb68942a923dc803659d04b1c80cf589bcb1 Mon Sep 17 00:00:00 2001 From: Gregory Hitz Date: Mon, 24 Aug 2015 13:31:14 +0200 Subject: [PATCH 0114/1738] added a test for annot_kws which overwrite the standard values --- seaborn/tests/test_matrix.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 5bd5504b02..218036fa3b 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -225,6 +225,17 @@ def test_heatmap_annotation(self): nt.assert_equal(text.get_fontsize(), 14) plt.close("all") + def test_heatmap_annotation_overwrite_kws(self): + + annot_kws = dict(color="0.3", va="bottom", ha="left") + ax = mat.heatmap(self.df_norm, annot=True, fmt=".1f", + annot_kws=annot_kws) + for text in ax.texts: + nt.assert_equal(text.get_color(), "0.3") + nt.assert_equal(text.get_ha(), "left") + nt.assert_equal(text.get_va(), "bottom") + plt.close("all") + def test_heatmap_annotation_with_mask(self): df = pd.DataFrame(data={'a': [1, 1, 1], From cbbdc5cdfc96f1b00e9bf5a336d6253daeeb2e29 Mon Sep 17 00:00:00 2001 From: Saulius Lukauskas Date: Wed, 26 Aug 2015 11:43:55 +0100 Subject: [PATCH 0115/1738] use LineCollection to draw dendrogram. Should be faster --- seaborn/matrix.py | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 4c2869843b..2710fc939d 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -3,6 +3,7 @@ import colorsys import matplotlib as mpl +from matplotlib.collections import LineCollection import matplotlib.pyplot as plt from matplotlib import gridspec import numpy as np @@ -511,6 +512,9 @@ def __init__(self, data, linkage, metric, method, axis, label, rotate): self.yticklabels, self.xticklabels = [], [] self.xlabel, self.ylabel = '', '' + self.dependent_coord = self.dendrogram['dcoord'] + self.independent_coord = self.dendrogram['icoord'] + if self.rotate: self.X = self.dendrogram['dcoord'] self.Y = self.dendrogram['icoord'] @@ -583,19 +587,33 @@ def plot(self, ax): Axes object upon which the dendrogram is plotted """ - for x, y in zip(self.X, self.Y): - ax.plot(x, y, color='k', linewidth=.5) + line_kwargs = dict(linewidths=.5, colors='k') + if self.rotate and self.axis == 0: + lines = LineCollection([list(zip(x, y)) for x, y in zip(self.dependent_coord, + self.independent_coord)], + **line_kwargs) + else: + lines = LineCollection([list(zip(x, y)) for x, y in zip(self.independent_coord, + self.dependent_coord)], + **line_kwargs) + + ax.add_collection(lines) + number_of_leaves = len(self.reordered_ind) + max_dependent_coord = max(map(max, self.dependent_coord)) if self.rotate and self.axis == 0: - ax.invert_xaxis() ax.yaxis.set_ticks_position('right') - - ymax = min(map(min, self.Y)) + max(map(max, self.Y)) - ax.set_ylim(0, ymax) ax.invert_yaxis() + + # Constants 10 and 1.05 come from _plot_dendrogram in scipy.cluster.hierarchy + ax.set_ylim(0, number_of_leaves * 10) + ax.set_xlim(0, max_dependent_coord * 1.05) + + ax.invert_xaxis() else: - xmax = min(map(min, self.X)) + max(map(max, self.X)) - ax.set_xlim(0, xmax) + # Constants 10 and 1.05 come from _plot_dendrogram in scipy.cluster.hierarchy + ax.set_xlim(0, number_of_leaves * 10) + ax.set_ylim(0, max_dependent_coord * 1.05) despine(ax=ax, bottom=True, left=True) From 64af6602fbc8901e2b53c02b3502b8174999ba81 Mon Sep 17 00:00:00 2001 From: Saulius Lukauskas Date: Wed, 26 Aug 2015 11:52:18 +0100 Subject: [PATCH 0116/1738] invert y axis --- seaborn/matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 2710fc939d..904111eb7e 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -603,13 +603,13 @@ def plot(self, ax): if self.rotate and self.axis == 0: ax.yaxis.set_ticks_position('right') - ax.invert_yaxis() # Constants 10 and 1.05 come from _plot_dendrogram in scipy.cluster.hierarchy ax.set_ylim(0, number_of_leaves * 10) ax.set_xlim(0, max_dependent_coord * 1.05) ax.invert_xaxis() + ax.invert_yaxis() else: # Constants 10 and 1.05 come from _plot_dendrogram in scipy.cluster.hierarchy ax.set_xlim(0, number_of_leaves * 10) From d65d3189d5b8ad4a8f56d02dc9f450e95187dc53 Mon Sep 17 00:00:00 2001 From: Saulius Lukauskas Date: Wed, 26 Aug 2015 11:56:50 +0100 Subject: [PATCH 0117/1738] removed .X and .Y in dendrogram plotter --- seaborn/matrix.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 904111eb7e..d3ee39215f 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -515,13 +515,6 @@ def __init__(self, data, linkage, metric, method, axis, label, rotate): self.dependent_coord = self.dendrogram['dcoord'] self.independent_coord = self.dendrogram['icoord'] - if self.rotate: - self.X = self.dendrogram['dcoord'] - self.Y = self.dendrogram['icoord'] - else: - self.X = self.dendrogram['icoord'] - self.Y = self.dendrogram['dcoord'] - def _calculate_linkage_scipy(self): if np.product(self.shape) >= 10000: UserWarning('This will be slow... (gentle suggestion: ' From 4d1dbb571318928d4b806643cf8aaefaf58e81cb Mon Sep 17 00:00:00 2001 From: Saulius Lukauskas Date: Wed, 26 Aug 2015 12:16:57 +0100 Subject: [PATCH 0118/1738] fixed dendrogram tests --- seaborn/matrix.py | 2 +- seaborn/tests/test_matrix.py | 34 ++++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index d3ee39215f..f044b6485b 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -594,7 +594,7 @@ def plot(self, ax): number_of_leaves = len(self.reordered_ind) max_dependent_coord = max(map(max, self.dependent_coord)) - if self.rotate and self.axis == 0: + if self.rotate: ax.yaxis.set_ticks_position('right') # Constants 10 and 1.05 come from _plot_dendrogram in scipy.cluster.hierarchy diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 8b6ce22d25..05c4ba8800 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -539,13 +539,15 @@ def test_dendrogram_plot(self): d = mat.dendrogram(self.x_norm, **self.default_kws) ax = plt.gca() - d.xmin, d.xmax = ax.get_xlim() - xmax = min(map(min, d.X)) + max(map(max, d.X)) - nt.assert_equal(d.xmin, 0) - nt.assert_equal(d.xmax, xmax) + xlim = ax.get_xlim() + # 10 comes from _plot_dendrogram in scipy.cluster.hierarchy + xmax = len(d.reordered_ind) * 10 + + nt.assert_equal(xlim[0], 0) + nt.assert_equal(xlim[1], xmax) + + nt.assert_equal(len(ax.collections[0].get_paths()), len(d.dependent_coord)) - nt.assert_equal(len(ax.get_lines()), len(d.X)) - nt.assert_equal(len(ax.get_lines()), len(d.Y)) plt.close('all') def test_dendrogram_rotate(self): @@ -555,10 +557,14 @@ def test_dendrogram_rotate(self): d = mat.dendrogram(self.x_norm, **kws) ax = plt.gca() - d.ymin, d.ymax = ax.get_ylim() - ymax = min(map(min, d.Y)) + max(map(max, d.Y)) - nt.assert_equal(d.ymin, 0) - nt.assert_equal(d.ymax, ymax) + ylim = ax.get_ylim() + + # 10 comes from _plot_dendrogram in scipy.cluster.hierarchy + ymax = len(d.reordered_ind) * 10 + + # Since y axis is inverted, ylim is (80, 0) and not (0, 80) as expected: + nt.assert_equal(ylim[1], 0) + nt.assert_equal(ylim[0], ymax) plt.close('all') def test_dendrogram_ticklabel_rotation(self): @@ -824,10 +830,10 @@ def test_savefig(self): def test_plot_dendrograms(self): cm = mat.clustermap(self.df_norm, **self.default_kws) - nt.assert_equal(len(cm.ax_row_dendrogram.get_lines()), - len(cm.dendrogram_row.X)) - nt.assert_equal(len(cm.ax_col_dendrogram.get_lines()), - len(cm.dendrogram_col.X)) + nt.assert_equal(len(cm.ax_row_dendrogram.collections[0].get_paths()), + len(cm.dendrogram_row.independent_coord)) + nt.assert_equal(len(cm.ax_col_dendrogram.collections[0].get_paths()), + len(cm.dendrogram_col.independent_coord)) data2d = self.df_norm.iloc[cm.dendrogram_row.reordered_ind, cm.dendrogram_col.reordered_ind] pdt.assert_frame_equal(cm.data2d, data2d) From 9cacc5ca5d2d5d9c55f61968dcd8a720e3c06615 Mon Sep 17 00:00:00 2001 From: Saulius Lukauskas Date: Wed, 26 Aug 2015 12:24:45 +0100 Subject: [PATCH 0119/1738] fixed pep8 line end warnigns --- seaborn/matrix.py | 16 ++++++++++------ seaborn/tests/test_matrix.py | 6 ++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index f044b6485b..dae283b331 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -582,12 +582,14 @@ def plot(self, ax): """ line_kwargs = dict(linewidths=.5, colors='k') if self.rotate and self.axis == 0: - lines = LineCollection([list(zip(x, y)) for x, y in zip(self.dependent_coord, - self.independent_coord)], + lines = LineCollection([list(zip(x, y)) + for x, y in zip(self.dependent_coord, + self.independent_coord)], **line_kwargs) else: - lines = LineCollection([list(zip(x, y)) for x, y in zip(self.independent_coord, - self.dependent_coord)], + lines = LineCollection([list(zip(x, y)) + for x, y in zip(self.independent_coord, + self.dependent_coord)], **line_kwargs) ax.add_collection(lines) @@ -597,14 +599,16 @@ def plot(self, ax): if self.rotate: ax.yaxis.set_ticks_position('right') - # Constants 10 and 1.05 come from _plot_dendrogram in scipy.cluster.hierarchy + # Constants 10 and 1.05 come from + # `scipy.cluster.hierarchy._plot_dendrogram` ax.set_ylim(0, number_of_leaves * 10) ax.set_xlim(0, max_dependent_coord * 1.05) ax.invert_xaxis() ax.invert_yaxis() else: - # Constants 10 and 1.05 come from _plot_dendrogram in scipy.cluster.hierarchy + # Constants 10 and 1.05 come from + # `scipy.cluster.hierarchy._plot_dendrogram` ax.set_xlim(0, number_of_leaves * 10) ax.set_ylim(0, max_dependent_coord * 1.05) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 05c4ba8800..5c8e906b4d 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -546,7 +546,8 @@ def test_dendrogram_plot(self): nt.assert_equal(xlim[0], 0) nt.assert_equal(xlim[1], xmax) - nt.assert_equal(len(ax.collections[0].get_paths()), len(d.dependent_coord)) + nt.assert_equal(len(ax.collections[0].get_paths()), + len(d.dependent_coord)) plt.close('all') @@ -562,7 +563,8 @@ def test_dendrogram_rotate(self): # 10 comes from _plot_dendrogram in scipy.cluster.hierarchy ymax = len(d.reordered_ind) * 10 - # Since y axis is inverted, ylim is (80, 0) and not (0, 80) as expected: + # Since y axis is inverted, ylim is (80, 0) + # and therefore not (0, 80) as usual: nt.assert_equal(ylim[1], 0) nt.assert_equal(ylim[0], ymax) plt.close('all') From f8b5f90709784a04597b209d5b806535b20ec076 Mon Sep 17 00:00:00 2001 From: Daniel Himmelstein Date: Wed, 26 Aug 2015 09:22:25 -0700 Subject: [PATCH 0120/1738] Update linearmodels.py Fixes a bug in lmplot. When `sharex=True`, the `scatter` argument was being overwritten and evaluating as True. Therefore, when lmplot was called with `scatter=False` a scatterplot would be drawn unless `sharex=False`. --- seaborn/linearmodels.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index 4781cf5fff..d59596d47c 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -557,8 +557,7 @@ def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, # by the limits of the plot if sharex: for ax in facets.axes.flat: - scatter = ax.scatter(data[x], np.ones(len(data)) * data[y].mean()) - scatter.remove() + ax.scatter(data[x], np.ones(len(data)) * data[y].mean()).remove() # Draw the regression plot on each facet regplot_kws = dict( From 232a167395cef896a537c32a7143b14b082bc079 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 27 Aug 2015 19:40:26 -0700 Subject: [PATCH 0121/1738] Update release notes --- doc/releases/v0.6.1.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.6.1.txt b/doc/releases/v0.6.1.txt index 5ae22e970d..4880c96f86 100644 --- a/doc/releases/v0.6.1.txt +++ b/doc/releases/v0.6.1.txt @@ -4,6 +4,8 @@ v0.6.1 (Unreleased) - Added an additional rule when determining category order in categorical plots. Now, when numeric variables are used in a categorical role, the default behavior is to sort the unique levels of the variable (i.e they will be in proper numerical order). This can still be overriden by the appropriate ``{*_}order`` parameter, and variables with a ``category`` dtype will still follow the category order even if the levels are strictly numerical. +- Improve performance for large dendrograms in :func:`clustermap`. + - Added ``font.size`` to the plotting context definition so that the default output from ``plt.text`` will be scaled appropriately. - Fixed a bug in :func:`clustermap` when ``fastcluster`` is not installed. From cb6a00aac48c6fe6c82b13a8ae620ed0bd289a6c Mon Sep 17 00:00:00 2001 From: Saulius Lukauskas Date: Fri, 28 Aug 2015 12:36:48 +0100 Subject: [PATCH 0122/1738] do not draw ticks at all if ticklabels are disabled --- seaborn/matrix.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index dae283b331..c7e054d238 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -130,12 +130,22 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, # Get the positions and used label for the ticks nx, ny = data.T.shape - xstart, xend, xstep = 0, nx, xtickevery - self.xticks = np.arange(xstart, xend, xstep) + .5 - self.xticklabels = xticklabels[xstart:xend:xstep] - ystart, yend, ystep = (ny - 1) % ytickevery, ny, ytickevery - self.yticks = np.arange(ystart, yend, ystep) + .5 - self.yticklabels = yticklabels[ystart:yend:ystep] + + if xticklabels == []: + self.xticks = [] + self.xticklabels = [] + else: + xstart, xend, xstep = 0, nx, xtickevery + self.xticks = np.arange(xstart, xend, xstep) + .5 + self.xticklabels = xticklabels[xstart:xend:xstep] + + if yticklabels == []: + self.yticks = [] + self.yticklabels = [] + else: + ystart, yend, ystep = (ny - 1) % ytickevery, ny, ytickevery + self.yticks = np.arange(ystart, yend, ystep) + .5 + self.yticklabels = yticklabels[ystart:yend:ystep] # Get good names for the axis labels xlabel = _index_to_label(data.columns) From 840789df67be1fb8d44f99870296caebe1d760b9 Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Tue, 1 Sep 2015 16:09:10 -0700 Subject: [PATCH 0123/1738] Added numerical tests. --- seaborn/categorical.py | 6 +- seaborn/tests/test_categorical.py | 199 ++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+), 3 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 4cdcaba08e..0efdfecdbc 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1464,7 +1464,7 @@ def __init__(self, x, y, hue, data, order, hue_order, self.linewidth = linewidth if scale is None: - scale = 'linear' + scale = 'exponential' self.scale = scale self.outlier_prop = outlier_prop @@ -3120,8 +3120,8 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, - orient=None, color=None, palette=None, saturation=None, - width=None, k_depth=None, linewidth=None, scale=None, + orient=None, color=None, palette=None, saturation=.75, + width=.8, k_depth='proportion', linewidth=None, scale='exponential', outlier_prop=None, ax=None, **kwargs): plotter = _LVPlotter(x, y, hue, data, order, hue_order, diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index e74e4551c6..ac6907253e 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1,3 +1,4 @@ +import itertools import numpy as np import pandas as pd import scipy @@ -2178,3 +2179,201 @@ def test_plot_colors(self): for l1, l2 in zip(ax.lines, g.ax.lines): nt.assert_equal(l1.get_color(), l2.get_color()) plt.close("all") + + +class TestLVPlotter(CategoricalFixture): + + def setUp(self): + def edge_calc(n, data): + f = 2**(-n) + mid = np.median(data) + maxi = np.max(data) + return np.array([mid*f, maxi - mid*f]) + + self.default_kws = dict(x=None, y=None, hue=None, data=None, + order=None, hue_order=None, + orient=None, color=None, palette=None, + saturation=.75, width=.8, + k_depth='proportion', linewidth=None, + scale='exponential', outlier_prop=None) + self.linear_data = np.arange(101) + self.n = len(self.linear_data) + self.expected_edges = map(lambda i: edge_calc(i, self.linear_data), + xrange(5, 0, -1)) + self.expected_k = int(np.log2(self.n)) - int(np.log2(self.n*0.08)) + 1 + self.outlier_data = np.concatenate((np.arange(100), [200])) + + def test_box_ends_finite(self): + p = cat._LVPlotter(**self.default_kws) + p.establish_variables("g", "y", data=self.df) + box_ends, k_vals = np.hsplit(np.asarray(map(p._lv_box_ends, + p.plot_data)), 2) + box_ends = box_ends.squeeze() + k_vals = k_vals.squeeze() + + # Check that all the box ends are finite and are within + # the bounds of the data + b_e = map(lambda a: np.all(np.isfinite(a)), box_ends) + npt.assert_equal(np.sum(b_e), len(box_ends)) + + def within(t): + a, d = t + return ((np.ravel(a) <= d.max()) & + (np.ravel(a) >= d.min())).all() + + b_w = map(within, itertools.izip(box_ends, p.plot_data)) + npt.assert_equal(np.sum(b_w), len(box_ends)) + + k_f = map(lambda k: (k > 0.) & np.isfinite(k), k_vals) + npt.assert_equal(np.sum(k_f), len(k_vals)) + + def test_box_ends_correct(self): + p = cat._LVPlotter(**self.default_kws) + calc_edges, calc_k = p._lv_box_ends(self.linear_data) + + npt.assert_equal(self.expected_edges, calc_edges) + + npt.assert_equal(self.expected_k, calc_k) + + def test_outliers(self): + p = cat._LVPlotter(**self.default_kws) + calc_edges, calc_k = p._lv_box_ends(self.outlier_data) + + npt.assert_equal(self.expected_edges, calc_edges) + + npt.assert_equal(self.expected_k, calc_k) + + out_calc = p._lv_outliers(self.outlier_data, calc_k) + out_exp = p._lv_outliers(self.outlier_data, self.expected_k) + + npt.assert_equal(out_exp, out_calc) + + def test_hue_offsets(self): + + p = cat._LVPlotter(**self.default_kws) + p.establish_variables("g", "y", "h", data=self.df) + npt.assert_array_equal(p.hue_offsets, [-.2, .2]) + + kws = self.default_kws.copy() + kws["width"] = .6 + p = cat._LVPlotter(**kws) + p.establish_variables("g", "y", "h", data=self.df) + npt.assert_array_equal(p.hue_offsets, [-.15, .15]) + + p = cat._LVPlotter(**kws) + p.establish_variables("h", "y", "g", data=self.df) + npt.assert_array_almost_equal(p.hue_offsets, [-.2, 0, .2]) + + def test_axes_data(self): + + ax = cat.lvplot("g", "y", data=self.df) + nt.assert_equal(len(ax.artists), 3) + + plt.close("all") + + ax = cat.lvplot("g", "y", "h", data=self.df) + nt.assert_equal(len(ax.artists), 6) + + plt.close("all") + + def test_box_colors(self): + + ax = cat.lvplot("g", "y", data=self.df, saturation=1) + pal = palettes.color_palette("deep", 3) + for patch, color in zip(ax.artists, pal): + nt.assert_equal(patch.get_facecolor()[:3], color) + + plt.close("all") + + ax = cat.lvplot("g", "y", "h", data=self.df, saturation=1) + pal = palettes.color_palette("deep", 2) + for patch, color in zip(ax.artists, pal * 2): + nt.assert_equal(patch.get_facecolor()[:3], color) + + plt.close("all") + + def test_draw_missing_boxes(self): + + ax = cat.lvplot("g", "y", data=self.df, + order=["a", "b", "c", "d"]) + nt.assert_equal(len(ax.artists), 3) + plt.close("all") + + def test_missing_data(self): + + x = ["a", "a", "b", "b", "c", "c", "d", "d"] + h = ["x", "y", "x", "y", "x", "y", "x", "y"] + y = self.rs.randn(8) + y[-2:] = np.nan + + ax = cat.lvplot(x, y) + nt.assert_equal(len(ax.artists), 3) + + plt.close("all") + + y[-1] = 0 + ax = cat.lvplot(x, y, h) + nt.assert_equal(len(ax.artists), 7) + + plt.close("all") + + def test_lvplots(self): + + # Smoke test the high level lvplot options + + cat.lvplot("y", data=self.df) + plt.close("all") + + cat.lvplot(y="y", data=self.df) + plt.close("all") + + cat.lvplot("g", "y", data=self.df) + plt.close("all") + + cat.lvplot("y", "g", data=self.df, orient="h") + plt.close("all") + + cat.lvplot("g", "y", "h", data=self.df) + plt.close("all") + + cat.lvplot("g", "y", "h", order=list("nabc"), data=self.df) + plt.close("all") + + cat.lvplot("g", "y", "h", hue_order=list("omn"), data=self.df) + plt.close("all") + + cat.lvplot("y", "g", "h", data=self.df, orient="h") + plt.close("all") + + def test_axes_annotation(self): + + ax = cat.lvplot("g", "y", data=self.df) + nt.assert_equal(ax.get_xlabel(), "g") + nt.assert_equal(ax.get_ylabel(), "y") + nt.assert_equal(ax.get_xlim(), (-.5, 2.5)) + npt.assert_array_equal(ax.get_xticks(), [0, 1, 2]) + npt.assert_array_equal([l.get_text() for l in ax.get_xticklabels()], + ["a", "b", "c"]) + + plt.close("all") + + ax = cat.lvplot("g", "y", "h", data=self.df) + nt.assert_equal(ax.get_xlabel(), "g") + nt.assert_equal(ax.get_ylabel(), "y") + npt.assert_array_equal(ax.get_xticks(), [0, 1, 2]) + npt.assert_array_equal([l.get_text() for l in ax.get_xticklabels()], + ["a", "b", "c"]) + npt.assert_array_equal([l.get_text() for l in ax.legend_.get_texts()], + ["m", "n"]) + + plt.close("all") + + ax = cat.lvplot("y", "g", data=self.df, orient="h") + nt.assert_equal(ax.get_xlabel(), "y") + nt.assert_equal(ax.get_ylabel(), "g") + nt.assert_equal(ax.get_ylim(), (2.5, -.5)) + npt.assert_array_equal(ax.get_yticks(), [0, 1, 2]) + npt.assert_array_equal([l.get_text() for l in ax.get_yticklabels()], + ["a", "b", "c"]) + + plt.close("all") From 40ecf7d10a62ba0106bb8d179691f4907c029c83 Mon Sep 17 00:00:00 2001 From: Clark Fitzgerald Date: Wed, 2 Sep 2015 10:14:22 -0700 Subject: [PATCH 0124/1738] use teardown method in PlotTestCase for most tests --- seaborn/tests/__init__.py | 7 ++ seaborn/tests/test_axisgrid.py | 114 +--------------------------- seaborn/tests/test_categorical.py | 39 +--------- seaborn/tests/test_distributions.py | 27 +------ seaborn/tests/test_linearmodels.py | 30 +------- seaborn/tests/test_matrix.py | 56 +------------- seaborn/tests/test_miscplot.py | 5 +- seaborn/tests/test_rcmod.py | 7 +- seaborn/tests/test_utils.py | 22 +----- 9 files changed, 31 insertions(+), 276 deletions(-) diff --git a/seaborn/tests/__init__.py b/seaborn/tests/__init__.py index e69de29bb2..80588c6758 100644 --- a/seaborn/tests/__init__.py +++ b/seaborn/tests/__init__.py @@ -0,0 +1,7 @@ +from matplotlib.pyplot import close + + +class PlotTestCase(object): + + def tearDown(self): + close('all') diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 6bbbacbe90..83875968ed 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -12,6 +12,7 @@ from numpy.testing.decorators import skipif import pandas.util.testing as tm +from . import PlotTestCase from .. import axisgrid as ag from .. import rcmod from ..palettes import color_palette @@ -25,7 +26,7 @@ old_matplotlib = LooseVersion(mpl.__version__) < "1.4" -class TestFacetGrid(object): +class TestFacetGrid(PlotTestCase): df = pd.DataFrame(dict(x=rs.normal(size=60), y=rs.gamma(4, size=60), @@ -38,13 +39,11 @@ def test_self_data(self): g = ag.FacetGrid(self.df) nt.assert_is(g.data, self.df) - plt.close("all") def test_self_fig(self): g = ag.FacetGrid(self.df) nt.assert_is_instance(g.fig, plt.Figure) - plt.close("all") def test_self_axes(self): @@ -52,8 +51,6 @@ def test_self_axes(self): for ax in g.axes.flat: nt.assert_is_instance(ax, plt.Axes) - plt.close("all") - def test_axes_array_size(self): g1 = ag.FacetGrid(self.df) @@ -74,8 +71,6 @@ def test_axes_array_size(self): for ax in g5.axes.flat: nt.assert_is_instance(ax, plt.Axes) - plt.close("all") - def test_single_axes(self): g1 = ag.FacetGrid(self.df) @@ -116,8 +111,6 @@ def test_col_wrap(self): g_missing_wrap = ag.FacetGrid(df, col="d", col_wrap=4) nt.assert_equal(g_missing_wrap.axes.shape, (9,)) - plt.close("all") - def test_normal_axes(self): null = np.empty(0, object).flat @@ -150,8 +143,6 @@ def test_normal_axes(self): npt.assert_array_equal(g._not_left_axes, g.axes[:, 1:].flat) npt.assert_array_equal(g._inner_axes, g.axes[:-1, 1:].flat) - plt.close("all") - def test_wrapped_axes(self): null = np.empty(0, object).flat @@ -164,8 +155,6 @@ def test_wrapped_axes(self): npt.assert_array_equal(g._not_left_axes, g.axes[np.array([1])].flat) npt.assert_array_equal(g._inner_axes, null) - plt.close("all") - def test_figure_size(self): g = ag.FacetGrid(self.df, row="a", col="b") @@ -177,8 +166,6 @@ def test_figure_size(self): g = ag.FacetGrid(self.df, col="c", size=4, aspect=.5) npt.assert_array_equal(g.fig.get_size_inches(), (6, 4)) - plt.close("all") - def test_figure_size_with_legend(self): g1 = ag.FacetGrid(self.df, col="a", hue="c", size=4, aspect=.5) @@ -192,8 +179,6 @@ def test_figure_size_with_legend(self): g2.add_legend() npt.assert_array_equal(g2.fig.get_size_inches(), (6, 4)) - plt.close("all") - def test_legend_data(self): g1 = ag.FacetGrid(self.df, hue="a") @@ -217,8 +202,6 @@ def test_legend_data(self): for label, level in zip(labels, a_levels): nt.assert_equal(label.get_text(), level) - plt.close("all") - def test_legend_data_missing_level(self): g1 = ag.FacetGrid(self.df, hue="a", hue_order=list("azbc")) @@ -244,8 +227,6 @@ def test_legend_data_missing_level(self): for label, level in zip(labels, list("azbc")): nt.assert_equal(label.get_text(), level) - plt.close("all") - def test_get_boolean_legend_data(self): self.df["b_bool"] = self.df.b == "m" @@ -270,8 +251,6 @@ def test_get_boolean_legend_data(self): for label, level in zip(labels, b_levels): nt.assert_equal(label.get_text(), level) - plt.close("all") - def test_legend_options(self): g1 = ag.FacetGrid(self.df, hue="b") @@ -382,8 +361,6 @@ def test_data_generator(self): nt.assert_equal(tup, (0, 0, 1)) nt.assert_true((data["c"] == "u").all()) - plt.close("all") - def test_map(self): g = ag.FacetGrid(self.df, row="a", col="b", hue="c") @@ -429,8 +406,6 @@ def test_set(self): npt.assert_array_equal(ax.get_xticks(), xticks) npt.assert_array_equal(ax.get_yticks(), yticks) - plt.close("all") - def test_set_titles(self): g = ag.FacetGrid(self.df, row="a", col="b") @@ -459,8 +434,6 @@ def test_set_titles(self): g = ag.FacetGrid(self.df, col="b", hue="b", dropna=False) g.map(plt.plot, 'x', 'y') - plt.close("all") - def test_set_titles_margin_titles(self): g = ag.FacetGrid(self.df, row="a", col="b", margin_titles=True) @@ -481,8 +454,6 @@ def test_set_titles_margin_titles(self): nt.assert_equal(g.axes[0, 1].get_title(), "b == n") nt.assert_equal(g.axes[1, 0].get_title(), "") - plt.close("all") - def test_set_ticklabels(self): g = ag.FacetGrid(self.df, row="a", col="b") @@ -515,7 +486,6 @@ def test_set_ticklabels(self): for ax in g._left_axes: for l in ax.get_yticklabels(): nt.assert_equal(l.get_rotation(), 75) - plt.close("all") def test_set_axis_labels(self): @@ -530,14 +500,12 @@ def test_set_axis_labels(self): got_y = [ax.get_ylabel() for ax in g.axes[:, 0]] npt.assert_array_equal(got_x, xlab) npt.assert_array_equal(got_y, ylab) - plt.close("all") def test_axis_lims(self): g = ag.FacetGrid(self.df, row="a", col="b", xlim=(0, 4), ylim=(-2, 3)) nt.assert_equal(g.axes[0, 0].get_xlim(), (0, 4)) nt.assert_equal(g.axes[0, 0].get_ylim(), (-2, 3)) - plt.close("all") def test_data_orders(self): @@ -568,8 +536,6 @@ def test_data_orders(self): nt.assert_equal(g.hue_names, list("qvtu")) nt.assert_equal(g.axes.shape, (4, 3)) - plt.close("all") - def test_palette(self): rcmod.set() @@ -593,8 +559,6 @@ def test_palette(self): palette=dict_pal) nt.assert_equal(g._colors, list_pal) - plt.close("all") - def test_hue_kws(self): kws = dict(marker=["o", "s", "D"]) @@ -616,15 +580,8 @@ def test_dropna(self): g = ag.FacetGrid(df, dropna=True, row="hasna") nt.assert_equal(g._not_na.sum(), 50) - plt.close("all") - - @classmethod - def teardown_class(cls): - """Ensure that all figures are closed on exit.""" - plt.close("all") - -class TestPairGrid(object): +class TestPairGrid(PlotTestCase): rs = np.random.RandomState(sum(map(ord, "PairGrid"))) df = pd.DataFrame(dict(x=rs.normal(size=80), @@ -637,7 +594,6 @@ def test_self_data(self): g = ag.PairGrid(self.df) nt.assert_is(g.data, self.df) - plt.close("all") def test_ignore_datelike_data(self): @@ -646,13 +602,11 @@ def test_ignore_datelike_data(self): result = ag.PairGrid(self.df).data expected = df.drop('date', axis=1) tm.assert_frame_equal(result, expected) - plt.close("all") def test_self_fig(self): g = ag.PairGrid(self.df) nt.assert_is_instance(g.fig, plt.Figure) - plt.close("all") def test_self_axes(self): @@ -660,8 +614,6 @@ def test_self_axes(self): for ax in g.axes.flat: nt.assert_is_instance(ax, plt.Axes) - plt.close("all") - def test_default_axes(self): g = ag.PairGrid(self.df) @@ -670,8 +622,6 @@ def test_default_axes(self): nt.assert_equal(g.y_vars, ["x", "y", "z"]) nt.assert_true(g.square_grid) - plt.close("all") - def test_specific_square_axes(self): vars = ["z", "x"] @@ -681,8 +631,6 @@ def test_specific_square_axes(self): nt.assert_equal(g.y_vars, vars) nt.assert_true(g.square_grid) - plt.close("all") - def test_specific_nonsquare_axes(self): x_vars = ["x", "y"] @@ -701,8 +649,6 @@ def test_specific_nonsquare_axes(self): nt.assert_equal(g.y_vars, list(y_vars)) nt.assert_true(not g.square_grid) - plt.close("all") - def test_specific_square_axes_with_array(self): vars = np.array(["z", "x"]) @@ -712,8 +658,6 @@ def test_specific_square_axes_with_array(self): nt.assert_equal(g.y_vars, list(vars)) nt.assert_true(g.square_grid) - plt.close("all") - def test_specific_nonsquare_axes_with_array(self): x_vars = np.array(["x", "y"]) @@ -724,8 +668,6 @@ def test_specific_nonsquare_axes_with_array(self): nt.assert_equal(g.y_vars, list(y_vars)) nt.assert_true(not g.square_grid) - plt.close("all") - def test_size(self): g1 = ag.PairGrid(self.df, size=3) @@ -738,8 +680,6 @@ def test_size(self): size=2, aspect=2) npt.assert_array_equal(g3.fig.get_size_inches(), (8, 2)) - plt.close("all") - def test_map(self): vars = ["x", "y", "z"] @@ -768,8 +708,6 @@ def test_map(self): npt.assert_array_equal(x_in_k, x_out) npt.assert_array_equal(y_in_k, y_out) - plt.close("all") - def test_map_nonsquare(self): x_vars = ["x"] @@ -785,8 +723,6 @@ def test_map_nonsquare(self): npt.assert_array_equal(x_in, x_out) npt.assert_array_equal(y_in, y_out) - plt.close("all") - def test_map_lower(self): vars = ["x", "y", "z"] @@ -805,8 +741,6 @@ def test_map_lower(self): ax = g.axes[i, j] nt.assert_equal(len(ax.collections), 0) - plt.close("all") - def test_map_upper(self): vars = ["x", "y", "z"] @@ -825,8 +759,6 @@ def test_map_upper(self): ax = g.axes[i, j] nt.assert_equal(len(ax.collections), 0) - plt.close("all") - @skipif(old_matplotlib) def test_map_diag(self): @@ -848,8 +780,6 @@ def test_map_diag(self): for ax in g3.diag_axes: nt.assert_equal(len(ax.patches), 40) - plt.close("all") - @skipif(old_matplotlib) def test_map_diag_and_offdiag(self): @@ -881,8 +811,6 @@ def test_map_diag_and_offdiag(self): ax = g.axes[i, j] nt.assert_equal(len(ax.collections), 0) - plt.close("all") - def test_palette(self): rcmod.set() @@ -906,8 +834,6 @@ def test_palette(self): palette=dict_pal) nt.assert_equal(g.palette, list_pal) - plt.close("all") - def test_hue_kws(self): kws = dict(marker=["o", "s", "d", "+"]) @@ -924,8 +850,6 @@ def test_hue_kws(self): for line, marker in zip(g.axes[0, 0].lines, kws["marker"]): nt.assert_equal(line.get_marker(), marker) - plt.close("all") - @skipif(old_matplotlib) def test_hue_order(self): @@ -1044,8 +968,6 @@ def test_nondefault_index(self): npt.assert_array_equal(x_in_k, x_out) npt.assert_array_equal(y_in_k, y_out) - plt.close("all") - @skipif(old_matplotlib) def test_pairplot(self): @@ -1075,8 +997,6 @@ def test_pairplot(self): ax = g.axes[i, j] nt.assert_equal(len(ax.collections), 0) - plt.close("all") - @skipif(old_matplotlib) def test_pairplot_reg(self): @@ -1112,8 +1032,6 @@ def test_pairplot_reg(self): ax = g.axes[i, j] nt.assert_equal(len(ax.collections), 0) - plt.close("all") - @skipif(old_matplotlib) def test_pairplot_kde(self): @@ -1143,8 +1061,6 @@ def test_pairplot_kde(self): ax = g.axes[i, j] nt.assert_equal(len(ax.collections), 0) - plt.close("all") - @skipif(old_matplotlib) def test_pairplot_markers(self): @@ -1157,13 +1073,8 @@ def test_pairplot_markers(self): with nt.assert_raises(ValueError): g = pairplot(self.df, hue="a", vars=vars, markers=markers[:-2]) - @classmethod - def teardown_class(cls): - """Ensure that all figures are closed on exit.""" - plt.close("all") - -class TestJointGrid(object): +class TestJointGrid(PlotTestCase): rs = np.random.RandomState(sum(map(ord, "JointGrid"))) x = rs.randn(100) @@ -1178,21 +1089,18 @@ def test_margin_grid_from_arrays(self): g = ag.JointGrid(self.x, self.y) npt.assert_array_equal(g.x, self.x) npt.assert_array_equal(g.y, self.y) - plt.close("all") def test_margin_grid_from_series(self): g = ag.JointGrid(self.data.x, self.data.y) npt.assert_array_equal(g.x, self.x) npt.assert_array_equal(g.y, self.y) - plt.close("all") def test_margin_grid_from_dataframe(self): g = ag.JointGrid("x", "y", self.data) npt.assert_array_equal(g.x, self.x) npt.assert_array_equal(g.y, self.y) - plt.close("all") def test_margin_grid_axis_labels(self): @@ -1206,7 +1114,6 @@ def test_margin_grid_axis_labels(self): xlabel, ylabel = g.ax_joint.get_xlabel(), g.ax_joint.get_ylabel() nt.assert_equal(xlabel, "x variable") nt.assert_equal(ylabel, "y variable") - plt.close("all") def test_dropna(self): @@ -1215,7 +1122,6 @@ def test_dropna(self): g = ag.JointGrid("x_na", "y", self.data, dropna=True) nt.assert_equal(len(g.x), pd.notnull(self.x_na).sum()) - plt.close("all") def test_axlims(self): @@ -1233,7 +1139,6 @@ def test_marginal_ticks(self): g = ag.JointGrid("x", "y", self.data) nt.assert_true(~len(g.ax_marg_x.get_xticks())) nt.assert_true(~len(g.ax_marg_y.get_yticks())) - plt.close("all") def test_bivariate_plot(self): @@ -1243,7 +1148,6 @@ def test_bivariate_plot(self): x, y = g.ax_joint.lines[0].get_xydata().T npt.assert_array_equal(x, self.x) npt.assert_array_equal(y, self.y) - plt.close("all") def test_univariate_plot(self): @@ -1253,7 +1157,6 @@ def test_univariate_plot(self): _, y1 = g.ax_marg_x.lines[0].get_xydata().T y2, _ = g.ax_marg_y.lines[0].get_xydata().T npt.assert_array_equal(y1, y2) - plt.close("all") def test_plot(self): @@ -1268,8 +1171,6 @@ def test_plot(self): y2, _ = g.ax_marg_y.lines[0].get_xydata().T npt.assert_array_equal(y1, y2) - plt.close("all") - def test_annotate(self): g = ag.JointGrid("x", "y", self.data) @@ -1297,8 +1198,6 @@ def rsquared(x, y): nt.assert_equal(annotation, template.format(stat="pearsonr", val=rp[0], p=rp[1])) - plt.close("all") - def test_space(self): g = ag.JointGrid("x", "y", self.data, space=0) @@ -1309,8 +1208,3 @@ def test_space(self): nt.assert_equal(joint_bounds[2], marg_x_bounds[2]) nt.assert_equal(joint_bounds[3], marg_y_bounds[3]) - - @classmethod - def teardown_class(cls): - """Ensure that all figures are closed on exit.""" - plt.close("all") diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index e74e4551c6..3818a3bd9a 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -12,11 +12,12 @@ import numpy.testing as npt from numpy.testing.decorators import skipif +from . import PlotTestCase from .. import categorical as cat from .. import palettes -class CategoricalFixture(object): +class CategoricalFixture(PlotTestCase): """Test boxplot (also base class for things like violinplots).""" rs = np.random.RandomState(30) n_total = 60 @@ -725,7 +726,6 @@ def test_draw_missing_boxes(self): ax = cat.boxplot("g", "y", data=self.df, order=["a", "b", "c", "d"]) nt.assert_equal(len(ax.artists), 3) - plt.close("all") def test_missing_data(self): @@ -1191,7 +1191,6 @@ def test_draw_quartiles(self): for val, line in zip(np.percentile(self.y, [25, 50, 75]), ax.lines): _, y = line.get_xydata().T npt.assert_array_equal(y, [val, val]) - plt.close("all") def test_draw_points(self): @@ -1434,8 +1433,6 @@ def test_stripplot_vertical(self): npt.assert_equal(ax.collections[i].get_facecolors()[0, :3], pal[i]) - plt.close("all") - @skipif(not pandas_has_categoricals) def test_stripplot_horiztonal(self): @@ -1450,8 +1447,6 @@ def test_stripplot_horiztonal(self): npt.assert_array_equal(x, vals) npt.assert_array_equal(y, np.ones(len(x)) * i) - plt.close("all") - def test_stripplot_jitter(self): pal = palettes.color_palette() @@ -1467,8 +1462,6 @@ def test_stripplot_jitter(self): npt.assert_equal(ax.collections[i].get_facecolors()[0, :3], pal[i]) - plt.close("all") - def test_split_nested_stripplot_vertical(self): pal = palettes.color_palette() @@ -1485,8 +1478,6 @@ def test_split_nested_stripplot_vertical(self): fc = ax.collections[i * 2 + j].get_facecolors()[0, :3] npt.assert_equal(fc, pal[j]) - plt.close("all") - @skipif(not pandas_has_categoricals) def test_split_nested_stripplot_horizontal(self): @@ -1502,8 +1493,6 @@ def test_split_nested_stripplot_horizontal(self): npt.assert_array_equal(x, vals) npt.assert_array_equal(y, np.ones(len(x)) * i + [-.2, .2][j]) - plt.close("all") - def test_unsplit_nested_stripplot_vertical(self): pal = palettes.color_palette() @@ -1521,8 +1510,6 @@ def test_unsplit_nested_stripplot_vertical(self): fc = ax.collections[i * 2 + j].get_facecolors()[0, :3] npt.assert_equal(fc, pal[j]) - plt.close("all") - @skipif(not pandas_has_categoricals) def test_unsplit_nested_stripplot_horizontal(self): @@ -1538,8 +1525,6 @@ def test_unsplit_nested_stripplot_horizontal(self): npt.assert_array_equal(x, vals) npt.assert_array_equal(y, np.ones(len(x)) * i) - plt.close("all") - class TestBarPlotter(CategoricalFixture): @@ -1583,8 +1568,6 @@ def test_draw_vertical_bars(self): nt.assert_equal(bar.get_height(), abs(stat)) nt.assert_equal(bar.get_width(), p.width) - plt.close("all") - def test_draw_horizontal_bars(self): kws = self.default_kws.copy() @@ -1607,8 +1590,6 @@ def test_draw_horizontal_bars(self): nt.assert_equal(bar.get_height(), p.width) nt.assert_equal(bar.get_width(), abs(stat)) - plt.close("all") - def test_draw_nested_vertical_bars(self): kws = self.default_kws.copy() @@ -1636,8 +1617,6 @@ def test_draw_nested_vertical_bars(self): nt.assert_almost_equal(bar.get_x(), pos - p.width / 2) nt.assert_almost_equal(bar.get_width(), p.nested_width) - plt.close("all") - def test_draw_nested_horizontal_bars(self): kws = self.default_kws.copy() @@ -1665,8 +1644,6 @@ def test_draw_nested_horizontal_bars(self): nt.assert_almost_equal(bar.get_x(), min(0, stat)) nt.assert_almost_equal(bar.get_width(), abs(stat)) - plt.close("all") - def test_draw_missing_bars(self): kws = self.default_kws.copy() @@ -1835,8 +1812,6 @@ def test_draw_vertical_points(self): p.colors): npt.assert_array_equal(got_color[:-1], want_color) - plt.close("all") - def test_draw_horizontal_points(self): kws = self.default_kws.copy() @@ -1859,8 +1834,6 @@ def test_draw_horizontal_points(self): p.colors): npt.assert_array_equal(got_color[:-1], want_color) - plt.close("all") - def test_draw_vertical_nested_points(self): kws = self.default_kws.copy() @@ -1887,8 +1860,6 @@ def test_draw_vertical_nested_points(self): for got_color in points.get_facecolors(): npt.assert_array_equal(got_color[:-1], color) - plt.close("all") - def test_draw_horizontal_nested_points(self): kws = self.default_kws.copy() @@ -1915,8 +1886,6 @@ def test_draw_horizontal_nested_points(self): for got_color in points.get_facecolors(): npt.assert_array_equal(got_color[:-1], color) - plt.close("all") - def test_pointplot_colors(self): # Test a single-color unnested plot @@ -2066,8 +2035,6 @@ def test_facet_organization(self): g = cat.factorplot("g", "y", col="u", row="h", data=self.df) nt.assert_equal(g.axes.shape, (2, 3)) - plt.close("all") - def test_plot_elements(self): g = cat.factorplot("g", "y", data=self.df) @@ -2127,8 +2094,6 @@ def test_plot_elements(self): want_elements = self.g.unique().size * self.h.unique().size nt.assert_equal(len(g.ax.collections), want_elements) - plt.close("all") - def test_bad_plot_kind_error(self): with nt.assert_raises(ValueError): diff --git a/seaborn/tests/test_distributions.py b/seaborn/tests/test_distributions.py index 9ab2594fa0..042b934ebe 100644 --- a/seaborn/tests/test_distributions.py +++ b/seaborn/tests/test_distributions.py @@ -7,6 +7,7 @@ import numpy.testing as npt from numpy.testing.decorators import skipif +from . import PlotTestCase from .. import distributions as dist try: @@ -17,7 +18,7 @@ _no_statsmodels = True -class TestKDE(object): +class TestKDE(PlotTestCase): rs = np.random.RandomState(0) x = rs.randn(50) @@ -108,10 +109,9 @@ def test_bivariate_kde_series(self): len(ax_values.collections)) nt.assert_equal(ax_series.collections[0].get_paths(), ax_values.collections[0].get_paths()) - plt.close("all") -class TestJointPlot(object): +class TestJointPlot(PlotTestCase): rs = np.random.RandomState(sum(map(ord, "jointplot"))) x = rs.randn(100) @@ -133,8 +133,6 @@ def test_scatter(self): y_bins = dist._freedman_diaconis_bins(self.y) nt.assert_equal(len(g.ax_marg_y.patches), y_bins) - plt.close("all") - def test_reg(self): g = dist.jointplot("x", "y", self.data, kind="reg") @@ -154,8 +152,6 @@ def test_reg(self): nt.assert_equal(len(g.ax_marg_x.lines), 1) nt.assert_equal(len(g.ax_marg_y.lines), 1) - plt.close("all") - def test_resid(self): g = dist.jointplot("x", "y", self.data, kind="resid") @@ -164,8 +160,6 @@ def test_resid(self): nt.assert_equal(len(g.ax_marg_x.lines), 0) nt.assert_equal(len(g.ax_marg_y.lines), 1) - plt.close("all") - def test_hex(self): g = dist.jointplot("x", "y", self.data, kind="hex") @@ -177,8 +171,6 @@ def test_hex(self): y_bins = dist._freedman_diaconis_bins(self.y) nt.assert_equal(len(g.ax_marg_y.patches), y_bins) - plt.close("all") - def test_kde(self): g = dist.jointplot("x", "y", self.data, kind="kde") @@ -190,8 +182,6 @@ def test_kde(self): nt.assert_equal(len(g.ax_marg_x.lines), 1) nt.assert_equal(len(g.ax_marg_y.lines), 1) - plt.close("all") - def test_color(self): g = dist.jointplot("x", "y", self.data, color="purple") @@ -203,8 +193,6 @@ def test_color(self): hist_color = g.ax_marg_x.patches[0].get_facecolor()[:3] nt.assert_equal(hist_color, purple) - plt.close("all") - def test_annotation(self): g = dist.jointplot("x", "y", self.data) @@ -213,8 +201,6 @@ def test_annotation(self): g = dist.jointplot("x", "y", self.data, stat_func=None) nt.assert_is(g.ax_joint.legend_, None) - plt.close("all") - def test_hex_customise(self): # test that default gridsize can be overridden @@ -224,14 +210,7 @@ def test_hex_customise(self): a = g.ax_joint.collections[0].get_array() nt.assert_equal(28, a.shape[0]) # 28 hexagons expected for gridsize 5 - plt.close("all") - def test_bad_kind(self): with nt.assert_raises(ValueError): dist.jointplot("x", "y", self.data, kind="not_a_kind") - - @classmethod - def teardown_class(cls): - """Ensure that all figures are closed on exit.""" - plt.close("all") diff --git a/seaborn/tests/test_linearmodels.py b/seaborn/tests/test_linearmodels.py index 8c7501f6e1..3ca63755c7 100644 --- a/seaborn/tests/test_linearmodels.py +++ b/seaborn/tests/test_linearmodels.py @@ -15,6 +15,7 @@ except ImportError: _no_statsmodels = True +from . import PlotTestCase from .. import linearmodels as lm from .. import algorithms as algo from .. import utils @@ -23,7 +24,7 @@ rs = np.random.RandomState(0) -class TestLinearPlotter(object): +class TestLinearPlotter(PlotTestCase): rs = np.random.RandomState(77) df = pd.DataFrame(dict(x=rs.normal(size=60), @@ -87,7 +88,7 @@ def test_dropna(self): pdt.assert_series_equal(p.y_na, self.df.y_na[mask]) -class TestRegressionPlotter(object): +class TestRegressionPlotter(PlotTestCase): rs = np.random.RandomState(49) @@ -395,10 +396,8 @@ def test_regression_limits(self): nt.assert_equal(grid.min(), self.df.x.min()) nt.assert_equal(grid.max(), self.df.x.max()) - plt.close("all") - -class TestRegressionPlots(object): +class TestRegressionPlots(PlotTestCase): rs = np.random.RandomState(56) df = pd.DataFrame(dict(x=rs.randn(90), @@ -421,8 +420,6 @@ def test_regplot_basic(self): npt.assert_array_equal(x, self.df.x) npt.assert_array_equal(y, self.df.y) - plt.close("all") - def test_regplot_selective(self): f, ax = plt.subplots() @@ -443,8 +440,6 @@ def test_regplot_selective(self): nt.assert_equal(len(ax.collections), 1) ax.clear() - plt.close("all") - def test_regplot_scatter_kws_alpha(self): f, ax = plt.subplots() @@ -469,16 +464,12 @@ def test_regplot_scatter_kws_alpha(self): ax = lm.regplot("x", "y", self.df, scatter_kws={'color': color}) nt.assert_equal(ax.collections[0]._alpha, 0.8) - plt.close("all") - def test_regplot_binned(self): ax = lm.regplot("x", "y", self.df, x_bins=5) nt.assert_equal(len(ax.lines), 6) nt.assert_equal(len(ax.collections), 2) - plt.close("all") - def test_lmplot_basic(self): g = lm.lmplot("x", "y", self.df) @@ -490,8 +481,6 @@ def test_lmplot_basic(self): npt.assert_array_equal(x, self.df.x) npt.assert_array_equal(y, self.df.y) - plt.close("all") - def test_lmplot_hue(self): g = lm.lmplot("x", "y", data=self.df, hue="h") @@ -499,7 +488,6 @@ def test_lmplot_hue(self): nt.assert_equal(len(ax.lines), 2) nt.assert_equal(len(ax.collections), 4) - plt.close("all") def test_lmplot_markers(self): @@ -512,8 +500,6 @@ def test_lmplot_markers(self): with nt.assert_raises(ValueError): lm.lmplot("x", "y", data=self.df, hue="h", markers=["o", "s", "d"]) - plt.close("all") - def test_lmplot_marker_linewidths(self): if mpl.__version__ == "1.4.2": @@ -525,7 +511,6 @@ def test_lmplot_marker_linewidths(self): nt.assert_equal(c[0].get_linewidths()[0], 0) rclw = mpl.rcParams["lines.linewidth"] nt.assert_equal(c[1].get_linewidths()[0], rclw) - plt.close("all") def test_lmplot_facets(self): @@ -537,13 +522,11 @@ def test_lmplot_facets(self): g = lm.lmplot("x", "y", data=self.df, hue="h", col="u") nt.assert_equal(g.axes.shape, (1, 6)) - plt.close("all") def test_lmplot_hue_col_nolegend(self): g = lm.lmplot("x", "y", data=self.df, col="h", hue="h") nt.assert_is(g._legend, None) - plt.close("all") def test_lmplot_scatter_kws(self): @@ -554,8 +537,6 @@ def test_lmplot_scatter_kws(self): npt.assert_array_equal(red, red_scatter.get_facecolors()[0, :3]) npt.assert_array_equal(blue, blue_scatter.get_facecolors()[0, :3]) - plt.close("all") - def test_residplot(self): x, y = self.df.x, self.df.y @@ -566,7 +547,6 @@ def test_residplot(self): npt.assert_array_equal(x, x_plot) npt.assert_array_almost_equal(resid, y_plot) - plt.close("all") @skipif(_no_statsmodels) def test_residplot_lowess(self): @@ -576,5 +556,3 @@ def test_residplot_lowess(self): x, y = ax.lines[1].get_xydata().T npt.assert_array_equal(x, np.sort(self.df.x)) - - plt.close("all") diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 5c8e906b4d..59eda3ddce 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -13,6 +13,7 @@ import pandas.util.testing as pdt from numpy.testing.decorators import skipif +from . import PlotTestCase from .. import matrix as mat from .. import color_palette from ..external.six.moves import range @@ -26,7 +27,7 @@ _no_fastcluster = True -class TestHeatmap(object): +class TestHeatmap(PlotTestCase): rs = np.random.RandomState(sum(map(ord, "heatmap"))) x_norm = rs.randn(4, 8) @@ -221,7 +222,6 @@ def test_heatmap_annotation(self): for val, text in zip(self.x_norm[::-1].flat, ax.texts): nt.assert_equal(text.get_text(), "{:.1f}".format(val)) nt.assert_equal(text.get_fontsize(), 14) - plt.close("all") def test_heatmap_annotation_overwrite_kws(self): @@ -232,7 +232,6 @@ def test_heatmap_annotation_overwrite_kws(self): nt.assert_equal(text.get_color(), "0.3") nt.assert_equal(text.get_ha(), "left") nt.assert_equal(text.get_va(), "bottom") - plt.close("all") def test_heatmap_annotation_with_mask(self): @@ -245,7 +244,6 @@ def test_heatmap_annotation_with_mask(self): nt.assert_equal(len(df_masked[::-1].compressed()), len(ax.texts)) for val, text in zip(df_masked[::-1].compressed(), ax.texts): nt.assert_equal("{:.1f}".format(val), text.get_text()) - plt.close("all") def test_heatmap_cbar(self): @@ -279,8 +277,6 @@ def test_heatmap_axes(self): nt.assert_equal(ax.get_xlim(), (0, 8)) nt.assert_equal(ax.get_ylim(), (0, 4)) - plt.close("all") - def test_heatmap_ticklabel_rotation(self): f, ax = plt.subplots(figsize=(2, 2)) @@ -317,13 +313,10 @@ def test_heatmap_inner_lines(self): nt.assert_equal(mesh.get_linewidths()[0], 2) nt.assert_equal(tuple(mesh.get_edgecolor()[0]), c) - plt.close("all") - def test_square_aspect(self): ax = mat.heatmap(self.df_norm, square=True) nt.assert_equal(ax.get_aspect(), "equal") - plt.close("all") def test_mask_validation(self): @@ -351,7 +344,7 @@ def test_missing_data_mask(self): npt.assert_array_equal(mask_out, [[True, True], [False, False]]) -class TestDendrogram(object): +class TestDendrogram(PlotTestCase): rs = np.random.RandomState(sum(map(ord, "dendrogram"))) x_norm = rs.randn(4, 8) + np.arange(8) @@ -549,8 +542,6 @@ def test_dendrogram_plot(self): nt.assert_equal(len(ax.collections[0].get_paths()), len(d.dependent_coord)) - plt.close('all') - def test_dendrogram_rotate(self): kws = self.default_kws.copy() kws['rotate'] = True @@ -567,7 +558,6 @@ def test_dendrogram_rotate(self): # and therefore not (0, 80) as usual: nt.assert_equal(ylim[1], 0) nt.assert_equal(ylim[0], ymax) - plt.close('all') def test_dendrogram_ticklabel_rotation(self): f, ax = plt.subplots(figsize=(2, 2)) @@ -597,7 +587,7 @@ def test_dendrogram_ticklabel_rotation(self): plt.close(f) -class TestClustermap(object): +class TestClustermap(PlotTestCase): rs = np.random.RandomState(sum(map(ord, "clustermap"))) x_norm = rs.randn(4, 8) + np.arange(8) @@ -639,14 +629,10 @@ def test_ndarray_input(self): nt.assert_equal(cm.ax_row_colors, None) nt.assert_equal(cm.ax_col_colors, None) - plt.close('all') - def test_df_input(self): cm = mat.ClusterGrid(self.df_norm, **self.default_kws) pdt.assert_frame_equal(cm.data, self.df_norm) - plt.close('all') - def test_corr_df_input(self): df = self.df_norm.corr() cg = mat.ClusterGrid(df, **self.default_kws) @@ -654,8 +640,6 @@ def test_corr_df_input(self): diag = cg.data2d.values[np.diag_indices_from(cg.data2d)] npt.assert_array_equal(diag, np.ones(cg.data2d.shape[0])) - plt.close('all') - def test_pivot_input(self): df_norm = self.df_norm.copy() df_norm.index.name = 'numbers' @@ -668,8 +652,6 @@ def test_pivot_input(self): pdt.assert_frame_equal(cm.data2d, df_norm) - plt.close('all') - def test_colors_input(self): kws = self.default_kws.copy() @@ -681,7 +663,6 @@ def test_colors_input(self): npt.assert_array_equal(cm.col_colors, self.col_colors) nt.assert_equal(len(cm.fig.axes), 6) - plt.close('all') def test_nested_colors_input(self): kws = self.default_kws.copy() @@ -696,7 +677,6 @@ def test_nested_colors_input(self): npt.assert_array_equal(cm.col_colors, col_colors) nt.assert_equal(len(cm.fig.axes), 6) - plt.close('all') def test_colors_input_custom_cmap(self): kws = self.default_kws.copy() @@ -710,7 +690,6 @@ def test_colors_input_custom_cmap(self): npt.assert_array_equal(cm.col_colors, self.col_colors) nt.assert_equal(len(cm.fig.axes), 6) - plt.close('all') def test_z_score(self): df = self.df_norm.copy() @@ -721,8 +700,6 @@ def test_z_score(self): cm = mat.ClusterGrid(self.df_norm, **kws) pdt.assert_frame_equal(cm.data2d, df) - plt.close('all') - def test_z_score_axis0(self): df = self.df_norm.copy() df = df.T @@ -734,8 +711,6 @@ def test_z_score_axis0(self): cm = mat.ClusterGrid(self.df_norm, **kws) pdt.assert_frame_equal(cm.data2d, df) - plt.close('all') - def test_standard_scale(self): df = self.df_norm.copy() df = (df - df.min()) / (df.max() - df.min()) @@ -745,8 +720,6 @@ def test_standard_scale(self): cm = mat.ClusterGrid(self.df_norm, **kws) pdt.assert_frame_equal(cm.data2d, df) - plt.close('all') - def test_standard_scale_axis0(self): df = self.df_norm.copy() df = df.T @@ -758,8 +731,6 @@ def test_standard_scale_axis0(self): cm = mat.ClusterGrid(self.df_norm, **kws) pdt.assert_frame_equal(cm.data2d, df) - plt.close('all') - def test_z_score_standard_scale(self): kws = self.default_kws.copy() kws['z_score'] = True @@ -767,8 +738,6 @@ def test_z_score_standard_scale(self): with nt.assert_raises(ValueError): cm = mat.ClusterGrid(self.df_norm, **kws) - plt.close('all') - def test_color_list_to_matrix_and_cmap(self): matrix, cmap = mat.ClusterGrid.color_list_to_matrix_and_cmap( self.col_colors, self.x_norm_leaves) @@ -783,8 +752,6 @@ def test_color_list_to_matrix_and_cmap(self): npt.assert_array_equal(matrix, matrix_test) npt.assert_array_equal(cmap.colors, cmap_test.colors) - plt.close('all') - def test_nested_color_list_to_matrix_and_cmap(self): colors = [self.col_colors, self.col_colors] matrix, cmap = mat.ClusterGrid.color_list_to_matrix_and_cmap( @@ -803,8 +770,6 @@ def test_nested_color_list_to_matrix_and_cmap(self): npt.assert_array_equal(matrix, matrix_test) npt.assert_array_equal(cmap.colors, cmap_test.colors) - plt.close('all') - def test_color_list_to_matrix_and_cmap_axis1(self): matrix, cmap = mat.ClusterGrid.color_list_to_matrix_and_cmap( self.col_colors, self.x_norm_leaves, axis=1) @@ -819,16 +784,12 @@ def test_color_list_to_matrix_and_cmap_axis1(self): npt.assert_array_equal(matrix, matrix_test) npt.assert_array_equal(cmap.colors, cmap_test.colors) - plt.close('all') - def test_savefig(self): # Not sure if this is the right way to test.... cm = mat.ClusterGrid(self.df_norm, **self.default_kws) cm.plot(**self.default_plot_kws) cm.savefig(tempfile.NamedTemporaryFile(), format='png') - plt.close('all') - def test_plot_dendrograms(self): cm = mat.clustermap(self.df_norm, **self.default_kws) @@ -839,7 +800,6 @@ def test_plot_dendrograms(self): data2d = self.df_norm.iloc[cm.dendrogram_row.reordered_ind, cm.dendrogram_col.reordered_ind] pdt.assert_frame_equal(cm.data2d, data2d) - plt.close('all') def test_cluster_false(self): kws = self.default_kws.copy() @@ -856,7 +816,6 @@ def test_cluster_false(self): nt.assert_equal(len(cm.ax_col_dendrogram.get_yticks()), 0) pdt.assert_frame_equal(cm.data2d, self.df_norm) - plt.close('all') def test_row_col_colors(self): kws = self.default_kws.copy() @@ -868,8 +827,6 @@ def test_row_col_colors(self): nt.assert_equal(len(cm.ax_row_colors.collections), 1) nt.assert_equal(len(cm.ax_col_colors.collections), 1) - plt.close('all') - def test_cluster_false_row_col_colors(self): kws = self.default_kws.copy() kws['row_cluster'] = False @@ -889,7 +846,6 @@ def test_cluster_false_row_col_colors(self): nt.assert_equal(len(cm.ax_col_colors.collections), 1) pdt.assert_frame_equal(cm.data2d, self.df_norm) - plt.close('all') def test_mask_reorganization(self): @@ -907,8 +863,6 @@ def test_mask_reorganization(self): self.df_norm.columns[ g.dendrogram_col.reordered_ind]) - plt.close("all") - def test_ticklabel_reorganization(self): kws = self.default_kws.copy() @@ -927,5 +881,3 @@ def test_ticklabel_reorganization(self): npt.assert_array_equal(xtl_actual, xtl_want) npt.assert_array_equal(ytl_actual, ytl_want) - - plt.close("all") diff --git a/seaborn/tests/test_miscplot.py b/seaborn/tests/test_miscplot.py index d804bda949..7ca4979af3 100644 --- a/seaborn/tests/test_miscplot.py +++ b/seaborn/tests/test_miscplot.py @@ -2,11 +2,12 @@ import numpy.testing as npt import matplotlib.pyplot as plt +from . import PlotTestCase from .. import miscplot as misc from seaborn import color_palette -class TestPalPlot(object): +class TestPalPlot(PlotTestCase): """Test the function that visualizes a color palette.""" def test_palplot_size(self): @@ -24,5 +25,3 @@ def test_palplot_size(self): misc.palplot(palbig, 2) sizebig = plt.gcf().get_size_inches() nt.assert_equal(tuple(sizebig), (6, 2)) - - plt.close("all") diff --git a/seaborn/tests/test_rcmod.py b/seaborn/tests/test_rcmod.py index b354ed695c..399c05de3a 100644 --- a/seaborn/tests/test_rcmod.py +++ b/seaborn/tests/test_rcmod.py @@ -6,6 +6,7 @@ import nose.tools as nt import numpy.testing as npt +from . import PlotTestCase from .. import rcmod @@ -164,7 +165,7 @@ def test_context_context_manager(self): self.assert_rc_params(orig_params) -class TestFonts(object): +class TestFonts(PlotTestCase): def test_set_font(self): @@ -183,7 +184,6 @@ def test_set_font(self): raise nose.SkipTest("Verdana font is not present") finally: rcmod.set() - plt.close("all") def test_set_serif_font(self): @@ -196,7 +196,6 @@ def test_set_serif_font(self): mpl.rcParams["font.serif"]) rcmod.set() - plt.close("all") def test_different_sans_serif(self): @@ -220,8 +219,6 @@ def test_different_sans_serif(self): raise nose.SkipTest("Verdana font is not present") finally: rcmod.set() - plt.close("all") - def has_verdana(): """Helper to verify if Verdana font is present""" diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index 958a70d7fa..f90b0b2f1d 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -16,14 +16,15 @@ pandas_has_categoricals = LooseVersion(pd.__version__) >= "0.15" from pandas.util.testing import network -from ..utils import get_dataset_names, load_dataset try: from bs4 import BeautifulSoup except ImportError: BeautifulSoup = None +from . import PlotTestCase from .. import utils, rcmod +from ..utils import get_dataset_names, load_dataset a_norm = np.random.randn(100) @@ -109,7 +110,7 @@ def test_iqr(): assert_equal(iqr, 2) -class TestSpineUtils(object): +class TestSpineUtils(PlotTestCase): sides = ["left", "right", "bottom", "top"] outer_sides = ["top", "right"] @@ -134,8 +135,6 @@ def test_despine(self): for side in self.sides: nt.assert_true(~ax.spines[side].get_visible()) - plt.close("all") - def test_despine_specific_axes(self): f, (ax1, ax2) = plt.subplots(2, 1) @@ -149,8 +148,6 @@ def test_despine_specific_axes(self): for side in self.inner_sides: nt.assert_true(ax2.spines[side].get_visible()) - plt.close("all") - def test_despine_with_offset(self): f, ax = plt.subplots() @@ -168,8 +165,6 @@ def test_despine_with_offset(self): else: nt.assert_equal(new_position, self.original_position) - plt.close("all") - def test_despine_with_offset_specific_axes(self): f, (ax1, ax2) = plt.subplots(2, 1) @@ -184,7 +179,6 @@ def test_despine_with_offset_specific_axes(self): else: nt.assert_equal(ax2.spines[side].get_position(), self.original_position) - plt.close("all") def test_despine_trim_spines(self): f, ax = plt.subplots() @@ -196,8 +190,6 @@ def test_despine_trim_spines(self): bounds = ax.spines[side].get_bounds() nt.assert_equal(bounds, (1, 3)) - plt.close("all") - def test_despine_trim_inverted(self): f, ax = plt.subplots() @@ -210,8 +202,6 @@ def test_despine_trim_inverted(self): bounds = ax.spines[side].get_bounds() nt.assert_equal(bounds, (1, 3)) - plt.close("all") - def test_despine_trim_noticks(self): f, ax = plt.subplots() @@ -229,8 +219,6 @@ def test_offset_spines_warns(self): nt.assert_true('deprecated' in str(w[0].message)) nt.assert_true(issubclass(w[0].category, UserWarning)) - plt.close('all') - def test_offset_spines(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always", category=UserWarning) @@ -246,8 +234,6 @@ def test_offset_spines(self): nt.assert_equal(ax.spines[side].get_position(), self.offset_position) - plt.close("all") - def test_offset_spines_specific_axes(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always", category=UserWarning) @@ -260,8 +246,6 @@ def test_offset_spines_specific_axes(self): self.original_position) nt.assert_equal(ax2.spines[side].get_position(), self.offset_position) - plt.close("all") - def test_ticklabels_overlap(): From cde7b08b794ac08aea356a8fcd3635ac6c2ff30d Mon Sep 17 00:00:00 2001 From: Clark Fitzgerald Date: Wed, 2 Sep 2015 10:51:44 -0700 Subject: [PATCH 0125/1738] pep8 line spacing --- seaborn/tests/test_rcmod.py | 1 + seaborn/tests/test_utils.py | 1 + 2 files changed, 2 insertions(+) diff --git a/seaborn/tests/test_rcmod.py b/seaborn/tests/test_rcmod.py index 399c05de3a..b2d11fa305 100644 --- a/seaborn/tests/test_rcmod.py +++ b/seaborn/tests/test_rcmod.py @@ -220,6 +220,7 @@ def test_different_sans_serif(self): finally: rcmod.set() + def has_verdana(): """Helper to verify if Verdana font is present""" # This import is relatively lengthy, so to prevent its import for diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index f90b0b2f1d..f5dbadbdf2 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -247,6 +247,7 @@ def test_offset_spines_specific_axes(self): nt.assert_equal(ax2.spines[side].get_position(), self.offset_position) + def test_ticklabels_overlap(): rcmod.set() From 03d991ab49e719b898975b8065302449a627bd31 Mon Sep 17 00:00:00 2001 From: Clark Fitzgerald Date: Wed, 2 Sep 2015 11:33:08 -0700 Subject: [PATCH 0126/1738] add setUp method with random seeding for tests --- seaborn/tests/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/seaborn/tests/__init__.py b/seaborn/tests/__init__.py index 80588c6758..0c21ad5dd0 100644 --- a/seaborn/tests/__init__.py +++ b/seaborn/tests/__init__.py @@ -1,7 +1,11 @@ +import numpy as np from matplotlib.pyplot import close class PlotTestCase(object): + def setUp(self): + np.random.seed(49) + def tearDown(self): close('all') From 5df928738a861b2c13ba53793867ab05672768f1 Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Thu, 3 Sep 2015 11:14:20 -0700 Subject: [PATCH 0127/1738] Changed from xrange to range for future compatability. --- seaborn/tests/test_categorical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index ac6907253e..37d77c43c4 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -2199,7 +2199,7 @@ def edge_calc(n, data): self.linear_data = np.arange(101) self.n = len(self.linear_data) self.expected_edges = map(lambda i: edge_calc(i, self.linear_data), - xrange(5, 0, -1)) + range(5, 0, -1)) self.expected_k = int(np.log2(self.n)) - int(np.log2(self.n*0.08)) + 1 self.outlier_data = np.concatenate((np.arange(100), [200])) From ad096d1221175e2613d23645f9bfe929f3093fcc Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Thu, 3 Sep 2015 13:58:21 -0700 Subject: [PATCH 0128/1738] Fixed single observation plotting bug. Fixed tests for number of patches, not artists. --- seaborn/categorical.py | 8 ++++---- seaborn/tests/test_categorical.py | 15 +++++++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 0efdfecdbc..e8d87c1615 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1522,7 +1522,7 @@ def _width_functions(self, width_func): def _lvplot(self, box_data, positions, color=[255. / 256., 185. / 256., 0.], - vert='v', widths=1, k_depth='proportion', + vert=True, widths=1, k_depth='proportion', ax=None, outlier_prop=None, scale='exponential', **kws): @@ -1531,12 +1531,13 @@ def _lvplot(self, box_data, positions, # If we only have one data point, plot a line if len(box_data) == 1: + kws.update({'color': self.gray, 'linestyle': '-'}) ys = [box_data[0], box_data[0]] xs = [x - widths / 2, x + widths / 2] if vert: - xx, yy = ys, xs - else: xx, yy = xs, ys + else: + xx, yy = ys, xs ax.plot(xx, yy, **kws) else: # Get the number of data points and calculate "depth" of @@ -1663,7 +1664,6 @@ def draw_letter_value_plot(self, ax, kws): color = self.colors[j] center = i + offsets[j] - artist_dict = self._lvplot(box_data, positions=[center], color=color, diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 37d77c43c4..a47104e18d 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -2190,6 +2190,8 @@ def edge_calc(n, data): maxi = np.max(data) return np.array([mid*f, maxi - mid*f]) + self.ispatch = lambda c: isinstance(c, mpl.collections.PatchCollection) + self.default_kws = dict(x=None, y=None, hue=None, data=None, order=None, hue_order=None, orient=None, color=None, palette=None, @@ -2267,12 +2269,14 @@ def test_hue_offsets(self): def test_axes_data(self): ax = cat.lvplot("g", "y", data=self.df) - nt.assert_equal(len(ax.artists), 3) + patches = filter(self.ispatch, ax.collections) + nt.assert_equal(len(patches), 3) plt.close("all") ax = cat.lvplot("g", "y", "h", data=self.df) - nt.assert_equal(len(ax.artists), 6) + patches = filter(self.ispatch, ax.collections) + nt.assert_equal(len(patches), 6) plt.close("all") @@ -2296,7 +2300,9 @@ def test_draw_missing_boxes(self): ax = cat.lvplot("g", "y", data=self.df, order=["a", "b", "c", "d"]) - nt.assert_equal(len(ax.artists), 3) + + patches = filter(self.ispatch, ax.collections) + nt.assert_equal(len(patches), 3) plt.close("all") def test_missing_data(self): @@ -2307,7 +2313,8 @@ def test_missing_data(self): y[-2:] = np.nan ax = cat.lvplot(x, y) - nt.assert_equal(len(ax.artists), 3) + patches = filter(self.ispatch, ax.collections) + nt.assert_equal(len(patches), 3) plt.close("all") From 9cf9159d6da5977af345e417290bb761dd493795 Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Thu, 3 Sep 2015 16:15:13 -0700 Subject: [PATCH 0129/1738] Finished letter value tests. --- seaborn/tests/test_categorical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index a47104e18d..58335a5c39 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -2314,13 +2314,13 @@ def test_missing_data(self): ax = cat.lvplot(x, y) patches = filter(self.ispatch, ax.collections) - nt.assert_equal(len(patches), 3) + nt.assert_equal(len(ax.lines), 3) plt.close("all") y[-1] = 0 ax = cat.lvplot(x, y, h) - nt.assert_equal(len(ax.artists), 7) + nt.assert_equal(len(ax.lines), 7) plt.close("all") From db7b10ee4b344fb5d7b24a452ee785378a8d1d3d Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Thu, 3 Sep 2015 16:40:48 -0700 Subject: [PATCH 0130/1738] Added outlier_prop default value, checking for value in appropriate range [0, 1]. --- seaborn/categorical.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index e8d87c1615..b5952dd8cb 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1480,8 +1480,12 @@ def _lv_box_ends(self, vals, k_depth='proportion', outlier_prop=None): n = len(vals) # If p is not set, calculate it so that 8 points are outliers if not outlier_prop: - p = 8./n + # Conventional boxplots assume this proportion of the data are + # outliers. + p = 0.007 else: + if ((outlier_prop > 1.) or (outlier_prop < 0.)): + raise ValueError('outlier_prop not in range [0, 1]!') p = outlier_prop # Select the depth, i.e. number of boxes to draw, based on the method k_dict = {'proportion': (np.log2(n)) - int(np.log2(n*p)) + 1, From 500f7d896b2f2cf09f769a102fe5febd16dc0c1b Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Thu, 3 Sep 2015 16:48:55 -0700 Subject: [PATCH 0131/1738] Docstring for outlier_prop --- seaborn/categorical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index b5952dd8cb..9f969a66a4 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -3176,8 +3176,8 @@ def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, is proportional to the percentage of data covered. outlier_prop : float, optional Proportion of data believed to be outliers. Is used in conjuction with - k_depth to determine the number of percentiles to draw. Defaults to 8 - outliers. + k_depth to determine the number of percentiles to draw. Defaults to + 0.007 as a proportion of outliers. Should be in range [0, 1]. {ax_in} kwargs : key, value mappings Other keyword arguments are passed through to ``plt.plot`` and From 2bf7da654d5eb84baffa566e2e46c47292d06560 Mon Sep 17 00:00:00 2001 From: Samuel St-Jean Date: Fri, 4 Sep 2015 12:01:12 -0400 Subject: [PATCH 0132/1738] Fixed typo --- seaborn/timeseries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/timeseries.py b/seaborn/timeseries.py index c08346f6b3..e0e501cd70 100644 --- a/seaborn/timeseries.py +++ b/seaborn/timeseries.py @@ -60,7 +60,7 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, {ci_band, ci_bars, boot_traces, boot_kde, unit_traces, unit_points}. Can use one or more than one method. ci : float or list of floats in [0, 100] - Confidence interaval size(s). If a list, it will stack the error + Confidence interval size(s). If a list, it will stack the error plots for each confidence interval. Only relevant for error styles with "ci" in the name. interpolate : boolean From b8ae90edc4d018bac188984e786be67595f33be5 Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Sat, 12 Sep 2015 12:52:14 -0700 Subject: [PATCH 0133/1738] Fixed tests. --- seaborn/tests/test_categorical.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 58335a5c39..46f6dec8fd 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -2185,10 +2185,9 @@ class TestLVPlotter(CategoricalFixture): def setUp(self): def edge_calc(n, data): - f = 2**(-n) - mid = np.median(data) - maxi = np.max(data) - return np.array([mid*f, maxi - mid*f]) + q = np.asanyarray([0.5**n, 1 - 0.5**n]) * 100 + q = list(np.unique(q)) + return np.percentile(data, q) self.ispatch = lambda c: isinstance(c, mpl.collections.PatchCollection) @@ -2200,10 +2199,12 @@ def edge_calc(n, data): scale='exponential', outlier_prop=None) self.linear_data = np.arange(101) self.n = len(self.linear_data) - self.expected_edges = map(lambda i: edge_calc(i, self.linear_data), - range(5, 0, -1)) - self.expected_k = int(np.log2(self.n)) - int(np.log2(self.n*0.08)) + 1 + self.expected_k = int(np.log2(self.n)) - int(np.log2(self.n*0.007)) + 1 + self.expected_edges_l = map(lambda i: edge_calc(i, self.linear_data), + range(self.expected_k + 2, 1, -1)) self.outlier_data = np.concatenate((np.arange(100), [200])) + self.expected_edges_o = map(lambda i: edge_calc(i, self.outlier_data), + range(self.expected_k + 2, 1, -1)) def test_box_ends_finite(self): p = cat._LVPlotter(**self.default_kws) @@ -2233,7 +2234,7 @@ def test_box_ends_correct(self): p = cat._LVPlotter(**self.default_kws) calc_edges, calc_k = p._lv_box_ends(self.linear_data) - npt.assert_equal(self.expected_edges, calc_edges) + npt.assert_equal(self.expected_edges_l, calc_edges) npt.assert_equal(self.expected_k, calc_k) @@ -2241,7 +2242,7 @@ def test_outliers(self): p = cat._LVPlotter(**self.default_kws) calc_edges, calc_k = p._lv_box_ends(self.outlier_data) - npt.assert_equal(self.expected_edges, calc_edges) + npt.assert_equal(self.expected_edges_o, calc_edges) npt.assert_equal(self.expected_k, calc_k) @@ -2270,13 +2271,13 @@ def test_axes_data(self): ax = cat.lvplot("g", "y", data=self.df) patches = filter(self.ispatch, ax.collections) - nt.assert_equal(len(patches), 3) + nt.assert_equal(len(list(patches)), 3) plt.close("all") ax = cat.lvplot("g", "y", "h", data=self.df) patches = filter(self.ispatch, ax.collections) - nt.assert_equal(len(patches), 6) + nt.assert_equal(len(list(patches)), 6) plt.close("all") @@ -2302,7 +2303,7 @@ def test_draw_missing_boxes(self): order=["a", "b", "c", "d"]) patches = filter(self.ispatch, ax.collections) - nt.assert_equal(len(patches), 3) + nt.assert_equal(len(list(patches)), 3) plt.close("all") def test_missing_data(self): From 79add922cf0188c56fa63edc2f5d925615c55ece Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Sat, 12 Sep 2015 13:26:44 -0700 Subject: [PATCH 0134/1738] Fixed tests so Python 3 compatible. --- seaborn/tests/test_categorical.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 46f6dec8fd..8f70bb8d7f 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -2209,10 +2209,9 @@ def edge_calc(n, data): def test_box_ends_finite(self): p = cat._LVPlotter(**self.default_kws) p.establish_variables("g", "y", data=self.df) - box_ends, k_vals = np.hsplit(np.asarray(map(p._lv_box_ends, - p.plot_data)), 2) - box_ends = box_ends.squeeze() - k_vals = k_vals.squeeze() + box_k = np.asarray(map(p._lv_box_ends, p.plot_data)) + box_ends = box_k[:, 0] + k_vals = box_k[:, 1] # Check that all the box ends are finite and are within # the bounds of the data @@ -2234,7 +2233,7 @@ def test_box_ends_correct(self): p = cat._LVPlotter(**self.default_kws) calc_edges, calc_k = p._lv_box_ends(self.linear_data) - npt.assert_equal(self.expected_edges_l, calc_edges) + npt.assert_equal(list(self.expected_edges_l), calc_edges) npt.assert_equal(self.expected_k, calc_k) @@ -2242,7 +2241,7 @@ def test_outliers(self): p = cat._LVPlotter(**self.default_kws) calc_edges, calc_k = p._lv_box_ends(self.outlier_data) - npt.assert_equal(self.expected_edges_o, calc_edges) + npt.assert_equal(list(self.expected_edges_o), calc_edges) npt.assert_equal(self.expected_k, calc_k) From 31854be90d9aafd85f79d3d86f3564fddd27987b Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Sat, 12 Sep 2015 13:52:36 -0700 Subject: [PATCH 0135/1738] Python 3 fixes. --- seaborn/tests/test_categorical.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 8f70bb8d7f..eeb9fcd456 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1995,8 +1995,8 @@ def test_simple_pointplots(self): nt.assert_equal(len(ax.collections), len(self.h.unique())) nt.assert_equal(len(ax.lines), (len(self.g.unique()) - * len(self.h.unique()) - + len(self.h.unique()))) + * len(self.h.unique()) + + len(self.h.unique()))) nt.assert_equal(ax.get_xlabel(), "g") nt.assert_equal(ax.get_ylabel(), "mean(y)") plt.close("all") @@ -2209,7 +2209,8 @@ def edge_calc(n, data): def test_box_ends_finite(self): p = cat._LVPlotter(**self.default_kws) p.establish_variables("g", "y", data=self.df) - box_k = np.asarray(map(p._lv_box_ends, p.plot_data)) + box_k = np.asarray([[b, k] + for b, k in map(p._lv_box_ends, p.plot_data)]) box_ends = box_k[:, 0] k_vals = box_k[:, 1] @@ -2299,7 +2300,7 @@ def test_box_colors(self): def test_draw_missing_boxes(self): ax = cat.lvplot("g", "y", data=self.df, - order=["a", "b", "c", "d"]) + order=["a", "b", "c", "d"]) patches = filter(self.ispatch, ax.collections) nt.assert_equal(len(list(patches)), 3) From 931b8bcf5f0e5bdf10b11af5bb9ff6e81fc200c9 Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Sat, 12 Sep 2015 14:08:24 -0700 Subject: [PATCH 0136/1738] More Python 3 fixes. --- seaborn/tests/test_categorical.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index eeb9fcd456..f24a2e142a 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -2217,7 +2217,7 @@ def test_box_ends_finite(self): # Check that all the box ends are finite and are within # the bounds of the data b_e = map(lambda a: np.all(np.isfinite(a)), box_ends) - npt.assert_equal(np.sum(b_e), len(box_ends)) + npt.assert_equal(np.sum(list(b_e)), len(box_ends)) def within(t): a, d = t @@ -2225,10 +2225,10 @@ def within(t): (np.ravel(a) >= d.min())).all() b_w = map(within, itertools.izip(box_ends, p.plot_data)) - npt.assert_equal(np.sum(b_w), len(box_ends)) + npt.assert_equal(np.sum(list(b_w)), len(box_ends)) k_f = map(lambda k: (k > 0.) & np.isfinite(k), k_vals) - npt.assert_equal(np.sum(k_f), len(k_vals)) + npt.assert_equal(np.sum(list(k_f)), len(k_vals)) def test_box_ends_correct(self): p = cat._LVPlotter(**self.default_kws) From 42046c603ad5d005ed11d6a84f7f63ac2616dcbe Mon Sep 17 00:00:00 2001 From: Drew O'Kane Date: Sat, 12 Sep 2015 14:52:53 -0700 Subject: [PATCH 0137/1738] More Python 3 fixes. Removed itertools references. --- seaborn/tests/test_categorical.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index f24a2e142a..f75f63cf01 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1,4 +1,3 @@ -import itertools import numpy as np import pandas as pd import scipy @@ -2224,7 +2223,7 @@ def within(t): return ((np.ravel(a) <= d.max()) & (np.ravel(a) >= d.min())).all() - b_w = map(within, itertools.izip(box_ends, p.plot_data)) + b_w = map(within, zip(box_ends, p.plot_data)) npt.assert_equal(np.sum(list(b_w)), len(box_ends)) k_f = map(lambda k: (k > 0.) & np.isfinite(k), k_vals) From df1415e94a4cb7d45344610578fed855a6251e2f Mon Sep 17 00:00:00 2001 From: Tamas Nagy Date: Sun, 11 Oct 2015 10:33:55 -0700 Subject: [PATCH 0138/1738] Fix typos and improve documentation consistency --- seaborn/distributions.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 687e6d83a0..7529ac195f 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -67,7 +67,7 @@ def distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, Color to plot everything but the fitted curve in. vertical : bool, optional If True, oberved values are on y-axis. - norm_hist : bool, otional + norm_hist : bool, optional If True, the histogram height shows a density rather than a count. This is implied if a KDE or fitted density is plotted. axlabel : string, False, or None, optional @@ -445,12 +445,12 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", ---------- data : 1d array-like Input data. - data2: 1d array-like + data2: 1d array-like, optional Second input data. If present, a bivariate KDE will be estimated. shade : bool, optional If True, shade in the area under the KDE curve (or draw with filled contours when data is bivariate). - vertical : bool + vertical : bool, optional If True, density is on x-axis. kernel : {'gau' | 'cos' | 'biw' | 'epa' | 'tri' | 'triw' }, optional Code for shape of kernel to fit with. Bivariate KDE can only use @@ -465,18 +465,18 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", clip : pair of scalars, or pair of pair of scalars, optional Lower and upper bounds for datapoints used to fit KDE. Can provide a pair of (low, high) bounds for bivariate plots. - legend : bool, optinal + legend : bool, optional If True, add a legend or label the axes when possible. - cumulative : bool + cumulative : bool, optional If True, draw the cumulative distribution estimated by the kde. - shade_lowest : bool + shade_lowest : bool, optional If True, shade the lowest contour of a bivariate KDE plot. Not relevant when drawing a univariate plot or when ``shade=False``. Setting this to ``False`` can be useful when you want multiple densities on the same Axes. ax : matplotlib axis, optional Axis to plot on, otherwise uses current axis. - kwargs : key, value pairings + kwargs : key, value pairs Other keyword arguments are passed to ``plt.plot()`` or ``plt.contour{f}`` depending on whether a univariate or bivariate plot is being drawn. @@ -614,9 +614,9 @@ def rugplot(a, height=.05, axis="x", ax=None, **kwargs): Height of ticks as proportion of the axis. axis : {'x' | 'y'}, optional Axis to draw rugplot on. - ax : matplotlib axes + ax : matplotlib axes, optional Axes to draw plot into; otherwise grabs current axes. - kwargs : key, value mappings + kwargs : key, value pairs Other keyword arguments are passed to ``axvline`` or ``axhline``. Returns @@ -656,7 +656,7 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, DataFrame when ``x`` and ``y`` are variable names. kind : { "scatter" | "reg" | "resid" | "kde" | "hex" }, optional Kind of plot to draw. - stat_func : callable or None + stat_func : callable or None, optional Function used to calculate a statistic about the relationship and annotate the plot. Should map `x` and `y` either to a single value or to a (value, p) tuple. Set to ``None`` if you don't want to @@ -673,7 +673,7 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, If True, remove observations that are missing from ``x`` and ``y``. {x, y}lim : two-tuples, optional Axis limits to set before plotting. - {joint, marginal, annot}_kws : dicts + {joint, marginal, annot}_kws : dicts, optional Additional keyword arguments for the plot components. kwargs : key, value pairs Additional keyword arguments are passed to the function used to From 17dedbe0ac60c05b7dab92a41d1a90b710bfd2ff Mon Sep 17 00:00:00 2001 From: Tamas Nagy Date: Sun, 11 Oct 2015 10:41:47 -0700 Subject: [PATCH 0139/1738] Improved wording for kwargs description --- seaborn/distributions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 7529ac195f..9a64c41745 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -476,7 +476,7 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", densities on the same Axes. ax : matplotlib axis, optional Axis to plot on, otherwise uses current axis. - kwargs : key, value pairs + kwargs : key, value pairings Other keyword arguments are passed to ``plt.plot()`` or ``plt.contour{f}`` depending on whether a univariate or bivariate plot is being drawn. @@ -616,7 +616,7 @@ def rugplot(a, height=.05, axis="x", ax=None, **kwargs): Axis to draw rugplot on. ax : matplotlib axes, optional Axes to draw plot into; otherwise grabs current axes. - kwargs : key, value pairs + kwargs : key, value pairings Other keyword arguments are passed to ``axvline`` or ``axhline``. Returns @@ -675,7 +675,7 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, Axis limits to set before plotting. {joint, marginal, annot}_kws : dicts, optional Additional keyword arguments for the plot components. - kwargs : key, value pairs + kwargs : key, value pairings Additional keyword arguments are passed to the function used to draw the plot on the joint Axes, superseding items in the ``joint_kws`` dictionary. From b2e4b9f3f660158481327ba949f75e322bdcd76a Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Sun, 25 Oct 2015 10:39:33 -0500 Subject: [PATCH 0140/1738] COMPAT: compat with mpl 1.5 color cycle --- matplotlibrc | 1 + seaborn/axisgrid.py | 2 +- seaborn/categorical.py | 2 +- seaborn/palettes.py | 5 ++--- seaborn/rcmod.py | 10 +++++++++- seaborn/tests/test_palettes.py | 18 +++++++++++++----- seaborn/timeseries.py | 2 +- seaborn/utils.py | 16 +++++++++++++++- 8 files changed, 43 insertions(+), 13 deletions(-) create mode 100644 matplotlibrc diff --git a/matplotlibrc b/matplotlibrc new file mode 100644 index 0000000000..13468274c2 --- /dev/null +++ b/matplotlibrc @@ -0,0 +1 @@ +backend : Agg diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index f5343fb017..65001d9738 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -146,7 +146,7 @@ def _get_palette(self, data, hue, hue_order, palette): # By default use either the current color palette or HUSL if palette is None: - current_palette = mpl.rcParams["axes.color_cycle"] + current_palette = utils.get_color_cycle() if n_colors > len(current_palette): colors = color_palette("husl", n_colors) else: diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 9d430b9302..d66c1ca07e 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -261,7 +261,7 @@ def establish_colors(self, color, palette, saturation): if color is None and palette is None: # Determine whether the current palette will have enough values # If not, we'll default to the husl palette so each is distinct - current_palette = mpl.rcParams["axes.color_cycle"] + current_palette = utils.get_color_cycle() if n_colors <= len(current_palette): colors = color_palette(n_colors=n_colors) else: diff --git a/seaborn/palettes.py b/seaborn/palettes.py index c2ad4881e6..a3f6dc23dc 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -11,12 +11,11 @@ from .external.six import string_types from .external.six.moves import range -from .utils import desaturate, set_hls_values +from .utils import desaturate, set_hls_values, get_color_cycle from .xkcd_rgb import xkcd_rgb from .crayons import crayons from .miscplot import palplot - SEABORN_PALETTES = dict( deep=["#4C72B0", "#55A868", "#C44E52", "#8172B2", "#CCB974", "#64B5CD"], @@ -148,7 +147,7 @@ def color_palette(palette=None, n_colors=None, desat=None): """ if palette is None: - palette = mpl.rcParams["axes.color_cycle"] + palette = get_color_cycle() if n_colors is None: n_colors = len(palette) diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index 50360a0ff2..ebf02a3045 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -1,9 +1,12 @@ """Functions that alter the matplotlib rc dictionary on the fly.""" +from distutils.version import LooseVersion + import numpy as np import matplotlib as mpl from . import palettes +mpl_ge_150 = LooseVersion(mpl.__version__) >= '1.5.0' _style_keys = ( @@ -490,7 +493,12 @@ def set_palette(palette, n_colors=None, desat=None, color_codes=False): """ colors = palettes.color_palette(palette, n_colors, desat) - mpl.rcParams["axes.color_cycle"] = list(colors) + if mpl_ge_150: + from cycler import cycler + cyl = cycler('color', colors) + mpl.rcParams['axes.prop_cycle'] = cyl + else: + mpl.rcParams["axes.color_cycle"] = list(colors) mpl.rcParams["patch.facecolor"] = colors[0] if color_codes: palettes.set_color_codes(palette) diff --git a/seaborn/tests/test_palettes.py b/seaborn/tests/test_palettes.py index cbd87d8e61..8b3b26aa12 100644 --- a/seaborn/tests/test_palettes.py +++ b/seaborn/tests/test_palettes.py @@ -1,3 +1,4 @@ +import warnings import colorsys import numpy as np import matplotlib as mpl @@ -16,7 +17,7 @@ def test_current_palette(self): pal = palettes.color_palette(["red", "blue", "green"], 3) rcmod.set_palette(pal, 3) - nt.assert_equal(pal, mpl.rcParams["axes.color_cycle"]) + nt.assert_equal(pal, utils.get_color_cycle()) rcmod.set() def test_palette_context(self): @@ -25,9 +26,9 @@ def test_palette_context(self): context_pal = palettes.color_palette("muted") with palettes.color_palette(context_pal): - nt.assert_equal(mpl.rcParams["axes.color_cycle"], context_pal) + nt.assert_equal(utils.get_color_cycle(), context_pal) - nt.assert_equal(mpl.rcParams["axes.color_cycle"], default_pal) + nt.assert_equal(utils.get_color_cycle(), default_pal) def test_big_palette_context(self): @@ -36,9 +37,9 @@ def test_big_palette_context(self): rcmod.set_palette(original_pal) with palettes.color_palette(context_pal, 10): - nt.assert_equal(mpl.rcParams["axes.color_cycle"], context_pal) + nt.assert_equal(utils.get_color_cycle(), context_pal) - nt.assert_equal(mpl.rcParams["axes.color_cycle"], original_pal) + nt.assert_equal(utils.get_color_cycle(), original_pal) # Reset default rcmod.set() @@ -287,3 +288,10 @@ def test_preserved_palette_length(self): pal_in = palettes.color_palette("Set1", 10) pal_out = palettes.color_palette(pal_in) nt.assert_equal(pal_in, pal_out) + + def test_get_color_cycle(self): + with warnings.catch_warnings(): + warnings.simplefilter('ignore') + result = utils.get_color_cycle() + expected = mpl.rcParams['axes.color_cycle'] + nt.assert_equal(result, expected) diff --git a/seaborn/timeseries.py b/seaborn/timeseries.py index e0e501cd70..2d0299d986 100644 --- a/seaborn/timeseries.py +++ b/seaborn/timeseries.py @@ -257,7 +257,7 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, # Set up the color palette if color is None: - current_palette = mpl.rcParams["axes.color_cycle"] + current_palette = utils.get_color_cycle() if len(current_palette) < n_cond: colors = color_palette("husl", n_cond) else: diff --git a/seaborn/utils.py b/seaborn/utils.py index ef0adb391f..0764d31994 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -7,12 +7,13 @@ import numpy as np from scipy import stats import pandas as pd +import matplotlib as mpl import matplotlib.colors as mplcol import matplotlib.pyplot as plt from distutils.version import LooseVersion pandas_has_categoricals = LooseVersion(pd.__version__) >= "0.15" - +mpl_ge_150 = LooseVersion(mpl.__version__) >= "1.5.0" from .external.six.moves.urllib.request import urlopen, urlretrieve @@ -527,3 +528,16 @@ def categorical_order(values, order=None): order = order order = filter(pd.notnull, order) return list(order) + + +def get_color_cycle(): + if mpl_ge_150: + cyl = mpl.rcParams['axes.prop_cycle'] + # matplotlib 1.5 verifies that axes.prop_cycle *is* a cycler + # but no garuantee that there's a `color` key. + # so users could have a custom rcParmas w/ no color... + try: + return [x['color'] for x in cyl] + except KeyError: + pass # just return axes.color style below + return mpl.rcParams['axes.color_cycle'] From d4e71f610f738967320ed508752f1d5fc52dc619 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Sun, 25 Oct 2015 12:47:25 -0500 Subject: [PATCH 0141/1738] COMPAT: pandas 0.17 broke date_range --- seaborn/axisgrid.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 65001d9738..81440f54ef 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -573,7 +573,8 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, >>> df = pd.DataFrame( ... data=np.random.randn(90, 4), ... columns=pd.Series(list("ABCD"), name="walk"), - ... index=pd.date_range("Jan 1", "March 31", name="date")) + ... index=pd.date_range("2015-01-01", "2015-03-31", + ... name="date")) >>> df = df.cumsum(axis=0).stack().reset_index(name="val") >>> def dateplot(x, y, **kwargs): ... ax = plt.gca() From c4d27d3e35e02ec825f1f91840cfa33e57707837 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Sun, 25 Oct 2015 15:44:23 -0500 Subject: [PATCH 0142/1738] TST: Avoid settingwithcopy --- matplotlibrc | 1 - seaborn/tests/test_linearmodels.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 matplotlibrc diff --git a/matplotlibrc b/matplotlibrc deleted file mode 100644 index 13468274c2..0000000000 --- a/matplotlibrc +++ /dev/null @@ -1 +0,0 @@ -backend : Agg diff --git a/seaborn/tests/test_linearmodels.py b/seaborn/tests/test_linearmodels.py index 3ca63755c7..0162e1323c 100644 --- a/seaborn/tests/test_linearmodels.py +++ b/seaborn/tests/test_linearmodels.py @@ -33,7 +33,7 @@ class TestLinearPlotter(PlotTestCase): s=np.tile(list("abcdefghij"), 6))) df["z"] = df.y + rs.randn(60) df["y_na"] = df.y.copy() - df.y_na.ix[[10, 20, 30]] = np.nan + df.loc[[10, 20, 30], 'y_na'] = np.nan def test_establish_variables_from_frame(self): @@ -109,7 +109,7 @@ class TestRegressionPlotter(PlotTestCase): p = 1 / (1 + np.exp(-(df.x * 2 + rs.randn(60)))) df["c"] = [rs.binomial(1, p_i) for p_i in p] - df.y_na.ix[[10, 20, 30]] = np.nan + df.loc[[10, 20, 30], 'y_na'] = np.nan def test_variables_from_frame(self): From 4600735f1a70a1c86ff5903c27e2b11e281e7c59 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 26 Oct 2015 17:22:46 -0700 Subject: [PATCH 0143/1738] Add compatability for IPython3/4/Jupyter in functions that use widgets Closes #699 --- doc/releases/v0.6.1.txt | 2 + seaborn/__init__.py | 1 + seaborn/palettes.py | 419 --------------------------------------- seaborn/widgets.py | 428 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 431 insertions(+), 419 deletions(-) create mode 100644 seaborn/widgets.py diff --git a/doc/releases/v0.6.1.txt b/doc/releases/v0.6.1.txt index 4880c96f86..a4556f8ec6 100644 --- a/doc/releases/v0.6.1.txt +++ b/doc/releases/v0.6.1.txt @@ -11,3 +11,5 @@ v0.6.1 (Unreleased) - Fixed a bug in :func:`clustermap` when ``fastcluster`` is not installed. - Fixed a bug in the zscore calculation in :func:`clustermap`. + +- Added compatability for various IPython (and Jupyter) versions in functions that use widgets. diff --git a/seaborn/__init__.py b/seaborn/__init__.py index 93d42bb44e..7bf6691e81 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -8,6 +8,7 @@ from .matrix import * from .miscplot import * from .axisgrid import * +from .widgets import * from .xkcd_rgb import xkcd_rgb from .crayons import crayons set() diff --git a/seaborn/palettes.py b/seaborn/palettes.py index a3f6dc23dc..6da06e6b1d 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -4,8 +4,6 @@ import numpy as np import matplotlib as mpl -import matplotlib.pyplot as plt -from matplotlib.colors import LinearSegmentedColormap from .external import husl from .external.six import string_types @@ -14,7 +12,6 @@ from .utils import desaturate, set_hls_values, get_color_cycle from .xkcd_rgb import xkcd_rgb from .crayons import crayons -from .miscplot import palplot SEABORN_PALETTES = dict( deep=["#4C72B0", "#55A868", "#C44E52", @@ -950,419 +947,3 @@ def set_color_codes(palette="deep"): rgb = mpl.colors.colorConverter.to_rgb(color) mpl.colors.colorConverter.colors[code] = rgb mpl.colors.colorConverter.cache[code] = rgb - - -def _init_mutable_colormap(): - """Create a matplotlib colormap that will be updated by the widgets.""" - greys = color_palette("Greys", 256) - cmap = LinearSegmentedColormap.from_list("interactive", greys) - cmap._init() - cmap._set_extremes() - return cmap - - -def _update_lut(cmap, colors): - """Change the LUT values in a matplotlib colormap in-place.""" - cmap._lut[:256] = colors - cmap._set_extremes() - - -def _show_cmap(cmap): - """Show a continuous matplotlib colormap.""" - from .rcmod import axes_style # Avoid circular import - with axes_style("white"): - f, ax = plt.subplots(figsize=(8.25, .75)) - ax.set(xticks=[], yticks=[]) - x = np.linspace(0, 1, 256)[np.newaxis, :] - ax.pcolormesh(x, cmap=cmap) - - -def choose_colorbrewer_palette(data_type, as_cmap=False): - """Select a palette from the ColorBrewer set. - - These palettes are built into matplotlib and can be used by name in - many seaborn functions, or by passing the object returned by this function. - - Parameters - ---------- - data_type : {'sequential', 'diverging', 'qualitative'} - This describes the kind of data you want to visualize. See the seaborn - color palette docs for more information about how to choose this value. - Note that you can pass substrings (e.g. 'q' for 'qualitative. - - as_cmap : bool - If True, the return value is a matplotlib colormap rather than a - list of discrete colors. - - Returns - ------- - pal or cmap : list of colors or matplotlib colormap - Object that can be passed to plotting functions. - - See Also - -------- - dark_palette : Create a sequential palette with dark low values. - light_palette : Create a sequential palette with bright low values. - diverging_palette : Create a diverging palette from selected colors. - cubehelix_palette : Create a sequential palette or colormap using the - cubehelix system. - - - """ - from IPython.html.widgets import interact, FloatSliderWidget - - if data_type.startswith("q") and as_cmap: - raise ValueError("Qualitative palettes cannot be colormaps.") - - pal = [] - if as_cmap: - cmap = _init_mutable_colormap() - - if data_type.startswith("s"): - opts = ["Greys", "Reds", "Greens", "Blues", "Oranges", "Purples", - "BuGn", "BuPu", "GnBu", "OrRd", "PuBu", "PuRd", "RdPu", "YlGn", - "PuBuGn", "YlGnBu", "YlOrBr", "YlOrRd"] - variants = ["regular", "reverse", "dark"] - - @interact - def choose_sequential(name=opts, n=(2, 18), - desat=FloatSliderWidget(min=0, max=1, value=1), - variant=variants): - if variant == "reverse": - name += "_r" - elif variant == "dark": - name += "_d" - - if as_cmap: - colors = color_palette(name, 256, desat) - _update_lut(cmap, np.c_[colors, np.ones(256)]) - _show_cmap(cmap) - else: - pal[:] = color_palette(name, n, desat) - palplot(pal) - - elif data_type.startswith("d"): - opts = ["RdBu", "RdGy", "PRGn", "PiYG", "BrBG", - "RdYlBu", "RdYlGn", "Spectral"] - variants = ["regular", "reverse"] - - @interact - def choose_diverging(name=opts, n=(2, 16), - desat=FloatSliderWidget(min=0, max=1, value=1), - variant=variants): - if variant == "reverse": - name += "_r" - if as_cmap: - colors = color_palette(name, 256, desat) - _update_lut(cmap, np.c_[colors, np.ones(256)]) - _show_cmap(cmap) - else: - pal[:] = color_palette(name, n, desat) - palplot(pal) - - elif data_type.startswith("q"): - opts = ["Set1", "Set2", "Set3", "Paired", "Accent", - "Pastel1", "Pastel2", "Dark2"] - - @interact - def choose_qualitative(name=opts, n=(2, 16), - desat=FloatSliderWidget(min=0, max=1, value=1)): - pal[:] = color_palette(name, n, desat) - palplot(pal) - - if as_cmap: - return cmap - return pal - - -def choose_dark_palette(input="husl", as_cmap=False): - """Launch an interactive widget to create a dark sequential palette. - - This corresponds with the :func:`dark_palette` function. This kind - of palette is good for data that range between relatively uninteresting - low values and interesting high values. - - Requires IPython 2+ and must be used in the notebook. - - Parameters - ---------- - input : {'husl', 'hls', 'rgb'} - Color space for defining the seed value. Note that the default is - different than the default input for :func:`dark_palette`. - as_cmap : bool - If True, the return value is a matplotlib colormap rather than a - list of discrete colors. - - Returns - ------- - pal or cmap : list of colors or matplotlib colormap - Object that can be passed to plotting functions. - - See Also - -------- - dark_palette : Create a sequential palette with dark low values. - light_palette : Create a sequential palette with bright low values. - cubehelix_palette : Create a sequential palette or colormap using the - cubehelix system. - - """ - from IPython.html.widgets import interact - - pal = [] - if as_cmap: - cmap = _init_mutable_colormap() - - if input == "rgb": - @interact - def choose_dark_palette_rgb(r=(0., 1.), - g=(0., 1.), - b=(0., 1.), - n=(3, 17)): - color = r, g, b - if as_cmap: - colors = dark_palette(color, 256, input="rgb") - _update_lut(cmap, colors) - _show_cmap(cmap) - else: - pal[:] = dark_palette(color, n, input="rgb") - palplot(pal) - - elif input == "hls": - @interact - def choose_dark_palette_hls(h=(0., 1.), - l=(0., 1.), - s=(0., 1.), - n=(3, 17)): - color = h, l, s - if as_cmap: - colors = dark_palette(color, 256, input="hls") - _update_lut(cmap, colors) - _show_cmap(cmap) - else: - pal[:] = dark_palette(color, n, input="hls") - palplot(pal) - - elif input == "husl": - @interact - def choose_dark_palette_husl(h=(0, 359), - s=(0, 99), - l=(0, 99), - n=(3, 17)): - color = h, s, l - if as_cmap: - colors = dark_palette(color, 256, input="husl") - _update_lut(cmap, colors) - _show_cmap(cmap) - else: - pal[:] = dark_palette(color, n, input="husl") - palplot(pal) - - if as_cmap: - return cmap - return pal - - -def choose_light_palette(input="husl", as_cmap=False): - """Launch an interactive widget to create a light sequential palette. - - This corresponds with the :func:`light_palette` function. This kind - of palette is good for data that range between relatively uninteresting - low values and interesting high values. - - Requires IPython 2+ and must be used in the notebook. - - Parameters - ---------- - input : {'husl', 'hls', 'rgb'} - Color space for defining the seed value. Note that the default is - different than the default input for :func:`light_palette`. - as_cmap : bool - If True, the return value is a matplotlib colormap rather than a - list of discrete colors. - - Returns - ------- - pal or cmap : list of colors or matplotlib colormap - Object that can be passed to plotting functions. - - See Also - -------- - light_palette : Create a sequential palette with bright low values. - dark_palette : Create a sequential palette with dark low values. - cubehelix_palette : Create a sequential palette or colormap using the - cubehelix system. - - """ - from IPython.html.widgets import interact - - pal = [] - if as_cmap: - cmap = _init_mutable_colormap() - - if input == "rgb": - @interact - def choose_light_palette_rgb(r=(0., 1.), - g=(0., 1.), - b=(0., 1.), - n=(3, 17)): - color = r, g, b - if as_cmap: - colors = light_palette(color, 256, input="rgb") - _update_lut(cmap, colors) - _show_cmap(cmap) - else: - pal[:] = light_palette(color, n, input="rgb") - palplot(pal) - - elif input == "hls": - @interact - def choose_light_palette_hls(h=(0., 1.), - l=(0., 1.), - s=(0., 1.), - n=(3, 17)): - color = h, l, s - if as_cmap: - colors = light_palette(color, 256, input="hls") - _update_lut(cmap, colors) - _show_cmap(cmap) - else: - pal[:] = light_palette(color, n, input="hls") - palplot(pal) - - elif input == "husl": - @interact - def choose_light_palette_husl(h=(0, 359), - s=(0, 99), - l=(0, 99), - n=(3, 17)): - color = h, s, l - if as_cmap: - colors = light_palette(color, 256, input="husl") - _update_lut(cmap, colors) - _show_cmap(cmap) - else: - pal[:] = light_palette(color, n, input="husl") - palplot(pal) - - if as_cmap: - return cmap - return pal - - -def choose_diverging_palette(as_cmap=False): - """Launch an interactive widget to choose a diverging color palette. - - This corresponds with the :func:`diverging_palette` function. This kind - of palette is good for data that range between interesting low values - and interesting high values with a meaningful midpoint. (For example, - change scores relative to some baseline value). - - Requires IPython 2+ and must be used in the notebook. - - Parameters - ---------- - as_cmap : bool - If True, the return value is a matplotlib colormap rather than a - list of discrete colors. - - Returns - ------- - pal or cmap : list of colors or matplotlib colormap - Object that can be passed to plotting functions. - - See Also - -------- - diverging_palette : Create a diverging color palette or colormap. - choose_colorbrewer_palette : Interactively choose palettes from the - colorbrewer set, including diverging palettes. - - """ - from IPython.html.widgets import interact, IntSliderWidget - - pal = [] - if as_cmap: - cmap = _init_mutable_colormap() - - @interact - def choose_diverging_palette(h_neg=IntSliderWidget(min=0, - max=359, - value=220), - h_pos=IntSliderWidget(min=0, - max=359, - value=10), - s=IntSliderWidget(min=0, max=99, value=74), - l=IntSliderWidget(min=0, max=99, value=50), - sep=IntSliderWidget(min=1, max=50, value=10), - n=(2, 16), - center=["light", "dark"]): - if as_cmap: - colors = diverging_palette(h_neg, h_pos, s, l, sep, 256, center) - _update_lut(cmap, colors) - _show_cmap(cmap) - else: - pal[:] = diverging_palette(h_neg, h_pos, s, l, sep, n, center) - palplot(pal) - - if as_cmap: - return cmap - return pal - - -def choose_cubehelix_palette(as_cmap=False): - """Launch an interactive widget to create a sequential cubehelix palette. - - This corresponds with the :func:`cubehelix_palette` function. This kind - of palette is good for data that range between relatively uninteresting - low values and interesting high values. The cubehelix system allows the - palette to have more hue variance across the range, which can be helpful - for distinguishing a wider range of values. - - Requires IPython 2+ and must be used in the notebook. - - Parameters - ---------- - as_cmap : bool - If True, the return value is a matplotlib colormap rather than a - list of discrete colors. - - Returns - ------- - pal or cmap : list of colors or matplotlib colormap - Object that can be passed to plotting functions. - - See Also - -------- - cubehelix_palette : Create a sequential palette or colormap using the - cubehelix system. - - """ - from IPython.html.widgets import (interact, - FloatSliderWidget, IntSliderWidget) - - pal = [] - if as_cmap: - cmap = _init_mutable_colormap() - - @interact - def choose_cubehelix(n_colors=IntSliderWidget(min=2, max=16, value=9), - start=FloatSliderWidget(min=0, max=3, value=0), - rot=FloatSliderWidget(min=-1, max=1, value=.4), - gamma=FloatSliderWidget(min=0, max=5, value=1), - hue=FloatSliderWidget(min=0, max=1, value=.8), - light=FloatSliderWidget(min=0, max=1, value=.85), - dark=FloatSliderWidget(min=0, max=1, value=.15), - reverse=False): - - if as_cmap: - colors = cubehelix_palette(256, start, rot, gamma, - hue, light, dark, reverse) - _update_lut(cmap, np.c_[colors, np.ones(256)]) - _show_cmap(cmap) - else: - pal[:] = cubehelix_palette(n_colors, start, rot, gamma, - hue, light, dark, reverse) - palplot(pal) - - if as_cmap: - return cmap - return pal diff --git a/seaborn/widgets.py b/seaborn/widgets.py new file mode 100644 index 0000000000..011e8f17b8 --- /dev/null +++ b/seaborn/widgets.py @@ -0,0 +1,428 @@ +from __future__ import division +import numpy as np +import matplotlib.pyplot as plt +from matplotlib.colors import LinearSegmentedColormap + +# Lots of different places that widgets could come from... +try: + from ipywidgets import interact, FloatSlider, IntSlider +except ImportError: + try: + from IPython.html.widgets import interact, FloatSlider, IntSlider + except ImportError: + try: + from IPython.html.widgets import (interact, + FloatSliderWidget as FloatSlider, + IntSliderWidget as IntSlider) + except ImportError: + pass + + +from .miscplot import palplot +from .palettes import (color_palette, dark_palette, light_palette, + diverging_palette, cubehelix_palette) + + +def _init_mutable_colormap(): + """Create a matplotlib colormap that will be updated by the widgets.""" + greys = color_palette("Greys", 256) + cmap = LinearSegmentedColormap.from_list("interactive", greys) + cmap._init() + cmap._set_extremes() + return cmap + + +def _update_lut(cmap, colors): + """Change the LUT values in a matplotlib colormap in-place.""" + cmap._lut[:256] = colors + cmap._set_extremes() + + +def _show_cmap(cmap): + """Show a continuous matplotlib colormap.""" + from .rcmod import axes_style # Avoid circular import + with axes_style("white"): + f, ax = plt.subplots(figsize=(8.25, .75)) + ax.set(xticks=[], yticks=[]) + x = np.linspace(0, 1, 256)[np.newaxis, :] + ax.pcolormesh(x, cmap=cmap) + + +def choose_colorbrewer_palette(data_type, as_cmap=False): + """Select a palette from the ColorBrewer set. + + These palettes are built into matplotlib and can be used by name in + many seaborn functions, or by passing the object returned by this function. + + Parameters + ---------- + data_type : {'sequential', 'diverging', 'qualitative'} + This describes the kind of data you want to visualize. See the seaborn + color palette docs for more information about how to choose this value. + Note that you can pass substrings (e.g. 'q' for 'qualitative. + + as_cmap : bool + If True, the return value is a matplotlib colormap rather than a + list of discrete colors. + + Returns + ------- + pal or cmap : list of colors or matplotlib colormap + Object that can be passed to plotting functions. + + See Also + -------- + dark_palette : Create a sequential palette with dark low values. + light_palette : Create a sequential palette with bright low values. + diverging_palette : Create a diverging palette from selected colors. + cubehelix_palette : Create a sequential palette or colormap using the + cubehelix system. + + + """ + if data_type.startswith("q") and as_cmap: + raise ValueError("Qualitative palettes cannot be colormaps.") + + pal = [] + if as_cmap: + cmap = _init_mutable_colormap() + + if data_type.startswith("s"): + opts = ["Greys", "Reds", "Greens", "Blues", "Oranges", "Purples", + "BuGn", "BuPu", "GnBu", "OrRd", "PuBu", "PuRd", "RdPu", "YlGn", + "PuBuGn", "YlGnBu", "YlOrBr", "YlOrRd"] + variants = ["regular", "reverse", "dark"] + + @interact + def choose_sequential(name=opts, n=(2, 18), + desat=FloatSlider(min=0, max=1, value=1), + variant=variants): + if variant == "reverse": + name += "_r" + elif variant == "dark": + name += "_d" + + if as_cmap: + colors = color_palette(name, 256, desat) + _update_lut(cmap, np.c_[colors, np.ones(256)]) + _show_cmap(cmap) + else: + pal[:] = color_palette(name, n, desat) + palplot(pal) + + elif data_type.startswith("d"): + opts = ["RdBu", "RdGy", "PRGn", "PiYG", "BrBG", + "RdYlBu", "RdYlGn", "Spectral"] + variants = ["regular", "reverse"] + + @interact + def choose_diverging(name=opts, n=(2, 16), + desat=FloatSlider(min=0, max=1, value=1), + variant=variants): + if variant == "reverse": + name += "_r" + if as_cmap: + colors = color_palette(name, 256, desat) + _update_lut(cmap, np.c_[colors, np.ones(256)]) + _show_cmap(cmap) + else: + pal[:] = color_palette(name, n, desat) + palplot(pal) + + elif data_type.startswith("q"): + opts = ["Set1", "Set2", "Set3", "Paired", "Accent", + "Pastel1", "Pastel2", "Dark2"] + + @interact + def choose_qualitative(name=opts, n=(2, 16), + desat=FloatSlider(min=0, max=1, value=1)): + pal[:] = color_palette(name, n, desat) + palplot(pal) + + if as_cmap: + return cmap + return pal + + +def choose_dark_palette(input="husl", as_cmap=False): + """Launch an interactive widget to create a dark sequential palette. + + This corresponds with the :func:`dark_palette` function. This kind + of palette is good for data that range between relatively uninteresting + low values and interesting high values. + + Requires IPython 2+ and must be used in the notebook. + + Parameters + ---------- + input : {'husl', 'hls', 'rgb'} + Color space for defining the seed value. Note that the default is + different than the default input for :func:`dark_palette`. + as_cmap : bool + If True, the return value is a matplotlib colormap rather than a + list of discrete colors. + + Returns + ------- + pal or cmap : list of colors or matplotlib colormap + Object that can be passed to plotting functions. + + See Also + -------- + dark_palette : Create a sequential palette with dark low values. + light_palette : Create a sequential palette with bright low values. + cubehelix_palette : Create a sequential palette or colormap using the + cubehelix system. + + """ + pal = [] + if as_cmap: + cmap = _init_mutable_colormap() + + if input == "rgb": + @interact + def choose_dark_palette_rgb(r=(0., 1.), + g=(0., 1.), + b=(0., 1.), + n=(3, 17)): + color = r, g, b + if as_cmap: + colors = dark_palette(color, 256, input="rgb") + _update_lut(cmap, colors) + _show_cmap(cmap) + else: + pal[:] = dark_palette(color, n, input="rgb") + palplot(pal) + + elif input == "hls": + @interact + def choose_dark_palette_hls(h=(0., 1.), + l=(0., 1.), + s=(0., 1.), + n=(3, 17)): + color = h, l, s + if as_cmap: + colors = dark_palette(color, 256, input="hls") + _update_lut(cmap, colors) + _show_cmap(cmap) + else: + pal[:] = dark_palette(color, n, input="hls") + palplot(pal) + + elif input == "husl": + @interact + def choose_dark_palette_husl(h=(0, 359), + s=(0, 99), + l=(0, 99), + n=(3, 17)): + color = h, s, l + if as_cmap: + colors = dark_palette(color, 256, input="husl") + _update_lut(cmap, colors) + _show_cmap(cmap) + else: + pal[:] = dark_palette(color, n, input="husl") + palplot(pal) + + if as_cmap: + return cmap + return pal + + +def choose_light_palette(input="husl", as_cmap=False): + """Launch an interactive widget to create a light sequential palette. + + This corresponds with the :func:`light_palette` function. This kind + of palette is good for data that range between relatively uninteresting + low values and interesting high values. + + Requires IPython 2+ and must be used in the notebook. + + Parameters + ---------- + input : {'husl', 'hls', 'rgb'} + Color space for defining the seed value. Note that the default is + different than the default input for :func:`light_palette`. + as_cmap : bool + If True, the return value is a matplotlib colormap rather than a + list of discrete colors. + + Returns + ------- + pal or cmap : list of colors or matplotlib colormap + Object that can be passed to plotting functions. + + See Also + -------- + light_palette : Create a sequential palette with bright low values. + dark_palette : Create a sequential palette with dark low values. + cubehelix_palette : Create a sequential palette or colormap using the + cubehelix system. + + """ + pal = [] + if as_cmap: + cmap = _init_mutable_colormap() + + if input == "rgb": + @interact + def choose_light_palette_rgb(r=(0., 1.), + g=(0., 1.), + b=(0., 1.), + n=(3, 17)): + color = r, g, b + if as_cmap: + colors = light_palette(color, 256, input="rgb") + _update_lut(cmap, colors) + _show_cmap(cmap) + else: + pal[:] = light_palette(color, n, input="rgb") + palplot(pal) + + elif input == "hls": + @interact + def choose_light_palette_hls(h=(0., 1.), + l=(0., 1.), + s=(0., 1.), + n=(3, 17)): + color = h, l, s + if as_cmap: + colors = light_palette(color, 256, input="hls") + _update_lut(cmap, colors) + _show_cmap(cmap) + else: + pal[:] = light_palette(color, n, input="hls") + palplot(pal) + + elif input == "husl": + @interact + def choose_light_palette_husl(h=(0, 359), + s=(0, 99), + l=(0, 99), + n=(3, 17)): + color = h, s, l + if as_cmap: + colors = light_palette(color, 256, input="husl") + _update_lut(cmap, colors) + _show_cmap(cmap) + else: + pal[:] = light_palette(color, n, input="husl") + palplot(pal) + + if as_cmap: + return cmap + return pal + + +def choose_diverging_palette(as_cmap=False): + """Launch an interactive widget to choose a diverging color palette. + + This corresponds with the :func:`diverging_palette` function. This kind + of palette is good for data that range between interesting low values + and interesting high values with a meaningful midpoint. (For example, + change scores relative to some baseline value). + + Requires IPython 2+ and must be used in the notebook. + + Parameters + ---------- + as_cmap : bool + If True, the return value is a matplotlib colormap rather than a + list of discrete colors. + + Returns + ------- + pal or cmap : list of colors or matplotlib colormap + Object that can be passed to plotting functions. + + See Also + -------- + diverging_palette : Create a diverging color palette or colormap. + choose_colorbrewer_palette : Interactively choose palettes from the + colorbrewer set, including diverging palettes. + + """ + pal = [] + if as_cmap: + cmap = _init_mutable_colormap() + + @interact + def choose_diverging_palette(h_neg=IntSlider(min=0, + max=359, + value=220), + h_pos=IntSlider(min=0, + max=359, + value=10), + s=IntSlider(min=0, max=99, value=74), + l=IntSlider(min=0, max=99, value=50), + sep=IntSlider(min=1, max=50, value=10), + n=(2, 16), + center=["light", "dark"]): + if as_cmap: + colors = diverging_palette(h_neg, h_pos, s, l, sep, 256, center) + _update_lut(cmap, colors) + _show_cmap(cmap) + else: + pal[:] = diverging_palette(h_neg, h_pos, s, l, sep, n, center) + palplot(pal) + + if as_cmap: + return cmap + return pal + + +def choose_cubehelix_palette(as_cmap=False): + """Launch an interactive widget to create a sequential cubehelix palette. + + This corresponds with the :func:`cubehelix_palette` function. This kind + of palette is good for data that range between relatively uninteresting + low values and interesting high values. The cubehelix system allows the + palette to have more hue variance across the range, which can be helpful + for distinguishing a wider range of values. + + Requires IPython 2+ and must be used in the notebook. + + Parameters + ---------- + as_cmap : bool + If True, the return value is a matplotlib colormap rather than a + list of discrete colors. + + Returns + ------- + pal or cmap : list of colors or matplotlib colormap + Object that can be passed to plotting functions. + + See Also + -------- + cubehelix_palette : Create a sequential palette or colormap using the + cubehelix system. + + """ + pal = [] + if as_cmap: + cmap = _init_mutable_colormap() + + @interact + def choose_cubehelix(n_colors=IntSlider(min=2, max=16, value=9), + start=FloatSlider(min=0, max=3, value=0), + rot=FloatSlider(min=-1, max=1, value=.4), + gamma=FloatSlider(min=0, max=5, value=1), + hue=FloatSlider(min=0, max=1, value=.8), + light=FloatSlider(min=0, max=1, value=.85), + dark=FloatSlider(min=0, max=1, value=.15), + reverse=False): + + if as_cmap: + colors = cubehelix_palette(256, start, rot, gamma, + hue, light, dark, reverse) + _update_lut(cmap, np.c_[colors, np.ones(256)]) + _show_cmap(cmap) + else: + pal[:] = cubehelix_palette(n_colors, start, rot, gamma, + hue, light, dark, reverse) + palplot(pal) + + if as_cmap: + return cmap + return pal From bb49a1e05a1cc7437e3dc97a80af881875dd69d9 Mon Sep 17 00:00:00 2001 From: Brigitta Sipocz Date: Sat, 7 Nov 2015 00:15:47 +0000 Subject: [PATCH 0144/1738] Updating .travis to reflect recent miniconda changes --- .travis.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 070d5338ab..6c6df07fa3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,10 +28,15 @@ before_install: - sudo apt-get update -yq - sudo sh -c "echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections" - sudo apt-get install msttcorefonts -qq - - wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh - - chmod +x miniconda.sh - - ./miniconda.sh -b - - export PATH=/home/travis/miniconda/bin:$PATH + + # http://conda.pydata.org/docs/travis.html#the-travis-yml-file + - wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh + - bash miniconda.sh -b -p $HOME/miniconda + - export PATH="$HOME/miniconda/bin:$PATH" + - hash -r + - conda config --set always_yes yes --set changeps1 no + - conda update -q conda + - conda info -a before_script: - if [ ${PYTHON:0:1} == "2" ]; then From 9d301dd1770369187eb156134330a21aa8e9a3ee Mon Sep 17 00:00:00 2001 From: Olga Botvinnik Date: Mon, 9 Nov 2015 11:03:10 -0800 Subject: [PATCH 0145/1738] remove color_list from dendrogram call --- seaborn/matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index c7e054d238..ca8426a8c9 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -574,7 +574,7 @@ def calculate_dendrogram(self): "reordered_ind" which indicates the re-ordering of the matrix """ return hierarchy.dendrogram(self.linkage, no_plot=True, - color_list=['k'], color_threshold=-np.inf) + color_threshold=-np.inf) @property def reordered_ind(self): From 2053b95d9374003363917520ef61abc4919c653a Mon Sep 17 00:00:00 2001 From: Julien Rebetez Date: Mon, 16 Nov 2015 17:29:34 +0100 Subject: [PATCH 0146/1738] If 'rasterized' is passed to heatmap, also rasterize the colorbar. This fixes white lines appearing in the rendered PDF. See #373 --- seaborn/matrix.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index ca8426a8c9..a5d821f65e 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -253,6 +253,10 @@ def plot(self, ax, cax, kws): cb = ax.figure.colorbar(mesh, cax, ax, ticks=ticker, **self.cbar_kws) cb.outline.set_linewidth(0) + # If rasterized is passed to pcolormesh, also rasterize the + # colorbar to avoid white lines on the PDF rendering + if kws.get('rasterized', False): + cb.solids.set_rasterized(True) def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, From 41d234f3b7208bc35ec9f6cf0e632617fc5192e7 Mon Sep 17 00:00:00 2001 From: "John C. Earls" Date: Tue, 17 Nov 2015 04:06:48 +0000 Subject: [PATCH 0147/1738] Force number of bins to be an integer. --- seaborn/distributions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 687e6d83a0..5762d6ffe2 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -27,9 +27,9 @@ def _freedman_diaconis_bins(a): h = 2 * iqr(a) / (len(a) ** (1 / 3)) # fall back to sqrt(a) bins if iqr is 0 if h == 0: - return np.sqrt(a.size) + return int(np.sqrt(a.size)) else: - return np.ceil((a.max() - a.min()) / h) + return int(np.ceil((a.max() - a.min()) / h)) def distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, From 6c6df3d6aeb3bca5d0b7685619f7940060cbba6c Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 19 Nov 2015 08:11:00 -0800 Subject: [PATCH 0148/1738] Fix broken example link in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c0cba69a30..eb27f0ddb7 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ Seaborn: statistical data visualization - - + +
From 57305d93989147cb53177bdd870831f62dc30771 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Fri, 20 Nov 2015 19:41:43 -0800 Subject: [PATCH 0149/1738] AxesStyle & PlottingContext as contextdecorators. cf #774. --- seaborn/rcmod.py | 58 +++++++++++++++++++------------------ seaborn/tests/test_rcmod.py | 18 ++++++++++-- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index ebf02a3045..9bb997f8fb 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -1,5 +1,6 @@ """Functions that alter the matplotlib rc dictionary on the fly.""" from distutils.version import LooseVersion +import functools import numpy as np import matplotlib as mpl @@ -114,34 +115,6 @@ def reset_orig(): mpl.rcParams.update(mpl.rcParamsOrig) -class _AxesStyle(dict): - """Light wrapper on a dict to set style temporarily.""" - def __enter__(self): - """Open the context.""" - rc = mpl.rcParams - self._orig_style = {k: rc[k] for k in _style_keys} - set_style(self) - return self - - def __exit__(self, *args): - """Close the context.""" - set_style(self._orig_style) - - -class _PlottingContext(dict): - """Light wrapper on a dict to set context temporarily.""" - def __enter__(self): - """Open the context.""" - rc = mpl.rcParams - self._orig_context = {k: rc[k] for k in _context_keys} - set_context(self) - return self - - def __exit__(self, *args): - """Close the context.""" - set_context(self._orig_context) - - def axes_style(style=None, rc=None): """Return a parameter dict for the aesthetic style of the plots. @@ -460,6 +433,35 @@ def set_context(context=None, font_scale=1, rc=None): mpl.rcParams.update(context_object) +class _StyleOrContext(dict): + def __enter__(self): + rc = mpl.rcParams + self._orig = {k: rc[k] for k in self._keys} + type(self)._set(self) + + def __exit__(self, exc_type, exc_value, exc_tb): + type(self)._set(self._orig) + + def __call__(self, func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + with self: + return func(*args, **kwargs) + return wrapper + + +class _AxesStyle(_StyleOrContext): + """Light wrapper on a dict to set style temporarily.""" + _keys = _style_keys + _set = set_style + + +class _PlottingContext(_StyleOrContext): + """Light wrapper on a dict to set context temporarily.""" + _keys = _context_keys + _set = set_context + + def set_palette(palette, n_colors=None, desat=None, color_codes=False): """Set the matplotlib color cycle using a seaborn palette. diff --git a/seaborn/tests/test_rcmod.py b/seaborn/tests/test_rcmod.py index b2d11fa305..73f13ee4ba 100644 --- a/seaborn/tests/test_rcmod.py +++ b/seaborn/tests/test_rcmod.py @@ -69,11 +69,18 @@ def test_style_context_manager(self): rcmod.set_style("darkgrid") orig_params = rcmod.axes_style() + context_params = rcmod.axes_style("whitegrid") + with rcmod.axes_style("whitegrid"): - context_params = rcmod.axes_style("whitegrid") self.assert_rc_params(context_params) self.assert_rc_params(orig_params) + @rcmod.axes_style("whitegrid") + def func(): + self.assert_rc_params(context_params) + func() + self.assert_rc_params(orig_params) + def test_style_context_independence(self): nt.assert_true(set(rcmod._style_keys) ^ set(rcmod._context_keys)) @@ -159,11 +166,18 @@ def test_context_context_manager(self): rcmod.set_context("notebook") orig_params = rcmod.plotting_context() + context_params = rcmod.plotting_context("paper") + with rcmod.plotting_context("paper"): - context_params = rcmod.plotting_context("paper") self.assert_rc_params(context_params) self.assert_rc_params(orig_params) + @rcmod.plotting_context("paper") + def func(): + self.assert_rc_params(context_params) + func() + self.assert_rc_params(orig_params) + class TestFonts(PlotTestCase): From 1f79004a94085801ea59fcb3961b2a97f25deffa Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sat, 21 Nov 2015 10:45:29 -0800 Subject: [PATCH 0150/1738] Minor fixes following code review. Rename base class to _RCAesthetics; use staticmethods. --- seaborn/rcmod.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index 9bb997f8fb..12d0269a05 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -433,14 +433,14 @@ def set_context(context=None, font_scale=1, rc=None): mpl.rcParams.update(context_object) -class _StyleOrContext(dict): +class _RCAesthetics(dict): def __enter__(self): rc = mpl.rcParams self._orig = {k: rc[k] for k in self._keys} - type(self)._set(self) + self._set(self) def __exit__(self, exc_type, exc_value, exc_tb): - type(self)._set(self._orig) + self._set(self._orig) def __call__(self, func): @functools.wraps(func) @@ -450,16 +450,16 @@ def wrapper(*args, **kwargs): return wrapper -class _AxesStyle(_StyleOrContext): +class _AxesStyle(_RCAesthetics): """Light wrapper on a dict to set style temporarily.""" _keys = _style_keys - _set = set_style + _set = staticmethod(set_style) -class _PlottingContext(_StyleOrContext): +class _PlottingContext(_RCAesthetics): """Light wrapper on a dict to set context temporarily.""" _keys = _context_keys - _set = set_context + _set = staticmethod(set_context) def set_palette(palette, n_colors=None, desat=None, color_codes=False): From 7a9577e36e4114cae7ee7e43f2472624a3f8a6f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20P=C3=B6lsterl?= Date: Wed, 25 Nov 2015 11:40:23 +0100 Subject: [PATCH 0151/1738] Use facecolor instead color keyword argument to avoid random behaviour See https://github.com/matplotlib/matplotlib/issues/5423 --- seaborn/categorical.py | 2 +- seaborn/distributions.py | 4 ++-- seaborn/linearmodels.py | 2 +- seaborn/timeseries.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 0999161d66..e3ab4a4a47 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -797,7 +797,7 @@ def draw_violins(self, ax): fill_func(support, grid - density * self.dwidth, grid + density * self.dwidth, - color=self.colors[i], + facecolor=self.colors[i], **kws) # Draw the interior representation of the data diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 5762d6ffe2..e0a4029cec 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -306,9 +306,9 @@ def _univariate_kdeplot(data, shade, vertical, kernel, bw, gridsize, cut, alpha = kwargs.get("alpha", 0.25) if shade: if vertical: - ax.fill_betweenx(y, 1e-12, x, color=color, alpha=alpha) + ax.fill_betweenx(y, 1e-12, x, facecolor=color, alpha=alpha) else: - ax.fill_between(x, 1e-12, y, color=color, alpha=alpha) + ax.fill_between(x, 1e-12, y, facecolor=color, alpha=alpha) # Draw the legend here if legend: diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index d59596d47c..bfb0dd5796 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -376,7 +376,7 @@ def lineplot(self, ax, kws): # Draw the regression line and confidence interval ax.plot(grid, yhat, **kws) if err_bands is not None: - ax.fill_between(grid, *err_bands, color=fill_color, alpha=.15) + ax.fill_between(grid, *err_bands, facecolor=fill_color, alpha=.15) ax.set_xlim(*xlim) diff --git a/seaborn/timeseries.py b/seaborn/timeseries.py index 2d0299d986..a2c4dfc932 100644 --- a/seaborn/timeseries.py +++ b/seaborn/timeseries.py @@ -350,7 +350,7 @@ def _plot_ci_band(ax, x, ci, color, err_kws, **kwargs): low, high = ci if "alpha" not in err_kws: err_kws["alpha"] = 0.2 - ax.fill_between(x, low, high, color=color, **err_kws) + ax.fill_between(x, low, high, facecolor=color, **err_kws) def _plot_ci_bars(ax, x, central_data, ci, color, err_kws, **kwargs): From 519581afdbf6af161c6682ff44d2d0f6cb6ffc9d Mon Sep 17 00:00:00 2001 From: Yoav Ram Date: Mon, 30 Nov 2015 20:15:56 +0200 Subject: [PATCH 0152/1738] typo --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 909913222e..211e6c4dda 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,4 +35,4 @@ Seaborn is primarily tested through a `nose` unit-test suite that interacts with To execute the test suite and doctests, run `make test` in the root source directory. You can also build a test coverage report with `make coverage`. -The `make lint` command will run `pep8` and `pyflakes` over the codebase to check for style issues. Doing so requires [this](https://github.com/dcramer/pyflakes) fork of pyflakes, which can be installed with `pip install https://github.com/dcramer/pyflakes/tarball/master`. Is also currently requires `pep8` 1.5 or older, as the rules got stricter and the codebase has not been updated. This is part of the Travis build, and the build will fail if there are issues, so please do this before submitting a pull request. +The `make lint` command will run `pep8` and `pyflakes` over the codebase to check for style issues. Doing so requires [this](https://github.com/dcramer/pyflakes) fork of pyflakes, which can be installed with `pip install https://github.com/dcramer/pyflakes/tarball/master`. It also currently requires `pep8` 1.5 or older, as the rules got stricter and the codebase has not been updated. This is part of the Travis build, and the build will fail if there are issues, so please do this before submitting a pull request. From db7c397d5dc22047d8f336b63c5869342da94419 Mon Sep 17 00:00:00 2001 From: Yoav Ram Date: Mon, 30 Nov 2015 15:52:41 +0200 Subject: [PATCH 0153/1738] Force color of plot in grid diagonal --- seaborn/axisgrid.py | 9 +++++++-- seaborn/tests/test_axisgrid.py | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 81440f54ef..ea4e5a7f19 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1309,6 +1309,7 @@ def map_diag(self, func, **kwargs): self.diag_axes = np.array(diag_axes, np.object) # Plot on each of the diagonal axes + color = kwargs.pop('color', None) for i, var in enumerate(self.x_vars): ax = self.diag_axes[i] hue_grouped = self.data[var].groupby(self.hue_vals) @@ -1323,7 +1324,9 @@ def map_diag(self, func, **kwargs): vals.append(np.asarray(hue_grouped.get_group(label))) except KeyError: vals.append(np.array([])) - func(vals, color=self.palette, histtype="barstacked", + if color is None: + color = self.palette + func(vals, color=color, histtype="barstacked", **kwargs) else: for k, label_k in enumerate(self.hue_names): @@ -1333,8 +1336,10 @@ def map_diag(self, func, **kwargs): except KeyError: data_k = np.array([]) plt.sca(ax) + if color is None: + color = self.palette[k] func(data_k, label=label_k, - color=self.palette[k], **kwargs) + color=color, **kwargs) self._clean_axis(ax) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 83875968ed..b3772685d5 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -780,6 +780,29 @@ def test_map_diag(self): for ax in g3.diag_axes: nt.assert_equal(len(ax.patches), 40) + @skipif(old_matplotlib) + def test_map_diag_color(self): + color_set = {mpl.colors.cnames[x].lower() + for x in ['red', 'white', 'black']} + + g1 = ag.PairGrid(self.df) + g1.map_diag(plt.hist, color='red') + + for ax in g1.diag_axes: + colors = [mpl.colors.rgb2hex(patch.get_facecolor()).lower() + for patch in ax.patches] + for color in colors: + nt.assert_true(color in color_set, color) + + g2 = ag.PairGrid(self.df) + g2.map_diag(kdeplot, color='red') + + for ax in g2.diag_axes: + colors = [mpl.colors.rgb2hex(patch.get_facecolor()).lower() + for patch in ax.patches] + for color in colors: + nt.assert_true(color in color_set, color) + @skipif(old_matplotlib) def test_map_diag_and_offdiag(self): From 265fbc74c30379e149a8435bb54a35a45c4e1385 Mon Sep 17 00:00:00 2001 From: Isaac Schwabacher Date: Fri, 4 Dec 2015 12:04:42 -0600 Subject: [PATCH 0154/1738] PERF: Speed up test for empty facet (trivial) Using the cached `size` property on an `ndarray` must be faster than converting the array to a list, and it's more obvious what's going on to boot. I can't imagine this makes much difference, but it might speed up plotting large datasets. --- seaborn/axisgrid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 81440f54ef..2344366c6b 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -695,7 +695,7 @@ def map(self, func, *args, **kwargs): for (row_i, col_j, hue_k), data_ijk in self.facet_data(): # If this subset is null, move on - if not data_ijk.values.tolist(): + if not data_ijk.values.size: continue # Get the current axis @@ -767,7 +767,7 @@ def map_dataframe(self, func, *args, **kwargs): for (row_i, col_j, hue_k), data_ijk in self.facet_data(): # If this subset is null, move on - if not data_ijk.values.tolist(): + if not data_ijk.values.size: continue # Get the current axis From f59c0579849954b948bc5cf7d89b9e741abeea7d Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 12 Dec 2015 09:58:33 -0800 Subject: [PATCH 0155/1738] Use hex colors in scatter to avoid 3-point bug --- seaborn/categorical.py | 28 ++++++++++++++-------------- seaborn/tests/test_categorical.py | 6 ++++++ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index e3ab4a4a47..cf557c6e51 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -15,7 +15,7 @@ from .external.six.moves import range from . import utils -from .utils import desaturate, iqr, categorical_order +from .utils import iqr, categorical_order from .algorithms import bootstrap from .palettes import color_palette, husl_palette, light_palette from .axisgrid import FacetGrid, _facet_docs @@ -290,20 +290,20 @@ def establish_colors(self, color, palette, saturation): colors = color_palette(palette, n_colors) - # Conver the colors to a common rgb representation - colors = [mpl.colors.colorConverter.to_rgb(c) for c in colors] - # Desaturate a bit because these are patches if saturation < 1: - colors = [desaturate(c, saturation) for c in colors] + colors = color_palette(colors, desat=saturation) + + # Conver the colors to a common representations + rgb_colors = color_palette(colors) # Determine the gray color to use for the lines framing the plot - light_vals = [colorsys.rgb_to_hls(*c)[1] for c in colors] + light_vals = [colorsys.rgb_to_hls(*c)[1] for c in rgb_colors] l = min(light_vals) * .6 - gray = (l, l, l) + gray = mpl.colors.rgb2hex((l, l, l)) # Assign object attributes - self.colors = colors + self.colors = rgb_colors self.gray = gray def infer_orient(self, x, y, orient=None): @@ -1010,7 +1010,7 @@ def draw_quartiles(self, ax, data, support, density, center, split=False): def draw_points(self, ax, data, center): """Draw individual observations as points at middle of the violin.""" kws = dict(s=np.square(self.linewidth * 2), - c=self.gray, + color=self.gray, edgecolor=self.gray) grid = np.ones(len(data)) * center @@ -1088,7 +1088,7 @@ def draw_stripplot(self, ax, kws): # Determine the positions of the points strip_data = remove_na(group_data) jitter = self.jitterer(len(strip_data)) - kws["color"] = self.colors[i] + kws["color"] = mpl.colors.rgb2hex(self.colors[i]) # Draw the plot if self.orient == "v": @@ -1107,7 +1107,7 @@ def draw_stripplot(self, ax, kws): strip_data = remove_na(group_data[hue_mask]) pos = i + offsets[j] if self.split else i jitter = self.jitterer(len(strip_data)) - kws["color"] = self.colors[j] + kws["color"] = mpl.colors.rgb2hex(self.colors[j]) # Only label one set of plots if i: @@ -1587,17 +1587,17 @@ def horz_perc_box(x, b, i, k, w): # Plot the medians ax.plot([x - widths / 2, x + widths / 2], [y, y], - c='k', alpha=.45, **kws) + c='.15', alpha=.45, **kws) ax.scatter(np.repeat(x, len(outliers)), outliers, - marker='d', c=color, **kws) + marker='d', c=mpl.colors.rgb2hex(color), **kws) else: boxes = [horz_perc_box(x, b[0], i, k, b[1]) for i, b in enumerate(zip(box_ends, w_area))] # Plot the medians ax.plot([y, y], [x - widths / 2, x + widths / 2], - c='k', alpha=.45, **kws) + c='.15', alpha=.45, **kws) ax.scatter(outliers, np.repeat(x, len(outliers)), marker='d', c=color, **kws) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 8aa5ba2b45..426744ca3d 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1525,6 +1525,12 @@ def test_unsplit_nested_stripplot_horizontal(self): npt.assert_array_equal(x, vals) npt.assert_array_equal(y, np.ones(len(x)) * i) + def test_three_strip_points(self): + + x = np.arange(3) + ax = cat.stripplot(x=x) + nt.assert_equal(ax.collections[0].get_facecolor().shape, (1, 4)) + class TestBarPlotter(CategoricalFixture): From 3f20d609bce6cb7f936e3d13d5add7a57cbaf376 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 12 Dec 2015 10:05:21 -0800 Subject: [PATCH 0156/1738] Move come code around to avoid numpy future warning --- seaborn/categorical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index cf557c6e51..0dc1c8fe70 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -461,7 +461,6 @@ def draw_boxplot(self, ax, kws): # Draw nested groups of boxes offsets = self.hue_offsets for j, hue_level in enumerate(self.hue_names): - hue_mask = self.plot_hues[i] == hue_level # Add a legend for this hue level if not i: @@ -471,6 +470,7 @@ def draw_boxplot(self, ax, kws): if group_data.size == 0: continue + hue_mask = self.plot_hues[i] == hue_level box_data = remove_na(group_data[hue_mask]) # Handle case where there is no non-null data @@ -1650,7 +1650,6 @@ def draw_letter_value_plot(self, ax, kws): # Draw nested groups of boxes offsets = self.hue_offsets for j, hue_level in enumerate(self.hue_names): - hue_mask = self.plot_hues[i] == hue_level # Add a legend for this hue level if not i: @@ -1660,6 +1659,7 @@ def draw_letter_value_plot(self, ax, kws): if group_data.size == 0: continue + hue_mask = self.plot_hues[i] == hue_level box_data = remove_na(group_data[hue_mask]) # Handle case where there is no non-null data From 5a534bb1a613831d4a5c1c42a6fc53135e2e4071 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 12 Dec 2015 17:12:41 -0800 Subject: [PATCH 0157/1738] Fix doc typo (closes #773) --- seaborn/axisgrid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 2344366c6b..8268e47ec1 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -179,7 +179,7 @@ def _get_palette(self, data, hue, hue_order, palette): span multiple rows. Incompatible with a ``row`` facet.\ """), share_xy=dedent("""\ - share_{x,y} : bool, optional + share{x,y} : bool, optional If true, the facets will share y axes across columns and/or x axes across rows.\ """), From 328ddc4e0dba2cb4f3c5373be3f94c593ff9ced9 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 12 Dec 2015 17:18:29 -0800 Subject: [PATCH 0158/1738] Fix rst formatting (closes #769) --- doc/tutorial/categorical.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorial/categorical.ipynb b/doc/tutorial/categorical.ipynb index 630cdf769d..644d82a512 100644 --- a/doc/tutorial/categorical.ipynb +++ b/doc/tutorial/categorical.ipynb @@ -198,7 +198,7 @@ "Violinplots\n", "^^^^^^^^^^^\n", "\n", - "A different approach is a :func:`violinplot`, which combines a boxplot with the kernel density estimation procedure described in the :ref:`distributions tutorial `:" + "A different approach is a :func:`violinplot`, which combines a boxplot with the kernel density estimation procedure described in the :ref:`distributions ` tutorial:" ] }, { @@ -570,4 +570,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} From e2d722ab3fbf5d9bf1a212525fa3f7707f0ff359 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 12 Dec 2015 17:19:41 -0800 Subject: [PATCH 0159/1738] Fix colormap article link (closes #693) --- doc/tutorial/color_palettes.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index 4b347e08b9..52edac4594 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -24,7 +24,7 @@ "raw_mimetype": "text/restructuredtext" }, "source": [ - "Color is more important than other aspects of figure style because color can reveal patterns in the data if used effectively or hide those patterns if used poorly. There are a number of great resources to learn about good techniques for using color in visualizations, I am partial to this `series of blog posts `_ from Rob Simmon and this `more technical paper `_. The matplotlib docs also now have a `nice tutorial `_ that illustrates some of the perceptual properties of the built in colormaps.\n", + "Color is more important than other aspects of figure style because color can reveal patterns in the data if used effectively or hide those patterns if used poorly. There are a number of great resources to learn about good techniques for using color in visualizations, I am partial to this `series of blog posts `_ from Rob Simmon and this `more technical paper `_. The matplotlib docs also now have a `nice tutorial `_ that illustrates some of the perceptual properties of the built in colormaps.\n", "\n", "Seaborn makes it easy to select and use color palettes that are suited to the kind of data you are working with and the goals you have in visualizing it." ] @@ -778,4 +778,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} From 3da93e26489d92f042b5fb9756ea1aab56669406 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 13 Dec 2015 17:52:26 -0800 Subject: [PATCH 0160/1738] Add basic swarm plotting code --- seaborn/categorical.py | 93 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 0dc1c8fe70..29b5fe47af 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1129,12 +1129,103 @@ def plot(self, ax, kws): ax.invert_yaxis() -class _SwarmPlotter(_BoxPlotter): +class _SwarmPlotter(_CategoricalPlotter): def __init__(self): pass + def overlap(self, xy_i, xy_j, d): + """Return True if two circles with the same diameter will overlap.""" + x_i, y_i = xy_i + x_j, y_j = xy_j + return np.linalg.norm([x_i - x_j, y_i - y_j]) < d + + def could_overlap(self, xy_i, swarm, d): + """Return a list of all swarm points that could overlap with target.""" + _, y_i = xy_i + neighbors = [] + for xy_j in swarm: + _, y_j = xy_j + if (y_i - y_j) < d: + neighbors.append(xy_j) + return neighbors + + def position_candidates(self, xy_i, neighbors, d): + """Return a list of (x, y) coordinates that might be valid.""" + candidates = [xy_i] + x_i, y_i = xy_i + for x_j, y_j in neighbors: + dy = y_i - y_j + dx = np.sqrt(d ** 2 - dy ** 2) * 1.05 + candidates.extend([(x_j - dx, y_i), (x_j + dx, y_i)]) + + return candidates + + def prune_candidates(self, candidates, neighbors, d): + """Remove candidates from the list of they overlap with the swarm.""" + good_candidates = [] + for xy_i in candidates: + good_candidate = True + for xy_j in neighbors: + if self.overlap(xy_i, xy_j, d): + good_candidate = False + if good_candidate: + good_candidates.append(xy_i) + assert good_candidates + return np.array(good_candidates) + + def plot_level(self, ax, x, y, s=30, **kws): + + # Sort the data so later steps are easier + y = np.sort(y) + + # Plot the data and set the xlim so that + # we can get a meaningful transformation + # from data to point coordinates + c = ax.scatter([x] * len(y), y, s=s, **kws) + + # Convert from point size (area) to diameter + default_lw = mpl.rcParams["patch.linewidth"] + lw = kws.get("linewidth", kws.get("lw", default_lw)) + d = np.sqrt(s) + lw + + # Transform the data coordinates to point coordinates. + # We'll figure out the swarm positions in the latter + # and then convert back and replot + orig_xy = ax.transData.transform(c.get_offsets()) + center = orig_xy[0, 0] + + # Start the swarm with the first point + swarm = [orig_xy[0]] + + # Loop over the remaining points + for xy_i in orig_xy[1:]: + + # Find the points in the swarm that could possibly + # overlap with the point we are currently placing + neighbors = self.could_overlap(xy_i, swarm, d) + + # Find positions that would be valid individually + # with respect to each of the swarm neighbors + candidates = self.position_candidates(xy_i, neighbors, d) + + # Remove the positions that overlap with any of the + # other neighbors + candidates = self.prune_candidates(candidates, neighbors, d) + + # Find the most central of the remaining positions + offsets = np.abs(candidates[:, 0] - center) + best_index = np.argmin(offsets) + new_xy_i = candidates[best_index] + swarm.append(new_xy_i) + + # Transform the point coordinates back to data coordinates + new_xy = ax.transData.inverted().transform(swarm) + + # Reposition the points so they do not overlap + c.set_offsets(new_xy) + def plot(self, ax): pass From e95a59ee606f0c8ce10604d477341112aee247be Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 14 Dec 2015 08:31:02 -0800 Subject: [PATCH 0161/1738] Add flexible orientation and gutters --- seaborn/categorical.py | 85 +++++++++++++++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 17 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 29b5fe47af..48401044ad 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1175,16 +1175,24 @@ def prune_candidates(self, candidates, neighbors, d): assert good_candidates return np.array(good_candidates) - def plot_level(self, ax, x, y, s=30, **kws): - - # Sort the data so later steps are easier - y = np.sort(y) - - # Plot the data and set the xlim so that - # we can get a meaningful transformation - # from data to point coordinates - c = ax.scatter([x] * len(y), y, s=s, **kws) - + def add_gutters(self, points, center): + """Stop points from extending beyond their territory.""" + half_width = self.width / 2 + low_gutter = center - half_width + off_low = points < low_gutter + if off_low.any(): + points[off_low] = low_gutter + high_gutter = center + half_width + off_high = points > high_gutter + if off_high.any(): + points[off_high] = high_gutter + + def swarm_points(self, ax, points, center, s, **kws): + """Find new positions on the categorical axis for each point. + + In this method, ``x`` means the categorical axis and ``y`` means the + data axis. + """ # Convert from point size (area) to diameter default_lw = mpl.rcParams["patch.linewidth"] lw = kws.get("linewidth", kws.get("lw", default_lw)) @@ -1193,8 +1201,15 @@ def plot_level(self, ax, x, y, s=30, **kws): # Transform the data coordinates to point coordinates. # We'll figure out the swarm positions in the latter # and then convert back and replot - orig_xy = ax.transData.transform(c.get_offsets()) - center = orig_xy[0, 0] + orig_xy = ax.transData.transform(points.get_offsets()) + + # Order the variables so that x is the caegorical axis + if self.orient == "v": + orig_x, orig_y = orig_xy.T + else: + orig_y, orig_x = orig_xy.T + orig_xy = np.c_[orig_x, orig_y] + midline = orig_x[0] # Start the swarm with the first point swarm = [orig_xy[0]] @@ -1215,20 +1230,56 @@ def plot_level(self, ax, x, y, s=30, **kws): candidates = self.prune_candidates(candidates, neighbors, d) # Find the most central of the remaining positions - offsets = np.abs(candidates[:, 0] - center) + offsets = np.abs(candidates[:, 0] - midline) best_index = np.argmin(offsets) new_xy_i = candidates[best_index] swarm.append(new_xy_i) # Transform the point coordinates back to data coordinates - new_xy = ax.transData.inverted().transform(swarm) + swarm = np.array(swarm) + if self.orient == "v": + new_xy = swarm[:, [0, 1]] + else: + new_xy = swarm[:, [1, 0]] + new_x, new_y = ax.transData.inverted().transform(new_xy).T + + # Add gutters + if self.orient == "v": + self.add_gutters(new_x, center) + else: + self.add_gutters(new_y, center) # Reposition the points so they do not overlap - c.set_offsets(new_xy) + points.set_offsets(np.c_[new_x, new_y]) - def plot(self, ax): + def draw_swarmplot(self, ax, kws): - pass + swarms = [] + for i, group_data in enumerate(self.plot_data): + + swarm_data = remove_na(group_data) + + # TODO Will have to be careful to account for hue data + swarm_data = np.sort(swarm_data) + + cat_pos = np.ones(swarm_data.size) * i + kws.update(color=self.colors[i]) + if self.orient == "v": + points = ax.scatter(cat_pos, swarm_data, **kws) + else: + points = ax.scatter(swarm_data, cat_pos, **kws) + swarms.append(points) + + s = kws.get("s", 7 ** 2) + for i, swarm in enumerate(swarms): + self.swarm_points(ax, swarm, i, s, **kws) + + def plot(self, ax, **kws): + + self.draw_swarmplot(ax, kws) + self.annotate_axes(ax) + if self.orient == "h": + ax.invert_yaxis() class _CategoricalStatPlotter(_CategoricalPlotter): From 115a925d2260b7619910dc6eb2b28873f9f97318 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 14 Dec 2015 21:39:30 -0800 Subject: [PATCH 0162/1738] Add nested hue plotting, currently without a split --- seaborn/categorical.py | 67 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 48401044ad..d0c615468f 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1057,7 +1057,45 @@ def plot(self, ax): ax.invert_yaxis() -class _StripPlotter(_CategoricalPlotter): +class _CategoricalScatterPlotter(_CategoricalPlotter): + + @property + def point_colors(self): + """Return a color for each scatter point based on group and hue.""" + colors = [] + for i, group_data in enumerate(self.plot_data): + + # Initialize the array for this group level + group_colors = np.empty((group_data.size, 3)) + + if self.plot_hues is None: + + # Use the same color for all points at this level + group_color = self.colors[i] + group_colors[:] = group_color + + else: + + # Color the points based on the hue level + for j, level in enumerate(self.hue_names): + hue_color = self.colors[j] + group_colors[self.plot_hues[i] == level] = hue_color + + colors.append(group_colors) + + return colors + + def add_legend_data(self, ax): + + if self.hue_names is not None: + for rgb, label in zip(self.colors, self.hue_names): + ax.scatter([], [], + color=mpl.colors.rgb2hex(rgb), + label=label, + s=60) + + +class _StripPlotter(_CategoricalScatterPlotter): """1-d scatterplot with categorical organization.""" def __init__(self, x, y, hue, data, order, hue_order, jitter, split, orient, color, palette): @@ -1129,7 +1167,7 @@ def plot(self, ax, kws): ax.invert_yaxis() -class _SwarmPlotter(_CategoricalPlotter): +class _SwarmPlotter(_CategoricalScatterPlotter): def __init__(self): @@ -1254,29 +1292,42 @@ def swarm_points(self, ax, points, center, s, **kws): def draw_swarmplot(self, ax, kws): + s = kws.pop("s", 7 ** 2) + swarms = [] + + # Set the categorical axes limits here for the swarm math + if self.orient == "v": + ax.set_xlim(-.5, len(self.plot_data) - .5) + else: + ax.set_ylim(-.5, len(self.plot_data) - .5) + + # Plot each swarm for i, group_data in enumerate(self.plot_data): swarm_data = remove_na(group_data) - # TODO Will have to be careful to account for hue data - swarm_data = np.sort(swarm_data) + sorter = np.argsort(swarm_data) + swarm_data = swarm_data[sorter] + point_colors = self.point_colors[i][sorter] cat_pos = np.ones(swarm_data.size) * i - kws.update(color=self.colors[i]) + kws.update(c=point_colors) if self.orient == "v": - points = ax.scatter(cat_pos, swarm_data, **kws) + points = ax.scatter(cat_pos, swarm_data, s=s, **kws) else: - points = ax.scatter(swarm_data, cat_pos, **kws) + points = ax.scatter(swarm_data, cat_pos, s=s, **kws) swarms.append(points) - s = kws.get("s", 7 ** 2) + # Update the position of each point on the categorical axis + # Do this after plotting so that the numerical axis limits are correct for i, swarm in enumerate(swarms): self.swarm_points(ax, swarm, i, s, **kws) def plot(self, ax, **kws): self.draw_swarmplot(ax, kws) + self.add_legend_data(ax) self.annotate_axes(ax) if self.orient == "h": ax.invert_yaxis() From 341e2c43faac551f279fdbf1379e5725b768d9ab Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 15 Dec 2015 19:39:37 -0800 Subject: [PATCH 0163/1738] Add tests for abstract categorical scatterplot code --- seaborn/categorical.py | 18 +++++----- seaborn/tests/test_categorical.py | 59 +++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index d0c615468f..4c80745ec8 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1086,7 +1086,7 @@ def point_colors(self): return colors def add_legend_data(self, ax): - + """Add empty scatterplot artists with labels for the legend.""" if self.hue_names is not None: for rgb, label in zip(self.colors, self.hue_names): ax.scatter([], [], @@ -1213,9 +1213,9 @@ def prune_candidates(self, candidates, neighbors, d): assert good_candidates return np.array(good_candidates) - def add_gutters(self, points, center): + def add_gutters(self, points, center, width): """Stop points from extending beyond their territory.""" - half_width = self.width / 2 + half_width = width / 2 low_gutter = center - half_width off_low = points < low_gutter if off_low.any(): @@ -1225,7 +1225,7 @@ def add_gutters(self, points, center): if off_high.any(): points[off_high] = high_gutter - def swarm_points(self, ax, points, center, s, **kws): + def swarm_points(self, ax, points, center, width, s, **kws): """Find new positions on the categorical axis for each point. In this method, ``x`` means the categorical axis and ``y`` means the @@ -1238,7 +1238,7 @@ def swarm_points(self, ax, points, center, s, **kws): # Transform the data coordinates to point coordinates. # We'll figure out the swarm positions in the latter - # and then convert back and replot + # and then convert back to data coordinates and replot orig_xy = ax.transData.transform(points.get_offsets()) # Order the variables so that x is the caegorical axis @@ -1247,6 +1247,8 @@ def swarm_points(self, ax, points, center, s, **kws): else: orig_y, orig_x = orig_xy.T orig_xy = np.c_[orig_x, orig_y] + + # Center of the swarm, in point (not data) coordinates midline = orig_x[0] # Start the swarm with the first point @@ -1283,9 +1285,9 @@ def swarm_points(self, ax, points, center, s, **kws): # Add gutters if self.orient == "v": - self.add_gutters(new_x, center) + self.add_gutters(new_x, center, width) else: - self.add_gutters(new_y, center) + self.add_gutters(new_y, center, width) # Reposition the points so they do not overlap points.set_offsets(np.c_[new_x, new_y]) @@ -1322,7 +1324,7 @@ def draw_swarmplot(self, ax, kws): # Update the position of each point on the categorical axis # Do this after plotting so that the numerical axis limits are correct for i, swarm in enumerate(swarms): - self.swarm_points(ax, swarm, i, s, **kws) + self.swarm_points(ax, swarm, i, self.width, s, **kws) def plot(self, ax, **kws): diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 426744ca3d..8aee382dd8 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1417,6 +1417,65 @@ def test_violinplots(self): plt.close("all") +class TestCategoricalScatterPlotter(CategoricalFixture): + + def test_group_point_colors(self): + + p = cat._CategoricalScatterPlotter() + + p.establish_variables(x="g", y="y", data=self.df) + p.establish_colors(None, "deep", 1) + + point_colors = p.point_colors + nt.assert_equal(len(point_colors), self.g.unique().size) + deep_colors = palettes.color_palette("deep", self.g.unique().size) + + for i, group_colors in enumerate(point_colors): + nt.assert_equal(tuple(deep_colors[i]), tuple(group_colors[0])) + for channel in group_colors.T: + nt.assert_equals(np.unique(channel).size, 1) + + def test_hue_point_colors(self): + + p = cat._CategoricalScatterPlotter() + + hue_order = ["m", "n"] + p.establish_variables(x="g", y="y", hue="h", + hue_order=hue_order, data=self.df) + p.establish_colors(None, "deep", 1) + + point_colors = p.point_colors + nt.assert_equal(len(point_colors), self.g.unique().size) + deep_colors = palettes.color_palette("deep", self.h.unique().size) + + for i, group_colors in enumerate(point_colors): + for j, point_color in enumerate(group_colors): + hue_level = p.plot_hues[i][j] + nt.assert_equal(tuple(point_color), + deep_colors[hue_order.index(hue_level)]) + + def test_scatterplot_legend(self): + + p = cat._CategoricalScatterPlotter() + + hue_order = ["m", "n"] + p.establish_variables(x="g", y="y", hue="h", + hue_order=hue_order, data=self.df) + p.establish_colors(None, "deep", 1) + deep_colors = palettes.color_palette("deep", self.h.unique().size) + + f, ax = plt.subplots() + p.add_legend_data(ax) + leg = ax.legend() + + for i, t in enumerate(leg.get_texts()): + nt.assert_equal(t.get_text(), hue_order[i]) + + for i, h in enumerate(leg.legendHandles): + rgb = h.get_facecolor()[0, :3] + nt.assert_equal(tuple(rgb), tuple(deep_colors[i])) + + class TestStripPlotter(CategoricalFixture): def test_stripplot_vertical(self): From b171cfaa7618e7fd71a039b214ec66c1284ddf44 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 15 Dec 2015 21:30:15 -0800 Subject: [PATCH 0164/1738] Add some tests for swarmplot internals --- seaborn/categorical.py | 5 ++++- seaborn/tests/test_categorical.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 4c80745ec8..a70e8c097f 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1180,7 +1180,10 @@ def overlap(self, xy_i, xy_j, d): return np.linalg.norm([x_i - x_j, y_i - y_j]) < d def could_overlap(self, xy_i, swarm, d): - """Return a list of all swarm points that could overlap with target.""" + """Return a list of all swarm points that could overlap with target. + + Assumes that we are working through a sorted storm. + """ _, y_i = xy_i neighbors = [] for xy_j in swarm: diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 8aee382dd8..1fcb11c4a8 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1591,6 +1591,36 @@ def test_three_strip_points(self): nt.assert_equal(ax.collections[0].get_facecolor().shape, (1, 4)) +class TestSwarmPlotter(CategoricalFixture): + + def test_overlap(self): + + p = cat._SwarmPlotter() + nt.assert_false(p.overlap((0, 0), (1, 1), np.sqrt(2))) + nt.assert_true(p.overlap((0, 0), (1, 1), np.sqrt(2.001))) + + def test_could_overlap(self): + + p = cat._SwarmPlotter() + neighbors = p.could_overlap((1, 1), [(0, 0), (1, .5), (.5, .5)], 1) + nt.assert_equal(neighbors, [(1, .5), (.5, .5)]) + + def test_position_candidates(self): + + p = cat._SwarmPlotter() + candidates = p.position_candidates((0, 1), [(0, 1), (0, 1.5)], 1) + dx1 = 1.05 + dx2 = np.sqrt(1 - .5 **2) * 1.05 + nt.assert_equal(candidates, + [(0, 1), (-dx1, 1), (dx1, 1), (-dx2, 1), (dx2, 1)]) + + def test_prune_candidates(self): + + p = cat._SwarmPlotter() + candidates = p.prune_candidates([(.5, 1), (1, 1)], [(0, 1)], 1) + npt.assert_array_equal(candidates, np.array([(1, 1)])) + + class TestBarPlotter(CategoricalFixture): default_kws = dict(x=None, y=None, hue=None, data=None, From aa9712eb992b8b00fa346503cda5dabf571af5fa Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 15 Dec 2015 21:31:17 -0800 Subject: [PATCH 0165/1738] PEP8 --- seaborn/tests/test_categorical.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 1fcb11c4a8..e45280c8bb 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -6,7 +6,6 @@ import matplotlib.pyplot as plt from distutils.version import LooseVersion -pandas_has_categoricals = LooseVersion(pd.__version__) >= "0.15" import nose.tools as nt import numpy.testing as npt @@ -17,6 +16,9 @@ from .. import palettes +pandas_has_categoricals = LooseVersion(pd.__version__) >= "0.15" + + class CategoricalFixture(PlotTestCase): """Test boxplot (also base class for things like violinplots).""" rs = np.random.RandomState(30) @@ -1610,7 +1612,7 @@ def test_position_candidates(self): p = cat._SwarmPlotter() candidates = p.position_candidates((0, 1), [(0, 1), (0, 1.5)], 1) dx1 = 1.05 - dx2 = np.sqrt(1 - .5 **2) * 1.05 + dx2 = np.sqrt(1 - .5 ** 2) * 1.05 nt.assert_equal(candidates, [(0, 1), (-dx1, 1), (dx1, 1), (-dx2, 1), (dx2, 1)]) @@ -2057,9 +2059,9 @@ def test_simple_pointplots(self): ax = cat.pointplot("g", "y", "h", data=self.df) nt.assert_equal(len(ax.collections), len(self.h.unique())) nt.assert_equal(len(ax.lines), - (len(self.g.unique()) - * len(self.h.unique()) - + len(self.h.unique()))) + (len(self.g.unique()) + + len(self.h.unique()) + + len(self.h.unique()))) nt.assert_equal(ax.get_xlabel(), "g") nt.assert_equal(ax.get_ylabel(), "mean(y)") plt.close("all") @@ -2067,9 +2069,9 @@ def test_simple_pointplots(self): ax = cat.pointplot("y", "g", "h", orient="h", data=self.df) nt.assert_equal(len(ax.collections), len(self.h.unique())) nt.assert_equal(len(ax.lines), - (len(self.g.unique()) - * len(self.h.unique()) - + len(self.h.unique()))) + (len(self.g.unique()) * + len(self.h.unique()) + + len(self.h.unique()))) nt.assert_equal(ax.get_xlabel(), "mean(y)") nt.assert_equal(ax.get_ylabel(), "g") plt.close("all") From da50a3d38634f9e653d6c7d47049f831f09705ff Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 16 Dec 2015 09:31:16 -0800 Subject: [PATCH 0166/1738] Reformat and add more swarmplot tests --- seaborn/categorical.py | 1 + seaborn/tests/test_categorical.py | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index a70e8c097f..fe0608dc05 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1227,6 +1227,7 @@ def add_gutters(self, points, center, width): off_high = points > high_gutter if off_high.any(): points[off_high] = high_gutter + return points def swarm_points(self, ax, points, center, width, s, **kws): """Find new positions on the categorical axis for each point. diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index e45280c8bb..abdc4519e3 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1610,7 +1610,9 @@ def test_could_overlap(self): def test_position_candidates(self): p = cat._SwarmPlotter() - candidates = p.position_candidates((0, 1), [(0, 1), (0, 1.5)], 1) + xy_i = (0, 1) + neighbors = [(0, 1), (0, 1.5)] + candidates = p.position_candidates(xy_i, neighbors, 1) dx1 = 1.05 dx2 = np.sqrt(1 - .5 ** 2) * 1.05 nt.assert_equal(candidates, @@ -1619,9 +1621,19 @@ def test_position_candidates(self): def test_prune_candidates(self): p = cat._SwarmPlotter() - candidates = p.prune_candidates([(.5, 1), (1, 1)], [(0, 1)], 1) + candidates = [(.5, 1), (1, 1)] + neighbors = [(0, 1)] + candidates = p.prune_candidates(candidates, neighbors, 1) npt.assert_array_equal(candidates, np.array([(1, 1)])) + def test_add_gutters(self): + + p = cat._SwarmPlotter() + points = np.array([0, -1, .4]) + points = p.add_gutters(points, 0, 1) + npt.assert_array_equal(points, + np.array([0, -.5, .4])) + class TestBarPlotter(CategoricalFixture): @@ -2059,7 +2071,7 @@ def test_simple_pointplots(self): ax = cat.pointplot("g", "y", "h", data=self.df) nt.assert_equal(len(ax.collections), len(self.h.unique())) nt.assert_equal(len(ax.lines), - (len(self.g.unique()) + + (len(self.g.unique()) * len(self.h.unique()) + len(self.h.unique()))) nt.assert_equal(ax.get_xlabel(), "g") From e8711e26c36ee40538b23d6c82f31af63c4fb5b1 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 18 Dec 2015 09:13:18 -0800 Subject: [PATCH 0167/1738] Abstract out core beeswarm algorithm and add test --- seaborn/categorical.py | 88 +++++++++++++++++-------------- seaborn/tests/test_categorical.py | 15 +++++- 2 files changed, 61 insertions(+), 42 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index fe0608dc05..aa74fd2524 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1196,11 +1196,17 @@ def position_candidates(self, xy_i, neighbors, d): """Return a list of (x, y) coordinates that might be valid.""" candidates = [xy_i] x_i, y_i = xy_i + left_first = True for x_j, y_j in neighbors: dy = y_i - y_j dx = np.sqrt(d ** 2 - dy ** 2) * 1.05 - candidates.extend([(x_j - dx, y_i), (x_j + dx, y_i)]) - + cl, cr = (x_j - dx, y_i), (x_j + dx, y_i) + if left_first: + new_candidates = [cl, cr] + else: + new_candidates = [cr, cl] + candidates.extend(new_candidates) + left_first = ~left_first return candidates def prune_candidates(self, candidates, neighbors, d): @@ -1216,6 +1222,38 @@ def prune_candidates(self, candidates, neighbors, d): assert good_candidates return np.array(good_candidates) + def beeswarm(self, orig_xy, d): + """Adjust x position of points to avoid overlaps.""" + # In this method, ``x`` is always the categorical axis + # Center of the swarm, in point coordinates + midline = orig_xy[0, 0] + + # Start the swarm with the first point + swarm = [orig_xy[0]] + + # Loop over the remaining points + for xy_i in orig_xy[1:]: + + # Find the points in the swarm that could possibly + # overlap with the point we are currently placing + neighbors = self.could_overlap(xy_i, swarm, d) + + # Find positions that would be valid individually + # with respect to each of the swarm neighbors + candidates = self.position_candidates(xy_i, neighbors, d) + + # Remove the positions that overlap with any of the + # other neighbors + candidates = self.prune_candidates(candidates, neighbors, d) + + # Find the most central of the remaining positions + offsets = np.abs(candidates[:, 0] - midline) + best_index = np.argmin(offsets) + new_xy_i = candidates[best_index] + swarm.append(new_xy_i) + + return np.array(swarm) + def add_gutters(self, points, center, width): """Stop points from extending beyond their territory.""" half_width = width / 2 @@ -1230,11 +1268,7 @@ def add_gutters(self, points, center, width): return points def swarm_points(self, ax, points, center, width, s, **kws): - """Find new positions on the categorical axis for each point. - - In this method, ``x`` means the categorical axis and ``y`` means the - data axis. - """ + """Find new positions on the categorical axis for each point.""" # Convert from point size (area) to diameter default_lw = mpl.rcParams["patch.linewidth"] lw = kws.get("linewidth", kws.get("lw", default_lw)) @@ -1246,41 +1280,13 @@ def swarm_points(self, ax, points, center, width, s, **kws): orig_xy = ax.transData.transform(points.get_offsets()) # Order the variables so that x is the caegorical axis - if self.orient == "v": - orig_x, orig_y = orig_xy.T - else: - orig_y, orig_x = orig_xy.T - orig_xy = np.c_[orig_x, orig_y] - - # Center of the swarm, in point (not data) coordinates - midline = orig_x[0] - - # Start the swarm with the first point - swarm = [orig_xy[0]] - - # Loop over the remaining points - for xy_i in orig_xy[1:]: - - # Find the points in the swarm that could possibly - # overlap with the point we are currently placing - neighbors = self.could_overlap(xy_i, swarm, d) - - # Find positions that would be valid individually - # with respect to each of the swarm neighbors - candidates = self.position_candidates(xy_i, neighbors, d) - - # Remove the positions that overlap with any of the - # other neighbors - candidates = self.prune_candidates(candidates, neighbors, d) + if self.orient == "h": + orig_xy = orig_xy[:, [1, 0]] - # Find the most central of the remaining positions - offsets = np.abs(candidates[:, 0] - midline) - best_index = np.argmin(offsets) - new_xy_i = candidates[best_index] - swarm.append(new_xy_i) + # Do the beeswarm in point coordinates + swarm = self.beeswarm(orig_xy, d) # Transform the point coordinates back to data coordinates - swarm = np.array(swarm) if self.orient == "v": new_xy = swarm[:, [0, 1]] else: @@ -1297,7 +1303,7 @@ def swarm_points(self, ax, points, center, width, s, **kws): points.set_offsets(np.c_[new_x, new_y]) def draw_swarmplot(self, ax, kws): - + """Plot the data.""" s = kws.pop("s", 7 ** 2) swarms = [] @@ -1331,7 +1337,7 @@ def draw_swarmplot(self, ax, kws): self.swarm_points(ax, swarm, i, self.width, s, **kws) def plot(self, ax, **kws): - + """Make the full plot.""" self.draw_swarmplot(ax, kws) self.add_legend_data(ax) self.annotate_axes(ax) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index abdc4519e3..d7c0dbfd19 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1,7 +1,7 @@ import numpy as np import pandas as pd import scipy -from scipy import stats +from scipy import stats, spatial import matplotlib as mpl import matplotlib.pyplot as plt @@ -1626,6 +1626,19 @@ def test_prune_candidates(self): candidates = p.prune_candidates(candidates, neighbors, 1) npt.assert_array_equal(candidates, np.array([(1, 1)])) + def test_beeswarm(self): + + p = cat._SwarmPlotter() + d = self.y.diff().mean() * 1.5 + x = np.zeros(self.y.size) + y = np.sort(self.y) + orig_xy = np.c_[x, y] + swarm = p.beeswarm(orig_xy, d) + dmat = spatial.distance.cdist(swarm, swarm) + triu = dmat[np.triu_indices_from(dmat, 1)] + npt.assert_array_less(d, triu) + npt.assert_array_equal(y, swarm[:, 1]) + def test_add_gutters(self): p = cat._SwarmPlotter() From c46e5b834e48a0e9ec5725a2c9eb35aa5efecfe1 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 18 Dec 2015 11:07:48 -0800 Subject: [PATCH 0168/1738] Begin swarmplot interface --- seaborn/categorical.py | 29 ++++++++++++++++++++++++++--- seaborn/tests/test_categorical.py | 4 ++-- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index aa74fd2524..aab6b5ff3e 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1169,9 +1169,15 @@ def plot(self, ax, kws): class _SwarmPlotter(_CategoricalScatterPlotter): - def __init__(self): + def __init__(self, x, y, hue, data, order, hue_order, + split, orient, color, palette): + """Initialize the plotter.""" + self.establish_variables(x, y, hue, data, orient, order, hue_order) + self.establish_colors(color, palette, 1) - pass + # Set object attributes + self.split = split + self.width = .8 def overlap(self, xy_i, xy_j, d): """Return True if two circles with the same diameter will overlap.""" @@ -1336,7 +1342,7 @@ def draw_swarmplot(self, ax, kws): for i, swarm in enumerate(swarms): self.swarm_points(ax, swarm, i, self.width, s, **kws) - def plot(self, ax, **kws): + def plot(self, ax, kws): """Make the full plot.""" self.draw_swarmplot(ax, kws) self.add_legend_data(ax) @@ -2619,6 +2625,23 @@ def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, """).format(**_categorical_docs) +def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, + split=False, orient=None, color=None, palette=None, + size=7, edgecolor="w", linewidth=1, ax=None, **kwargs): + + plotter = _SwarmPlotter(x, y, hue, data, order, hue_order, + split, orient, color, palette) + if ax is None: + ax = plt.gca() + + kwargs.update(dict(s=size ** 2, edgecolor=edgecolor, linewidth=linewidth)) + if edgecolor == "gray": + kwargs["edgecolor"] = plotter.gray + + plotter.plot(ax, kwargs) + return ax + + def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, estimator=np.mean, ci=95, n_boot=1000, units=None, orient=None, color=None, palette=None, saturation=.75, diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index d7c0dbfd19..24aeab5d2a 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1642,10 +1642,10 @@ def test_beeswarm(self): def test_add_gutters(self): p = cat._SwarmPlotter() - points = np.array([0, -1, .4]) + points = np.array([0, -1, .4, .8]) points = p.add_gutters(points, 0, 1) npt.assert_array_equal(points, - np.array([0, -.5, .4])) + np.array([0, -.5, .4, .5])) class TestBarPlotter(CategoricalFixture): From 352f0b97901fffee0512eb1d2705ccbcd7bd6519 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 19 Dec 2015 08:54:16 -0800 Subject: [PATCH 0169/1738] Add default keywords to swarmplot tests --- seaborn/tests/test_categorical.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 24aeab5d2a..2c96c90277 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1595,21 +1595,25 @@ def test_three_strip_points(self): class TestSwarmPlotter(CategoricalFixture): + default_kws = dict(x=None, y=None, hue=None, data=None, + order=None, hue_order=None, split=False, + orient=None, color=None, palette=None) + def test_overlap(self): - p = cat._SwarmPlotter() + p = cat._SwarmPlotter(**self.default_kws) nt.assert_false(p.overlap((0, 0), (1, 1), np.sqrt(2))) nt.assert_true(p.overlap((0, 0), (1, 1), np.sqrt(2.001))) def test_could_overlap(self): - p = cat._SwarmPlotter() + p = cat._SwarmPlotter(**self.default_kws) neighbors = p.could_overlap((1, 1), [(0, 0), (1, .5), (.5, .5)], 1) nt.assert_equal(neighbors, [(1, .5), (.5, .5)]) def test_position_candidates(self): - p = cat._SwarmPlotter() + p = cat._SwarmPlotter(**self.default_kws) xy_i = (0, 1) neighbors = [(0, 1), (0, 1.5)] candidates = p.position_candidates(xy_i, neighbors, 1) @@ -1620,7 +1624,7 @@ def test_position_candidates(self): def test_prune_candidates(self): - p = cat._SwarmPlotter() + p = cat._SwarmPlotter(**self.default_kws) candidates = [(.5, 1), (1, 1)] neighbors = [(0, 1)] candidates = p.prune_candidates(candidates, neighbors, 1) @@ -1628,7 +1632,7 @@ def test_prune_candidates(self): def test_beeswarm(self): - p = cat._SwarmPlotter() + p = cat._SwarmPlotter(**self.default_kws) d = self.y.diff().mean() * 1.5 x = np.zeros(self.y.size) y = np.sort(self.y) @@ -1641,7 +1645,7 @@ def test_beeswarm(self): def test_add_gutters(self): - p = cat._SwarmPlotter() + p = cat._SwarmPlotter(**self.default_kws) points = np.array([0, -1, .4, .8]) points = p.add_gutters(points, 0, 1) npt.assert_array_equal(points, From 73dea8ca518ed1fd5e164e9b403257289c9f1b48 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 19 Dec 2015 09:06:34 -0800 Subject: [PATCH 0170/1738] PEP8 --- seaborn/tests/test_categorical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 2c96c90277..ad95d00e01 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1596,8 +1596,8 @@ def test_three_strip_points(self): class TestSwarmPlotter(CategoricalFixture): default_kws = dict(x=None, y=None, hue=None, data=None, - order=None, hue_order=None, split=False, - orient=None, color=None, palette=None) + order=None, hue_order=None, split=False, + orient=None, color=None, palette=None) def test_overlap(self): From 5f5c07f1cfe49dd9cf68e1de3a012f266b7b73e3 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 19 Dec 2015 16:11:38 -0800 Subject: [PATCH 0171/1738] Add split swarmplots --- seaborn/categorical.py | 70 +++++++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index aab6b5ff3e..1db961471d 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1312,6 +1312,7 @@ def draw_swarmplot(self, ax, kws): """Plot the data.""" s = kws.pop("s", 7 ** 2) + centers = [] swarms = [] # Set the categorical axes limits here for the swarm math @@ -1323,24 +1324,57 @@ def draw_swarmplot(self, ax, kws): # Plot each swarm for i, group_data in enumerate(self.plot_data): - swarm_data = remove_na(group_data) + if self.plot_hues is None or not self.split: - sorter = np.argsort(swarm_data) - swarm_data = swarm_data[sorter] - point_colors = self.point_colors[i][sorter] + width = self.width + swarm_data = remove_na(group_data) + + # Sort the points for the beeswarm algorithm + sorter = np.argsort(swarm_data) + swarm_data = swarm_data[sorter] + point_colors = self.point_colors[i][sorter] + + # Plot the points in centered positions + cat_pos = np.ones(swarm_data.size) * i + kws.update(c=point_colors) + if self.orient == "v": + points = ax.scatter(cat_pos, swarm_data, s=s, **kws) + else: + points = ax.scatter(swarm_data, cat_pos, s=s, **kws) + + centers.append(i) + swarms.append(points) - cat_pos = np.ones(swarm_data.size) * i - kws.update(c=point_colors) - if self.orient == "v": - points = ax.scatter(cat_pos, swarm_data, s=s, **kws) else: - points = ax.scatter(swarm_data, cat_pos, s=s, **kws) - swarms.append(points) + offsets = self.hue_offsets + width = self.nested_width + + for j, hue_level in enumerate(self.hue_names): + hue_mask = self.plot_hues[i] == hue_level + swarm_data = remove_na(group_data[hue_mask]) + + # Sort the points for the beeswarm algorithm + sorter = np.argsort(swarm_data) + swarm_data = swarm_data[sorter] + point_colors = self.point_colors[i][hue_mask][sorter] + + # Plot the points in centered positions + center = i + offsets[j] + cat_pos = np.ones(swarm_data.size) * center + kws.update(c=point_colors) + if self.orient == "v": + points = ax.scatter(cat_pos, swarm_data, s=s, **kws) + else: + points = ax.scatter(swarm_data, cat_pos, s=s, **kws) + + centers.append(center) + swarms.append(points) # Update the position of each point on the categorical axis # Do this after plotting so that the numerical axis limits are correct - for i, swarm in enumerate(swarms): - self.swarm_points(ax, swarm, i, self.width, s, **kws) + for center, swarm in zip(centers, swarms): + if swarm.get_offsets().size: + self.swarm_points(ax, swarm, center, width, s, **kws) def plot(self, ax, kws): """Make the full plot.""" @@ -2627,16 +2661,22 @@ def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, split=False, orient=None, color=None, palette=None, - size=7, edgecolor="w", linewidth=1, ax=None, **kwargs): + size=7, edgecolor="w", linewidth=None, ax=None, **kwargs): plotter = _SwarmPlotter(x, y, hue, data, order, hue_order, split, orient, color, palette) if ax is None: ax = plt.gca() - kwargs.update(dict(s=size ** 2, edgecolor=edgecolor, linewidth=linewidth)) + kwargs.setdefault("zorder", 3) + size = kwargs.get("s", size) + if linewidth is None: + linewidth = size / 10 if edgecolor == "gray": - kwargs["edgecolor"] = plotter.gray + edgecolor = plotter.gray + kwargs.update(dict(s=size ** 2, + edgecolor=edgecolor, + linewidth=linewidth)) plotter.plot(ax, kwargs) return ax From 1966e78230575a15663f134ef3fe1416bf2cd341 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 19 Dec 2015 16:11:53 -0800 Subject: [PATCH 0172/1738] Add tests for swarmplot plot results --- seaborn/tests/test_categorical.py | 96 +++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index ad95d00e01..2945e56549 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1651,6 +1651,102 @@ def test_add_gutters(self): npt.assert_array_equal(points, np.array([0, -.5, .4, .5])) + def test_swarmplot_vertical(self): + + pal = palettes.color_palette() + + ax = cat.swarmplot("g", "y", data=self.df) + for i, (_, vals) in enumerate(self.y.groupby(self.g)): + + x, y = ax.collections[i].get_offsets().T + npt.assert_array_almost_equal(y, np.sort(vals)) + + fc = ax.collections[i].get_facecolors()[0, :3] + npt.assert_equal(fc, pal[i]) + + def test_swarmplot_horizontal(self): + + pal = palettes.color_palette() + + ax = cat.swarmplot("y", "g", data=self.df, orient="h") + for i, (_, vals) in enumerate(self.y.groupby(self.g)): + + x, y = ax.collections[i].get_offsets().T + npt.assert_array_almost_equal(x, np.sort(vals)) + + fc = ax.collections[i].get_facecolors()[0, :3] + npt.assert_equal(fc, pal[i]) + + def test_split_nested_swarmplot_vetical(self): + + pal = palettes.color_palette() + + ax = cat.swarmplot("g", "y", "h", data=self.df, split=True) + for i, (_, group_vals) in enumerate(self.y.groupby(self.g)): + for j, (_, vals) in enumerate(group_vals.groupby(self.h)): + + x, y = ax.collections[i * 2 + j].get_offsets().T + npt.assert_array_almost_equal(y, np.sort(vals)) + + fc = ax.collections[i * 2 + j].get_facecolors()[0, :3] + npt.assert_equal(fc, pal[j]) + + def test_split_nested_swarmplot_horizontal(self): + + pal = palettes.color_palette() + + ax = cat.swarmplot("y", "g", "h", data=self.df, orient="h", split=True) + for i, (_, group_vals) in enumerate(self.y.groupby(self.g)): + for j, (_, vals) in enumerate(group_vals.groupby(self.h)): + + x, y = ax.collections[i * 2 + j].get_offsets().T + npt.assert_array_almost_equal(x, np.sort(vals)) + + fc = ax.collections[i * 2 + j].get_facecolors()[0, :3] + npt.assert_equal(fc, pal[j]) + + def test_unsplit_nested_swarmplot_vertical(self): + + ax = cat.swarmplot("g", "y", "h", data=self.df) + + pal = palettes.color_palette() + hue_names = self.h.unique().tolist() + grouped_hues = list(self.h.groupby(self.g)) + + for i, (_, vals) in enumerate(self.y.groupby(self.g)): + + points = ax.collections[i] + x, y = points.get_offsets().T + sorter = np.argsort(vals) + npt.assert_array_almost_equal(y, vals.iloc[sorter]) + + _, hue_vals = grouped_hues[i] + for hue, fc in zip(hue_vals.values[sorter], + points.get_facecolors()): + + npt.assert_equal(fc[:3], pal[hue_names.index(hue)]) + + def test_unsplit_nested_swarmplot_horizontal(self): + + ax = cat.swarmplot("y", "g", "h", data=self.df, orient="h") + + pal = palettes.color_palette() + hue_names = self.h.unique().tolist() + grouped_hues = list(self.h.groupby(self.g)) + + for i, (_, vals) in enumerate(self.y.groupby(self.g)): + + points = ax.collections[i] + x, y = points.get_offsets().T + sorter = np.argsort(vals) + npt.assert_array_almost_equal(x, vals.iloc[sorter]) + + _, hue_vals = grouped_hues[i] + for hue, fc in zip(hue_vals.values[sorter], + points.get_facecolors()): + + npt.assert_equal(fc[:3], pal[hue_names.index(hue)]) + class TestBarPlotter(CategoricalFixture): From 5021fb91acc304bb8d7fc353c2e5f23a6d55fb79 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 19 Dec 2015 17:53:01 -0800 Subject: [PATCH 0173/1738] Handle missing data better --- seaborn/categorical.py | 14 ++++++++++---- seaborn/tests/test_categorical.py | 6 +++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 1db961471d..0f936c5af3 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1212,7 +1212,7 @@ def position_candidates(self, xy_i, neighbors, d): else: new_candidates = [cr, cl] candidates.extend(new_candidates) - left_first = ~left_first + left_first = not left_first return candidates def prune_candidates(self, candidates, neighbors, d): @@ -1327,12 +1327,18 @@ def draw_swarmplot(self, ax, kws): if self.plot_hues is None or not self.split: width = self.width - swarm_data = remove_na(group_data) + + if self.hue_names is None: + hue_mask = np.ones(group_data.size, np.bool) + else: + hue_mask = np.in1d(self.plot_hues[i], self.hue_names) + + swarm_data = group_data[hue_mask] # Sort the points for the beeswarm algorithm sorter = np.argsort(swarm_data) swarm_data = swarm_data[sorter] - point_colors = self.point_colors[i][sorter] + point_colors = self.point_colors[i][hue_mask][sorter] # Plot the points in centered positions cat_pos = np.ones(swarm_data.size) * i @@ -1351,7 +1357,7 @@ def draw_swarmplot(self, ax, kws): for j, hue_level in enumerate(self.hue_names): hue_mask = self.plot_hues[i] == hue_level - swarm_data = remove_na(group_data[hue_mask]) + swarm_data = group_data[hue_mask] # Sort the points for the beeswarm algorithm sorter = np.argsort(swarm_data) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 2945e56549..2afb90b2c2 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1441,14 +1441,14 @@ def test_hue_point_colors(self): p = cat._CategoricalScatterPlotter() - hue_order = ["m", "n"] + hue_order = self.h.unique().tolist() p.establish_variables(x="g", y="y", hue="h", hue_order=hue_order, data=self.df) p.establish_colors(None, "deep", 1) point_colors = p.point_colors nt.assert_equal(len(point_colors), self.g.unique().size) - deep_colors = palettes.color_palette("deep", self.h.unique().size) + deep_colors = palettes.color_palette("deep", len(hue_order)) for i, group_colors in enumerate(point_colors): for j, point_color in enumerate(group_colors): @@ -1620,7 +1620,7 @@ def test_position_candidates(self): dx1 = 1.05 dx2 = np.sqrt(1 - .5 ** 2) * 1.05 nt.assert_equal(candidates, - [(0, 1), (-dx1, 1), (dx1, 1), (-dx2, 1), (dx2, 1)]) + [(0, 1), (-dx1, 1), (dx1, 1), (dx2, 1), (-dx2, 1)]) def test_prune_candidates(self): From 1e62facafd08b7dfacb4f4556df1cdbc010f943e Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 19 Dec 2015 18:10:58 -0800 Subject: [PATCH 0174/1738] Avoid numpy 1.6 bug in tests --- seaborn/tests/test_categorical.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 2afb90b2c2..a197018d07 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -7,6 +7,7 @@ from distutils.version import LooseVersion +import nose import nose.tools as nt import numpy.testing as npt from numpy.testing.decorators import skipif @@ -1707,6 +1708,9 @@ def test_split_nested_swarmplot_horizontal(self): def test_unsplit_nested_swarmplot_vertical(self): + if LooseVersion(np.__version__) < "1.7": + raise nose.SkipTest + ax = cat.swarmplot("g", "y", "h", data=self.df) pal = palettes.color_palette() @@ -1728,6 +1732,9 @@ def test_unsplit_nested_swarmplot_vertical(self): def test_unsplit_nested_swarmplot_horizontal(self): + if LooseVersion(np.__version__) < "1.7": + raise nose.SkipTest + ax = cat.swarmplot("y", "g", "h", data=self.df, orient="h") pal = palettes.color_palette() From cfd7cd082adec58d106e6113db3ba5d21d105240 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 19 Dec 2015 19:16:10 -0800 Subject: [PATCH 0175/1738] Add swarmplot docstring --- doc/api.rst | 1 + doc/sphinxext/ipython_directive.py | 3 +- seaborn/categorical.py | 144 ++++++++++++++++++++++++++++- 3 files changed, 145 insertions(+), 3 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 3290dc4ba0..1945900a37 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -45,6 +45,7 @@ Categorical plots boxplot violinplot stripplot + swarmplot pointplot barplot countplot diff --git a/doc/sphinxext/ipython_directive.py b/doc/sphinxext/ipython_directive.py index 3f9be95609..a79d2f45e9 100644 --- a/doc/sphinxext/ipython_directive.py +++ b/doc/sphinxext/ipython_directive.py @@ -127,7 +127,8 @@ from sphinx.util.compat import Directive # Our own -from IPython import Config, InteractiveShell +from traitlets.config import Config +from IPython import InteractiveShell from IPython.core.profiledir import ProfileDir from IPython.utils import io from IPython.utils.py3compat import PY3 diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 0f936c5af3..510195eb17 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1079,7 +1079,8 @@ def point_colors(self): # Color the points based on the hue level for j, level in enumerate(self.hue_names): hue_color = self.colors[j] - group_colors[self.plot_hues[i] == level] = hue_color + if group_data.size: + group_colors[self.plot_hues[i] == level] = hue_color colors.append(group_colors) @@ -2046,7 +2047,11 @@ def plot(self, ax, boxplot_kws): """), stripplot=dedent("""\ stripplot : A scatterplot where one variable is categorical. Can be used - in conjunction with a other plots to show each observation.\ + in conjunction with other plots to show each observation.\ + """), + swarmplot=dedent("""\ + swarmplot : A categorical scatterplot where the points do not overlap. Can + be used with other plots to show each observation.\ """), barplot=dedent("""\ barplot : Show point estimates and confidence intervals using bars.\ @@ -2688,6 +2693,141 @@ def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, return ax +swarmplot.__doc__ = dedent("""\ + Draw a categorical scatterplot with non-overlapping points. + + This function is similar to :func:`stripplot`, but the points are adjusted + (only along the categorical axis) so that they don't overlap. This gives a + better representation of the distribution of values, although it does not + scale as well to large numbers of observations (both in terms of the + ability to show all the points and in terms of the computation needed + to arrange them). + + A swarm plot can be drawn on its own, but it is also a good complement + to a box or violin plot in cases where you want to show all observations + along with some representation of the underlying distribution. + + {main_api_narrative} + + Parameters + ---------- + {input_params} + {categorical_data} + {order_vars} + split : bool, optional + When using ``hue`` nesting, setting this to ``True`` will separate + the strips for different hue levels along the categorical axis. + Otherwise, the points for each level will be plotted in one swarm. + {orient} + {color} + {palette} + size : float, optional + Diameter of the markers, in points. (Although ``plt.scatter`` is used + to draw the points, the ``size`` argument here takes a "normal" + markersize and not size^2 like ``plt.scatter``. + edgecolor : matplotlib color, "gray" is special-cased, optional + Color of the lines around each point. If you pass ``"gray"``, the + brightness is determined by the color palette used for the body + of the points. + {linewidth} + {ax_in} + + Returns + ------- + {ax_out} + + See Also + -------- + {boxplot} + {violinplot} + + Examples + -------- + + Draw a single horizontal swarm plot: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns + >>> sns.set_style("whitegrid") + >>> tips = sns.load_dataset("tips") + >>> ax = sns.swarmplot(x=tips["total_bill"]) + + Group the swarms by a categorical variable: + + .. plot:: + :context: close-figs + + >>> ax = sns.swarmplot(x="day", y="total_bill", data=tips) + + Draw horizontal swarms: + + .. plot:: + :context: close-figs + + >>> ax = sns.swarmplot(x="total_bill", y="day", data=tips) + + Color the points using a second categorical variable: + + .. plot:: + :context: close-figs + + >>> ax = sns.swarmplot(x="day", y="total_bill", hue="sex", data=tips) + + Split each level of the ``hue`` variable along the categorical axis: + + .. plot:: + :context: close-figs + + >>> ax = sns.swarmplot(x="day", y="total_bill", hue="smoker", + ... data=tips, palette="Set2", split=True) + + Control swarm order by sorting the input data: + + .. plot:: + :context: close-figs + + >>> ax = sns.swarmplot(x="size", y="tip", + ... data=tips.sort_values("size")) + + Control swarm order by passing an explicit order: + + .. plot:: + :context: close-figs + + >>> ax = sns.swarmplot(x="size", y="tip", data=tips, + ... order=np.arange(1, 7), palette="Blues_d") + + Plot using smaller points: + + .. plot:: + :context: close-figs + + >>> ax = sns.swarmplot(x="size", y="tip", data=tips, size=4, + ... order=np.arange(1, 7), palette="Blues_d") + + + Draw swarms of observations on top of a box plot: + + .. plot:: + :context: close-figs + + >>> ax = sns.boxplot(x="tip", y="day", data=tips, whis=np.inf) + >>> ax = sns.swarmplot(x="tip", y="day", data=tips) + + Draw swarms of observations on top of a violin plot: + + .. plot:: + :context: close-figs + + >>> ax = sns.violinplot(x="day", y="total_bill", data=tips, inner=None) + >>> ax = sns.swarmplot(x="day", y="total_bill", data=tips, + ... color="white", edgecolor="gray") + + """).format(**_categorical_docs) + + def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, estimator=np.mean, ci=95, n_boot=1000, units=None, orient=None, color=None, palette=None, saturation=.75, From c1dc0f097fd0493e88f23ab9c2b7fceca0ad1183 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 19 Dec 2015 19:28:01 -0800 Subject: [PATCH 0176/1738] Drop Python 3.3 in favor of 3.5 --- .travis.yml | 2 +- seaborn/categorical.py | 2 ++ testing/{deps_modern_3.3.txt => deps_modern_3.5.txt} | 0 3 files changed, 3 insertions(+), 1 deletion(-) rename testing/{deps_modern_3.3.txt => deps_modern_3.5.txt} (100%) diff --git a/.travis.yml b/.travis.yml index 6c6df07fa3..91be122282 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,8 @@ language: python env: - PYTHON=2.7 DEPS=modern - PYTHON=2.7 DEPS=minimal - - PYTHON=3.3 DEPS=modern - PYTHON=3.4 DEPS=modern + - PYTHON=3.5 DEPS=modern install: - conda update conda --yes diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 510195eb17..95f0604ac4 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2703,6 +2703,8 @@ def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, ability to show all the points and in terms of the computation needed to arrange them). + This style of plot is often called a "beeswarm". + A swarm plot can be drawn on its own, but it is also a good complement to a box or violin plot in cases where you want to show all observations along with some representation of the underlying distribution. diff --git a/testing/deps_modern_3.3.txt b/testing/deps_modern_3.5.txt similarity index 100% rename from testing/deps_modern_3.3.txt rename to testing/deps_modern_3.5.txt From e746c53d3707fd943000125ac3b1246e82eb272a Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 19 Dec 2015 19:49:50 -0800 Subject: [PATCH 0177/1738] Punt on 3.5 for now; have to figure out Jupyter changes --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 91be122282..8c8b7cf2dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ env: - PYTHON=2.7 DEPS=modern - PYTHON=2.7 DEPS=minimal - PYTHON=3.4 DEPS=modern - - PYTHON=3.5 DEPS=modern + #- PYTHON=3.5 DEPS=modern install: - conda update conda --yes From 1dec6cce46ce7772092b6462465314a3259aa564 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 20 Dec 2015 10:00:01 -0800 Subject: [PATCH 0178/1738] Add some more docstring information --- seaborn/categorical.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 95f0604ac4..6e1fd774fc 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2177,6 +2177,7 @@ def boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, -------- {violinplot} {stripplot} + {swarmplot} Examples -------- @@ -2237,14 +2238,14 @@ def boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> iris = sns.load_dataset("iris") >>> ax = sns.boxplot(data=iris, orient="h", palette="Set2") - Use :func:`stripplot` to show the datapoints on top of the boxes: + Use :func:`swarmplot` to show the datapoints on top of the boxes: .. plot:: :context: close-figs >>> ax = sns.boxplot(x="day", y="total_bill", data=tips) - >>> ax = sns.stripplot(x="day", y="total_bill", data=tips, - ... size=4, jitter=True, edgecolor="gray") + >>> ax = sns.swarmplot(x="day", y="total_bill", data=tips, + ... size=4, edgecolor="gray") Draw a box plot on to a :class:`FacetGrid` to group within an additional categorical variable: @@ -2378,6 +2379,7 @@ def violinplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, -------- {boxplot} {stripplot} + {swarmplot} Examples -------- @@ -2563,6 +2565,7 @@ def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, See Also -------- + {swarmplot} {boxplot} {violinplot} @@ -2709,6 +2712,10 @@ def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, to a box or violin plot in cases where you want to show all observations along with some representation of the underlying distribution. + Note that arranging the points properly requires an accurate transformation + between data and point coordinates. This means that non-default axis limits + should be set *before* drawing the swarm plot. + {main_api_narrative} Parameters @@ -2742,6 +2749,8 @@ def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, -------- {boxplot} {violinplot} + {stripplot} + {factorplot} Examples -------- From 94f0c4acb9a22183dab901035a4e576ff0abe00a Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 20 Dec 2015 13:17:07 -0800 Subject: [PATCH 0179/1738] Smaller points and no edges by default --- seaborn/categorical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 6e1fd774fc..a459a54bf5 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2675,7 +2675,7 @@ def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, split=False, orient=None, color=None, palette=None, - size=7, edgecolor="w", linewidth=None, ax=None, **kwargs): + size=5, edgecolor="gray", linewidth=0, ax=None, **kwargs): plotter = _SwarmPlotter(x, y, hue, data, order, hue_order, split, orient, color, palette) From 67a9a959796043619bcd887c2503f3268d6a94c9 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 20 Dec 2015 14:19:07 -0800 Subject: [PATCH 0180/1738] Performance improvements --- seaborn/categorical.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index a459a54bf5..b817914c33 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1184,19 +1184,21 @@ def overlap(self, xy_i, xy_j, d): """Return True if two circles with the same diameter will overlap.""" x_i, y_i = xy_i x_j, y_j = xy_j - return np.linalg.norm([x_i - x_j, y_i - y_j]) < d + return (x_i - x_j) ** 2 + (y_i - y_j) ** 2 < d ** 2 def could_overlap(self, xy_i, swarm, d): """Return a list of all swarm points that could overlap with target. - Assumes that we are working through a sorted storm. + Assumes that swarm is a sorted list of all points below xy_i. """ _, y_i = xy_i neighbors = [] - for xy_j in swarm: + for xy_j in reversed(swarm): _, y_j = xy_j if (y_i - y_j) < d: neighbors.append(xy_j) + else: + break return neighbors def position_candidates(self, xy_i, neighbors, d): @@ -1217,7 +1219,7 @@ def position_candidates(self, xy_i, neighbors, d): return candidates def prune_candidates(self, candidates, neighbors, d): - """Remove candidates from the list of they overlap with the swarm.""" + """Remove candidates from the list if they overlap with the swarm.""" good_candidates = [] for xy_i in candidates: good_candidate = True From abab51db701bf187748144820f96ea7cc6870dd7 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 20 Dec 2015 14:33:45 -0800 Subject: [PATCH 0181/1738] Better test --- seaborn/categorical.py | 4 ++-- seaborn/tests/test_categorical.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index b817914c33..ef8d0782fd 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1184,7 +1184,7 @@ def overlap(self, xy_i, xy_j, d): """Return True if two circles with the same diameter will overlap.""" x_i, y_i = xy_i x_j, y_j = xy_j - return (x_i - x_j) ** 2 + (y_i - y_j) ** 2 < d ** 2 + return ((x_i - x_j) ** 2 + (y_i - y_j) ** 2) < (d ** 2) def could_overlap(self, xy_i, swarm, d): """Return a list of all swarm points that could overlap with target. @@ -1199,7 +1199,7 @@ def could_overlap(self, xy_i, swarm, d): neighbors.append(xy_j) else: break - return neighbors + return list(reversed(neighbors)) def position_candidates(self, xy_i, neighbors, d): """Return a list of (x, y) coordinates that might be valid.""" diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index a197018d07..753b2e298a 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1603,7 +1603,7 @@ class TestSwarmPlotter(CategoricalFixture): def test_overlap(self): p = cat._SwarmPlotter(**self.default_kws) - nt.assert_false(p.overlap((0, 0), (1, 1), np.sqrt(2))) + nt.assert_false(p.overlap((0, 0), (1, 1), np.sqrt(1.999))) nt.assert_true(p.overlap((0, 0), (1, 1), np.sqrt(2.001))) def test_could_overlap(self): From 58927205d2d560f039df8fc5389b824fba413367 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 20 Dec 2015 15:20:21 -0800 Subject: [PATCH 0182/1738] Change categorical scatterplot example to use swarmplot --- doc/index.rst | 4 ++-- examples/scatterplot_categorical.py | 5 ++--- seaborn/categorical.py | 3 +-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index 02afaedbfc..a3e0f7bea7 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -53,9 +53,9 @@ Seaborn: statistical data visualization
- +
- +
diff --git a/examples/scatterplot_categorical.py b/examples/scatterplot_categorical.py index 5f4813d44f..1c8fde57cb 100644 --- a/examples/scatterplot_categorical.py +++ b/examples/scatterplot_categorical.py @@ -5,7 +5,7 @@ """ import pandas as pd import seaborn as sns -sns.set(style="whitegrid", palette="pastel") +sns.set(style="whitegrid", palette="muted") # Load the example iris dataset iris = sns.load_dataset("iris") @@ -14,5 +14,4 @@ iris = pd.melt(iris, "species", var_name="measurement") # Draw a categorical scatterplot to show each observation -sns.stripplot(x="measurement", y="value", hue="species", data=iris, - jitter=True, edgecolor="gray") +sns.swarmplot(x="measurement", y="value", hue="species", data=iris) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index ef8d0782fd..1957e585de 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2246,8 +2246,7 @@ def boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, :context: close-figs >>> ax = sns.boxplot(x="day", y="total_bill", data=tips) - >>> ax = sns.swarmplot(x="day", y="total_bill", data=tips, - ... size=4, edgecolor="gray") + >>> ax = sns.swarmplot(x="day", y="total_bill", data=tips, color=".25") Draw a box plot on to a :class:`FacetGrid` to group within an additional categorical variable: From d68d37de20713d98b49eba2d43c395d25f888a7e Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 20 Dec 2015 18:23:20 -0800 Subject: [PATCH 0183/1738] Better variable names --- seaborn/categorical.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 1957e585de..d750e69364 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1228,7 +1228,6 @@ def prune_candidates(self, candidates, neighbors, d): good_candidate = False if good_candidate: good_candidates.append(xy_i) - assert good_candidates return np.array(good_candidates) def beeswarm(self, orig_xy, d): @@ -1293,13 +1292,11 @@ def swarm_points(self, ax, points, center, width, s, **kws): orig_xy = orig_xy[:, [1, 0]] # Do the beeswarm in point coordinates - swarm = self.beeswarm(orig_xy, d) + new_xy = self.beeswarm(orig_xy, d) # Transform the point coordinates back to data coordinates - if self.orient == "v": - new_xy = swarm[:, [0, 1]] - else: - new_xy = swarm[:, [1, 0]] + if self.orient == "h": + new_xy = new_xy[:, [1, 0]] new_x, new_y = ax.transData.inverted().transform(new_xy).T # Add gutters From a8b7dcee0c3ac253a864f429b99ed8126d95fc7b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 20 Dec 2015 18:25:36 -0800 Subject: [PATCH 0184/1738] Add release note about swarmplot --- doc/releases/v0.6.1.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.6.1.txt b/doc/releases/v0.6.1.txt index a4556f8ec6..7a6550bfef 100644 --- a/doc/releases/v0.6.1.txt +++ b/doc/releases/v0.6.1.txt @@ -2,6 +2,8 @@ v0.6.1 (Unreleased) ------------------- +- Added the :func:`swarmplot` function, which draws beeswarm plots. These are categorical scatterplots, similar to those produced by :func:`stripplot`, but position of the points on the categorical axis is chosen to avoid overlapping points. See the :ref:`categorical plot tutorial ` for more information. + - Added an additional rule when determining category order in categorical plots. Now, when numeric variables are used in a categorical role, the default behavior is to sort the unique levels of the variable (i.e they will be in proper numerical order). This can still be overriden by the appropriate ``{*_}order`` parameter, and variables with a ``category`` dtype will still follow the category order even if the levels are strictly numerical. - Improve performance for large dendrograms in :func:`clustermap`. From acf3773146bf6610c12fd32b297b9bada4327396 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 20 Dec 2015 18:54:42 -0800 Subject: [PATCH 0185/1738] Add swarmplot information to categorical plot tutorial --- doc/tutorial/categorical.ipynb | 88 ++++++++++++++++++++++++------- doc/tutorial/color_palettes.ipynb | 2 +- 2 files changed, 70 insertions(+), 20 deletions(-) diff --git a/doc/tutorial/categorical.ipynb b/doc/tutorial/categorical.ipynb index 644d82a512..e58322d50a 100644 --- a/doc/tutorial/categorical.ipynb +++ b/doc/tutorial/categorical.ipynb @@ -22,7 +22,7 @@ "source": [ "We :ref:`previously ` learned how to use scatterplots and regression model fits to visualize the relationship between two variables and how it changes across levels of additional categorical variables. However, what if one of the main variables you are interested in is categorical? In this case, the scatterplot and regression model approach won't work. There are several options, however, for visualizing such a relationship, which we will discuss in this tutorial.\n", "\n", - "It's useful to divide seaborn's categorical plots into two groups: those that show the full distribution of observations within each level of the categorical variable, and those that apply a statistical estimation to show a measure of central tendency and confidence interval. The former includes the functions :func:`stripplot`, :func:`boxplot`, and :func:`violinplot`, while the latter includes the functions :func:`barplot`, :func:`countplot`, and :func:`pointplot`. These functions all share a basic API for how they accept data, although each has specific parameters that control the particulars of the visualization that is applied to that data.\n", + "It's useful to divide seaborn's categorical plots into two groups: those that show the full distribution of observations within each level of the categorical variable, and those that apply a statistical estimation to show a measure of central tendency and confidence interval. The former includes the functions :func:`swarmplot`, :func:`stripplot`, :func:`boxplot`, and :func:`violinplot`, while the latter includes the functions :func:`barplot`, :func:`countplot`, and :func:`pointplot`. These functions all share a basic API for how they accept data, although each has specific parameters that control the particulars of the visualization that is applied to that data.\n", "\n", "Much like the relationship between :func:`regplot` and :func:`lmplot`, in seaborn there are both relatively low-level and relatively high-level approaches for making categorical plots. The functions named above are all low-level in that they plot onto a specific matplotlib axes. There is also the higher-level :func:`factorplot`, which combines these functions with a :class:`FacetGrid` to apply a categorical plot across a grid of figure panels.\n", "\n", @@ -116,6 +116,42 @@ "sns.stripplot(x=\"day\", y=\"total_bill\", data=tips);" ] }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "In a strip plot, the scatterplot points will usually overlap. This makes it difficult to see the full distribution of data. One easy solution is to adjust the positions (only along the categorical axis) using some random \"jitter\": " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.stripplot(x=\"day\", y=\"total_bill\", data=tips, jitter=True);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "A different approach would be to use the function :func:`swarmplot`, which positions each scatterplot point on the categorical axis with an algorithm that avoids overlapping points:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.swarmplot(x=\"day\", y=\"total_bill\", data=tips)" + ] + }, { "cell_type": "raw", "metadata": {}, @@ -131,13 +167,15 @@ }, "outputs": [], "source": [ - "sns.stripplot(x=\"day\", y=\"total_bill\", hue=\"time\", data=tips);" + "sns.swarmplot(x=\"day\", y=\"total_bill\", hue=\"time\", data=tips);" ] }, { "cell_type": "raw", "metadata": {}, - "source": [] + "source": [ + "In general, the seaborn categorical plotting functions try to infer the order of categories from the data. If your data have a pandas ``Categorical`` datatype, then the default order of the categories can be set there. For other datatypes, string-typed categories will be plotted in the order they appear in the DataFrame, but categories that look numerical will be sorted:" + ] }, { "cell_type": "code", @@ -147,14 +185,14 @@ }, "outputs": [], "source": [ - "sns.stripplot(x=\"size\", y=\"total_bill\", data=tips.sort(\"size\"));" + "sns.swarmplot(x=\"size\", y=\"total_bill\", data=tips);" ] }, { "cell_type": "raw", "metadata": {}, "source": [ - "With these plots, it's often helpful to put the categorical variable on the vertical axis (this is particularly useful when the category names are relatively long or there are many categories):" + "With these plots, it's often helpful to put the categorical variable on the vertical axis (this is particularly useful when the category names are relatively long or there are many categories). You can force an orientation using the ``orient`` keyword, but usually plot orientation can be inferred from the datatypes of the variables passed to ``x`` and/or ``y``:" ] }, { @@ -165,7 +203,7 @@ }, "outputs": [], "source": [ - "sns.stripplot(x=\"total_bill\", y=\"day\", hue=\"time\", data=tips);" + "sns.swarmplot(x=\"total_bill\", y=\"day\", hue=\"time\", data=tips);" ] }, { @@ -272,7 +310,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "It can also be useful to combine :func:`stripplot` with :func:`violinplot` or :func:`boxplot` to show each observation along with a summary of the distribution:" + "It can also be useful to combine :func:`swarmplot` or :func:`swarmplot` with :func:`violinplot` or :func:`boxplot` to show each observation along with a summary of the distribution:" ] }, { @@ -284,7 +322,7 @@ "outputs": [], "source": [ "sns.violinplot(x=\"day\", y=\"total_bill\", data=tips, inner=None)\n", - "sns.stripplot(x=\"day\", y=\"total_bill\", data=tips, jitter=True, size=4);" + "sns.swarmplot(x=\"day\", y=\"total_bill\", data=tips, color=\"w\", alpha=.5);" ] }, { @@ -396,12 +434,10 @@ "cell_type": "raw", "metadata": {}, "source": [ - "Drawing multi-panel categorical plots\n", - "-------------------------------------\n", - "\n", - "As we mentioned above, there are two ways to draw categorical plots in seaborn. Similar to the duality in the regression plots, you can either use the functions introduced above, or the higher-level function :func:`factorplot`, which combines these functions with a :func:`FacetGrid` to add the ability to examine additional categories through the larger structure of the figure.\n", + "Plotting \"wide-form\" data\n", + "-------------------------\n", "\n", - "While the main options for each plot kind are available either way, the lower-level functions have a bit more flexibility in the kind of inputs they can take. For instance, you can just pass a ``DataFrame`` to the ``data`` parameter, and the distribution or central tendency of each *column* in the dataframe will be shown:" + "While using \"long-form\" or \"tidy\" data is preferred, these functions can also by applied to \"wide-form\" data in a variety of formats, including pandas DataFrames or two-dimensional numpy arrays. These objects should be passed directly to the ``data`` parameter:" ] }, { @@ -456,7 +492,10 @@ "cell_type": "raw", "metadata": {}, "source": [ - "The :func:`factorplot` function is a higher-level wrapper on these plots that produces a matplotlib figure managed through a :class:`FacetGrid`:" + "Drawing multi-panel categorical plots\n", + "-------------------------------------\n", + "\n", + "As we mentioned above, there are two ways to draw categorical plots in seaborn. Similar to the duality in the regression plots, you can either use the functions introduced above, or the higher-level function :func:`factorplot`, which combines these functions with a :func:`FacetGrid` to add the ability to examine additional categories through the larger structure of the figure. By default, :func:`factorplot` produces a :func:`pairplot`:" ] }, { @@ -474,7 +513,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "By default it uses :func:`pairplot`, but the ``kind`` parameter lets you chose any of the kinds of plots discussed above:" + "However, the ``kind`` parameter lets you chose any of the kinds of plots discussed above:" ] }, { @@ -492,7 +531,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "The key advantage of :func:`factorplot` is that it's easy to add faceting by additional variables in the ``DataFrame``, such as along the columns:" + "The main advantage of using a :func:`factorplot` is that it is very easy to \"facet\" the plot and investigate the role of other categorical variables:" ] }, { @@ -504,7 +543,7 @@ "outputs": [], "source": [ "sns.factorplot(x=\"day\", y=\"total_bill\", hue=\"smoker\",\n", - " col=\"time\", data=tips, kind=\"bar\");" + " col=\"time\", data=tips, kind=\"swarm\");" ] }, { @@ -530,6 +569,8 @@ "cell_type": "raw", "metadata": {}, "source": [ + "It is important to note that you could also make this plot by using :func:`boxplot` and :class:`FacetGrid` directly. However, special care must be taken to ensure that the order of the categorical variables is enforced in each facet, either by using data with a ``Categorical`` datatype or by passing ``order`` and ``hue_order``.\n", + "\n", "Because of the generalized API of the categorical plots, they should be easy to apply to other more complex contexts. For example, they are easily combined with a :class:`PairGrid` to show categorical relationships across several different variables:" ] }, @@ -547,6 +588,15 @@ " aspect=.75, size=3.5)\n", "g.map(sns.violinplot, palette=\"pastel\");" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] } ], "metadata": { @@ -565,9 +615,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", - "version": "2.7.10" + "version": "2.7.11" } }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index 52edac4594..8f6cacbe89 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -778,4 +778,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file From 8fa11130c1661a1ac9ad4c66b5be733d745f493d Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 20 Dec 2015 18:57:16 -0800 Subject: [PATCH 0186/1738] Change release version in docs --- doc/releases/{v0.6.1.txt => v0.7.0.txt} | 2 +- doc/whatsnew.rst | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) rename doc/releases/{v0.6.1.txt => v0.7.0.txt} (98%) diff --git a/doc/releases/v0.6.1.txt b/doc/releases/v0.7.0.txt similarity index 98% rename from doc/releases/v0.6.1.txt rename to doc/releases/v0.7.0.txt index 7a6550bfef..2ab740e2c7 100644 --- a/doc/releases/v0.6.1.txt +++ b/doc/releases/v0.7.0.txt @@ -1,5 +1,5 @@ -v0.6.1 (Unreleased) +v0.7.0 (Unreleased) ------------------- - Added the :func:`swarmplot` function, which draws beeswarm plots. These are categorical scatterplots, similar to those produced by :func:`stripplot`, but position of the points on the categorical axis is chosen to avoid overlapping points. See the :ref:`categorical plot tutorial ` for more information. diff --git a/doc/whatsnew.rst b/doc/whatsnew.rst index 6c87ccf0ab..0be879984a 100644 --- a/doc/whatsnew.rst +++ b/doc/whatsnew.rst @@ -7,6 +7,8 @@ What's new in the package A catalog of new features, improvements, and bug-fixes in each release. +.. include:: releases/v0.7.0.txt + .. include:: releases/v0.6.0.txt .. include:: releases/v0.5.1.txt From 62e386a063179f5deecdb5216a7a0c75486f9f26 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 22 Dec 2015 19:21:48 -0800 Subject: [PATCH 0187/1738] Use matplotlib parameters that are more robust --- seaborn/categorical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index d750e69364..0cbeef646d 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -490,7 +490,7 @@ def draw_boxplot(self, ax, kws): def restyle_boxplot(self, artist_dict, color, kws): """Take a drawn matplotlib boxplot and make it look nice.""" for box in artist_dict["boxes"]: - box.update(dict(color=color, + box.update(dict(facecolor=color, zorder=.9, edgecolor=self.gray, linewidth=self.linewidth)) @@ -509,7 +509,7 @@ def restyle_boxplot(self, artist_dict, color, kws): linewidth=self.linewidth)) med.update(kws.get("medianprops", {})) for fly in artist_dict["fliers"]: - fly.update(dict(color=self.gray, + fly.update(dict(markerfacecolor=self.gray, marker="d", markeredgecolor=self.gray, markersize=self.fliersize)) From 5074412cb50192cd01fee1a3f5d4a84df9a020e7 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 22 Dec 2015 19:43:18 -0800 Subject: [PATCH 0188/1738] Handle property dicts differently from other kwargs --- seaborn/categorical.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 0cbeef646d..b54dd2fe5c 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -433,6 +433,10 @@ def draw_boxplot(self, ax, kws): """Use matplotlib to draw a boxplot on an Axes.""" vert = self.orient == "v" + props = {} + for obj in ["box", "whisker", "cap", "median", "flier"]: + props[obj] = kws.pop(obj + "props", {}) + for i, group_data in enumerate(self.plot_data): if self.plot_hues is None: @@ -456,7 +460,7 @@ def draw_boxplot(self, ax, kws): widths=self.width, **kws) color = self.colors[i] - self.restyle_boxplot(artist_dict, color, kws) + self.restyle_boxplot(artist_dict, color, props) else: # Draw nested groups of boxes offsets = self.hue_offsets @@ -484,36 +488,36 @@ def draw_boxplot(self, ax, kws): positions=[center], widths=self.nested_width, **kws) - self.restyle_boxplot(artist_dict, self.colors[j], kws) + self.restyle_boxplot(artist_dict, self.colors[j], props) # Add legend data, but just for one set of boxes - def restyle_boxplot(self, artist_dict, color, kws): + def restyle_boxplot(self, artist_dict, color, props): """Take a drawn matplotlib boxplot and make it look nice.""" for box in artist_dict["boxes"]: box.update(dict(facecolor=color, zorder=.9, edgecolor=self.gray, linewidth=self.linewidth)) - box.update(kws.get("boxprops", {})) + box.update(props["box"]) for whisk in artist_dict["whiskers"]: whisk.update(dict(color=self.gray, linewidth=self.linewidth, linestyle="-")) - whisk.update(kws.get("whiskerprops", {})) + whisk.update(props["whisker"]) for cap in artist_dict["caps"]: cap.update(dict(color=self.gray, linewidth=self.linewidth)) - cap.update(kws.get("capprops", {})) + cap.update(props["cap"]) for med in artist_dict["medians"]: med.update(dict(color=self.gray, linewidth=self.linewidth)) - med.update(kws.get("medianprops", {})) + med.update(props["median"]) for fly in artist_dict["fliers"]: fly.update(dict(markerfacecolor=self.gray, marker="d", markeredgecolor=self.gray, markersize=self.fliersize)) - fly.update(kws.get("flierprops", {})) + fly.update(props["flier"]) def plot(self, ax, boxplot_kws): """Make the plot.""" From 47a2038f3d3624b64bb4b5343b1fa0d714df56f0 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 22 Dec 2015 19:51:00 -0800 Subject: [PATCH 0189/1738] Update release notes --- doc/releases/v0.7.0.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.7.0.txt b/doc/releases/v0.7.0.txt index 2ab740e2c7..870658dd4b 100644 --- a/doc/releases/v0.7.0.txt +++ b/doc/releases/v0.7.0.txt @@ -14,4 +14,6 @@ v0.7.0 (Unreleased) - Fixed a bug in the zscore calculation in :func:`clustermap`. +- Added workarounds for some matplotlib boxplot issues, such as strange colors of outlier points. + - Added compatability for various IPython (and Jupyter) versions in functions that use widgets. From cc648c14ba43a53051a593915507a2819649c845 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 20 Jan 2016 08:56:24 -0800 Subject: [PATCH 0190/1738] Pass hex colors to regression scatterplot Avoids problem where matplotlib interprets rgb colors with three scatter points as a variable to be colormapped. Closes #802 --- seaborn/linearmodels.py | 3 +++ seaborn/tests/test_linearmodels.py | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index bfb0dd5796..bab47c9c1a 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -313,6 +313,9 @@ def plot(self, ax, scatter_kws, line_kws): else: color = self.color + # Ensure that color is hex to avoid matplotlib weidness + color = mpl.colors.rgb2hex(mpl.colors.colorConverter.to_rgb(color)) + # Let color in keyword arguments override overall plot color scatter_kws.setdefault("color", color) line_kws.setdefault("color", color) diff --git a/seaborn/tests/test_linearmodels.py b/seaborn/tests/test_linearmodels.py index 0162e1323c..6e6bc40ec3 100644 --- a/seaborn/tests/test_linearmodels.py +++ b/seaborn/tests/test_linearmodels.py @@ -556,3 +556,11 @@ def test_residplot_lowess(self): x, y = ax.lines[1].get_xydata().T npt.assert_array_equal(x, np.sort(self.df.x)) + + def test_three_point_colors(self): + + x, y = np.random.randn(2, 3) + ax = lm.regplot(x, y, color=(1, 0, 0)) + color = ax.collections[0].get_facecolors() + npt.assert_almost_equal(color[0, :3], + (1, 0, 0)) From a1c2109a0e4368c3e4075fe18dae35b51e706ad3 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 21 Jan 2016 08:25:10 -0800 Subject: [PATCH 0191/1738] Update release notes --- doc/releases/v0.7.0.txt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/releases/v0.7.0.txt b/doc/releases/v0.7.0.txt index 870658dd4b..5399e94d33 100644 --- a/doc/releases/v0.7.0.txt +++ b/doc/releases/v0.7.0.txt @@ -4,7 +4,7 @@ v0.7.0 (Unreleased) - Added the :func:`swarmplot` function, which draws beeswarm plots. These are categorical scatterplots, similar to those produced by :func:`stripplot`, but position of the points on the categorical axis is chosen to avoid overlapping points. See the :ref:`categorical plot tutorial ` for more information. -- Added an additional rule when determining category order in categorical plots. Now, when numeric variables are used in a categorical role, the default behavior is to sort the unique levels of the variable (i.e they will be in proper numerical order). This can still be overriden by the appropriate ``{*_}order`` parameter, and variables with a ``category`` dtype will still follow the category order even if the levels are strictly numerical. +- Added an additional rule when determining category order in categorical plots. Now, when numeric variables are used in a categorical role, the default behavior is to sort the unique levels of the variable (i.e they will be in proper numerical order). This can still be overridden by the appropriate ``{*_}order`` parameter, and variables with a ``category`` datatype will still follow the category order even if the levels are strictly numerical. - Improve performance for large dendrograms in :func:`clustermap`. @@ -14,6 +14,16 @@ v0.7.0 (Unreleased) - Fixed a bug in the zscore calculation in :func:`clustermap`. +- Fixed a bug in :func:`distplot` where sometimes the default number of bins would not be an integer. + +- Heatmap colorbars are now rasterized for better performance in vector plots. + - Added workarounds for some matplotlib boxplot issues, such as strange colors of outlier points. +- Added workarounds for an issue where violinplot edges would be missing or have random colors. + +- Fixed a bug on newer versions of matplotlib where a colormap would be erroneously applied to scatterplots with only three observations. + +- Updated seaborn for compatibility with matplotlib 1.5. + - Added compatability for various IPython (and Jupyter) versions in functions that use widgets. From 4dfd659bca1272a36226c7a1c8b6d3f91feb2c9c Mon Sep 17 00:00:00 2001 From: gkunter Date: Thu, 21 Jan 2016 19:24:57 +0100 Subject: [PATCH 0192/1738] Update mesh colors before drawing annotations This commit fixes Issue #830. Currently, ```mesh.get_facecolors()``` returns only the color code for one cell in the grid, but calling ```mesh.update_scalarmappable()``` changes this so that the color information is shown for all cells in the grid. --- seaborn/matrix.py | 1 + 1 file changed, 1 insertion(+) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index a5d821f65e..a50fb7c781 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -205,6 +205,7 @@ def _determine_cmap_params(self, plot_data, vmin, vmax, def _annotate_heatmap(self, ax, mesh): """Add textual labels with the value in each cell.""" + mesh.update_scalarmappable() xpos, ypos = np.meshgrid(ax.get_xticks(), ax.get_yticks()) for x, y, val, color in zip(xpos.flat, ypos.flat, mesh.get_array(), mesh.get_facecolors()): From 442219fb1c57ea9e3b733ec24efadd6dbf45125d Mon Sep 17 00:00:00 2001 From: gkunter Date: Thu, 21 Jan 2016 21:32:47 +0100 Subject: [PATCH 0193/1738] Add another test for heatmap annotations This commit adds a test for #831. Without the fix, the new test ```TestHeatmap.test_heatmap_annotation_mesh_color``` fails (```Assertion error: 1 != 32``). Also failing is ```TestHeatmap.test_heatmap_annotation_with_mask``` (```Assertion error: 7 != 2```). With the fix, both tests pass. --- seaborn/tests/test_matrix.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 59eda3ddce..cbcb523984 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -245,6 +245,14 @@ def test_heatmap_annotation_with_mask(self): for val, text in zip(df_masked[::-1].compressed(), ax.texts): nt.assert_equal("{:.1f}".format(val), text.get_text()) + def test_heatmap_annotation_mesh_colors(self): + + ax = mat.heatmap(self.df_norm, annot=True) + mesh = ax.collections[0] + nt.assert_equal(len(mesh.get_facecolors()), 4 * 8) + + plt.close("all") + def test_heatmap_cbar(self): f = plt.figure() From d32f42711b7d9452d8d727ea0db123aa3393ee4f Mon Sep 17 00:00:00 2001 From: gkunter Date: Thu, 21 Jan 2016 22:04:43 +0100 Subject: [PATCH 0194/1738] Get expected number of rows from self.df_norm.size --- seaborn/tests/test_matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index cbcb523984..960dd5e855 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -249,7 +249,7 @@ def test_heatmap_annotation_mesh_colors(self): ax = mat.heatmap(self.df_norm, annot=True) mesh = ax.collections[0] - nt.assert_equal(len(mesh.get_facecolors()), 4 * 8) + nt.assert_equal(len(mesh.get_facecolors()), self.df_norm.size) plt.close("all") From 6ec25277fedd37695c6c56f917159e86a0ffec1b Mon Sep 17 00:00:00 2001 From: gkunter Date: Thu, 21 Jan 2016 22:27:22 +0100 Subject: [PATCH 0195/1738] Use df_norm.values.size to ensure backward compability --- seaborn/tests/test_matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 960dd5e855..1ae4a38890 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -249,7 +249,7 @@ def test_heatmap_annotation_mesh_colors(self): ax = mat.heatmap(self.df_norm, annot=True) mesh = ax.collections[0] - nt.assert_equal(len(mesh.get_facecolors()), self.df_norm.size) + nt.assert_equal(len(mesh.get_facecolors()), self.df_norm.values.size) plt.close("all") From 86714f75700caa4cf1e5a92e141e1e7b1309b6aa Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 23 Jan 2016 17:04:58 -0800 Subject: [PATCH 0196/1738] Plot unsplit stripplots with a single scatter cloud --- seaborn/categorical.py | 49 +++++++++++++++---------------- seaborn/tests/test_categorical.py | 26 +++++++--------- 2 files changed, 35 insertions(+), 40 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index b54dd2fe5c..1e178e2e95 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1125,48 +1125,47 @@ def draw_stripplot(self, ax, kws): # Set the default zorder to 2.1, so that the points # will be drawn on top of line elements (like in a boxplot) kws.setdefault("zorder", 2.1) + s = kws.pop("s", 7 ** 2) for i, group_data in enumerate(self.plot_data): - if self.plot_hues is None: + if self.plot_hues is None or not self.split: - # Determine the positions of the points - strip_data = remove_na(group_data) - jitter = self.jitterer(len(strip_data)) - kws["color"] = mpl.colors.rgb2hex(self.colors[i]) + if self.hue_names is None: + hue_mask = np.ones(group_data.size, np.bool) + else: + hue_mask = np.in1d(self.plot_hues[i], self.hue_names) - # Draw the plot + strip_data = group_data[hue_mask] + + # Plot the points in centered positions + cat_pos = np.ones(strip_data.size) * i + cat_pos += self.jitterer(len(strip_data)) + kws.update(c=self.point_colors[i][hue_mask]) if self.orient == "v": - ax.scatter(i + jitter, strip_data, **kws) + ax.scatter(cat_pos, strip_data, s=s, **kws) else: - ax.scatter(strip_data, i + jitter, **kws) + ax.scatter(strip_data, cat_pos, s=s, **kws) else: offsets = self.hue_offsets for j, hue_level in enumerate(self.hue_names): hue_mask = self.plot_hues[i] == hue_level - if not hue_mask.any(): - continue - - # Determine the positions of the points - strip_data = remove_na(group_data[hue_mask]) - pos = i + offsets[j] if self.split else i - jitter = self.jitterer(len(strip_data)) - kws["color"] = mpl.colors.rgb2hex(self.colors[j]) - - # Only label one set of plots - if i: - kws.pop("label", None) - else: - kws["label"] = hue_level + strip_data = group_data[hue_mask] - # Draw the plot + # Plot the points in centered positions + center = i + offsets[j] + cat_pos = np.ones(strip_data.size) * center + cat_pos += self.jitterer(len(strip_data)) + kws.update(c=self.point_colors[i][hue_mask]) if self.orient == "v": - ax.scatter(pos + jitter, strip_data, **kws) + ax.scatter(cat_pos, strip_data, s=s, **kws) else: - ax.scatter(strip_data, pos + jitter, **kws) + ax.scatter(strip_data, cat_pos, s=s, **kws) + def plot(self, ax, kws): """Make the plot.""" self.draw_stripplot(ax, kws) + self.add_legend_data(ax) self.annotate_axes(ax) if self.orient == "h": ax.invert_yaxis() diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 753b2e298a..a67c04873f 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1557,20 +1557,14 @@ def test_split_nested_stripplot_horizontal(self): def test_unsplit_nested_stripplot_vertical(self): - pal = palettes.color_palette() - # Test a simple vertical strip plot ax = cat.stripplot("g", "y", "h", data=self.df, split=False) for i, (_, group_vals) in enumerate(self.y.groupby(self.g)): - for j, (_, vals) in enumerate(group_vals.groupby(self.h)): - - x, y = ax.collections[i * 2 + j].get_offsets().T - npt.assert_array_equal(x, np.ones(len(x)) * i) - npt.assert_array_equal(y, vals) + x, y = ax.collections[i].get_offsets().T - fc = ax.collections[i * 2 + j].get_facecolors()[0, :3] - npt.assert_equal(fc, pal[j]) + npt.assert_array_equal(x, np.ones(len(x)) * i) + npt.assert_array_equal(y, group_vals) @skipif(not pandas_has_categoricals) def test_unsplit_nested_stripplot_horizontal(self): @@ -1580,18 +1574,19 @@ def test_unsplit_nested_stripplot_horizontal(self): ax = cat.stripplot("y", "g", "h", data=df, split=False) for i, (_, group_vals) in enumerate(self.y.groupby(self.g)): - for j, (_, vals) in enumerate(group_vals.groupby(self.h)): - x, y = ax.collections[i * 2 + j].get_offsets().T + x, y = ax.collections[i].get_offsets().T - npt.assert_array_equal(x, vals) - npt.assert_array_equal(y, np.ones(len(x)) * i) + npt.assert_array_equal(x, group_vals) + npt.assert_array_equal(y, np.ones(len(x)) * i) def test_three_strip_points(self): x = np.arange(3) ax = cat.stripplot(x=x) - nt.assert_equal(ax.collections[0].get_facecolor().shape, (1, 4)) + facecolors = ax.collections[0].get_facecolor() + nt.assert_equal(facecolors.shape, (3, 4)) + npt.assert_array_equal(facecolors[0], facecolors[1]) class TestSwarmPlotter(CategoricalFixture): @@ -2320,7 +2315,8 @@ def test_plot_elements(self): nt.assert_equal(len(g.ax.collections), want_elements) g = cat.factorplot("g", "y", "h", data=self.df, kind="strip") - want_elements = self.g.unique().size * self.h.unique().size + n_hues = self.h.unique().size + want_elements = self.g.unique().size * n_hues + n_hues nt.assert_equal(len(g.ax.collections), want_elements) def test_bad_plot_kind_error(self): From 0fb813fe99f2937dc62638cb5596d917d87aa463 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 23 Jan 2016 18:19:40 -0800 Subject: [PATCH 0197/1738] PEP8 --- seaborn/categorical.py | 1 - 1 file changed, 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 1e178e2e95..5e5b3ac6e2 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1161,7 +1161,6 @@ def draw_stripplot(self, ax, kws): else: ax.scatter(strip_data, cat_pos, s=s, **kws) - def plot(self, ax, kws): """Make the plot.""" self.draw_stripplot(ax, kws) From 89d80975e44f151f28f83f3e097d561e051b3231 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 23 Jan 2016 18:30:33 -0800 Subject: [PATCH 0198/1738] Avoid old numpy incompatability in tests --- seaborn/categorical.py | 13 +++---------- seaborn/tests/test_categorical.py | 6 ++++++ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 5e5b3ac6e2..272aa15320 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2504,7 +2504,7 @@ def violinplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, - jitter=False, split=True, orient=None, color=None, palette=None, + jitter=False, split=False, orient=None, color=None, palette=None, size=7, edgecolor="w", linewidth=1, ax=None, **kwargs): plotter = _StripPlotter(x, y, hue, data, order, hue_order, @@ -2619,7 +2619,7 @@ def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.stripplot(x="sex", y="total_bill", hue="day", ... data=tips, jitter=True) - Draw each level of the ``hue`` variable at the same location on the + Draw each level of the ``hue`` variable at different locations on the major categorical axis: .. plot:: @@ -2627,14 +2627,7 @@ def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.stripplot(x="day", y="total_bill", hue="smoker", ... data=tips, jitter=True, - ... palette="Set2", split=False) - - Control strip order by sorting the input data: - - .. plot:: - :context: close-figs - - >>> ax = sns.stripplot(x="size", y="tip", data=tips.sort("size")) + ... palette="Set2", split=True) Control strip order by passing an explicit order: diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index a67c04873f..66086a2cff 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1557,6 +1557,9 @@ def test_split_nested_stripplot_horizontal(self): def test_unsplit_nested_stripplot_vertical(self): + if LooseVersion(np.__version__) < "1.7": + raise nose.SkipTest + # Test a simple vertical strip plot ax = cat.stripplot("g", "y", "h", data=self.df, split=False) for i, (_, group_vals) in enumerate(self.y.groupby(self.g)): @@ -1569,6 +1572,9 @@ def test_unsplit_nested_stripplot_vertical(self): @skipif(not pandas_has_categoricals) def test_unsplit_nested_stripplot_horizontal(self): + if LooseVersion(np.__version__) < "1.7": + raise nose.SkipTest + df = self.df.copy() df.g = df.g.astype("category") From d47cd2d6604a62fa88a90ed84854ea5466994823 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 23 Jan 2016 18:55:40 -0800 Subject: [PATCH 0199/1738] Align stripplot defaults with swarmplot --- seaborn/categorical.py | 41 ++++++++++++++++++++----------- seaborn/tests/test_categorical.py | 7 +++--- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 272aa15320..32bfda497d 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1124,8 +1124,6 @@ def draw_stripplot(self, ax, kws): """Draw the points onto `ax`.""" # Set the default zorder to 2.1, so that the points # will be drawn on top of line elements (like in a boxplot) - kws.setdefault("zorder", 2.1) - s = kws.pop("s", 7 ** 2) for i, group_data in enumerate(self.plot_data): if self.plot_hues is None or not self.split: @@ -1141,9 +1139,9 @@ def draw_stripplot(self, ax, kws): cat_pos += self.jitterer(len(strip_data)) kws.update(c=self.point_colors[i][hue_mask]) if self.orient == "v": - ax.scatter(cat_pos, strip_data, s=s, **kws) + ax.scatter(cat_pos, strip_data, **kws) else: - ax.scatter(strip_data, cat_pos, s=s, **kws) + ax.scatter(strip_data, cat_pos, **kws) else: offsets = self.hue_offsets @@ -1157,9 +1155,9 @@ def draw_stripplot(self, ax, kws): cat_pos += self.jitterer(len(strip_data)) kws.update(c=self.point_colors[i][hue_mask]) if self.orient == "v": - ax.scatter(cat_pos, strip_data, s=s, **kws) + ax.scatter(cat_pos, strip_data, **kws) else: - ax.scatter(strip_data, cat_pos, s=s, **kws) + ax.scatter(strip_data, cat_pos, **kws) def plot(self, ax, kws): """Make the plot.""" @@ -1312,7 +1310,7 @@ def swarm_points(self, ax, points, center, width, s, **kws): def draw_swarmplot(self, ax, kws): """Plot the data.""" - s = kws.pop("s", 7 ** 2) + s = kws.pop("s") centers = [] swarms = [] @@ -2505,16 +2503,22 @@ def violinplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, jitter=False, split=False, orient=None, color=None, palette=None, - size=7, edgecolor="w", linewidth=1, ax=None, **kwargs): + size=5, edgecolor="gray", linewidth=0, ax=None, **kwargs): plotter = _StripPlotter(x, y, hue, data, order, hue_order, jitter, split, orient, color, palette) if ax is None: ax = plt.gca() - kwargs.update(dict(s=size ** 2, edgecolor=edgecolor, linewidth=linewidth)) + kwargs.setdefault("zorder", 3) + size = kwargs.get("s", size) + if linewidth is None: + linewidth = size / 10 if edgecolor == "gray": - kwargs["edgecolor"] = plotter.gray + edgecolor = plotter.gray + kwargs.update(dict(s=size ** 2, + edgecolor=edgecolor, + linewidth=linewidth)) plotter.plot(ax, kwargs) return ax @@ -2611,6 +2615,14 @@ def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.stripplot(x="total_bill", y="day", data=tips, ... jitter=True) + Draw outlines around the points: + + .. plot:: + :context: close-figs + + >>> ax = sns.stripplot(x="total_bill", y="day", data=tips, + ... jitter=True, linewidth=1) + Nest the strips within a second categorical variable: .. plot:: @@ -2652,16 +2664,17 @@ def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, :context: close-figs >>> ax = sns.boxplot(x="tip", y="day", data=tips, whis=np.inf) - >>> ax = sns.stripplot(x="tip", y="day", data=tips, jitter=True) + >>> ax = sns.stripplot(x="tip", y="day", data=tips, + ... jitter=True, color=".3") Draw strips of observations on top of a violin plot: .. plot:: :context: close-figs - >>> ax = sns.violinplot(x="day", y="total_bill", data=tips, inner=None) - >>> ax = sns.stripplot(x="day", y="total_bill", data=tips, - ... jitter=True, color="white", edgecolor="gray") + >>> ax = sns.violinplot(x="day", y="total_bill", data=tips, + ... inner=None, color=".8") + >>> ax = sns.stripplot(x="day", y="total_bill", data=tips, jitter=True) """).format(**_categorical_docs) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 66086a2cff..f5f00c92f8 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1528,7 +1528,7 @@ def test_split_nested_stripplot_vertical(self): pal = palettes.color_palette() - ax = cat.stripplot("g", "y", "h", data=self.df) + ax = cat.stripplot("g", "y", "h", data=self.df, split=True) for i, (_, group_vals) in enumerate(self.y.groupby(self.g)): for j, (_, vals) in enumerate(group_vals.groupby(self.h)): @@ -1546,7 +1546,7 @@ def test_split_nested_stripplot_horizontal(self): df = self.df.copy() df.g = df.g.astype("category") - ax = cat.stripplot("y", "g", "h", data=df) + ax = cat.stripplot("y", "g", "h", data=df, split=True) for i, (_, group_vals) in enumerate(self.y.groupby(self.g)): for j, (_, vals) in enumerate(group_vals.groupby(self.h)): @@ -2321,8 +2321,7 @@ def test_plot_elements(self): nt.assert_equal(len(g.ax.collections), want_elements) g = cat.factorplot("g", "y", "h", data=self.df, kind="strip") - n_hues = self.h.unique().size - want_elements = self.g.unique().size * n_hues + n_hues + want_elements = self.g.unique().size + self.h.unique().size nt.assert_equal(len(g.ax.collections), want_elements) def test_bad_plot_kind_error(self): From 8f0b89b714cebd698730e226726c44b63a4a497e Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 23 Jan 2016 18:58:25 -0800 Subject: [PATCH 0200/1738] Update release notes --- doc/releases/v0.7.0.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/releases/v0.7.0.txt b/doc/releases/v0.7.0.txt index 5399e94d33..ee5d9f6e58 100644 --- a/doc/releases/v0.7.0.txt +++ b/doc/releases/v0.7.0.txt @@ -6,6 +6,10 @@ v0.7.0 (Unreleased) - Added an additional rule when determining category order in categorical plots. Now, when numeric variables are used in a categorical role, the default behavior is to sort the unique levels of the variable (i.e they will be in proper numerical order). This can still be overridden by the appropriate ``{*_}order`` parameter, and variables with a ``category`` datatype will still follow the category order even if the levels are strictly numerical. +- Changed how :func:`stripplot` draws points when using ``hue`` nesting with ``split=False`` so that the different ``hue`` levels are not drawn strictly on top of each other. + +- Changed some of the :func:`stripplot` defaults to be closer to :func:`swarmplot`: points are somewhat smaller, have no outlines, and are not split by default when using ``hue``. + - Improve performance for large dendrograms in :func:`clustermap`. - Added ``font.size`` to the plotting context definition so that the default output from ``plt.text`` will be scaled appropriately. @@ -16,6 +20,8 @@ v0.7.0 (Unreleased) - Fixed a bug in :func:`distplot` where sometimes the default number of bins would not be an integer. +- Fixed a bug in :func:`stripplot` where a legend item would not appear for a ``hue`` level if there were no observations in the first group of points. + - Heatmap colorbars are now rasterized for better performance in vector plots. - Added workarounds for some matplotlib boxplot issues, such as strange colors of outlier points. From d252d20b8d95bdbe0a70f3afa36799c201a1b018 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 23 Jan 2016 19:11:37 -0800 Subject: [PATCH 0201/1738] Numpy 1.7 compat --- seaborn/categorical.py | 10 ++++++++-- seaborn/tests/test_categorical.py | 13 ------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 32bfda497d..397cc58b47 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1130,7 +1130,10 @@ def draw_stripplot(self, ax, kws): if self.hue_names is None: hue_mask = np.ones(group_data.size, np.bool) else: - hue_mask = np.in1d(self.plot_hues[i], self.hue_names) + hue_mask = np.array([h in self.hue_names + for h in self.plot_hues[i]]) + # Broken on older numpys + # hue_mask = np.in1d(self.plot_hues[i], self.hue_names) strip_data = group_data[hue_mask] @@ -1331,7 +1334,10 @@ def draw_swarmplot(self, ax, kws): if self.hue_names is None: hue_mask = np.ones(group_data.size, np.bool) else: - hue_mask = np.in1d(self.plot_hues[i], self.hue_names) + hue_mask = np.array([h in self.hue_names + for h in self.plot_hues[i]]) + # Broken on older numpys + # hue_mask = np.in1d(self.plot_hues[i], self.hue_names) swarm_data = group_data[hue_mask] diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index f5f00c92f8..3c0f921b90 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -7,7 +7,6 @@ from distutils.version import LooseVersion -import nose import nose.tools as nt import numpy.testing as npt from numpy.testing.decorators import skipif @@ -1557,9 +1556,6 @@ def test_split_nested_stripplot_horizontal(self): def test_unsplit_nested_stripplot_vertical(self): - if LooseVersion(np.__version__) < "1.7": - raise nose.SkipTest - # Test a simple vertical strip plot ax = cat.stripplot("g", "y", "h", data=self.df, split=False) for i, (_, group_vals) in enumerate(self.y.groupby(self.g)): @@ -1572,9 +1568,6 @@ def test_unsplit_nested_stripplot_vertical(self): @skipif(not pandas_has_categoricals) def test_unsplit_nested_stripplot_horizontal(self): - if LooseVersion(np.__version__) < "1.7": - raise nose.SkipTest - df = self.df.copy() df.g = df.g.astype("category") @@ -1709,9 +1702,6 @@ def test_split_nested_swarmplot_horizontal(self): def test_unsplit_nested_swarmplot_vertical(self): - if LooseVersion(np.__version__) < "1.7": - raise nose.SkipTest - ax = cat.swarmplot("g", "y", "h", data=self.df) pal = palettes.color_palette() @@ -1733,9 +1723,6 @@ def test_unsplit_nested_swarmplot_vertical(self): def test_unsplit_nested_swarmplot_horizontal(self): - if LooseVersion(np.__version__) < "1.7": - raise nose.SkipTest - ax = cat.swarmplot("y", "g", "h", data=self.df, orient="h") pal = palettes.color_palette() From 0ca58a717d88cd344d1c5975ec244982ec8ce64f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Jan 2016 17:37:48 -0800 Subject: [PATCH 0202/1738] Force boolean mask type --- seaborn/categorical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 397cc58b47..3998f9d039 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1131,7 +1131,7 @@ def draw_stripplot(self, ax, kws): hue_mask = np.ones(group_data.size, np.bool) else: hue_mask = np.array([h in self.hue_names - for h in self.plot_hues[i]]) + for h in self.plot_hues[i]], np.bool) # Broken on older numpys # hue_mask = np.in1d(self.plot_hues[i], self.hue_names) @@ -1335,7 +1335,7 @@ def draw_swarmplot(self, ax, kws): hue_mask = np.ones(group_data.size, np.bool) else: hue_mask = np.array([h in self.hue_names - for h in self.plot_hues[i]]) + for h in self.plot_hues[i]], np.bool) # Broken on older numpys # hue_mask = np.in1d(self.plot_hues[i], self.hue_names) From 9db701bc8ab8252c8b821747f63111d63c52d29f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Jan 2016 17:44:41 -0800 Subject: [PATCH 0203/1738] Update release notes for 0.7 release --- doc/releases/v0.7.0.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/doc/releases/v0.7.0.txt b/doc/releases/v0.7.0.txt index ee5d9f6e58..93c31e9c48 100644 --- a/doc/releases/v0.7.0.txt +++ b/doc/releases/v0.7.0.txt @@ -1,6 +1,8 @@ -v0.7.0 (Unreleased) -------------------- +v0.7.0 (January 2016) +--------------------- + +This is a major release from 0.6. The main new feature is :func:`swarmplot` which implements the beeswarm approach for drawing categorical scatterplots. There are also some performance improvements, bug fixes, and updates for compatibility with new versions of dependencies. - Added the :func:`swarmplot` function, which draws beeswarm plots. These are categorical scatterplots, similar to those produced by :func:`stripplot`, but position of the points on the categorical axis is chosen to avoid overlapping points. See the :ref:`categorical plot tutorial ` for more information. @@ -28,8 +30,10 @@ v0.7.0 (Unreleased) - Added workarounds for an issue where violinplot edges would be missing or have random colors. +- Added a workaround for an issue where only one :func:`heatmap` cell would be annotated on some matplotlib backends. + - Fixed a bug on newer versions of matplotlib where a colormap would be erroneously applied to scatterplots with only three observations. - Updated seaborn for compatibility with matplotlib 1.5. -- Added compatability for various IPython (and Jupyter) versions in functions that use widgets. +- Added compatibility for various IPython (and Jupyter) versions in functions that use widgets. From cb84f65ddade9077e4343135de31a0c0bf15075f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Jan 2016 17:44:54 -0800 Subject: [PATCH 0204/1738] Reformat categorical docs into three kinds of plots --- doc/tutorial/categorical.ipynb | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/doc/tutorial/categorical.ipynb b/doc/tutorial/categorical.ipynb index e58322d50a..82bb9e652c 100644 --- a/doc/tutorial/categorical.ipynb +++ b/doc/tutorial/categorical.ipynb @@ -22,7 +22,7 @@ "source": [ "We :ref:`previously ` learned how to use scatterplots and regression model fits to visualize the relationship between two variables and how it changes across levels of additional categorical variables. However, what if one of the main variables you are interested in is categorical? In this case, the scatterplot and regression model approach won't work. There are several options, however, for visualizing such a relationship, which we will discuss in this tutorial.\n", "\n", - "It's useful to divide seaborn's categorical plots into two groups: those that show the full distribution of observations within each level of the categorical variable, and those that apply a statistical estimation to show a measure of central tendency and confidence interval. The former includes the functions :func:`swarmplot`, :func:`stripplot`, :func:`boxplot`, and :func:`violinplot`, while the latter includes the functions :func:`barplot`, :func:`countplot`, and :func:`pointplot`. These functions all share a basic API for how they accept data, although each has specific parameters that control the particulars of the visualization that is applied to that data.\n", + "It's useful to divide seaborn's categorical plots into three groups: those that show each observation at each level of the categorical variable, those that show an abstract representation of each *distribution* of observations, and those that apply a statistical estimation to show a measure of central tendency and confidence interval. The first includes the functions :func:`swarmplot` and :func:`stripplot`, the second includes :func:`boxplot` and :func:`violinplot`, and the third includes :func:`barplot` and :func:`pointplot`. These functions all share a basic API for how they accept data, although each has specific parameters that control the particulars of the visualization that is applied to that data.\n", "\n", "Much like the relationship between :func:`regplot` and :func:`lmplot`, in seaborn there are both relatively low-level and relatively high-level approaches for making categorical plots. The functions named above are all low-level in that they plot onto a specific matplotlib axes. There is also the higher-level :func:`factorplot`, which combines these functions with a :class:`FacetGrid` to apply a categorical plot across a grid of figure panels.\n", "\n", @@ -94,15 +94,10 @@ "cell_type": "raw", "metadata": {}, "source": [ - "Distributions of observations within categories\n", - "-----------------------------------------------\n", - "\n", - "The first set of functions shows the full distribution of the quantitative variable within each level of the categorical variable(s). These generalize some of the approaches we discussed in the :ref:`chapter ` to the case where we want to quickly compare across several distributions.\n", - "\n", "Categorical scatterplots\n", - "^^^^^^^^^^^^^^^^^^^^^^^^\n", + "------------------------\n", "\n", - "A simple way to show the distribution of some quantitative variable across the levels of a categorical variable uses :func:`stripplot`, which generalizes a scatterplot to the case where one of the variables is categorical:" + "A simple way to show the the values of some quantitative variable across the levels of a categorical variable uses :func:`stripplot`, which generalizes a scatterplot to the case where one of the variables is categorical:" ] }, { @@ -149,7 +144,7 @@ }, "outputs": [], "source": [ - "sns.swarmplot(x=\"day\", y=\"total_bill\", data=tips)" + "sns.swarmplot(x=\"day\", y=\"total_bill\", data=tips);" ] }, { @@ -167,7 +162,7 @@ }, "outputs": [], "source": [ - "sns.swarmplot(x=\"day\", y=\"total_bill\", hue=\"time\", data=tips);" + "sns.swarmplot(x=\"day\", y=\"total_bill\", hue=\"sex\", data=tips);" ] }, { @@ -210,11 +205,14 @@ "cell_type": "raw", "metadata": {}, "source": [ + "Distributions of observations within categories\n", + "-----------------------------------------------\n", + "\n", + "At a certain point, the categorical scatterplot approach becomes limited in the information it can provide about the distribution of values within each category. There are several ways to summarize this information in ways that facilitate easy comparisons across the category levels. These generalize some of the approaches we discussed in the :ref:`chapter ` to the case where we want to quickly compare across several distributions.\n", + "\n", "Boxplots\n", "^^^^^^^^\n", "\n", - "At a certain point, the categorical scatterplot approach becomes limited in the information it can provide about the distribution of values within each category. There are several ways to summarize this information in ways that facilitate easy comparisons across the category levels.\n", - "\n", "The first is the familiar :func:`boxplot`. This kind of plot shows the three quartile values of the distribution along with extreme values. The \"whiskers\" extend to points that lie within 1.5 IQRs of the lower and upper quartile, and then observations that fall outside this range are displayed independently. Importantly, this means that each value in the boxplot corresponds to an actual observation in the data:" ] }, From d0e5fd86454729be8c2746fa785b0dc20c971ae8 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Jan 2016 18:42:06 -0800 Subject: [PATCH 0205/1738] Reorder release notes --- doc/releases/v0.7.0.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/releases/v0.7.0.txt b/doc/releases/v0.7.0.txt index 93c31e9c48..669d663cef 100644 --- a/doc/releases/v0.7.0.txt +++ b/doc/releases/v0.7.0.txt @@ -8,9 +8,9 @@ This is a major release from 0.6. The main new feature is :func:`swarmplot` whic - Added an additional rule when determining category order in categorical plots. Now, when numeric variables are used in a categorical role, the default behavior is to sort the unique levels of the variable (i.e they will be in proper numerical order). This can still be overridden by the appropriate ``{*_}order`` parameter, and variables with a ``category`` datatype will still follow the category order even if the levels are strictly numerical. -- Changed how :func:`stripplot` draws points when using ``hue`` nesting with ``split=False`` so that the different ``hue`` levels are not drawn strictly on top of each other. +- Changed some of the :func:`stripplot` defaults to be closer to :func:`swarmplot` points are somewhat smaller, have no outlines, and are not split by default when using ``hue``. -- Changed some of the :func:`stripplot` defaults to be closer to :func:`swarmplot`: points are somewhat smaller, have no outlines, and are not split by default when using ``hue``. +- Changed how :func:`stripplot` draws points when using ``hue`` nesting with ``split=False`` so that the different ``hue`` levels are not drawn strictly on top of each other. - Improve performance for large dendrograms in :func:`clustermap`. From 7b6fdb400dc1ba9e8a00fc0d0b6e0fcfd87363bf Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Jan 2016 19:03:35 -0800 Subject: [PATCH 0206/1738] Remove DataFrame sorts from doctests to avoid version headaches --- seaborn/axisgrid.py | 2 +- seaborn/categorical.py | 72 +++++++++++------------------------------- 2 files changed, 19 insertions(+), 55 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 8268e47ec1..9b1dd769dc 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -607,7 +607,7 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, .. plot:: :context: close-figs - >>> g = sns.FacetGrid(tips.sort("size"), col="size", col_wrap=3) + >>> g = sns.FacetGrid(tips, col="size", col_wrap=3) >>> g = (g.map(plt.hist, "tip", bins=np.arange(0, 13), color="c") ... .set_titles("{{col_name}} diners")) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 3998f9d039..867df00226 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2220,20 +2220,13 @@ def boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.boxplot(x="day", y="total_bill", hue="time", ... data=tips, linewidth=2.5) - Control box order by sorting the input data: - - .. plot:: - :context: close-figs - - >>> ax = sns.boxplot(x="size", y="tip", data=tips.sort("size")) - Control box order by passing an explicit order: .. plot:: :context: close-figs - >>> ax = sns.boxplot(x="size", y="tip", data=tips, - ... order=np.arange(1, 7), palette="Blues_d") + >>> ax = sns.boxplot(x="time", y="tip", data=tips, + ... order=["Dinner", "Lunch"]) Draw a boxplot for each numeric variable in a DataFrame: @@ -2421,20 +2414,13 @@ def violinplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.violinplot(x="day", y="total_bill", hue="smoker", ... data=tips, palette="muted", split=True) - Control violin order by sorting the input data: - - .. plot:: - :context: close-figs - - >>> ax = sns.violinplot(x="size", y="tip", data=tips.sort("size")) - Control violin order by passing an explicit order: .. plot:: :context: close-figs - >>> ax = sns.violinplot(x="size", y="tip", data=tips, - ... order=np.arange(1, 7), palette="Blues_d") + >>> ax = sns.violinplot(x="time", y="tip", data=tips, + ... order=["Dinner", "Lunch"]) Scale the violin width by the number of observations in each bin: @@ -2652,8 +2638,8 @@ def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, .. plot:: :context: close-figs - >>> ax = sns.stripplot(x="size", y="tip", data=tips, - ... order=np.arange(1, 7), palette="Blues_d") + >>> ax = sns.stripplot(x="time", y="tip", data=tips, + ... order=["Dinner", "Lunch"]) Draw strips with large points and different aesthetics: @@ -2819,16 +2805,15 @@ def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, .. plot:: :context: close-figs - >>> ax = sns.swarmplot(x="size", y="tip", data=tips, - ... order=np.arange(1, 7), palette="Blues_d") + >>> ax = sns.swarmplot(x="time", y="tip", data=tips, + ... order=["Dinner", "Lunch"]) - Plot using smaller points: + Plot using larger points: .. plot:: :context: close-figs - >>> ax = sns.swarmplot(x="size", y="tip", data=tips, size=4, - ... order=np.arange(1, 7), palette="Blues_d") + >>> ax = sns.swarmplot(x="time", y="tip", data=tips, size=6) Draw swarms of observations on top of a box plot: @@ -2958,20 +2943,13 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.barplot(x="tip", y="day", data=tips) - Control bar order by sorting the input data: - - .. plot:: - :context: close-figs - - >>> ax = sns.barplot(x="size", y="tip", data=tips.sort("size")) - Control bar order by passing an explicit order: .. plot:: :context: close-figs - >>> ax = sns.barplot(x="size", y="tip", data=tips, - ... order=np.arange(1, 7), palette="Blues_d") + >>> ax = sns.barplot(x="time", y="tip", data=tips, + ... order=["Dinner", "Lunch"]) Use median as the estimate of central tendency: @@ -2993,7 +2971,7 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, .. plot:: :context: close-figs - >>> ax = sns.barplot("size", y="total_bill", data=tips.sort("size"), + >>> ax = sns.barplot("size", y="total_bill", data=tips, ... palette="Blues_d") Plot all bars in a single color: @@ -3001,7 +2979,7 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, .. plot:: :context: close-figs - >>> ax = sns.barplot("size", y="total_bill", data=tips.sort("size"), + >>> ax = sns.barplot("size", y="total_bill", data=tips, ... color="salmon", saturation=.5) Use ``plt.bar`` keyword arguments to further change the aesthetic: @@ -3172,20 +3150,13 @@ def pointplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.pointplot(x="time", y="total_bill", hue="smoker", ... data=tips, palette="Set2") - Control point order by sorting the input data: - - .. plot:: - :context: close-figs - - >>> ax = sns.pointplot(x="size", y="tip", data=tips.sort("size")) - Control point order by passing an explicit order: .. plot:: :context: close-figs - >>> ax = sns.pointplot(x="size", y="tip", data=tips, - ... order=np.arange(1, 7), palette="Blues_d") + >>> ax = sns.pointplot(x="time", y="tip", data=tips, + ... order=["Dinner", "Lunch"]) Use median as the estimate of central tendency: @@ -3665,20 +3636,13 @@ def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.lvplot(x="day", y="total_bill", hue="time", ... data=tips, linewidth=2.5) - Control box order by sorting the input data: - - .. plot:: - :context: close-figs - - >>> ax = sns.lvplot(x="size", y="tip", data=tips.sort("size")) - Control box order by passing an explicit order: .. plot:: :context: close-figs - >>> ax = sns.lvplot(x="size", y="tip", data=tips, - ... order=np.arange(1, 7), palette="Blues_d") + >>> ax = sns.lvplot(x="time", y="tip", data=tips, + ... order=["Dinner", "Lunch"]) Draw a letter value plot for each numeric variable in a DataFrame: From 5676135503653b7db48003edc2b8c7280b755130 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Jan 2016 19:04:06 -0800 Subject: [PATCH 0207/1738] PEP8 --- seaborn/categorical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 867df00226..eaaa957095 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -3626,7 +3626,7 @@ def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, :context: close-figs >>> ax = sns.lvplot(x="day", y="total_bill", hue="smoker", - ... data=tips, palette="Set3") + ... data=tips, palette="Set3") Draw a letter value plot with nested grouping when some bins are empty: @@ -3634,7 +3634,7 @@ def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, :context: close-figs >>> ax = sns.lvplot(x="day", y="total_bill", hue="time", - ... data=tips, linewidth=2.5) + ... data=tips, linewidth=2.5) Control box order by passing an explicit order: From 551db95d5dc456f78edc37dca7900102a0879d33 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Jan 2016 19:15:30 -0800 Subject: [PATCH 0208/1738] Update DataFrame sort in example scripts --- examples/horizontal_barplot.py | 2 +- examples/pairgrid_dotplot.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/horizontal_barplot.py b/examples/horizontal_barplot.py index 0b50d53265..0e022f8b54 100644 --- a/examples/horizontal_barplot.py +++ b/examples/horizontal_barplot.py @@ -11,7 +11,7 @@ f, ax = plt.subplots(figsize=(6, 15)) # Load the example car crash dataset -crashes = sns.load_dataset("car_crashes").sort("total", ascending=False) +crashes = sns.load_dataset("car_crashes").sort_values("total", ascending=False) # Plot the total crashes sns.set_color_codes("pastel") diff --git a/examples/pairgrid_dotplot.py b/examples/pairgrid_dotplot.py index 73dda57871..778621e557 100644 --- a/examples/pairgrid_dotplot.py +++ b/examples/pairgrid_dotplot.py @@ -11,7 +11,7 @@ crashes = sns.load_dataset("car_crashes") # Make the PairGrid -g = sns.PairGrid(crashes.sort("total", ascending=False), +g = sns.PairGrid(crashes.sort_values("total", ascending=False), x_vars=crashes.columns[:-3], y_vars=["abbrev"], size=10, aspect=.25) From 8d38c5b9e2bcb305782aba616fb94b9d78e51957 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Jan 2016 19:21:55 -0800 Subject: [PATCH 0209/1738] Remove additional DataFrame sort --- seaborn/categorical.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index eaaa957095..49b56729aa 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2792,14 +2792,6 @@ def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.swarmplot(x="day", y="total_bill", hue="smoker", ... data=tips, palette="Set2", split=True) - Control swarm order by sorting the input data: - - .. plot:: - :context: close-figs - - >>> ax = sns.swarmplot(x="size", y="tip", - ... data=tips.sort_values("size")) - Control swarm order by passing an explicit order: .. plot:: From 22a886b3563de4d9080ae21244bc5325a6fdf16a Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Jan 2016 19:54:14 -0800 Subject: [PATCH 0210/1738] Bump version for 0.7.0 release --- seaborn/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/__init__.py b/seaborn/__init__.py index 7bf6691e81..671ef945c8 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -13,4 +13,4 @@ from .crayons import crayons set() -__version__ = "0.7.dev" +__version__ = "0.7.0" diff --git a/setup.py b/setup.py index 6c0872fd10..17813d28e7 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ URL = 'http://stanford.edu/~mwaskom/software/seaborn/' LICENSE = 'BSD (3-clause)' DOWNLOAD_URL = 'https://github.com/mwaskom/seaborn/' -VERSION = '0.7.0.dev' +VERSION = '0.7.0' try: from setuptools import setup From 683dec73472b541a500bc6294baaac43863c603b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Jan 2016 20:10:15 -0800 Subject: [PATCH 0211/1738] Reorder and update release notes --- doc/releases/v0.7.0.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/releases/v0.7.0.txt b/doc/releases/v0.7.0.txt index 669d663cef..d809c10320 100644 --- a/doc/releases/v0.7.0.txt +++ b/doc/releases/v0.7.0.txt @@ -6,9 +6,9 @@ This is a major release from 0.6. The main new feature is :func:`swarmplot` whic - Added the :func:`swarmplot` function, which draws beeswarm plots. These are categorical scatterplots, similar to those produced by :func:`stripplot`, but position of the points on the categorical axis is chosen to avoid overlapping points. See the :ref:`categorical plot tutorial ` for more information. -- Added an additional rule when determining category order in categorical plots. Now, when numeric variables are used in a categorical role, the default behavior is to sort the unique levels of the variable (i.e they will be in proper numerical order). This can still be overridden by the appropriate ``{*_}order`` parameter, and variables with a ``category`` datatype will still follow the category order even if the levels are strictly numerical. +- Changed some of the :func:`stripplot` defaults to be closer to :func:`swarmplot`. Points are now somewhat smaller, have no outlines, and are not split by default when using ``hue``. These settings remain customizable through function parameters. -- Changed some of the :func:`stripplot` defaults to be closer to :func:`swarmplot` points are somewhat smaller, have no outlines, and are not split by default when using ``hue``. +- Added an additional rule when determining category order in categorical plots. Now, when numeric variables are used in a categorical role, the default behavior is to sort the unique levels of the variable (i.e they will be in proper numerical order). This can still be overridden by the appropriate ``{*_}order`` parameter, and variables with a ``category`` datatype will still follow the category order even if the levels are strictly numerical. - Changed how :func:`stripplot` draws points when using ``hue`` nesting with ``split=False`` so that the different ``hue`` levels are not drawn strictly on top of each other. From fb179fcba0778b275ccf60d4b7f324daa99faf28 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Jan 2016 20:22:36 -0800 Subject: [PATCH 0212/1738] Back to development --- seaborn/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/__init__.py b/seaborn/__init__.py index 671ef945c8..e4881d378e 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -13,4 +13,4 @@ from .crayons import crayons set() -__version__ = "0.7.0" +__version__ = "0.8.dev" diff --git a/setup.py b/setup.py index 17813d28e7..c3c9515523 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ URL = 'http://stanford.edu/~mwaskom/software/seaborn/' LICENSE = 'BSD (3-clause)' DOWNLOAD_URL = 'https://github.com/mwaskom/seaborn/' -VERSION = '0.7.0' +VERSION = '0.8.dev' try: from setuptools import setup From 6d29f21942332d7cea7f100b8f1e123e27c32742 Mon Sep 17 00:00:00 2001 From: Brian Landry Date: Mon, 25 Jan 2016 18:41:46 -0600 Subject: [PATCH 0213/1738] Change luminance calculations for Annotation color choice --- seaborn/matrix.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index a50fb7c781..cd22eed09b 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -210,8 +210,12 @@ def _annotate_heatmap(self, ax, mesh): for x, y, val, color in zip(xpos.flat, ypos.flat, mesh.get_array(), mesh.get_facecolors()): if val is not np.ma.masked: - _, l, _ = colorsys.rgb_to_hls(*color[:3]) - text_color = ".15" if l > .5 else "w" + r_s, g_s, b_s = color[:3] + r = r_s/12.92 if r_s <= 0.03928 else ((r_s+0.055)/1.055) ** 2.4 + g = g_s/12.92 if g_s <= 0.03928 else ((g_s+0.055)/1.055) ** 2.4 + b = b_s/12.92 if b_s <= 0.03928 else ((b_s+0.055)/1.055) ** 2.4 + l = 0.2126 * r + 0.7152 * g + 0.0722 * b + text_color = ".15" if l > .408 else "w" val = ("{:" + self.fmt + "}").format(val) text_kwargs = dict(color=text_color, ha="center", va="center") text_kwargs.update(self.annot_kws) From 250fa094bd51d0ec651bcb069ac68cc457444fa7 Mon Sep 17 00:00:00 2001 From: Brian Landry Date: Tue, 26 Jan 2016 16:27:30 -0600 Subject: [PATCH 0214/1738] Refactored and unit tested luminance calculation --- seaborn/matrix.py | 9 ++------- seaborn/tests/test_utils.py | 11 +++++++++++ seaborn/utils.py | 24 ++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index cd22eed09b..91e076b776 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -1,7 +1,6 @@ """Functions to visualize matrices of data.""" import itertools -import colorsys import matplotlib as mpl from matplotlib.collections import LineCollection import matplotlib.pyplot as plt @@ -13,7 +12,7 @@ from .axisgrid import Grid from .palettes import cubehelix_palette -from .utils import despine, axis_ticklabels_overlap +from .utils import despine, axis_ticklabels_overlap, relative_luminance from .external.six.moves import range @@ -210,11 +209,7 @@ def _annotate_heatmap(self, ax, mesh): for x, y, val, color in zip(xpos.flat, ypos.flat, mesh.get_array(), mesh.get_facecolors()): if val is not np.ma.masked: - r_s, g_s, b_s = color[:3] - r = r_s/12.92 if r_s <= 0.03928 else ((r_s+0.055)/1.055) ** 2.4 - g = g_s/12.92 if g_s <= 0.03928 else ((g_s+0.055)/1.055) ** 2.4 - b = b_s/12.92 if b_s <= 0.03928 else ((b_s+0.055)/1.055) ** 2.4 - l = 0.2126 * r + 0.7152 * g + 0.0722 * b + l = relative_luminance(color) text_color = ".15" if l > .408 else "w" val = ("{:" + self.fmt + "}").format(val) text_kwargs = dict(color=text_color, ha="center", va="center") diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index f5dbadbdf2..636f9291a0 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -368,3 +368,14 @@ def test_load_cached_datasets(): # does not get in effect, so we need to call explicitly # yield check_load_dataset, name check_load_cached_dataset(name) + +def test_relative_luminance(): + """Test relative luminance.""" + out1 = utils.relative_luminance("white") + assert_equal(out1, 1) + + out2 = utils.relative_luminance("#000000") + assert_equal(out2, 0) + + out3 = utils.relative_luminance((.25,.5,.75)) + nose.tools.assert_almost_equal(out3, 0.201624536) diff --git a/seaborn/utils.py b/seaborn/utils.py index 0764d31994..f597e12aa5 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -541,3 +541,27 @@ def get_color_cycle(): except KeyError: pass # just return axes.color style below return mpl.rcParams['axes.color_cycle'] + +def relative_luminance(color): + """Calculate the relative luminance of a color according to W3C standards + + Parameters + ---------- + color : matplotlib color + hex, rgb-tuple, or html color name + + Returns + ------- + luminance : float between 0 and 1 + + """ + # Get rgb tuple rep + r_s, g_s, b_s = mplcol.colorConverter.to_rgb(color) + + #Calculate relative luminance + r = r_s/12.92 if r_s <= 0.03928 else ((r_s+0.055)/1.055) ** 2.4 + g = g_s/12.92 if g_s <= 0.03928 else ((g_s+0.055)/1.055) ** 2.4 + b = b_s/12.92 if b_s <= 0.03928 else ((b_s+0.055)/1.055) ** 2.4 + luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b + + return luminance From a0ab6e2c8e1d36ae6f4d5cd1f0f60ab6a7ebfe42 Mon Sep 17 00:00:00 2001 From: Brian Landry Date: Tue, 26 Jan 2016 19:30:14 -0600 Subject: [PATCH 0215/1738] Fixed build issues with formatting --- seaborn/tests/test_utils.py | 3 ++- seaborn/utils.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index 636f9291a0..2b79b21c66 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -369,6 +369,7 @@ def test_load_cached_datasets(): # yield check_load_dataset, name check_load_cached_dataset(name) + def test_relative_luminance(): """Test relative luminance.""" out1 = utils.relative_luminance("white") @@ -377,5 +378,5 @@ def test_relative_luminance(): out2 = utils.relative_luminance("#000000") assert_equal(out2, 0) - out3 = utils.relative_luminance((.25,.5,.75)) + out3 = utils.relative_luminance((.25, .5, .75)) nose.tools.assert_almost_equal(out3, 0.201624536) diff --git a/seaborn/utils.py b/seaborn/utils.py index f597e12aa5..94aeed36d2 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -542,6 +542,7 @@ def get_color_cycle(): pass # just return axes.color style below return mpl.rcParams['axes.color_cycle'] + def relative_luminance(color): """Calculate the relative luminance of a color according to W3C standards @@ -558,10 +559,10 @@ def relative_luminance(color): # Get rgb tuple rep r_s, g_s, b_s = mplcol.colorConverter.to_rgb(color) - #Calculate relative luminance + # Calculate relative luminance r = r_s/12.92 if r_s <= 0.03928 else ((r_s+0.055)/1.055) ** 2.4 g = g_s/12.92 if g_s <= 0.03928 else ((g_s+0.055)/1.055) ** 2.4 b = b_s/12.92 if b_s <= 0.03928 else ((b_s+0.055)/1.055) ** 2.4 - luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b + luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b return luminance From 4cbdd6ed113896fb4a90495de33a3f6c49a92659 Mon Sep 17 00:00:00 2001 From: toddrme2178 Date: Fri, 29 Jan 2016 16:03:22 +0100 Subject: [PATCH 0216/1738] Remove color_list from dendrogram call in tests Fixes #844 --- seaborn/tests/test_matrix.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 1ae4a38890..9868ed0c8f 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -371,7 +371,6 @@ class TestDendrogram(PlotTestCase): x_norm_distances = distance.pdist(x_norm.T, metric='euclidean') x_norm_linkage = hierarchy.linkage(x_norm_distances, method='single') x_norm_dendrogram = hierarchy.dendrogram(x_norm_linkage, no_plot=True, - color_list=['k'], color_threshold=-np.inf) x_norm_leaves = x_norm_dendrogram['leaves'] df_norm_leaves = np.asarray(df_norm.columns[x_norm_leaves]) @@ -480,7 +479,6 @@ def test_custom_linkage(self): d = distance.pdist(self.x_norm, metric='euclidean') linkage = hierarchy.linkage(d, method='single') dendrogram = hierarchy.dendrogram(linkage, no_plot=True, - color_list=['k'], color_threshold=-np.inf) kws['linkage'] = linkage p = mat._DendrogramPlotter(self.df_norm, **kws) @@ -614,7 +612,6 @@ class TestClustermap(PlotTestCase): x_norm_distances = distance.pdist(x_norm.T, metric='euclidean') x_norm_linkage = hierarchy.linkage(x_norm_distances, method='single') x_norm_dendrogram = hierarchy.dendrogram(x_norm_linkage, no_plot=True, - color_list=['k'], color_threshold=-np.inf) x_norm_leaves = x_norm_dendrogram['leaves'] df_norm_leaves = np.asarray(df_norm.columns[x_norm_leaves]) From 54ea3d1b1b6f3ce769f1572c4094a1b3cb2ad8cc Mon Sep 17 00:00:00 2001 From: "Gravish, Nick" Date: Wed, 17 Feb 2016 11:49:04 -0500 Subject: [PATCH 0217/1738] Added ability to override histtype when plotting PairGrid histograms using map_diag --- seaborn/axisgrid.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 9b1dd769dc..ceff806ea9 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1323,8 +1323,13 @@ def map_diag(self, func, **kwargs): vals.append(np.asarray(hue_grouped.get_group(label))) except KeyError: vals.append(np.array([])) - func(vals, color=self.palette, histtype="barstacked", - **kwargs) + + # check and see if histtype override was provided in kwargs + if 'histtype' in kwargs: + func(vals, color=self.palette, **kwargs) + else: + func(vals, color=self.palette, histtype="barstacked", + **kwargs) else: for k, label_k in enumerate(self.hue_names): # Attempt to get data for this level, allowing for empty From 96fdebc8a9f29cbb708b914882370f03571f421c Mon Sep 17 00:00:00 2001 From: "Gravish, Nick" Date: Wed, 17 Feb 2016 18:13:04 -0500 Subject: [PATCH 0218/1738] fixed whitespace error and added an example script --- examples/custom_map_diag_hist.py | 37 ++++++++++++++++++++++++++++++++ seaborn/axisgrid.py | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 examples/custom_map_diag_hist.py diff --git a/examples/custom_map_diag_hist.py b/examples/custom_map_diag_hist.py new file mode 100644 index 0000000000..32deefa55a --- /dev/null +++ b/examples/custom_map_diag_hist.py @@ -0,0 +1,37 @@ +""" +Example of alternative map_diag histograms +========================= + +""" +import numpy as np +import matplotlib.pyplot as plt +import seaborn as sns +import pandas as pd + +sns.set(style="white", palette="muted", color_codes=True) +rs = np.random.RandomState(10) + +# make some test data with a boolean label of unequal sample size +df = pd.DataFrame({'data1':np.random.randn(2000), + 'data2':np.random.randn(2000), + 'label': np.random.randn(2000) > 0}) + +# Default barstacked histogram +g = sns.PairGrid(df, x_vars= ['data1', 'data2'], + y_vars= ['data1', 'data2'], + hue = "label", diag_sharey = False, + palette = "Set1") + +g.map_diag(plt.hist, normed = False, bins = np.arange(-5,5,.25)) + +# Updated custom histtype, and also illustrate how normalization should appear +g = sns.PairGrid(df, x_vars= ['data1', 'data2'], + y_vars= ['data1', 'data2'], + hue = "label", diag_sharey = False, + palette = "Set1") + +g.map_diag(plt.hist, normed = True, + alpha = 0.8, + histtype = 'step', + linewidth = 3, + bins = np.arange(-5,5,.25)) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index ceff806ea9..265e4dda0c 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1323,7 +1323,7 @@ def map_diag(self, func, **kwargs): vals.append(np.asarray(hue_grouped.get_group(label))) except KeyError: vals.append(np.array([])) - + # check and see if histtype override was provided in kwargs if 'histtype' in kwargs: func(vals, color=self.palette, **kwargs) From 8fa72bf36446e4d3f4c8f22f02880b58b7ce1790 Mon Sep 17 00:00:00 2001 From: "Gravish, Nick" Date: Wed, 17 Feb 2016 19:05:23 -0500 Subject: [PATCH 0219/1738] Added unit test for histtype behavior --- seaborn/tests/test_axisgrid.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 83875968ed..6098b3d861 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -780,6 +780,14 @@ def test_map_diag(self): for ax in g3.diag_axes: nt.assert_equal(len(ax.patches), 40) + g4 = ag.PairGrid(self.df, hue="a") + g4.map_diag(plt.hist, histtype='step') + + for ax in g4.diag_axes: + for ptch in ax.patches: + nt.assert_equal(ptch.fill, False) + + @skipif(old_matplotlib) def test_map_diag_and_offdiag(self): From 8062f29606f568878e0f9f0e7b407276a3b7ed6e Mon Sep 17 00:00:00 2001 From: "Gravish, Nick" Date: Wed, 17 Feb 2016 19:35:10 -0500 Subject: [PATCH 0220/1738] another PEP8 fix --- examples/custom_map_diag_hist.py | 6 +++--- seaborn/tests/test_axisgrid.py | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/custom_map_diag_hist.py b/examples/custom_map_diag_hist.py index 32deefa55a..7726bf00a2 100644 --- a/examples/custom_map_diag_hist.py +++ b/examples/custom_map_diag_hist.py @@ -12,9 +12,9 @@ rs = np.random.RandomState(10) # make some test data with a boolean label of unequal sample size -df = pd.DataFrame({'data1':np.random.randn(2000), - 'data2':np.random.randn(2000), - 'label': np.random.randn(2000) > 0}) +df = pd.DataFrame({'data1':np.random.randn(20000), + 'data2':np.random.randn(20000), + 'label': np.random.randn(20000) > 0}) # Default barstacked histogram g = sns.PairGrid(df, x_vars= ['data1', 'data2'], diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 6098b3d861..670e2ddac6 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -787,7 +787,6 @@ def test_map_diag(self): for ptch in ax.patches: nt.assert_equal(ptch.fill, False) - @skipif(old_matplotlib) def test_map_diag_and_offdiag(self): From cdc214791ce390a40bcbd0abc8b5550001e5cb73 Mon Sep 17 00:00:00 2001 From: "Gravish, Nick" Date: Thu, 18 Feb 2016 08:13:38 -0500 Subject: [PATCH 0221/1738] small change to example, added off_diag plots --- examples/custom_map_diag_hist.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/custom_map_diag_hist.py b/examples/custom_map_diag_hist.py index 7726bf00a2..1f6970db64 100644 --- a/examples/custom_map_diag_hist.py +++ b/examples/custom_map_diag_hist.py @@ -12,25 +12,27 @@ rs = np.random.RandomState(10) # make some test data with a boolean label of unequal sample size -df = pd.DataFrame({'data1':np.random.randn(20000), - 'data2':np.random.randn(20000), - 'label': np.random.randn(20000) > 0}) +df = pd.DataFrame({'data1':np.random.randn(2000), + 'data2':np.random.randn(2000), + 'label': np.random.randn(2000) > 0}) # Default barstacked histogram -g = sns.PairGrid(df, x_vars= ['data1', 'data2'], +g1 = sns.PairGrid(df, x_vars= ['data1', 'data2'], y_vars= ['data1', 'data2'], hue = "label", diag_sharey = False, palette = "Set1") -g.map_diag(plt.hist, normed = False, bins = np.arange(-5,5,.25)) +g1.map_offdiag(plt.scatter) +g1.map_diag(plt.hist, normed = False, bins = np.arange(-5,5,.25)) # Updated custom histtype, and also illustrate how normalization should appear -g = sns.PairGrid(df, x_vars= ['data1', 'data2'], +g2 = sns.PairGrid(df, x_vars= ['data1', 'data2'], y_vars= ['data1', 'data2'], hue = "label", diag_sharey = False, palette = "Set1") -g.map_diag(plt.hist, normed = True, +g2.map_offdiag(plt.scatter) +g2.map_diag(plt.hist, normed = True, alpha = 0.8, histtype = 'step', linewidth = 3, From 06cd363025d7de25ba892df78e8a292c253740a6 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 22 Feb 2016 08:26:11 -0800 Subject: [PATCH 0222/1738] Update Zenodo DOI --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb27f0ddb7..d9b81f438d 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ The documentation has an [example gallery](http://stanford.edu/~mwaskom/software Citing ------ -Seaborn can be cited using a DOI provided through Zenodo: [![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.19108.svg)](http://dx.doi.org/10.5281/zenodo.19108) +Seaborn can be cited using a DOI provided through Zenodo: [![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.45133.svg)](http://dx.doi.org/10.5281/zenodo.45133) Dependencies ------------ From 149b438889bfeca99b0d529fcd4cb348bd93cc8c Mon Sep 17 00:00:00 2001 From: Julian de Ruiter Date: Tue, 1 Mar 2016 22:24:21 +0100 Subject: [PATCH 0223/1738] Support dataframes as row_colors and col_colors. --- seaborn/matrix.py | 59 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index a50fb7c781..bd9e0f9ea6 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -36,14 +36,19 @@ def _index_to_ticklabels(index): def _convert_colors(colors): """Convert either a list of colors or nested lists of colors to RGB.""" to_rgb = mpl.colors.colorConverter.to_rgb - try: - to_rgb(colors[0]) - # If this works, there is only one level of colors - return list(map(to_rgb, colors)) - except ValueError: - # If we get here, we have nested lists - return [list(map(to_rgb, l)) for l in colors] + if isinstance(colors, pd.DataFrame): + # Convert dataframe + return pd.DataFrame({col: colors[col].map(to_rgb) + for col in colors}) + else: + try: + to_rgb(colors[0]) + # If this works, there is only one level of colors + return list(map(to_rgb, colors)) + except ValueError: + # If we get here, we have nested lists + return [list(map(to_rgb, l)) for l in colors] def _matrix_mask(data, mask): """Ensure that data and mask are compatabile and add missing values. @@ -708,9 +713,16 @@ def __init__(self, data, pivot_kws=None, z_score=None, standard_scale=None, self.fig = plt.figure(figsize=figsize) if row_colors is not None: + if isinstance(row_colors, pd.DataFrame): + # Ensure colors match data indices + row_colors = row_colors.ix[data.index] row_colors = _convert_colors(row_colors) self.row_colors = row_colors + if col_colors is not None: + if isinstance(col_colors, pd.DataFrame): + # Ensure colors match data indices + col_colors = col_colors.ix[data.columns] col_colors = _convert_colors(col_colors) self.col_colors = col_colors @@ -899,6 +911,10 @@ def color_list_to_matrix_and_cmap(colors, ind, axis=0): all_colors = set(itertools.chain(*colors)) n = len(colors) m = len(colors[0]) + elif isinstance(colors, pd.DataFrame): + all_colors = set(itertools.chain(*colors.values)) + m, n = colors.shape + colors = colors.T.values else: all_colors = set(colors) n = 1 @@ -964,18 +980,43 @@ def plot_colors(self, xind, yind, **kws): if self.row_colors is not None: matrix, cmap = self.color_list_to_matrix_and_cmap( self.row_colors, yind, axis=0) + + # Get labels from colors if given as dataframe + if isinstance(self.row_colors, pd.DataFrame): + xticklabels = self.row_colors.columns + else: + xticklabels = False + heatmap(matrix, cmap=cmap, cbar=False, ax=self.ax_row_colors, - xticklabels=False, yticklabels=False, + xticklabels=xticklabels, yticklabels=False, **kws) + + # Adjust rotation of labels + if xticklabels is not False: + self.ax_row_colors.set_xticklabels( + self.ax_row_colors.get_xticklabels(), rotation=90) else: despine(self.ax_row_colors, left=True, bottom=True) if self.col_colors is not None: matrix, cmap = self.color_list_to_matrix_and_cmap( self.col_colors, xind, axis=1) + + # Get labels from colors if given as dataframe + if isinstance(self.col_colors, pd.DataFrame): + yticklabels = self.col_colors.columns + else: + yticklabels = False + heatmap(matrix, cmap=cmap, cbar=False, ax=self.ax_col_colors, - xticklabels=False, yticklabels=False, + xticklabels=False, yticklabels=yticklabels, **kws) + + # Adjust rotation of labels, place on right side + if yticklabels is not False: + self.ax_col_colors.set_yticklabels( + self.ax_col_colors.get_yticklabels(), rotation=0) + self.ax_col_colors.yaxis.tick_right() else: despine(self.ax_col_colors, left=True, bottom=True) From 2a357613a62becfe7133add46307eb2d0aa96e41 Mon Sep 17 00:00:00 2001 From: Julian de Ruiter Date: Tue, 1 Mar 2016 22:55:28 +0100 Subject: [PATCH 0224/1738] Also support series names as labels. --- seaborn/matrix.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index bd9e0f9ea6..c2822a7ac8 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -41,6 +41,8 @@ def _convert_colors(colors): # Convert dataframe return pd.DataFrame({col: colors[col].map(to_rgb) for col in colors}) + elif isinstance(colors, pd.Series): + return colors.map(to_rgb) else: try: to_rgb(colors[0]) @@ -713,14 +715,14 @@ def __init__(self, data, pivot_kws=None, z_score=None, standard_scale=None, self.fig = plt.figure(figsize=figsize) if row_colors is not None: - if isinstance(row_colors, pd.DataFrame): + if isinstance(row_colors, (pd.DataFrame, pd.Series)): # Ensure colors match data indices row_colors = row_colors.ix[data.index] row_colors = _convert_colors(row_colors) self.row_colors = row_colors if col_colors is not None: - if isinstance(col_colors, pd.DataFrame): + if isinstance(col_colors, (pd.DataFrame, pd.Series)): # Ensure colors match data indices col_colors = col_colors.ix[data.columns] col_colors = _convert_colors(col_colors) @@ -984,6 +986,9 @@ def plot_colors(self, xind, yind, **kws): # Get labels from colors if given as dataframe if isinstance(self.row_colors, pd.DataFrame): xticklabels = self.row_colors.columns + elif isinstance(self.row_colors, pd.Series) and \ + self.row_colors.name: + xticklabels = [self.row_colors.name] else: xticklabels = False @@ -1005,6 +1010,9 @@ def plot_colors(self, xind, yind, **kws): # Get labels from colors if given as dataframe if isinstance(self.col_colors, pd.DataFrame): yticklabels = self.col_colors.columns + elif isinstance(self.col_colors, pd.Series) and \ + self.col_colors.name: + yticklabels = [self.col_colors.name] else: yticklabels = False From ddced1d9a962244b41bc9a49a6d37be4bc27d383 Mon Sep 17 00:00:00 2001 From: Julian de Ruiter Date: Wed, 2 Mar 2016 21:23:20 +0100 Subject: [PATCH 0225/1738] Refactoring of dataframe/series color support + added tests. --- seaborn/matrix.py | 97 ++++++++++++++++------------- seaborn/tests/test_matrix.py | 114 +++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 41 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index c2822a7ac8..dbae95cab7 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -52,6 +52,7 @@ def _convert_colors(colors): # If we get here, we have nested lists return [list(map(to_rgb, l)) for l in colors] + def _matrix_mask(data, mask): """Ensure that data and mask are compatabile and add missing values. @@ -714,19 +715,21 @@ def __init__(self, data, pivot_kws=None, z_score=None, standard_scale=None, figsize = (width, height) self.fig = plt.figure(figsize=figsize) - if row_colors is not None: - if isinstance(row_colors, (pd.DataFrame, pd.Series)): - # Ensure colors match data indices - row_colors = row_colors.ix[data.index] - row_colors = _convert_colors(row_colors) - self.row_colors = row_colors - - if col_colors is not None: - if isinstance(col_colors, (pd.DataFrame, pd.Series)): - # Ensure colors match data indices - col_colors = col_colors.ix[data.columns] - col_colors = _convert_colors(col_colors) - self.col_colors = col_colors + # if row_colors is not None: + # if isinstance(row_colors, (pd.DataFrame, pd.Series)): + # # Ensure colors match data indices + # row_colors = row_colors.ix[data.index] + # row_colors = _convert_colors(row_colors) + self.row_colors, self.row_color_labels = \ + self._preprocess_colors(data, row_colors, axis=0) + + # if col_colors is not None: + # if isinstance(col_colors, (pd.DataFrame, pd.Series)): + # # Ensure colors match data indices + # col_colors = col_colors.ix[data.columns] + # col_colors = _convert_colors(col_colors) + self.col_colors, self.col_color_labels = \ + self._preprocess_colors(data, col_colors, axis=1) width_ratios = self.dim_ratios(self.row_colors, figsize=figsize, @@ -765,6 +768,32 @@ def __init__(self, data, pivot_kws=None, z_score=None, standard_scale=None, self.dendrogram_row = None self.dendrogram_col = None + def _preprocess_colors(self, data, colors, axis): + labels = None + + if colors is not None: + if isinstance(colors, (pd.DataFrame, pd.Series)): + # Ensure colors match data indices + if axis == 0: + colors = colors.ix[data.index] + else: + colors = colors.ix[data.columns] + + # Replace na's with background color + colors = colors.fillna('white') + + # Extract color values and labels from frame/series + if isinstance(colors, pd.DataFrame): + labels = colors.columns + colors = colors.T.values + else: + labels = [colors.name] + colors = colors.values + + colors = _convert_colors(colors) + + return colors, labels + def format_data(self, data, pivot_kws, z_score=None, standard_scale=None): """Extract variables from data or use directly.""" @@ -913,10 +942,6 @@ def color_list_to_matrix_and_cmap(colors, ind, axis=0): all_colors = set(itertools.chain(*colors)) n = len(colors) m = len(colors[0]) - elif isinstance(colors, pd.DataFrame): - all_colors = set(itertools.chain(*colors.values)) - m, n = colors.shape - colors = colors.T.values else: all_colors = set(colors) n = 1 @@ -983,23 +1008,18 @@ def plot_colors(self, xind, yind, **kws): matrix, cmap = self.color_list_to_matrix_and_cmap( self.row_colors, yind, axis=0) - # Get labels from colors if given as dataframe - if isinstance(self.row_colors, pd.DataFrame): - xticklabels = self.row_colors.columns - elif isinstance(self.row_colors, pd.Series) and \ - self.row_colors.name: - xticklabels = [self.row_colors.name] + # Get row_color labels + if self.row_color_labels is not None: + row_color_labels = self.row_color_labels else: - xticklabels = False + row_color_labels = False heatmap(matrix, cmap=cmap, cbar=False, ax=self.ax_row_colors, - xticklabels=xticklabels, yticklabels=False, - **kws) + xticklabels=row_color_labels, yticklabels=False, **kws) # Adjust rotation of labels - if xticklabels is not False: - self.ax_row_colors.set_xticklabels( - self.ax_row_colors.get_xticklabels(), rotation=90) + if row_color_labels is not False: + plt.setp(self.ax_row_colors.get_xticklabels(), rotation=90) else: despine(self.ax_row_colors, left=True, bottom=True) @@ -1007,24 +1027,19 @@ def plot_colors(self, xind, yind, **kws): matrix, cmap = self.color_list_to_matrix_and_cmap( self.col_colors, xind, axis=1) - # Get labels from colors if given as dataframe - if isinstance(self.col_colors, pd.DataFrame): - yticklabels = self.col_colors.columns - elif isinstance(self.col_colors, pd.Series) and \ - self.col_colors.name: - yticklabels = [self.col_colors.name] + # Get col_color labels + if self.row_color_labels is not None: + col_color_labels = self.col_color_labels else: - yticklabels = False + col_color_labels = False heatmap(matrix, cmap=cmap, cbar=False, ax=self.ax_col_colors, - xticklabels=False, yticklabels=yticklabels, - **kws) + xticklabels=False, yticklabels=col_color_labels, **kws) # Adjust rotation of labels, place on right side - if yticklabels is not False: - self.ax_col_colors.set_yticklabels( - self.ax_col_colors.get_yticklabels(), rotation=0) + if col_color_labels is not False: self.ax_col_colors.yaxis.tick_right() + plt.setp(self.ax_col_colors.get_yticklabels(), rotation=0) else: despine(self.ax_col_colors, left=True, bottom=True) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 9868ed0c8f..49df015ec9 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -852,6 +852,120 @@ def test_cluster_false_row_col_colors(self): pdt.assert_frame_equal(cm.data2d, self.df_norm) + def test_row_col_colors_df(self): + kws = self.default_kws.copy() + kws['row_colors'] = pd.DataFrame({'row_annot': list(self.row_colors)}, + index=self.df_norm.index) + kws['col_colors'] = pd.DataFrame({'col_annot': list(self.col_colors)}, + index=self.df_norm.columns) + + cm = mat.clustermap(self.df_norm, **kws) + + row_labels = [l.get_text() for l in + cm.ax_row_colors.get_xticklabels()] + nt.assert_equal(row_labels, ['row_annot']) + + col_labels = [l.get_text() for l in + cm.ax_col_colors.get_yticklabels()] + nt.assert_equal(col_labels, ['col_annot']) + + def test_row_col_colors_df_shuffled(self): + # Tests if colors are properly matched, even if given in wrong order + + m, n = self.df_norm.shape + shuffled_inds = [self.df_norm.index[i] for i in + list(range(0, m, 2)) + list(range(1, m, 2))] + shuffled_cols = [self.df_norm.columns[i] for i in + list(range(0, n, 2)) + list(range(1, n, 2))] + + kws = self.default_kws.copy() + + row_colors = pd.DataFrame({'row_annot': list(self.row_colors)}, + index=self.df_norm.index) + kws['row_colors'] = row_colors.ix[shuffled_inds] + + col_colors = pd.DataFrame({'col_annot': list(self.col_colors)}, + index=self.df_norm.columns) + kws['col_colors'] = col_colors.ix[shuffled_cols] + + cm = mat.clustermap(self.df_norm, **kws) + nt.assert_equal(list(cm.col_colors)[0], list(self.col_colors)) + nt.assert_equal(list(cm.row_colors)[0], list(self.row_colors)) + + def test_row_col_colors_df_missing(self): + kws = self.default_kws.copy() + row_colors = pd.DataFrame({'row_annot': list(self.row_colors)}, + index=self.df_norm.index) + kws['row_colors'] = row_colors.drop(self.df_norm.index[0]) + + col_colors = pd.DataFrame({'col_annot': list(self.col_colors)}, + index=self.df_norm.columns) + kws['col_colors'] = col_colors.drop(self.df_norm.columns[0]) + + cm = mat.clustermap(self.df_norm, **kws) + + nt.assert_equal(list(cm.col_colors)[0], + [(1.0, 1.0, 1.0)] + list(self.col_colors[1:])) + nt.assert_equal(list(cm.row_colors)[0], + [(1.0, 1.0, 1.0)] + list(self.row_colors[1:])) + + def test_row_col_colors_series(self): + kws = self.default_kws.copy() + kws['row_colors'] = pd.Series(list(self.row_colors), name='row_annot', + index=self.df_norm.index) + kws['col_colors'] = pd.Series(list(self.col_colors), name='col_annot', + index=self.df_norm.columns) + + cm = mat.clustermap(self.df_norm, **kws) + + row_labels = [l.get_text() for l in + cm.ax_row_colors.get_xticklabels()] + nt.assert_equal(row_labels, ['row_annot']) + + col_labels = [l.get_text() for l in + cm.ax_col_colors.get_yticklabels()] + nt.assert_equal(col_labels, ['col_annot']) + + def test_row_col_colors_series_shuffled(self): + # Tests if colors are properly matched, even if given in wrong order + + m, n = self.df_norm.shape + shuffled_inds = [self.df_norm.index[i] for i in + list(range(0, m, 2)) + list(range(1, m, 2))] + shuffled_cols = [self.df_norm.columns[i] for i in + list(range(0, n, 2)) + list(range(1, n, 2))] + + kws = self.default_kws.copy() + + row_colors = pd.Series(list(self.row_colors), name='row_annot', + index=self.df_norm.index) + kws['row_colors'] = row_colors.ix[shuffled_inds] + + col_colors = pd.Series(list(self.col_colors), name='col_annot', + index=self.df_norm.columns) + kws['col_colors'] = col_colors.ix[shuffled_cols] + + cm = mat.clustermap(self.df_norm, **kws) + + nt.assert_equal(list(cm.col_colors), list(self.col_colors)) + nt.assert_equal(list(cm.row_colors), list(self.row_colors)) + + def test_row_col_colors_series_missing(self): + kws = self.default_kws.copy() + row_colors = pd.Series(list(self.row_colors), name='row_annot', + index=self.df_norm.index) + kws['row_colors'] = row_colors.drop(self.df_norm.index[0]) + + col_colors = pd.Series(list(self.col_colors), name='col_annot', + index=self.df_norm.columns) + kws['col_colors'] = col_colors.drop(self.df_norm.columns[0]) + + cm = mat.clustermap(self.df_norm, **kws) + nt.assert_equal(list(cm.col_colors), + [(1.0, 1.0, 1.0)] + list(self.col_colors[1:])) + nt.assert_equal(list(cm.row_colors), + [(1.0, 1.0, 1.0)] + list(self.row_colors[1:])) + def test_mask_reorganization(self): kws = self.default_kws.copy() From 205153b6b8d2540459abd088240a4faa48c06986 Mon Sep 17 00:00:00 2001 From: Julian de Ruiter Date: Wed, 2 Mar 2016 21:24:23 +0100 Subject: [PATCH 0226/1738] Remove commented out code. --- seaborn/matrix.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index dbae95cab7..cc20ff748c 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -715,19 +715,8 @@ def __init__(self, data, pivot_kws=None, z_score=None, standard_scale=None, figsize = (width, height) self.fig = plt.figure(figsize=figsize) - # if row_colors is not None: - # if isinstance(row_colors, (pd.DataFrame, pd.Series)): - # # Ensure colors match data indices - # row_colors = row_colors.ix[data.index] - # row_colors = _convert_colors(row_colors) self.row_colors, self.row_color_labels = \ self._preprocess_colors(data, row_colors, axis=0) - - # if col_colors is not None: - # if isinstance(col_colors, (pd.DataFrame, pd.Series)): - # # Ensure colors match data indices - # col_colors = col_colors.ix[data.columns] - # col_colors = _convert_colors(col_colors) self.col_colors, self.col_color_labels = \ self._preprocess_colors(data, col_colors, axis=1) From be36ea1898b38c0551039bb12080d2a28866c729 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 5 Mar 2016 10:38:14 -0800 Subject: [PATCH 0227/1738] Add file for 0.7.1 release notes --- doc/releases/v0.7.1.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 doc/releases/v0.7.1.txt diff --git a/doc/releases/v0.7.1.txt b/doc/releases/v0.7.1.txt new file mode 100644 index 0000000000..bc320ef439 --- /dev/null +++ b/doc/releases/v0.7.1.txt @@ -0,0 +1,5 @@ + +v0.7.1 (Unreleased) +------------------- + + From f9a25bc12760889c2c405f2c66e10def73676ed8 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 22 Feb 2016 08:26:11 -0800 Subject: [PATCH 0228/1738] Update Zenodo DOI --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb27f0ddb7..d9b81f438d 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ The documentation has an [example gallery](http://stanford.edu/~mwaskom/software Citing ------ -Seaborn can be cited using a DOI provided through Zenodo: [![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.19108.svg)](http://dx.doi.org/10.5281/zenodo.19108) +Seaborn can be cited using a DOI provided through Zenodo: [![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.45133.svg)](http://dx.doi.org/10.5281/zenodo.45133) Dependencies ------------ From 7652792408af34899f0b91a47e506c8c31445eb0 Mon Sep 17 00:00:00 2001 From: Olga Botvinnik Date: Tue, 23 Feb 2016 10:13:52 -0800 Subject: [PATCH 0229/1738] Move default cbar ticks setting to __init__ of HeatMapper, and use set_default so it can be overwritten --- seaborn/matrix.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index a50fb7c781..10dcfaedcc 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -165,8 +165,9 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, self.annot_kws = {} if annot_kws is None else annot_kws self.cbar = cbar self.cbar_kws = {} if cbar_kws is None else cbar_kws + self.cbar_kws.setdefault('ticks', mpl.ticker.MaxNLocator(6)) - def _determine_cmap_params(self, plot_data, vmin, vmax, + def _determine_cmap_params(self, plot_data, vmin, vmax, cmap, center, robust): """Use some heuristics to set good defaults for colorbar and range.""" calc_data = plot_data.data[~np.isnan(plot_data.data)] @@ -250,9 +251,7 @@ def plot(self, ax, cax, kws): # Possibly add a colorbar if self.cbar: - ticker = mpl.ticker.MaxNLocator(6) - cb = ax.figure.colorbar(mesh, cax, ax, - ticks=ticker, **self.cbar_kws) + cb = ax.figure.colorbar(mesh, cax, ax, **self.cbar_kws) cb.outline.set_linewidth(0) # If rasterized is passed to pcolormesh, also rasterize the # colorbar to avoid white lines on the PDF rendering From a151406c014d987d706b5763ed0362d5fa72b9e3 Mon Sep 17 00:00:00 2001 From: Olga Botvinnik Date: Tue, 23 Feb 2016 10:29:10 -0800 Subject: [PATCH 0230/1738] Add test for setting number of colorbar ticks --- seaborn/tests/test_matrix.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 9868ed0c8f..8516451506 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -351,6 +351,13 @@ def test_missing_data_mask(self): mask_out = mat._matrix_mask(data, mask_in) npt.assert_array_equal(mask_out, [[True, True], [False, False]]) + def test_cbar_ticks(self): + locator = mpl.ticker.MaxNLocator(4) + f, (ax1, ax2) = plt.subplots(2) + mat.heatmap(self.df_norm, ax=ax1, cbar_ax=ax2, + cbar_kws=dict(ticks=locator)) + nt.assert_equal(len(ax2.yaxis.get_ticklabels()), 4) + plt.close(f) class TestDendrogram(PlotTestCase): rs = np.random.RandomState(sum(map(ord, "dendrogram"))) @@ -886,3 +893,4 @@ def test_ticklabel_reorganization(self): npt.assert_array_equal(xtl_actual, xtl_want) npt.assert_array_equal(ytl_actual, ytl_want) + From fef4679d7453b4f36ab1cac656006dfb7383f2b4 Mon Sep 17 00:00:00 2001 From: Olga Botvinnik Date: Tue, 23 Feb 2016 12:19:22 -0800 Subject: [PATCH 0231/1738] fix random indenting error --- seaborn/matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 10dcfaedcc..65ba49707f 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -167,7 +167,7 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, self.cbar_kws = {} if cbar_kws is None else cbar_kws self.cbar_kws.setdefault('ticks', mpl.ticker.MaxNLocator(6)) - def _determine_cmap_params(self, plot_data, vmin, vmax, + def _determine_cmap_params(self, plot_data, vmin, vmax, cmap, center, robust): """Use some heuristics to set good defaults for colorbar and range.""" calc_data = plot_data.data[~np.isnan(plot_data.data)] From e8542f117e5a0b8778e72a86fa6e150dbb4d64d9 Mon Sep 17 00:00:00 2001 From: Olga Botvinnik Date: Tue, 23 Feb 2016 15:58:38 -0800 Subject: [PATCH 0232/1738] fix lint --- seaborn/tests/test_matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 8516451506..6216633775 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -359,6 +359,7 @@ def test_cbar_ticks(self): nt.assert_equal(len(ax2.yaxis.get_ticklabels()), 4) plt.close(f) + class TestDendrogram(PlotTestCase): rs = np.random.RandomState(sum(map(ord, "dendrogram"))) @@ -893,4 +894,3 @@ def test_ticklabel_reorganization(self): npt.assert_array_equal(xtl_actual, xtl_want) npt.assert_array_equal(ytl_actual, ytl_want) - From 3bc9b92714e88ce22afd24a35c2a73b5320fb616 Mon Sep 17 00:00:00 2001 From: Olga Botvinnik Date: Tue, 23 Feb 2016 15:59:28 -0800 Subject: [PATCH 0233/1738] Change max number of ticks for testing to 3 --- seaborn/tests/test_matrix.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 6216633775..6e0aa739f4 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -352,11 +352,13 @@ def test_missing_data_mask(self): npt.assert_array_equal(mask_out, [[True, True], [False, False]]) def test_cbar_ticks(self): - locator = mpl.ticker.MaxNLocator(4) + max_n_ticks = 3 + + locator = mpl.ticker.MaxNLocator(max_n_ticks) f, (ax1, ax2) = plt.subplots(2) mat.heatmap(self.df_norm, ax=ax1, cbar_ax=ax2, cbar_kws=dict(ticks=locator)) - nt.assert_equal(len(ax2.yaxis.get_ticklabels()), 4) + nt.assert_equal(len(ax2.yaxis.get_ticklabels()), max_n_ticks) plt.close(f) From 83e16e1ffecbd6b509d677018b919fdcdd7b5cae Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 5 Mar 2016 10:40:12 -0800 Subject: [PATCH 0234/1738] Update release notes --- doc/releases/v0.7.1.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/releases/v0.7.1.txt b/doc/releases/v0.7.1.txt index bc320ef439..8dd5cb31fb 100644 --- a/doc/releases/v0.7.1.txt +++ b/doc/releases/v0.7.1.txt @@ -2,4 +2,4 @@ v0.7.1 (Unreleased) ------------------- - +- Made it possible to pass a tick locator object to the :func:`heatmap` colorbar. From 633e85dd17890c9104967961e2e96b4d0ab12521 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 5 Mar 2016 10:57:01 -0800 Subject: [PATCH 0235/1738] Vectorize relative luminance computation --- seaborn/tests/test_utils.py | 22 +++++++++++++++------- seaborn/utils.py | 23 ++++++++++------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index 2b79b21c66..1c9a9103f2 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -5,6 +5,7 @@ import numpy as np import pandas as pd +import matplotlib as mpl import matplotlib.pyplot as plt import nose import nose.tools as nt @@ -371,12 +372,19 @@ def test_load_cached_datasets(): def test_relative_luminance(): - """Test relative luminance.""" - out1 = utils.relative_luminance("white") - assert_equal(out1, 1) + """Test relative luminance.""" + out1 = utils.relative_luminance("white") + assert_equal(out1, 1) - out2 = utils.relative_luminance("#000000") - assert_equal(out2, 0) + out2 = utils.relative_luminance("#000000") + assert_equal(out2, 0) - out3 = utils.relative_luminance((.25, .5, .75)) - nose.tools.assert_almost_equal(out3, 0.201624536) + out3 = utils.relative_luminance((.25, .5, .75)) + nose.tools.assert_almost_equal(out3, 0.201624536) + + rgbs = mpl.cm.RdBu(np.linspace(0, 1, 10)) + lums1 = [utils.relative_luminance(rgb) for rgb in rgbs] + lums2 = utils.relative_luminance(rgbs) + + for lum1, lum2 in zip(lums1, lums2): + nose.tools.assert_almost_equal(lum1, lum2) diff --git a/seaborn/utils.py b/seaborn/utils.py index 94aeed36d2..6e91b98d0b 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -548,21 +548,18 @@ def relative_luminance(color): Parameters ---------- - color : matplotlib color - hex, rgb-tuple, or html color name + color : matplotlib color or sequence of matplotlib colors + Hex code, rgb-tuple, or html color name. Returns ------- - luminance : float between 0 and 1 + luminance : float(s) between 0 and 1 """ - # Get rgb tuple rep - r_s, g_s, b_s = mplcol.colorConverter.to_rgb(color) - - # Calculate relative luminance - r = r_s/12.92 if r_s <= 0.03928 else ((r_s+0.055)/1.055) ** 2.4 - g = g_s/12.92 if g_s <= 0.03928 else ((g_s+0.055)/1.055) ** 2.4 - b = b_s/12.92 if b_s <= 0.03928 else ((b_s+0.055)/1.055) ** 2.4 - luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b - - return luminance + rgb = mpl.colors.colorConverter.to_rgba_array(color)[:, :3] + rgb = np.where(rgb <= .03928, rgb / 12.92, ((rgb + .055) / 1.055) ** 2.4) + lum = rgb.dot([.2126, .7152, .0722]) + try: + return lum.item() + except ValueError: + return lum From 9ff1cd51d9ffed4e7803a56a57889fd3a8d4d7d1 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 5 Mar 2016 10:57:09 -0800 Subject: [PATCH 0236/1738] Update release notes --- doc/releases/v0.7.1.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.7.1.txt b/doc/releases/v0.7.1.txt index 8dd5cb31fb..35d672bfd1 100644 --- a/doc/releases/v0.7.1.txt +++ b/doc/releases/v0.7.1.txt @@ -2,4 +2,6 @@ v0.7.1 (Unreleased) ------------------- +- Improved the luminance calculation that determines the annotation color in :func:`heatmap`. + - Made it possible to pass a tick locator object to the :func:`heatmap` colorbar. From 7cd48f0743ae07173c1258d566588747360dc60a Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 5 Mar 2016 11:07:18 -0800 Subject: [PATCH 0237/1738] Show multiple step histrograms in API examples --- examples/custom_map_diag_hist.py | 39 -------------------------------- seaborn/axisgrid.py | 13 ++++++++++- 2 files changed, 12 insertions(+), 40 deletions(-) delete mode 100644 examples/custom_map_diag_hist.py diff --git a/examples/custom_map_diag_hist.py b/examples/custom_map_diag_hist.py deleted file mode 100644 index 1f6970db64..0000000000 --- a/examples/custom_map_diag_hist.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -Example of alternative map_diag histograms -========================= - -""" -import numpy as np -import matplotlib.pyplot as plt -import seaborn as sns -import pandas as pd - -sns.set(style="white", palette="muted", color_codes=True) -rs = np.random.RandomState(10) - -# make some test data with a boolean label of unequal sample size -df = pd.DataFrame({'data1':np.random.randn(2000), - 'data2':np.random.randn(2000), - 'label': np.random.randn(2000) > 0}) - -# Default barstacked histogram -g1 = sns.PairGrid(df, x_vars= ['data1', 'data2'], - y_vars= ['data1', 'data2'], - hue = "label", diag_sharey = False, - palette = "Set1") - -g1.map_offdiag(plt.scatter) -g1.map_diag(plt.hist, normed = False, bins = np.arange(-5,5,.25)) - -# Updated custom histtype, and also illustrate how normalization should appear -g2 = sns.PairGrid(df, x_vars= ['data1', 'data2'], - y_vars= ['data1', 'data2'], - hue = "label", diag_sharey = False, - palette = "Set1") - -g2.map_offdiag(plt.scatter) -g2.map_diag(plt.hist, normed = True, - alpha = 0.8, - histtype = 'step', - linewidth = 3, - bins = np.arange(-5,5,.25)) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 265e4dda0c..a68976ee36 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1121,7 +1121,18 @@ def __init__(self, data, hue=None, hue_order=None, palette=None, :context: close-figs >>> g = sns.PairGrid(iris, hue="species") - >>> g = g.map(plt.scatter) + >>> g = g.map_diag(plt.hist) + >>> g = g.map_offdiag(plt.scatter) + >>> g = g.add_legend() + + Use a different style to show multiple histograms: + + .. plot:: + :context: close-figs + + >>> g = sns.PairGrid(iris, hue="species") + >>> g = g.map_diag(plt.hist, histtype="step", linewidth=3) + >>> g = g.map_offdiag(plt.scatter) >>> g = g.add_legend() Plot a subset of variables From 7585787e3bf6ff26e6937693b7eacf83279000ce Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 5 Mar 2016 11:16:38 -0800 Subject: [PATCH 0238/1738] Update release notes --- doc/releases/v0.7.1.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.7.1.txt b/doc/releases/v0.7.1.txt index 35d672bfd1..832661ece5 100644 --- a/doc/releases/v0.7.1.txt +++ b/doc/releases/v0.7.1.txt @@ -5,3 +5,5 @@ v0.7.1 (Unreleased) - Improved the luminance calculation that determines the annotation color in :func:`heatmap`. - Made it possible to pass a tick locator object to the :func:`heatmap` colorbar. + +- Made it possible to use different styles (e.g., step) for :class:`PairGrid` histograms when there are multiple hue levels. From 16084b3ce30eff993246d40ebb6a5063846a325f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 5 Mar 2016 11:36:15 -0800 Subject: [PATCH 0239/1738] Update clustermap example script to use labeled color arrays --- examples/structured_heatmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/structured_heatmap.py b/examples/structured_heatmap.py index 1fa909749f..4f1d9ac598 100644 --- a/examples/structured_heatmap.py +++ b/examples/structured_heatmap.py @@ -26,7 +26,7 @@ # Convert the palette to vectors that will be drawn on the side of the matrix networks = df.columns.get_level_values("network") -network_colors = pd.Series(networks).map(network_lut) +network_colors = pd.Series(networks, index=df.columns).map(network_lut) # Create a custom colormap for the heatmap values cmap = sns.diverging_palette(h_neg=210, h_pos=350, s=90, l=30, as_cmap=True) From c5c3de12e48645174c32a6c47f82becf4c2445d5 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 5 Mar 2016 11:36:19 -0800 Subject: [PATCH 0240/1738] Update release notes --- doc/releases/v0.7.1.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.7.1.txt b/doc/releases/v0.7.1.txt index 832661ece5..cc8444f095 100644 --- a/doc/releases/v0.7.1.txt +++ b/doc/releases/v0.7.1.txt @@ -2,6 +2,8 @@ v0.7.1 (Unreleased) ------------------- +- Improved the row and column colors display in :func:`clustermap`. It is now possible to pass Pandas objects for these elements and, when possible, the semantic information in the Pandas objects will be used to add labels to the plot. When Pandas objects are used, the color data is matched against the main heatmap based on the index, not on position. This is more accurate, but may lead to different results if current code assumed positional matching. + - Improved the luminance calculation that determines the annotation color in :func:`heatmap`. - Made it possible to pass a tick locator object to the :func:`heatmap` colorbar. From 1b515379602fa4361c9ac1fcc58768553778f22a Mon Sep 17 00:00:00 2001 From: Julian de Ruiter Date: Sun, 6 Mar 2016 11:09:46 +0100 Subject: [PATCH 0241/1738] Convert labels to list, fix bug in checking for col_color_labels. --- seaborn/matrix.py | 13 ++++++--- seaborn/tests/test_matrix.py | 54 ++++++++++++++++++++++++++++++------ 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index cc20ff748c..029e25f668 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -758,6 +758,7 @@ def __init__(self, data, pivot_kws=None, z_score=None, standard_scale=None, self.dendrogram_col = None def _preprocess_colors(self, data, colors, axis): + """Preprocess {row/col}_colors to extract labels and convert colors.""" labels = None if colors is not None: @@ -773,7 +774,7 @@ def _preprocess_colors(self, data, colors, axis): # Extract color values and labels from frame/series if isinstance(colors, pd.DataFrame): - labels = colors.columns + labels = list(colors.columns) colors = colors.T.values else: labels = [colors.name] @@ -1017,7 +1018,7 @@ def plot_colors(self, xind, yind, **kws): self.col_colors, xind, axis=1) # Get col_color labels - if self.row_color_labels is not None: + if self.col_color_labels is not None: col_color_labels = self.col_color_labels else: col_color_labels = False @@ -1115,10 +1116,14 @@ def clustermap(data, pivot_kws=None, method='average', metric='euclidean', {row,col}_linkage : numpy.array, optional Precomputed linkage matrix for the rows or columns. See scipy.cluster.hierarchy.linkage for specific formats. - {row,col}_colors : list-like, optional + {row,col}_colors : list-like or pandas DataFrame/Series, optional List of colors to label for either the rows or columns. Useful to evaluate whether samples within a group are clustered together. Can - use nested lists for multiple color levels of labeling. + use nested lists or DataFrame for multiple color levels of labeling. + If given as a DataFrame or Series, labels for the colors are extracted + from the DataFrames column names or from the name of the Series. + DataFrame/Series colors are also matched to the data by their + index, ensuring colors are drawn in the correct order. mask : boolean array or DataFrame, optional If passed, data will not be shown in cells where ``mask`` is True. Cells with missing values are automatically masked. Only used for diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 49df015ec9..372972c27d 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -854,20 +854,26 @@ def test_cluster_false_row_col_colors(self): def test_row_col_colors_df(self): kws = self.default_kws.copy() - kws['row_colors'] = pd.DataFrame({'row_annot': list(self.row_colors)}, - index=self.df_norm.index) - kws['col_colors'] = pd.DataFrame({'col_annot': list(self.col_colors)}, - index=self.df_norm.columns) + kws['row_colors'] = pd.DataFrame({'row_1': list(self.row_colors), + 'row_2': list(self.row_colors)}, + index=self.df_norm.index, + columns=['row_1', 'row_2']) + kws['col_colors'] = pd.DataFrame({'col_1': list(self.col_colors), + 'col_2': list(self.col_colors)}, + index=self.df_norm.columns, + columns=['col_1', 'col_2']) cm = mat.clustermap(self.df_norm, **kws) row_labels = [l.get_text() for l in cm.ax_row_colors.get_xticklabels()] - nt.assert_equal(row_labels, ['row_annot']) + nt.assert_equal(cm.row_color_labels, ['row_1', 'row_2']) + nt.assert_equal(row_labels, cm.row_color_labels) col_labels = [l.get_text() for l in cm.ax_col_colors.get_yticklabels()] - nt.assert_equal(col_labels, ['col_annot']) + nt.assert_equal(cm.col_color_labels, ['col_1', 'col_2']) + nt.assert_equal(col_labels[::-1], cm.col_color_labels) def test_row_col_colors_df_shuffled(self): # Tests if colors are properly matched, even if given in wrong order @@ -909,6 +915,36 @@ def test_row_col_colors_df_missing(self): nt.assert_equal(list(cm.row_colors)[0], [(1.0, 1.0, 1.0)] + list(self.row_colors[1:])) + def test_row_col_colors_df_one_axis(self): + # Test case with only row annotation. + kws1 = self.default_kws.copy() + kws1['row_colors'] = pd.DataFrame({'row_1': list(self.row_colors), + 'row_2': list(self.row_colors)}, + index=self.df_norm.index, + columns=['row_1', 'row_2']) + + cm1 = mat.clustermap(self.df_norm, **kws1) + + row_labels = [l.get_text() for l in + cm1.ax_row_colors.get_xticklabels()] + nt.assert_equal(cm1.row_color_labels, ['row_1', 'row_2']) + nt.assert_equal(row_labels, cm1.row_color_labels) + + # Test case with onl col annotation. + kws2 = self.default_kws.copy() + kws2['col_colors'] = pd.DataFrame({'col_1': list(self.col_colors), + 'col_2': list(self.col_colors)}, + index=self.df_norm.columns, + columns=['col_1', 'col_2']) + + cm2 = mat.clustermap(self.df_norm, **kws2) + + col_labels = [l.get_text() for l in + cm2.ax_col_colors.get_yticklabels()] + nt.assert_equal(cm2.col_color_labels, ['col_1', 'col_2']) + nt.assert_equal(col_labels[::-1], cm2.col_color_labels) + + def test_row_col_colors_series(self): kws = self.default_kws.copy() kws['row_colors'] = pd.Series(list(self.row_colors), name='row_annot', @@ -920,11 +956,13 @@ def test_row_col_colors_series(self): row_labels = [l.get_text() for l in cm.ax_row_colors.get_xticklabels()] - nt.assert_equal(row_labels, ['row_annot']) + nt.assert_equal(cm.row_color_labels, ['row_annot']) + nt.assert_equal(row_labels, cm.row_color_labels) col_labels = [l.get_text() for l in cm.ax_col_colors.get_yticklabels()] - nt.assert_equal(col_labels, ['col_annot']) + nt.assert_equal(cm.col_color_labels, ['col_annot']) + nt.assert_equal(col_labels, cm.col_color_labels) def test_row_col_colors_series_shuffled(self): # Tests if colors are properly matched, even if given in wrong order From 3d25d05e16531e4bd44de7d1ce62b39350834603 Mon Sep 17 00:00:00 2001 From: Julian de Ruiter Date: Sun, 6 Mar 2016 11:28:13 +0100 Subject: [PATCH 0242/1738] Remove extra blank line for pep8. --- seaborn/tests/test_matrix.py | 1 - 1 file changed, 1 deletion(-) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 372972c27d..1a361529d4 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -944,7 +944,6 @@ def test_row_col_colors_df_one_axis(self): nt.assert_equal(cm2.col_color_labels, ['col_1', 'col_2']) nt.assert_equal(col_labels[::-1], cm2.col_color_labels) - def test_row_col_colors_series(self): kws = self.default_kws.copy() kws['row_colors'] = pd.Series(list(self.row_colors), name='row_annot', From 9142db7ed8306536981b23cdd7546ab35ef86123 Mon Sep 17 00:00:00 2001 From: gkunter Date: Sun, 13 Mar 2016 19:40:06 +0100 Subject: [PATCH 0243/1738] Add tests for Unicode-related #719 This commit includes seven tests for FacetGrid with different data frames containing either one Unicode string as a column label or as values in one grouping column. Under Python 3.x, which has full Unicode support, all seven tests pass. Under Python 2.7, all tests fail in FacetGrid.set_titles() in one of the lines containing the instruction ```title = template.format(**args)```. --- seaborn/tests/test_axisgrid.py | 133 +++++++++++++++++++++++++++++++-- 1 file changed, 126 insertions(+), 7 deletions(-) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 670e2ddac6..14b2367be8 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -580,6 +580,132 @@ def test_dropna(self): g = ag.FacetGrid(df, dropna=True, row="hasna") nt.assert_equal(g._not_na.sum(), 50) + def test_unicode_column_label_with_rows(self): + + # use a smaller copy of the default testing data frame: + df = self.df.copy() + df = df[["a", "b", "x"]] + + # rename column 'a' (which will be used for the columns in the grid) + # by using a Unicode string: + unicode_column_label = u"\u01ff\u02ff\u03ff" + df = df.rename(columns={"a": unicode_column_label}) + + # ensure that the data frame columns have the expected names: + nt.assert_equal(list(df.columns), [unicode_column_label, "b", "x"]) + + # plot the grid -- if successful, no UnicodeEncodingError should + # occur: + g = ag.FacetGrid(df, col=unicode_column_label, row="b") + g = g.map(plt.plot, "x") + + def test_unicode_column_label_no_rows(self): + + # use a smaller copy of the default testing data frame: + df = self.df.copy() + df = df[["a", "x"]] + + # rename column 'a' (which will be used for the columns in the grid) + # by using a Unicode string: + unicode_column_label = u"\u01ff\u02ff\u03ff" + df = df.rename(columns={"a": unicode_column_label}) + + # ensure that the data frame columns have the expected names: + nt.assert_equal(list(df.columns), [unicode_column_label, "x"]) + + # plot the grid -- if successful, no UnicodeEncodingError should + # occur: + g = ag.FacetGrid(df, col=unicode_column_label) + g = g.map(plt.plot, "x") + + def test_unicode_row_label_with_columns(self): + + # use a smaller copy of the default testing data frame: + df = self.df.copy() + df = df[["a", "b", "x"]] + + # rename column 'b' (which will be used for the rows in the grid) + # by using a Unicode string: + unicode_row_label = u"\u01ff\u02ff\u03ff" + df = df.rename(columns={"b": unicode_row_label}) + + # ensure that the data frame columns have the expected names: + nt.assert_equal(list(df.columns), ["a", unicode_row_label, "x"]) + + # plot the grid -- if successful, no UnicodeEncodingError should + # occur: + g = ag.FacetGrid(df, col="a", row=unicode_row_label) + g = g.map(plt.plot, "x") + + def test_unicode_row_label_no_columns(self): + + # use a smaller copy of the default testing data frame: + df = self.df.copy() + df = df[["b", "x"]] + + # rename column 'b' (which will be used for the rows in the grid) + # by using a Unicode string: + unicode_row_label = u"\u01ff\u02ff\u03ff" + df = df.rename(columns={"b": unicode_row_label}) + + # ensure that the data frame columns have the expected names: + nt.assert_equal(list(df.columns), [unicode_row_label, "x"]) + + # plot the grid -- if successful, no UnicodeEncodingError should + # occur: + g = ag.FacetGrid(df, row=unicode_row_label) + g = g.map(plt.plot, "x") + + def test_unicode_content_with_row_and_column(self): + + df = self.df.copy() + + # replace content of column 'a' (which will form the columns in the + # grid) by Unicode characters: + unicode_column_val = np.repeat((u'\u01ff', u'\u02ff', u'\u03ff'), 20) + df["a"] = unicode_column_val + + # make sure that the replacement worked as expected: + nt.assert_equal(list(df["a"]), [u'\u01ff'] * 20 + [u'\u02ff'] * 20 + [u'\u03ff'] * 20) + + # plot the grid -- if successful, no UnicodeEncodingError should + # occur: + g = ag.FacetGrid(df, col="a", row="b") + g = g.map(plt.plot, "x") + + def test_unicode_content_no_rows(self): + + df = self.df.copy() + + # replace content of column 'a' (which will form the columns in the + # grid) by Unicode characters: + unicode_column_val = np.repeat((u'\u01ff', u'\u02ff', u'\u03ff'), 20) + df["a"] = unicode_column_val + + # make sure that the replacement worked as expected: + nt.assert_equal(list(df["a"]), [u'\u01ff'] * 20 + [u'\u02ff'] * 20 + [u'\u03ff'] * 20) + + # plot the grid -- if successful, no UnicodeEncodingError should + # occur: + g = ag.FacetGrid(df, col="a") + g = g.map(plt.plot, "x") + + def test_unicode_content_no_columns(self): + + df = self.df.copy() + + # replace content of column 'a' (which will form the rows in the + # grid) by Unicode characters: + unicode_column_val = np.repeat((u'\u01ff', u'\u02ff', u'\u03ff'), 20) + df["b"] = unicode_column_val + + # make sure that the replacement worked as expected: + nt.assert_equal(list(df["b"]), [u'\u01ff'] * 20 + [u'\u02ff'] * 20 + [u'\u03ff'] * 20) + + # plot the grid -- if successful, no UnicodeEncodingError should + # occur: + g = ag.FacetGrid(df, row="b") + g = g.map(plt.plot, "x") class TestPairGrid(PlotTestCase): @@ -780,13 +906,6 @@ def test_map_diag(self): for ax in g3.diag_axes: nt.assert_equal(len(ax.patches), 40) - g4 = ag.PairGrid(self.df, hue="a") - g4.map_diag(plt.hist, histtype='step') - - for ax in g4.diag_axes: - for ptch in ax.patches: - nt.assert_equal(ptch.fill, False) - @skipif(old_matplotlib) def test_map_diag_and_offdiag(self): From 70285ee872ecbd70c5dd229976522350bf9aa6e3 Mon Sep 17 00:00:00 2001 From: gkunter Date: Sun, 13 Mar 2016 20:22:47 +0100 Subject: [PATCH 0244/1738] Fix #719 by converting the format templates in FacetGrid.set_titles() to Unicode. This commit adds the function ``to_utf8`` to utils.py which reliably converts Python objects to unicode strings both in Python 2.7 and 3.x. This function is used in ``FacetGrid.set_titles()`` to ensure that the string format templates used in the plot titles are always unicode strings. The function ``to_utf8`` is somewhat similar in spirit to the one suggested in #724. That commit, however, worked only in Python 2.7, and broke Python 3.x compatibility. --- seaborn/axisgrid.py | 5 +++++ seaborn/utils.py | 53 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index a68976ee36..a1cabcc485 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -922,6 +922,10 @@ def set_titles(self, template=None, row_template=None, col_template=None, template = row_template else: template = " | ".join([row_template, col_template]) + + row_template = utils.to_utf8(row_template) + col_template = utils.to_utf8(col_template) + template = utils.to_utf8(template) if self._margin_titles: if self.row_names is not None: @@ -1836,3 +1840,4 @@ def savefig(self, *args, **kwargs): """Wrap figure.savefig defaulting to tight bounding box.""" kwargs.setdefault("bbox_inches", "tight") self.fig.savefig(*args, **kwargs) + diff --git a/seaborn/utils.py b/seaborn/utils.py index 6e91b98d0b..e4a78a8470 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -563,3 +563,56 @@ def relative_luminance(color): return lum.item() except ValueError: return lum + + row_template = _to_utf8(row_template) + +def to_utf8(obj): + """Return a Unicode string representing a Python object. + + Unicode strings (i.e. type ``unicode`` in Python 2.7 and type ``str`` in + Python 3.x) are returned unchanged. + + Byte strings (i.e. type ``str`` in Python 2.7 and type ``bytes`` in + Python 3.x) are returned as UTF-8-encoded strings. + + For other objects, the method ``__str__()`` is called, and the result is + returned as a UTF-8-encoded string. + + Parameters + ---------- + obj : object + Any Python object + + Returns + ------- + s : unicode (Python 2.7) / str (Python 3.x) + UTF-8-encoded string representation of ``obj`` + """ + if isinstance(obj, str): + try: + # If obj is a string, try to return it as a Unicode-encoded + # string: + return obj.decode("utf-8") + except AttributeError: + # Python 3.x strings are already Unicode, and do not have a + # decode() method, so the unchanged string is returned + return obj + + # + try: + if isinstance(obj, unicode): + # do not attemt a conversion if string is already a Unicode + # string: + return obj + else: + # call __str__() for non-string object, and return the + # result to Unicode: + return obj.__str__().decode("utf-8") + except NameError: + # NameError is raised in Python 3.x as type 'unicode' is not + # defined. + if isinstance(obj, bytes): + return obj.decode("utf-8") + else: + return obj.__str__() + From d76a6adc8785d5bac9e3914760b7efdbcd8890bc Mon Sep 17 00:00:00 2001 From: gkunter Date: Sun, 13 Mar 2016 20:36:41 +0100 Subject: [PATCH 0245/1738] Restore code accidentally deleted in previous commit --- seaborn/axisgrid.py | 1 - seaborn/tests/test_axisgrid.py | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index a1cabcc485..e23d03b699 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1840,4 +1840,3 @@ def savefig(self, *args, **kwargs): """Wrap figure.savefig defaulting to tight bounding box.""" kwargs.setdefault("bbox_inches", "tight") self.fig.savefig(*args, **kwargs) - diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 14b2367be8..a2b18f56b7 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -906,6 +906,13 @@ def test_map_diag(self): for ax in g3.diag_axes: nt.assert_equal(len(ax.patches), 40) + g4 = ag.PairGrid(self.df, hue="a") + g4.map_diag(plt.hist, histtype='step') + + for ax in g4.diag_axes: + for ptch in ax.patches: + nt.assert_equal(ptch.fill, False) + @skipif(old_matplotlib) def test_map_diag_and_offdiag(self): From 628a782ee66908994e7ca5dd2d2ab54744656bb9 Mon Sep 17 00:00:00 2001 From: gkunter Date: Sun, 13 Mar 2016 20:57:19 +0100 Subject: [PATCH 0246/1738] Add test for ``to_utf8`` function. --- seaborn/tests/test_utils.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index 1c9a9103f2..d5b8ad897f 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -110,7 +110,13 @@ def test_iqr(): iqr = utils.iqr(a) assert_equal(iqr, 2) - +def test_str_to_utf8(): + """Test the to_utf8 function: string to Unicode""" + s = "\u01ff\u02ff" + u = utils.to_utf8(s) + assert_equal(type(s), type(str())) + assert_equal(type(u), type(u"\u01ff\u02ff")) + class TestSpineUtils(PlotTestCase): sides = ["left", "right", "bottom", "top"] From 9e55a80f41ce3358255dd4c31688d0390b5c1a4c Mon Sep 17 00:00:00 2001 From: gkunter Date: Sun, 13 Mar 2016 21:15:32 +0100 Subject: [PATCH 0247/1738] Fix typo --- seaborn/utils.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/seaborn/utils.py b/seaborn/utils.py index e4a78a8470..61b1449ebe 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -564,8 +564,6 @@ def relative_luminance(color): except ValueError: return lum - row_template = _to_utf8(row_template) - def to_utf8(obj): """Return a Unicode string representing a Python object. From 29b61c37a3fd4a4a31e47881ff735e329be22dd3 Mon Sep 17 00:00:00 2001 From: gkunter Date: Sun, 13 Mar 2016 21:43:04 +0100 Subject: [PATCH 0248/1738] PEP8 fixes --- seaborn/axisgrid.py | 2 +- seaborn/tests/test_axisgrid.py | 41 ++++++++++++++++++++-------------- seaborn/tests/test_utils.py | 4 +++- seaborn/utils.py | 21 +++++++++-------- 4 files changed, 38 insertions(+), 30 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index e23d03b699..425b6cf44a 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -922,7 +922,7 @@ def set_titles(self, template=None, row_template=None, col_template=None, template = row_template else: template = " | ".join([row_template, col_template]) - + row_template = utils.to_utf8(row_template) col_template = utils.to_utf8(col_template) template = utils.to_utf8(template) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index a2b18f56b7..0129141aaf 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -581,7 +581,7 @@ def test_dropna(self): nt.assert_equal(g._not_na.sum(), 50) def test_unicode_column_label_with_rows(self): - + # use a smaller copy of the default testing data frame: df = self.df.copy() df = df[["a", "b", "x"]] @@ -590,17 +590,17 @@ def test_unicode_column_label_with_rows(self): # by using a Unicode string: unicode_column_label = u"\u01ff\u02ff\u03ff" df = df.rename(columns={"a": unicode_column_label}) - + # ensure that the data frame columns have the expected names: nt.assert_equal(list(df.columns), [unicode_column_label, "b", "x"]) - # plot the grid -- if successful, no UnicodeEncodingError should + # plot the grid -- if successful, no UnicodeEncodingError should # occur: g = ag.FacetGrid(df, col=unicode_column_label, row="b") g = g.map(plt.plot, "x") def test_unicode_column_label_no_rows(self): - + # use a smaller copy of the default testing data frame: df = self.df.copy() df = df[["a", "x"]] @@ -609,11 +609,11 @@ def test_unicode_column_label_no_rows(self): # by using a Unicode string: unicode_column_label = u"\u01ff\u02ff\u03ff" df = df.rename(columns={"a": unicode_column_label}) - + # ensure that the data frame columns have the expected names: nt.assert_equal(list(df.columns), [unicode_column_label, "x"]) - # plot the grid -- if successful, no UnicodeEncodingError should + # plot the grid -- if successful, no UnicodeEncodingError should # occur: g = ag.FacetGrid(df, col=unicode_column_label) g = g.map(plt.plot, "x") @@ -632,7 +632,7 @@ def test_unicode_row_label_with_columns(self): # ensure that the data frame columns have the expected names: nt.assert_equal(list(df.columns), ["a", unicode_row_label, "x"]) - # plot the grid -- if successful, no UnicodeEncodingError should + # plot the grid -- if successful, no UnicodeEncodingError should # occur: g = ag.FacetGrid(df, col="a", row=unicode_row_label) g = g.map(plt.plot, "x") @@ -651,7 +651,7 @@ def test_unicode_row_label_no_columns(self): # ensure that the data frame columns have the expected names: nt.assert_equal(list(df.columns), [unicode_row_label, "x"]) - # plot the grid -- if successful, no UnicodeEncodingError should + # plot the grid -- if successful, no UnicodeEncodingError should # occur: g = ag.FacetGrid(df, row=unicode_row_label) g = g.map(plt.plot, "x") @@ -660,15 +660,17 @@ def test_unicode_content_with_row_and_column(self): df = self.df.copy() - # replace content of column 'a' (which will form the columns in the + # replace content of column 'a' (which will form the columns in the # grid) by Unicode characters: unicode_column_val = np.repeat((u'\u01ff', u'\u02ff', u'\u03ff'), 20) df["a"] = unicode_column_val # make sure that the replacement worked as expected: - nt.assert_equal(list(df["a"]), [u'\u01ff'] * 20 + [u'\u02ff'] * 20 + [u'\u03ff'] * 20) + nt.assert_equal( + list(df["a"]), + [u'\u01ff'] * 20 + [u'\u02ff'] * 20 + [u'\u03ff'] * 20) - # plot the grid -- if successful, no UnicodeEncodingError should + # plot the grid -- if successful, no UnicodeEncodingError should # occur: g = ag.FacetGrid(df, col="a", row="b") g = g.map(plt.plot, "x") @@ -677,15 +679,17 @@ def test_unicode_content_no_rows(self): df = self.df.copy() - # replace content of column 'a' (which will form the columns in the + # replace content of column 'a' (which will form the columns in the # grid) by Unicode characters: unicode_column_val = np.repeat((u'\u01ff', u'\u02ff', u'\u03ff'), 20) df["a"] = unicode_column_val # make sure that the replacement worked as expected: - nt.assert_equal(list(df["a"]), [u'\u01ff'] * 20 + [u'\u02ff'] * 20 + [u'\u03ff'] * 20) + nt.assert_equal( + list(df["a"]), + [u'\u01ff'] * 20 + [u'\u02ff'] * 20 + [u'\u03ff'] * 20) - # plot the grid -- if successful, no UnicodeEncodingError should + # plot the grid -- if successful, no UnicodeEncodingError should # occur: g = ag.FacetGrid(df, col="a") g = g.map(plt.plot, "x") @@ -694,19 +698,22 @@ def test_unicode_content_no_columns(self): df = self.df.copy() - # replace content of column 'a' (which will form the rows in the + # replace content of column 'a' (which will form the rows in the # grid) by Unicode characters: unicode_column_val = np.repeat((u'\u01ff', u'\u02ff', u'\u03ff'), 20) df["b"] = unicode_column_val # make sure that the replacement worked as expected: - nt.assert_equal(list(df["b"]), [u'\u01ff'] * 20 + [u'\u02ff'] * 20 + [u'\u03ff'] * 20) + nt.assert_equal( + list(df["b"]), + [u'\u01ff'] * 20 + [u'\u02ff'] * 20 + [u'\u03ff'] * 20) - # plot the grid -- if successful, no UnicodeEncodingError should + # plot the grid -- if successful, no UnicodeEncodingError should # occur: g = ag.FacetGrid(df, row="b") g = g.map(plt.plot, "x") + class TestPairGrid(PlotTestCase): rs = np.random.RandomState(sum(map(ord, "PairGrid"))) diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index d5b8ad897f..82a2ab596a 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -110,13 +110,15 @@ def test_iqr(): iqr = utils.iqr(a) assert_equal(iqr, 2) + def test_str_to_utf8(): """Test the to_utf8 function: string to Unicode""" s = "\u01ff\u02ff" u = utils.to_utf8(s) assert_equal(type(s), type(str())) assert_equal(type(u), type(u"\u01ff\u02ff")) - + + class TestSpineUtils(PlotTestCase): sides = ["left", "right", "bottom", "top"] diff --git a/seaborn/utils.py b/seaborn/utils.py index 61b1449ebe..1cc9d6e820 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -564,23 +564,24 @@ def relative_luminance(color): except ValueError: return lum + def to_utf8(obj): """Return a Unicode string representing a Python object. - + Unicode strings (i.e. type ``unicode`` in Python 2.7 and type ``str`` in Python 3.x) are returned unchanged. - - Byte strings (i.e. type ``str`` in Python 2.7 and type ``bytes`` in + + Byte strings (i.e. type ``str`` in Python 2.7 and type ``bytes`` in Python 3.x) are returned as UTF-8-encoded strings. - For other objects, the method ``__str__()`` is called, and the result is + For other objects, the method ``__str__()`` is called, and the result is returned as a UTF-8-encoded string. Parameters ---------- obj : object Any Python object - + Returns ------- s : unicode (Python 2.7) / str (Python 3.x) @@ -588,29 +589,27 @@ def to_utf8(obj): """ if isinstance(obj, str): try: - # If obj is a string, try to return it as a Unicode-encoded + # If obj is a string, try to return it as a Unicode-encoded # string: return obj.decode("utf-8") except AttributeError: - # Python 3.x strings are already Unicode, and do not have a + # Python 3.x strings are already Unicode, and do not have a # decode() method, so the unchanged string is returned return obj - # try: if isinstance(obj, unicode): # do not attemt a conversion if string is already a Unicode # string: return obj else: - # call __str__() for non-string object, and return the + # call __str__() for non-string object, and return the # result to Unicode: return obj.__str__().decode("utf-8") except NameError: - # NameError is raised in Python 3.x as type 'unicode' is not + # NameError is raised in Python 3.x as type 'unicode' is not # defined. if isinstance(obj, bytes): return obj.decode("utf-8") else: return obj.__str__() - From 10bdb18f47bb5fc0a30d34954ff6f174b4cf5881 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 13 Mar 2016 18:21:52 -0400 Subject: [PATCH 0249/1738] Update release notes --- doc/releases/v0.7.1.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.7.1.txt b/doc/releases/v0.7.1.txt index cc8444f095..d71edf31e0 100644 --- a/doc/releases/v0.7.1.txt +++ b/doc/releases/v0.7.1.txt @@ -9,3 +9,5 @@ v0.7.1 (Unreleased) - Made it possible to pass a tick locator object to the :func:`heatmap` colorbar. - Made it possible to use different styles (e.g., step) for :class:`PairGrid` histograms when there are multiple hue levels. + +- Improved unicode compatibility in :class:`FacetGrid`. From bfeeaa476a825fdf4b2d2fe28a5fbd007f59e241 Mon Sep 17 00:00:00 2001 From: Olga Botvinnik Date: Mon, 14 Mar 2016 15:56:21 -0700 Subject: [PATCH 0250/1738] added annot_data arugment and propagated to classes/methods --- seaborn/matrix.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index ff94bea352..a63b17fab5 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -93,7 +93,7 @@ class _HeatMapper(object): """Draw a heatmap plot of a matrix with nice labels and colormaps.""" def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, - annot_kws, cbar, cbar_kws, + annot_kws, annot_data, cbar, cbar_kws, xticklabels=True, yticklabels=True, mask=None): """Initialize the plotting object.""" # We always want to have a DataFrame with semantic information @@ -168,6 +168,12 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, self.data = data self.plot_data = plot_data self.annot = annot + + if annot_data is None: + self.annot_data = plot_data + else: + self.annot_data = annot_data.ix[::-1].values + self.fmt = fmt self.annot_kws = {} if annot_kws is None else annot_kws self.cbar = cbar @@ -215,15 +221,16 @@ def _annotate_heatmap(self, ax, mesh): """Add textual labels with the value in each cell.""" mesh.update_scalarmappable() xpos, ypos = np.meshgrid(ax.get_xticks(), ax.get_yticks()) - for x, y, val, color in zip(xpos.flat, ypos.flat, - mesh.get_array(), mesh.get_facecolors()): - if val is not np.ma.masked: + for x, y, m, color, val in zip(xpos.flat, ypos.flat, + mesh.get_array(), mesh.get_facecolors(), + self.annot_data.flat): + if m is not np.ma.masked: l = relative_luminance(color) text_color = ".15" if l > .408 else "w" - val = ("{:" + self.fmt + "}").format(val) + annotation = ("{:" + self.fmt + "}").format(val) text_kwargs = dict(color=text_color, ha="center", va="center") text_kwargs.update(self.annot_kws) - ax.text(x, y, val, **text_kwargs) + ax.text(x, y, annotation, **text_kwargs) def plot(self, ax, cax, kws): """Draw the heatmap on the provided Axes.""" @@ -267,7 +274,7 @@ def plot(self, ax, cax, kws): def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, - annot=False, fmt=".2g", annot_kws=None, + annot=False, fmt=".2g", annot_kws=None, annot_data=None, linewidths=0, linecolor="white", cbar=True, cbar_kws=None, cbar_ax=None, square=False, ax=None, xticklabels=True, yticklabels=True, @@ -454,8 +461,8 @@ def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, """ # Initialize the plotter object plotter = _HeatMapper(data, vmin, vmax, cmap, center, robust, annot, fmt, - annot_kws, cbar, cbar_kws, xticklabels, yticklabels, - mask) + annot_kws, annot_data, cbar, cbar_kws, xticklabels, + yticklabels, mask) # Add the pcolormesh kwargs here kwargs["linewidths"] = linewidths From 50972283cdb0a3cafd6c4e99a4faf5d6df6cd60e Mon Sep 17 00:00:00 2001 From: Olga Botvinnik Date: Mon, 14 Mar 2016 16:04:31 -0700 Subject: [PATCH 0251/1738] Add case for when annot_data is a numpy array --- seaborn/matrix.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index a63b17fab5..58ee6bdfc6 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -172,7 +172,10 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, if annot_data is None: self.annot_data = plot_data else: - self.annot_data = annot_data.ix[::-1].values + if isinstance(annot_data, pd.DataFrame): + self.annot_data = annot_data.ix[::-1].values + else: + self.annot_data = annot_data[::-1] self.fmt = fmt self.annot_kws = {} if annot_kws is None else annot_kws From 840fcb7d78c0a597d081bc3713c42f83467f3d78 Mon Sep 17 00:00:00 2001 From: Olga Botvinnik Date: Tue, 15 Mar 2016 08:04:07 -0700 Subject: [PATCH 0252/1738] Add annot_data as default kws for testing --- seaborn/tests/test_matrix.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index a70c28489b..4c6fb46255 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -39,7 +39,7 @@ class TestHeatmap(PlotTestCase): default_kws = dict(vmin=None, vmax=None, cmap=None, center=None, robust=False, annot=False, fmt=".2f", annot_kws=None, - cbar=True, cbar_kws=None, mask=None) + cbar=True, cbar_kws=None, mask=None, annot_data=None) def test_ndarray_input(self): @@ -253,6 +253,16 @@ def test_heatmap_annotation_mesh_colors(self): plt.close("all") + def test_heatmap_annotation_other_data(self): + annot_data = self.df_norm + 10 + + ax = mat.heatmap(self.df_norm, annot=True, fmt=".1f", + annot_kws={"fontsize": 14}, annot_data=annot_data) + + for val, text in zip(annot_data.values[::-1].flat, ax.texts): + nt.assert_equal(text.get_text(), "{:.1f}".format(val)) + nt.assert_equal(text.get_fontsize(), 14) + def test_heatmap_cbar(self): f = plt.figure() From 441c22c2908a64b51991c76caebb5c292d887c0b Mon Sep 17 00:00:00 2001 From: Olga Botvinnik Date: Fri, 18 Mar 2016 12:54:51 -0700 Subject: [PATCH 0253/1738] Remove annot_data as kw, use annot instead. Gracefully check if annot is bool or not, and assign internal annot_data variable respectively --- seaborn/matrix.py | 26 ++++++++++++++++---------- seaborn/tests/test_matrix.py | 6 +++--- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 58ee6bdfc6..43fb488d56 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -93,7 +93,7 @@ class _HeatMapper(object): """Draw a heatmap plot of a matrix with nice labels and colormaps.""" def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, - annot_kws, annot_data, cbar, cbar_kws, + annot_kws, cbar, cbar_kws, xticklabels=True, yticklabels=True, mask=None): """Initialize the plotting object.""" # We always want to have a DataFrame with semantic information @@ -169,13 +169,17 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, self.plot_data = plot_data self.annot = annot - if annot_data is None: + if isinstance(self.annot, bool) and self.annot: self.annot_data = plot_data - else: - if isinstance(annot_data, pd.DataFrame): - self.annot_data = annot_data.ix[::-1].values + elif not isinstance(self.annot, bool): + if isinstance(self.annot, pd.DataFrame): + self.annot_data = self.annot.ix[::-1].values else: - self.annot_data = annot_data[::-1] + self.annot_data = self.annot[::-1] + if self.annot.shape != self.plot_data.shape: + raise ValueError('Data supplied to "annot" must be the same ' + 'shape as the data to plot.') + self.annot = True self.fmt = fmt self.annot_kws = {} if annot_kws is None else annot_kws @@ -277,7 +281,7 @@ def plot(self, ax, cax, kws): def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, - annot=False, fmt=".2g", annot_kws=None, annot_data=None, + annot=False, fmt=".2g", annot_kws=None, linewidths=0, linecolor="white", cbar=True, cbar_kws=None, cbar_ax=None, square=False, ax=None, xticklabels=True, yticklabels=True, @@ -314,8 +318,10 @@ def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, robust : bool, optional If True and ``vmin`` or ``vmax`` are absent, the colormap range is computed with robust quantiles instead of the extreme values. - annot : bool, optional - If True, write the data value in each cell. + annot : bool, or array, optional + If True, write the data value in each cell. If an array of the same + shape as ``data``, then use this to annotate the heatmap instead of the + raw data. fmt : string, optional String formatting code to use when ``annot`` is True. annot_kws : dict of key, value mappings, optional @@ -464,7 +470,7 @@ def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, """ # Initialize the plotter object plotter = _HeatMapper(data, vmin, vmax, cmap, center, robust, annot, fmt, - annot_kws, annot_data, cbar, cbar_kws, xticklabels, + annot_kws, cbar, cbar_kws, xticklabels, yticklabels, mask) # Add the pcolormesh kwargs here diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 4c6fb46255..140f652aa7 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -39,7 +39,7 @@ class TestHeatmap(PlotTestCase): default_kws = dict(vmin=None, vmax=None, cmap=None, center=None, robust=False, annot=False, fmt=".2f", annot_kws=None, - cbar=True, cbar_kws=None, mask=None, annot_data=None) + cbar=True, cbar_kws=None, mask=None) def test_ndarray_input(self): @@ -256,8 +256,8 @@ def test_heatmap_annotation_mesh_colors(self): def test_heatmap_annotation_other_data(self): annot_data = self.df_norm + 10 - ax = mat.heatmap(self.df_norm, annot=True, fmt=".1f", - annot_kws={"fontsize": 14}, annot_data=annot_data) + ax = mat.heatmap(self.df_norm, annot=annot_data, fmt=".1f", + annot_kws={"fontsize": 14}) for val, text in zip(annot_data.values[::-1].flat, ax.texts): nt.assert_equal(text.get_text(), "{:.1f}".format(val)) From 7603d9c0e1929bf8af673fbae1a0df34f63d8023 Mon Sep 17 00:00:00 2001 From: "David C. Gemperline" Date: Wed, 30 Mar 2016 18:06:03 -0500 Subject: [PATCH 0254/1738] Added jat255's cchanges with some edits to get tests passing stil some errors --- seaborn/categorical.py | 84 +++++++++++++++++++++++++------ seaborn/tests/test_categorical.py | 4 +- 2 files changed, 70 insertions(+), 18 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 49b56729aa..bb7d9ceaec 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1498,24 +1498,43 @@ def estimate_statistic(self, estimator, ci, n_boot): self.value_label = "{}({})".format(estimator.__name__, self.value_label) - def draw_confints(self, ax, at_group, confint, colors, **kws): + def draw_confints(self, + ax, at_group, + confint, + colors, + conf_lw=1, + capsize=0, + **kws): - kws.setdefault("lw", mpl.rcParams["lines.linewidth"] * 1.8) + kws.setdefault("lw", mpl.rcParams["lines.linewidth"] * conf_lw) + kws.pop('lw') for at, (ci_low, ci_high), color in zip(at_group, confint, colors): if self.orient == "v": - ax.plot([at, at], [ci_low, ci_high], color=color, **kws) + ax.plot([at, at], [ci_low, ci_high], color=color, + lw=conf_lw, **kws) + ax.plot([at - capsize / 2, at + capsize / 2], + [ci_low, ci_low], color=color, lw=conf_lw, **kws) + ax.plot([at - capsize / 2, at + capsize / 2], + [ci_high, ci_high], color=color, lw=conf_lw, **kws) else: - ax.plot([ci_low, ci_high], [at, at], color=color, **kws) - + ax.plot([ci_low, ci_high], [at, at], color=color, + lw=conf_lw, **kws) + ax.plot([ci_low, ci_low], + [at - capsize /2, at + capsize /2], + color=color, lw=conf_lw, **kws) + ax.plot([ci_high, ci_high], + [at - capsize /2, at + capsize /2], + color=color, lw=conf_lw, **kws) class _BarPlotter(_CategoricalStatPlotter): """Show point estimates and confidence intervals with bars.""" def __init__(self, x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, - orient, color, palette, saturation, errcolor): + orient, color, palette, saturation, errcolor, conf_lw, + capsize): """Initialize the plotter.""" self.establish_variables(x, y, hue, data, orient, order, hue_order, units) @@ -1523,6 +1542,8 @@ def __init__(self, x, y, hue, data, order, hue_order, self.estimate_statistic(estimator, ci, n_boot) self.errcolor = errcolor + self.conf_lw = conf_lw + self.capsize = capsize def draw_bars(self, ax, kws): """Draw the bars onto `ax`.""" @@ -1538,7 +1559,12 @@ def draw_bars(self, ax, kws): # Draw the confidence intervals errcolors = [self.errcolor] * len(barpos) - self.draw_confints(ax, barpos, self.confint, errcolors) + self.draw_confints(ax, + barpos, + self.confint, + errcolors, + self.conf_lw, + self.capsize) else: @@ -1554,7 +1580,12 @@ def draw_bars(self, ax, kws): if self.confint.size: confint = self.confint[:, j] errcolors = [self.errcolor] * len(offpos) - self.draw_confints(ax, offpos, confint, errcolors) + self.draw_confints(ax, + offpos, + confint, + errcolors, + self.conf_lw, + self.capsize) def plot(self, ax, bar_kws): """Make the plot.""" @@ -1569,7 +1600,7 @@ class _PointPlotter(_CategoricalStatPlotter): def __init__(self, x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, markers, linestyles, dodge, join, scale, - orient, color, palette): + orient, color, palette, conf_lw, capsize): """Initialize the plotter.""" self.establish_variables(x, y, hue, data, orient, order, hue_order, units) @@ -1602,6 +1633,8 @@ def __init__(self, x, y, hue, data, order, hue_order, self.dodge = dodge self.join = join self.scale = scale + self.conf_lw = conf_lw + self.capsize = capsize @property def hue_offsets(self): @@ -1634,7 +1667,8 @@ def draw_points(self, ax): color=color, ls=ls, lw=lw) # Draw the confidence intervals - self.draw_confints(ax, pointpos, self.confint, self.colors, lw=lw) + self.draw_confints(ax, pointpos, self.confint, self.colors, + self.conf_lw, self.capsize, lw=lw) # Draw the estimate points marker = self.markers[0] @@ -1675,6 +1709,7 @@ def draw_points(self, ax): confint = self.confint[:, j] errcolors = [self.colors[j]] * len(offpos) self.draw_confints(ax, offpos, confint, errcolors, + self.conf_lw, self.capsize, zorder=z, lw=lw) # Draw the estimate points @@ -2025,6 +2060,17 @@ def plot(self, ax, boxplot_kws): ``1`` if you want the plot colors to perfectly match the input color spec.\ """), + capsize=dedent("""\ + capsize : float, optional + Length of caps on confidence interval (drawn perpendicular to primary + line. If 0.0 (default), no caps will be drawn. Typical values are + between 0.03 and 0.1.\ + """), + conf_lw = dedent("""\ + conf_lw : float, optional + Thickness of lines draw for the confidence interval (and caps). + Default is 1.8.\ + """), width=dedent("""\ width : float, optional Width of a full element when not using hue nesting, or width of all the @@ -2074,6 +2120,9 @@ def plot(self, ax, boxplot_kws): lvplot=dedent("""\ lvplot : An extension of the boxplot for long-tailed and large data sets. """), + + + ) _categorical_docs.update(_facet_docs) @@ -2831,7 +2880,7 @@ def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, estimator=np.mean, ci=95, n_boot=1000, units=None, orient=None, color=None, palette=None, saturation=.75, - errcolor=".26", ax=None, **kwargs): + errcolor=".26", conf_lw=1.8, capsize=0, ax=None, **kwargs): # Handle some deprecated arguments if "hline" in kwargs: @@ -2850,7 +2899,7 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, plotter = _BarPlotter(x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, orient, color, palette, saturation, - errcolor) + errcolor, conf_lw, capsize) if ax is None: ax = plt.gca() @@ -2894,6 +2943,8 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, errcolor : matplotlib color Color for the lines that represent the confidence interval. {ax_in} + {conf_lw} + {capsize} kwargs : key, value mappings Other keyword arguments are passed through to ``plt.bar`` at draw time. @@ -2989,7 +3040,8 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def pointplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, estimator=np.mean, ci=95, n_boot=1000, units=None, markers="o", linestyles="-", dodge=False, join=True, scale=1, - orient=None, color=None, palette=None, ax=None, **kwargs): + orient=None, color=None, palette=None, ax=None, conf_lw=1.8, + capsize=0, **kwargs): # Handle some deprecated arguments if "hline" in kwargs: @@ -3008,7 +3060,7 @@ def pointplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, plotter = _PointPlotter(x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, markers, linestyles, dodge, join, scale, - orient, color, palette) + orient, color, palette, conf_lw, capsize) if ax is None: ax = plt.gca() @@ -3187,12 +3239,12 @@ def countplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, elif x is not None and y is not None: raise TypeError("Cannot pass values for both `x` and `y`") else: - raise TypeError("Must pass valus for either `x` or `y`") + raise TypeError("Must pass values for either `x` or `y`") plotter = _BarPlotter(x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, orient, color, palette, saturation, - errcolor) + errcolor, conf_lw=1, capsize=0) plotter.value_label = "count" diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 3c0f921b90..27c41389c0 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1749,7 +1749,7 @@ class TestBarPlotter(CategoricalFixture): estimator=np.mean, ci=95, n_boot=100, units=None, order=None, hue_order=None, orient=None, color=None, palette=None, - saturation=.75, errcolor=".26") + saturation=.75, errcolor=".26", conf_lw=1.8, capsize=0) def test_nested_width(self): @@ -1974,7 +1974,7 @@ class TestPointPlotter(CategoricalFixture): order=None, hue_order=None, markers="o", linestyles="-", dodge=0, join=True, scale=1, - orient=None, color=None, palette=None) + orient=None, color=None, palette=None, conf_lw=1, capsize=0) def test_different_defualt_colors(self): From fef4fcd3cb3595b5bee0f84279ac7e4d685588ed Mon Sep 17 00:00:00 2001 From: "David C. Gemperline" Date: Wed, 30 Mar 2016 23:56:10 -0500 Subject: [PATCH 0255/1738] Added Error Bar Cap Functionality All Tests Passing --- seaborn/categorical.py | 78 +++++++++++++++++-------------- seaborn/tests/test_categorical.py | 4 +- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index bb7d9ceaec..33e65b9137 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1502,39 +1502,49 @@ def draw_confints(self, ax, at_group, confint, colors, - conf_lw=1, - capsize=0, + conf_lw=None, + capsize=None, **kws): - kws.setdefault("lw", mpl.rcParams["lines.linewidth"] * conf_lw) - kws.pop('lw') - - for at, (ci_low, ci_high), color in zip(at_group, - confint, - colors): - if self.orient == "v": - ax.plot([at, at], [ci_low, ci_high], color=color, - lw=conf_lw, **kws) - ax.plot([at - capsize / 2, at + capsize / 2], - [ci_low, ci_low], color=color, lw=conf_lw, **kws) - ax.plot([at - capsize / 2, at + capsize / 2], - [ci_high, ci_high], color=color, lw=conf_lw, **kws) - else: - ax.plot([ci_low, ci_high], [at, at], color=color, - lw=conf_lw, **kws) - ax.plot([ci_low, ci_low], - [at - capsize /2, at + capsize /2], - color=color, lw=conf_lw, **kws) - ax.plot([ci_high, ci_high], - [at - capsize /2, at + capsize /2], - color=color, lw=conf_lw, **kws) + if conf_lw: + kws.setdefault("lw", mpl.rcParams["lines.linewidth"] * conf_lw) + kws.pop("lw") + else: + kws.setdefault("lw", mpl.rcParams["lines.linewidth"] * 1.8) + + if capsize: + for at, (ci_low, ci_high), color in zip(at_group, + confint, + colors): + if self.orient == "v": + ax.plot([at, at], [ci_low, ci_high], color=color, **kws) + ax.plot([at - capsize / 2, at + capsize / 2], + [ci_low, ci_low], color=color, **kws) + ax.plot([at - capsize / 2, at + capsize / 2], + [ci_high, ci_high], color=color, **kws) + else: + ax.plot([ci_low, ci_high], [at, at], color=color, **kws) + ax.plot([ci_low, ci_low], + [at - capsize /2, at + capsize /2], + color=color, **kws) + ax.plot([ci_high, ci_high], + [at - capsize /2, at + capsize /2], + color=color, **kws) + else: + for at, (ci_low, ci_high), color in zip(at_group, + confint, + colors): + if self.orient == "v": + ax.plot([at, at], [ci_low, ci_high], color=color, **kws) + else: + ax.plot([ci_low, ci_high], [at, at], color=color, **kws) class _BarPlotter(_CategoricalStatPlotter): """Show point estimates and confidence intervals with bars.""" def __init__(self, x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, - orient, color, palette, saturation, errcolor, conf_lw, - capsize): + orient, color, palette, saturation, errcolor, conf_lw=None, + capsize=None): """Initialize the plotter.""" self.establish_variables(x, y, hue, data, orient, order, hue_order, units) @@ -1587,6 +1597,7 @@ def draw_bars(self, ax, kws): self.conf_lw, self.capsize) + def plot(self, ax, bar_kws): """Make the plot.""" self.draw_bars(ax, bar_kws) @@ -1600,7 +1611,7 @@ class _PointPlotter(_CategoricalStatPlotter): def __init__(self, x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, markers, linestyles, dodge, join, scale, - orient, color, palette, conf_lw, capsize): + orient, color, palette, conf_lw=None, capsize=None): """Initialize the plotter.""" self.establish_variables(x, y, hue, data, orient, order, hue_order, units) @@ -1668,8 +1679,7 @@ def draw_points(self, ax): # Draw the confidence intervals self.draw_confints(ax, pointpos, self.confint, self.colors, - self.conf_lw, self.capsize, lw=lw) - + self.conf_lw, self.capsize) # Draw the estimate points marker = self.markers[0] if self.orient == "h": @@ -1710,7 +1720,7 @@ def draw_points(self, ax): errcolors = [self.colors[j]] * len(offpos) self.draw_confints(ax, offpos, confint, errcolors, self.conf_lw, self.capsize, - zorder=z, lw=lw) + zorder=z) # Draw the estimate points marker = self.markers[j] @@ -2880,7 +2890,7 @@ def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, estimator=np.mean, ci=95, n_boot=1000, units=None, orient=None, color=None, palette=None, saturation=.75, - errcolor=".26", conf_lw=1.8, capsize=0, ax=None, **kwargs): + errcolor=".26", conf_lw=None, capsize=None, ax=None, **kwargs): # Handle some deprecated arguments if "hline" in kwargs: @@ -3040,8 +3050,8 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def pointplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, estimator=np.mean, ci=95, n_boot=1000, units=None, markers="o", linestyles="-", dodge=False, join=True, scale=1, - orient=None, color=None, palette=None, ax=None, conf_lw=1.8, - capsize=0, **kwargs): + orient=None, color=None, palette=None, ax=None, conf_lw=None, + capsize=None, **kwargs): # Handle some deprecated arguments if "hline" in kwargs: @@ -3244,7 +3254,7 @@ def countplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, plotter = _BarPlotter(x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, orient, color, palette, saturation, - errcolor, conf_lw=1, capsize=0) + errcolor) plotter.value_label = "count" diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 27c41389c0..3c0f921b90 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1749,7 +1749,7 @@ class TestBarPlotter(CategoricalFixture): estimator=np.mean, ci=95, n_boot=100, units=None, order=None, hue_order=None, orient=None, color=None, palette=None, - saturation=.75, errcolor=".26", conf_lw=1.8, capsize=0) + saturation=.75, errcolor=".26") def test_nested_width(self): @@ -1974,7 +1974,7 @@ class TestPointPlotter(CategoricalFixture): order=None, hue_order=None, markers="o", linestyles="-", dodge=0, join=True, scale=1, - orient=None, color=None, palette=None, conf_lw=1, capsize=0) + orient=None, color=None, palette=None) def test_different_defualt_colors(self): From 0676d1f5948dee4825caf29297dbcfdb08399af5 Mon Sep 17 00:00:00 2001 From: "David C. Gemperline" Date: Thu, 31 Mar 2016 00:40:23 -0500 Subject: [PATCH 0256/1738] Final Changes to Error Bar Caps --- seaborn/categorical.py | 1 - 1 file changed, 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 33e65b9137..415cb2c864 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1508,7 +1508,6 @@ def draw_confints(self, if conf_lw: kws.setdefault("lw", mpl.rcParams["lines.linewidth"] * conf_lw) - kws.pop("lw") else: kws.setdefault("lw", mpl.rcParams["lines.linewidth"] * 1.8) From 2715aa066c13c1369f72b8bf7021822f4eeb940f Mon Sep 17 00:00:00 2001 From: "David C. Gemperline" Date: Thu, 31 Mar 2016 01:22:06 -0500 Subject: [PATCH 0257/1738] formatting changes --- seaborn/categorical.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 415cb2c864..84e0aa7a0f 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1524,22 +1524,22 @@ def draw_confints(self, else: ax.plot([ci_low, ci_high], [at, at], color=color, **kws) ax.plot([ci_low, ci_low], - [at - capsize /2, at + capsize /2], + [at - capsize / 2, at + capsize / 2], color=color, **kws) ax.plot([ci_high, ci_high], - [at - capsize /2, at + capsize /2], + [at - capsize / 2, at + capsize / 2], color=color, **kws) else: - for at, (ci_low, ci_high), color in zip(at_group, - confint, - colors): + for at, (ci_low, ci_high), color in zip(at_group, confint, colors): if self.orient == "v": ax.plot([at, at], [ci_low, ci_high], color=color, **kws) else: ax.plot([ci_low, ci_high], [at, at], color=color, **kws) + class _BarPlotter(_CategoricalStatPlotter): """Show point estimates and confidence intervals with bars.""" + def __init__(self, x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, orient, color, palette, saturation, errcolor, conf_lw=None, @@ -1596,7 +1596,6 @@ def draw_bars(self, ax, kws): self.conf_lw, self.capsize) - def plot(self, ax, bar_kws): """Make the plot.""" self.draw_bars(ax, bar_kws) @@ -2071,11 +2070,11 @@ def plot(self, ax, boxplot_kws): """), capsize=dedent("""\ capsize : float, optional - Length of caps on confidence interval (drawn perpendicular to primary - line. If 0.0 (default), no caps will be drawn. Typical values are - between 0.03 and 0.1.\ + Length of caps on confidence interval (drawn perpendicular to + primary line. If 0.0 (default), no caps will be drawn. + Typical values are between 0.03 and 0.1.\ """), - conf_lw = dedent("""\ + conf_lw=dedent("""\ conf_lw : float, optional Thickness of lines draw for the confidence interval (and caps). Default is 1.8.\ From ace49a906d6ff4872f7f73bd20fbe3746131c719 Mon Sep 17 00:00:00 2001 From: "David C. Gemperline" Date: Thu, 7 Apr 2016 13:10:28 -0500 Subject: [PATCH 0258/1738] Added Tests for errobar caps --- seaborn/tests/test_categorical.py | 36 ++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 3c0f921b90..866b0a1d13 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -650,6 +650,32 @@ def test_draw_cis(self): plt.close("all") + # Test vertical CIs with endcaps + p.orient = "v" + + f, ax = plt.subplots() + p.draw_confints(ax, at_group, confints, colors, capsize=0.3) + capline = ax.lines[len(ax.lines) - 1] + caplinestart = capline.get_xdata()[0] + caplineend = capline.get_xdata()[1] + caplinelength = abs(caplineend - caplinestart) + nt.assert_almost_equal(caplinelength, 0.3) + nt.assert_equal(len(ax.lines), 6) + + plt.close("all") + + # Test horizontal CIs with endcaps + p.orient = "h" + + f, ax = plt.subplots() + p.draw_confints(ax, at_group, confints, colors, capsize=0.3) + capline = ax.lines[len(ax.lines) - 1] + caplinestart = capline.get_ydata()[0] + caplineend = capline.get_ydata()[1] + caplinelength = abs(caplineend - caplinestart) + nt.assert_almost_equal(caplinelength, 0.3) + nt.assert_equal(len(ax.lines), 6) + # Test extra keyword arguments f, ax = plt.subplots() p.draw_confints(ax, at_group, confints, colors, lw=4) @@ -658,6 +684,15 @@ def test_draw_cis(self): plt.close("all") + # Test conf_lw is set appropriately + f, ax = plt.subplots() + p.draw_confints(ax, at_group, confints, colors, conf_lw=2) + capline = ax.lines[len(ax.lines)-1] + nt.assert_equal(capline._linewidth, 2) + nt.assert_equal(len(ax.lines), 2) + + plt.close("all") + class TestBoxPlotter(CategoricalFixture): @@ -1966,7 +2001,6 @@ def test_simple_barplots(self): nt.assert_equal(ax.get_ylabel(), "g") plt.close("all") - class TestPointPlotter(CategoricalFixture): default_kws = dict(x=None, y=None, hue=None, data=None, From 752a59e2c04494ec19e5cfb0254ab319aae73358 Mon Sep 17 00:00:00 2001 From: "David C. Gemperline" Date: Thu, 7 Apr 2016 13:21:52 -0500 Subject: [PATCH 0259/1738] Explicitly test against None --- seaborn/categorical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 84e0aa7a0f..100d7baf20 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1506,12 +1506,12 @@ def draw_confints(self, capsize=None, **kws): - if conf_lw: + if conf_lw is not None: kws.setdefault("lw", mpl.rcParams["lines.linewidth"] * conf_lw) else: kws.setdefault("lw", mpl.rcParams["lines.linewidth"] * 1.8) - if capsize: + if capsize is not None: for at, (ci_low, ci_high), color in zip(at_group, confint, colors): From a030a0fda03a74bac26f30ead01b0c7ecb0f6ce5 Mon Sep 17 00:00:00 2001 From: "David C. Gemperline" Date: Thu, 7 Apr 2016 13:24:11 -0500 Subject: [PATCH 0260/1738] Change conf_lw to errwidth --- seaborn/categorical.py | 36 +++++++++++++++---------------- seaborn/tests/test_categorical.py | 4 ++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 100d7baf20..f41f5fbf2a 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1502,12 +1502,12 @@ def draw_confints(self, ax, at_group, confint, colors, - conf_lw=None, + errwidth=None, capsize=None, **kws): - if conf_lw is not None: - kws.setdefault("lw", mpl.rcParams["lines.linewidth"] * conf_lw) + if errwidth is not None: + kws.setdefault("lw", mpl.rcParams["lines.linewidth"] * errwidth) else: kws.setdefault("lw", mpl.rcParams["lines.linewidth"] * 1.8) @@ -1542,7 +1542,7 @@ class _BarPlotter(_CategoricalStatPlotter): def __init__(self, x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, - orient, color, palette, saturation, errcolor, conf_lw=None, + orient, color, palette, saturation, errcolor, errwidth=None, capsize=None): """Initialize the plotter.""" self.establish_variables(x, y, hue, data, orient, @@ -1551,7 +1551,7 @@ def __init__(self, x, y, hue, data, order, hue_order, self.estimate_statistic(estimator, ci, n_boot) self.errcolor = errcolor - self.conf_lw = conf_lw + self.errwidth = errwidth self.capsize = capsize def draw_bars(self, ax, kws): @@ -1572,7 +1572,7 @@ def draw_bars(self, ax, kws): barpos, self.confint, errcolors, - self.conf_lw, + self.errwidth, self.capsize) else: @@ -1593,7 +1593,7 @@ def draw_bars(self, ax, kws): offpos, confint, errcolors, - self.conf_lw, + self.errwidth, self.capsize) def plot(self, ax, bar_kws): @@ -1609,7 +1609,7 @@ class _PointPlotter(_CategoricalStatPlotter): def __init__(self, x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, markers, linestyles, dodge, join, scale, - orient, color, palette, conf_lw=None, capsize=None): + orient, color, palette, errwidth=None, capsize=None): """Initialize the plotter.""" self.establish_variables(x, y, hue, data, orient, order, hue_order, units) @@ -1642,7 +1642,7 @@ def __init__(self, x, y, hue, data, order, hue_order, self.dodge = dodge self.join = join self.scale = scale - self.conf_lw = conf_lw + self.errwidth = errwidth self.capsize = capsize @property @@ -1677,7 +1677,7 @@ def draw_points(self, ax): # Draw the confidence intervals self.draw_confints(ax, pointpos, self.confint, self.colors, - self.conf_lw, self.capsize) + self.errwidth, self.capsize) # Draw the estimate points marker = self.markers[0] if self.orient == "h": @@ -1717,7 +1717,7 @@ def draw_points(self, ax): confint = self.confint[:, j] errcolors = [self.colors[j]] * len(offpos) self.draw_confints(ax, offpos, confint, errcolors, - self.conf_lw, self.capsize, + self.errwidth, self.capsize, zorder=z) # Draw the estimate points @@ -2074,8 +2074,8 @@ def plot(self, ax, boxplot_kws): primary line. If 0.0 (default), no caps will be drawn. Typical values are between 0.03 and 0.1.\ """), - conf_lw=dedent("""\ - conf_lw : float, optional + errwidth=dedent("""\ + errwidth : float, optional Thickness of lines draw for the confidence interval (and caps). Default is 1.8.\ """), @@ -2888,7 +2888,7 @@ def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, estimator=np.mean, ci=95, n_boot=1000, units=None, orient=None, color=None, palette=None, saturation=.75, - errcolor=".26", conf_lw=None, capsize=None, ax=None, **kwargs): + errcolor=".26", errwidth=None, capsize=None, ax=None, **kwargs): # Handle some deprecated arguments if "hline" in kwargs: @@ -2907,7 +2907,7 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, plotter = _BarPlotter(x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, orient, color, palette, saturation, - errcolor, conf_lw, capsize) + errcolor, errwidth, capsize) if ax is None: ax = plt.gca() @@ -2951,7 +2951,7 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, errcolor : matplotlib color Color for the lines that represent the confidence interval. {ax_in} - {conf_lw} + {errwidth} {capsize} kwargs : key, value mappings Other keyword arguments are passed through to ``plt.bar`` at draw @@ -3048,7 +3048,7 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def pointplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, estimator=np.mean, ci=95, n_boot=1000, units=None, markers="o", linestyles="-", dodge=False, join=True, scale=1, - orient=None, color=None, palette=None, ax=None, conf_lw=None, + orient=None, color=None, palette=None, ax=None, errwidth=None, capsize=None, **kwargs): # Handle some deprecated arguments @@ -3068,7 +3068,7 @@ def pointplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, plotter = _PointPlotter(x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, markers, linestyles, dodge, join, scale, - orient, color, palette, conf_lw, capsize) + orient, color, palette, errwidth, capsize) if ax is None: ax = plt.gca() diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 866b0a1d13..d24efc474d 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -684,9 +684,9 @@ def test_draw_cis(self): plt.close("all") - # Test conf_lw is set appropriately + # Test errwidth is set appropriately f, ax = plt.subplots() - p.draw_confints(ax, at_group, confints, colors, conf_lw=2) + p.draw_confints(ax, at_group, confints, colors, errwidth=2) capline = ax.lines[len(ax.lines)-1] nt.assert_equal(capline._linewidth, 2) nt.assert_equal(len(ax.lines), 2) From 5e3e0b1ca8bd80a231dd50283c8a85b28675bb4a Mon Sep 17 00:00:00 2001 From: "David C. Gemperline" Date: Thu, 7 Apr 2016 13:25:34 -0500 Subject: [PATCH 0261/1738] Fix'd docstring so default value isn't specified --- seaborn/categorical.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index f41f5fbf2a..19e8f34435 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2076,8 +2076,7 @@ def plot(self, ax, boxplot_kws): """), errwidth=dedent("""\ errwidth : float, optional - Thickness of lines draw for the confidence interval (and caps). - Default is 1.8.\ + Thickness of lines drawn for the confidence interval (and caps).\ """), width=dedent("""\ width : float, optional From a5e71f7c24db6e8de8d1569d5f18573e0efdcc23 Mon Sep 17 00:00:00 2001 From: "David C. Gemperline" Date: Thu, 7 Apr 2016 13:26:31 -0500 Subject: [PATCH 0262/1738] Fix'd docstring for capsize --- seaborn/categorical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 19e8f34435..13ab4bd38c 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2071,7 +2071,7 @@ def plot(self, ax, boxplot_kws): capsize=dedent("""\ capsize : float, optional Length of caps on confidence interval (drawn perpendicular to - primary line. If 0.0 (default), no caps will be drawn. + primary line). If unspecified, no caps will be drawn. Typical values are between 0.03 and 0.1.\ """), errwidth=dedent("""\ From c60e94b4d11dcb7327cd0980d19e017a8c850908 Mon Sep 17 00:00:00 2001 From: "David C. Gemperline" Date: Thu, 7 Apr 2016 13:31:38 -0500 Subject: [PATCH 0263/1738] Set errwidth directly instead of scaling --- seaborn/categorical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 13ab4bd38c..7428425499 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1507,9 +1507,9 @@ def draw_confints(self, **kws): if errwidth is not None: - kws.setdefault("lw", mpl.rcParams["lines.linewidth"] * errwidth) + kws.setdefault("lw", errwidth) else: - kws.setdefault("lw", mpl.rcParams["lines.linewidth"] * 1.8) + kws.setdefault("lw", 1.8) if capsize is not None: for at, (ci_low, ci_high), color in zip(at_group, From 07302f4f1095f08ebf2d9a60dd2f5a959f54905d Mon Sep 17 00:00:00 2001 From: "David C. Gemperline" Date: Thu, 7 Apr 2016 13:34:43 -0500 Subject: [PATCH 0264/1738] restructured for loop to handle capsize logic inside loop, and write loop only once --- seaborn/categorical.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 7428425499..a2a273f81b 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1511,30 +1511,25 @@ def draw_confints(self, else: kws.setdefault("lw", 1.8) - if capsize is not None: - for at, (ci_low, ci_high), color in zip(at_group, - confint, - colors): - if self.orient == "v": - ax.plot([at, at], [ci_low, ci_high], color=color, **kws) + for at, (ci_low, ci_high), color in zip(at_group, + confint, + colors): + if self.orient == "v": + ax.plot([at, at], [ci_low, ci_high], color=color, **kws) + if capsize is not None: ax.plot([at - capsize / 2, at + capsize / 2], [ci_low, ci_low], color=color, **kws) ax.plot([at - capsize / 2, at + capsize / 2], [ci_high, ci_high], color=color, **kws) - else: - ax.plot([ci_low, ci_high], [at, at], color=color, **kws) + else: + ax.plot([ci_low, ci_high], [at, at], color=color, **kws) + if capsize is not None: ax.plot([ci_low, ci_low], [at - capsize / 2, at + capsize / 2], color=color, **kws) ax.plot([ci_high, ci_high], [at - capsize / 2, at + capsize / 2], color=color, **kws) - else: - for at, (ci_low, ci_high), color in zip(at_group, confint, colors): - if self.orient == "v": - ax.plot([at, at], [ci_low, ci_high], color=color, **kws) - else: - ax.plot([ci_low, ci_high], [at, at], color=color, **kws) class _BarPlotter(_CategoricalStatPlotter): From 9fbc9f78b90a52704118980807462296cd8021ad Mon Sep 17 00:00:00 2001 From: "David C. Gemperline" Date: Thu, 7 Apr 2016 13:42:51 -0500 Subject: [PATCH 0265/1738] Formatting issues fixed --- seaborn/tests/test_categorical.py | 1 + 1 file changed, 1 insertion(+) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index d24efc474d..be54281a5d 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -2001,6 +2001,7 @@ def test_simple_barplots(self): nt.assert_equal(ax.get_ylabel(), "g") plt.close("all") + class TestPointPlotter(CategoricalFixture): default_kws = dict(x=None, y=None, hue=None, data=None, From cfdd3a84a361fff1867b46c1b2d334d1f45d7f82 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Apr 2016 09:21:26 -0700 Subject: [PATCH 0266/1738] Revert change to default linewidth --- seaborn/categorical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index a2a273f81b..3f8927b95b 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1509,7 +1509,7 @@ def draw_confints(self, if errwidth is not None: kws.setdefault("lw", errwidth) else: - kws.setdefault("lw", 1.8) + kws.setdefault("lw", mpl.rcParams["lines.linewidth"] * 1.8) for at, (ci_low, ci_high), color in zip(at_group, confint, From 4f88dd640f93d752871d19932f529882a17debbd Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Apr 2016 14:33:51 -0700 Subject: [PATCH 0267/1738] Update release notes --- doc/releases/v0.7.1.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/releases/v0.7.1.txt b/doc/releases/v0.7.1.txt index d71edf31e0..0e80f463be 100644 --- a/doc/releases/v0.7.1.txt +++ b/doc/releases/v0.7.1.txt @@ -2,7 +2,9 @@ v0.7.1 (Unreleased) ------------------- -- Improved the row and column colors display in :func:`clustermap`. It is now possible to pass Pandas objects for these elements and, when possible, the semantic information in the Pandas objects will be used to add labels to the plot. When Pandas objects are used, the color data is matched against the main heatmap based on the index, not on position. This is more accurate, but may lead to different results if current code assumed positional matching. +- Added the ability to put "caps" on the error bars that are drawn by :func:`barplot` or :func:`pointplot` (and, by extension, :func:`factorplot`). Additionally, the line width of the error bars can now be controlled. These changes involve the new parameters ``capsize`` and ``errwidth``. See the `github pull request `_ for examples of usage. + +- Improved the row and column colors display in :func:`clustermap`. It is now possible to pass Pandas objects for these elements and, when possible, the semantic information in the Pandas objects will be used to add labels to the plot. When Pandas objects are used, the color data is matched against the main heatmap based on the index, not on position. This is more accurate, but it may lead to different results if current code assumed positional matching. - Improved the luminance calculation that determines the annotation color in :func:`heatmap`. From 29082e73c204e564b11005eb7deb8628f39db42c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Apr 2016 14:34:28 -0700 Subject: [PATCH 0268/1738] Add error bar caps to gallery script and API docs --- examples/pointplot_anova.py | 2 +- seaborn/categorical.py | 31 ++++++++++++++++++------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/examples/pointplot_anova.py b/examples/pointplot_anova.py index 0b210848e2..53822fd35b 100644 --- a/examples/pointplot_anova.py +++ b/examples/pointplot_anova.py @@ -12,5 +12,5 @@ # Draw a pointplot to show pulse as a function of three categorical factors g = sns.factorplot(x="time", y="pulse", hue="kind", col="diet", data=df, - palette="YlGnBu_d", size=6, aspect=.75) + capsize=.2, palette="YlGnBu_d", size=6, aspect=.75) g.despine(left=True) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 3f8927b95b..874a65b793 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1498,13 +1498,8 @@ def estimate_statistic(self, estimator, ci, n_boot): self.value_label = "{}({})".format(estimator.__name__, self.value_label) - def draw_confints(self, - ax, at_group, - confint, - colors, - errwidth=None, - capsize=None, - **kws): + def draw_confints(self, ax, at_group, confint, colors, + errwidth=None, capsize=None, **kws): if errwidth is not None: kws.setdefault("lw", errwidth) @@ -2065,13 +2060,11 @@ def plot(self, ax, boxplot_kws): """), capsize=dedent("""\ capsize : float, optional - Length of caps on confidence interval (drawn perpendicular to - primary line). If unspecified, no caps will be drawn. - Typical values are between 0.03 and 0.1.\ + Width of the "caps" on error bars. """), errwidth=dedent("""\ errwidth : float, optional - Thickness of lines drawn for the confidence interval (and caps).\ + Thickness of error bar lines (and caps).\ """), width=dedent("""\ width : float, optional @@ -2123,8 +2116,6 @@ def plot(self, ax, boxplot_kws): lvplot : An extension of the boxplot for long-tailed and large data sets. """), - - ) _categorical_docs.update(_facet_docs) @@ -3011,6 +3002,13 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.barplot(x="day", y="tip", data=tips, ci=68) + Add "caps" to the error bars: + + .. plot:: + :context: close-figs + + >>> ax = sns.barplot(x="day", y="tip", data=tips, capsize=.2) + Use a different color palette for the bars: .. plot:: @@ -3219,6 +3217,13 @@ def pointplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.pointplot(x="day", y="tip", data=tips, ci=68) + Add "caps" to the error bars: + + .. plot:: + :context: close-figs + + >>> ax = sns.pointplot(x="day", y="tip", data=tips, capsize=.2) + """).format(**_categorical_docs) From 58b5121abfafe855301139ad80e3caf601e86e56 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Apr 2016 16:00:35 -0700 Subject: [PATCH 0269/1738] Update github parsing code --- seaborn/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/utils.py b/seaborn/utils.py index 1cc9d6e820..394c530150 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -369,7 +369,7 @@ def get_dataset_names(): gh_list = BeautifulSoup(http) return [l.text.replace('.csv', '') - for l in gh_list.find_all("a", {"class": "js-directory-link"}) + for l in gh_list.find_all("a", {"class": "js-navigation-open"}) if l.text.endswith('.csv')] From f1e55788e28e5603f3cb7a0941930c4053ca6b97 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Apr 2016 17:45:17 -0700 Subject: [PATCH 0270/1738] Streamline heatmap prep and clarify docs --- seaborn/matrix.py | 49 ++++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 43fb488d56..f1676ecca4 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -109,8 +109,8 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, # Reverse the rows so the plot looks like the matrix plot_data = plot_data[::-1] - data = data.ix[::-1] - mask = mask.ix[::-1] + data = data.iloc[::-1] + mask = mask.iloc[::-1] plot_data = np.ma.masked_where(np.asarray(mask), plot_data) @@ -164,22 +164,31 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, self._determine_cmap_params(plot_data, vmin, vmax, cmap, center, robust) + # Sort out the annotations + if annot is None: + annot = False + annot_data = None + elif isinstance(annot, bool): + if annot: + annot_data = plot_data + else: + annot_data = None + else: + try: + annot_data = annot.values[::-1] + except AttributeError: + annot_data = annot[::-1] + if annot.shape != plot_data.shape: + raise ValueError('Data supplied to "annot" must be the same ' + 'shape as the data to plot.') + annot = True + # Save other attributes to the object self.data = data self.plot_data = plot_data - self.annot = annot - if isinstance(self.annot, bool) and self.annot: - self.annot_data = plot_data - elif not isinstance(self.annot, bool): - if isinstance(self.annot, pd.DataFrame): - self.annot_data = self.annot.ix[::-1].values - else: - self.annot_data = self.annot[::-1] - if self.annot.shape != self.plot_data.shape: - raise ValueError('Data supplied to "annot" must be the same ' - 'shape as the data to plot.') - self.annot = True + self.annot = annot + self.annot_data = annot_data self.fmt = fmt self.annot_kws = {} if annot_kws is None else annot_kws @@ -281,7 +290,7 @@ def plot(self, ax, cax, kws): def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, - annot=False, fmt=".2g", annot_kws=None, + annot=None, fmt=".2g", annot_kws=None, linewidths=0, linecolor="white", cbar=True, cbar_kws=None, cbar_ax=None, square=False, ax=None, xticklabels=True, yticklabels=True, @@ -318,12 +327,12 @@ def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, robust : bool, optional If True and ``vmin`` or ``vmax`` are absent, the colormap range is computed with robust quantiles instead of the extreme values. - annot : bool, or array, optional - If True, write the data value in each cell. If an array of the same - shape as ``data``, then use this to annotate the heatmap instead of the - raw data. + annot : bool or rectangular dataset, optional + If True, write the data value in each cell. If an array-like with the + same shape as ``data``, then use this to annotate the heatmap instead + of the raw data. fmt : string, optional - String formatting code to use when ``annot`` is True. + String formatting code to use when adding annotations. annot_kws : dict of key, value mappings, optional Keyword arguments for ``ax.text`` when ``annot`` is True. linewidths : float, optional From 9b29de44f8853b8b44e354976938b5122cbcea32 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Apr 2016 17:47:52 -0700 Subject: [PATCH 0271/1738] Update release notes --- doc/releases/v0.7.1.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.7.1.txt b/doc/releases/v0.7.1.txt index 0e80f463be..c69f0f600c 100644 --- a/doc/releases/v0.7.1.txt +++ b/doc/releases/v0.7.1.txt @@ -8,6 +8,8 @@ v0.7.1 (Unreleased) - Improved the luminance calculation that determines the annotation color in :func:`heatmap`. +- The ``annot`` parameter of :func:`heatmap` now accepts a rectangular dataset in addition to a boolean value. If a dataset is passed, its values will be used for the annotations, while the main dataset will be used for the heatmap cell colors. + - Made it possible to pass a tick locator object to the :func:`heatmap` colorbar. - Made it possible to use different styles (e.g., step) for :class:`PairGrid` histograms when there are multiple hue levels. From 542087cb1a50b51f80834ae3821e9bc9ae3b4853 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Apr 2016 17:53:02 -0700 Subject: [PATCH 0272/1738] Fix despine example in aesthetics docs (closes #865) --- doc/tutorial/aesthetics.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/tutorial/aesthetics.ipynb b/doc/tutorial/aesthetics.ipynb index d2b754c96d..c792829b44 100644 --- a/doc/tutorial/aesthetics.ipynb +++ b/doc/tutorial/aesthetics.ipynb @@ -249,7 +249,7 @@ "outputs": [], "source": [ "f, ax = plt.subplots()\n", - "sns.violinplot(data)\n", + "sns.violinplot(data=data)\n", "sns.despine(offset=10, trim=True);" ] }, @@ -460,9 +460,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", - "version": "2.7.10" + "version": "2.7.11" } }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} From 9fe6a08999e61efdc3ef9aa8b03275b575cddbeb Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Apr 2016 18:29:34 -0700 Subject: [PATCH 0273/1738] Capture original rcparams at import time and use to reset --- seaborn/__init__.py | 7 +++++++ seaborn/rcmod.py | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/seaborn/__init__.py b/seaborn/__init__.py index e4881d378e..9aa7c5cb8e 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -1,3 +1,8 @@ +# Capture the original matplotlib rcParams +import matplotlib as mpl +_orig_rc_params = mpl.rcParams.copy() + +# Import seaborn objects from .rcmod import * from .utils import * from .palettes import * @@ -11,6 +16,8 @@ from .widgets import * from .xkcd_rgb import xkcd_rgb from .crayons import crayons + +# Set default aesthetics set() __version__ = "0.8.dev" diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index 12d0269a05..985923c019 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -5,7 +5,7 @@ import numpy as np import matplotlib as mpl -from . import palettes +from . import palettes, _orig_rc_params mpl_ge_150 = LooseVersion(mpl.__version__) >= '1.5.0' @@ -112,7 +112,7 @@ def reset_defaults(): def reset_orig(): """Restore all RC params to original settings (respects custom rc).""" - mpl.rcParams.update(mpl.rcParamsOrig) + mpl.rcParams.update(_orig_rc_params) def axes_style(style=None, rc=None): From dcc2edff2896deef8c7add2d14a158b074d0e72a Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 24 Apr 2016 18:33:12 -0700 Subject: [PATCH 0274/1738] Update release notes --- doc/releases/v0.7.1.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.7.1.txt b/doc/releases/v0.7.1.txt index c69f0f600c..e0c80fcbed 100644 --- a/doc/releases/v0.7.1.txt +++ b/doc/releases/v0.7.1.txt @@ -14,4 +14,6 @@ v0.7.1 (Unreleased) - Made it possible to use different styles (e.g., step) for :class:`PairGrid` histograms when there are multiple hue levels. +- The :func:`reset_orig` function (and, by extension, importing ``seaborn.apionly``) resets matplotlib rcParams to their values at the time seaborn itself was imported, which should work better with rcParams changed by the jupyter notebook backend. + - Improved unicode compatibility in :class:`FacetGrid`. From 69e7f371d27725160d092a528c96cf1fce99b8b4 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 26 Apr 2016 09:13:41 -0700 Subject: [PATCH 0275/1738] Clean up top-level seaborn namespace --- seaborn/axisgrid.py | 5 +++-- seaborn/categorical.py | 4 ++++ seaborn/distributions.py | 3 +++ seaborn/linearmodels.py | 5 +++++ seaborn/matrix.py | 4 +++- seaborn/miscplot.py | 3 +++ seaborn/palettes.py | 7 +++++++ seaborn/rcmod.py | 7 +++++++ seaborn/tests/test_palettes.py | 3 ++- seaborn/timeseries.py | 4 +++- seaborn/utils.py | 4 ++++ seaborn/widgets.py | 5 +++++ 12 files changed, 49 insertions(+), 5 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 425b6cf44a..551bbf0c8d 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -9,12 +9,13 @@ import matplotlib as mpl import matplotlib.pyplot as plt -from six import string_types - from . import utils from .palettes import color_palette +__all__ = ["FacetGrid", "PairGrid", "JointGrid"] + + class Grid(object): """Base class for grids of subplots.""" _margin_titles = False diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 874a65b793..7229e9387c 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -21,6 +21,10 @@ from .axisgrid import FacetGrid, _facet_docs +__all__ = ["boxplot", "violinplot", "stripplot", "swarmplot", "lvplot", + "pointplot", "barplot", "countplot", "factorplot"] + + class _CategoricalPlotter(object): width = .8 diff --git a/seaborn/distributions.py b/seaborn/distributions.py index f0633e8d95..ee089da465 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -20,6 +20,9 @@ from .axisgrid import JointGrid +__all__ = ["distplot", "kdeplot", "rugplot", "jointplot"] + + def _freedman_diaconis_bins(a): """Calculate number of hist bins using Freedman-Diaconis rule.""" # From http://stats.stackexchange.com/questions/798/ diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index bab47c9c1a..2c906178b4 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -28,6 +28,11 @@ from .distributions import kdeplot +__all__ = ["lmplot", "regplot", "residplot", + "coefplot", "interactplot", + "pairplot"] + + class _LinearPlotter(object): """Base class for plotting relational data in tidy format. diff --git a/seaborn/matrix.py b/seaborn/matrix.py index f1676ecca4..5ff9ff9235 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -13,7 +13,9 @@ from .axisgrid import Grid from .palettes import cubehelix_palette from .utils import despine, axis_ticklabels_overlap, relative_luminance -from .external.six.moves import range + + +__all__ = ["heatmap", "clustermap"] def _index_to_label(index): diff --git a/seaborn/miscplot.py b/seaborn/miscplot.py index b0d1f7fcdf..8eac76b003 100644 --- a/seaborn/miscplot.py +++ b/seaborn/miscplot.py @@ -4,6 +4,9 @@ import matplotlib.pyplot as plt +__call__ = ["palplot", "puppyplot"] + + def palplot(pal, size=1): """Plot the values in a color palette as a horizontal array. diff --git a/seaborn/palettes.py b/seaborn/palettes.py index 6da06e6b1d..ac5edbe246 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -13,6 +13,13 @@ from .xkcd_rgb import xkcd_rgb from .crayons import crayons + +__all__ = ["color_palette", "hls_palette", "husl_palette", "mpl_palette", + "dark_palette", "light_palette", "diverging_palette", + "blend_palette", "xkcd_palette", "crayon_palette", + "cubehelix_palette", "set_color_codes"] + + SEABORN_PALETTES = dict( deep=["#4C72B0", "#55A868", "#C44E52", "#8172B2", "#CCB974", "#64B5CD"], diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index 985923c019..7c9bf2f0c6 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -7,8 +7,15 @@ from . import palettes, _orig_rc_params + mpl_ge_150 = LooseVersion(mpl.__version__) >= '1.5.0' + +__all__ = ["set", "reset_defaults", "reset_orig", + "axes_style", "set_style", "plotting_context", "set_context", + "set_palette"] + + _style_keys = ( "axes.facecolor", diff --git a/seaborn/tests/test_palettes.py b/seaborn/tests/test_palettes.py index 8b3b26aa12..87c5ed49b8 100644 --- a/seaborn/tests/test_palettes.py +++ b/seaborn/tests/test_palettes.py @@ -6,7 +6,8 @@ import nose.tools as nt import numpy.testing as npt -from .. import palettes, utils, rcmod, husl +from .. import palettes, utils, rcmod +from ..external import husl from ..xkcd_rgb import xkcd_rgb from ..crayons import crayons diff --git a/seaborn/timeseries.py b/seaborn/timeseries.py index a2c4dfc932..45be35552c 100644 --- a/seaborn/timeseries.py +++ b/seaborn/timeseries.py @@ -8,12 +8,14 @@ from .external.six import string_types - from . import utils from . import algorithms as algo from .palettes import color_palette +__all__ = ["tsplot"] + + def tsplot(data, time=None, unit=None, condition=None, value=None, err_style="ci_band", ci=68, interpolate=True, color=None, estimator=np.mean, n_boot=5000, err_palette=None, err_kws=None, diff --git a/seaborn/utils.py b/seaborn/utils.py index 394c530150..f612905656 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -17,6 +17,10 @@ from .external.six.moves.urllib.request import urlopen, urlretrieve +__all__ = ["desaturate", "saturate", "set_hls_values", + "despine", "get_dataset_names", "load_dataset"] + + def ci_to_errsize(cis, heights): """Convert intervals to error arguments relative to plot heights. diff --git a/seaborn/widgets.py b/seaborn/widgets.py index 011e8f17b8..9f450f100d 100644 --- a/seaborn/widgets.py +++ b/seaborn/widgets.py @@ -23,6 +23,11 @@ diverging_palette, cubehelix_palette) +__all__ = ["choose_colorbrewer_palette", "choose_cubehelix_palette", + "choose_dark_palette", "choose_light_palette", + "choose_diverging_palette"] + + def _init_mutable_colormap(): """Create a matplotlib colormap that will be updated by the widgets.""" greys = color_palette("Greys", 256) From dfdd1126626f7ed0fe3737528edecb71346e9eb0 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 26 Apr 2016 09:31:34 -0700 Subject: [PATCH 0276/1738] Upate release notes --- doc/releases/v0.7.1.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.7.1.txt b/doc/releases/v0.7.1.txt index e0c80fcbed..9e69035876 100644 --- a/doc/releases/v0.7.1.txt +++ b/doc/releases/v0.7.1.txt @@ -16,4 +16,6 @@ v0.7.1 (Unreleased) - The :func:`reset_orig` function (and, by extension, importing ``seaborn.apionly``) resets matplotlib rcParams to their values at the time seaborn itself was imported, which should work better with rcParams changed by the jupyter notebook backend. +- Removed some objects from the top-level ``seaborn`` namespace. + - Improved unicode compatibility in :class:`FacetGrid`. From 436fe24e46fedca4a4438d8b715f735dc1b73e79 Mon Sep 17 00:00:00 2001 From: Adel Qalieh Date: Fri, 13 May 2016 20:41:10 -0400 Subject: [PATCH 0277/1738] Fix header interference in doc linking, fixes #610 --- doc/_static/style.css | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/doc/_static/style.css b/doc/_static/style.css index f168489b34..2cb8474df5 100644 --- a/doc/_static/style.css +++ b/doc/_static/style.css @@ -18,12 +18,9 @@ code { color: #2c3e50 !important; } -.headerlink:before { - - display: block; - context: " "; - height: 200px; /*same height as header*/ - margin-top: -200px; /*same height as header*/ - visibility: hidden; - +.function dt { + padding-top: 150px; + margin-top: -150px; + -webkit-background-clip: content-box; + background-clip: content-box; } From f9f09981251de6b4e17e60e1c4aed6ee08895c8f Mon Sep 17 00:00:00 2001 From: Olga Botvinnik Date: Fri, 20 May 2016 12:02:05 -0700 Subject: [PATCH 0278/1738] Use one-step linkage calculation with both scipy and fastcluster --- seaborn/matrix.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 5ff9ff9235..5db9e2367d 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -567,10 +567,8 @@ def _calculate_linkage_scipy(self): if np.product(self.shape) >= 10000: UserWarning('This will be slow... (gentle suggestion: ' '"pip install fastcluster")') - - pairwise_dists = distance.pdist(self.array, metric=self.metric) - linkage = hierarchy.linkage(pairwise_dists, method=self.method) - del pairwise_dists + linkage = hierarchy.linkage(self.array, method=self.method, + metric=self.metric) return linkage def _calculate_linkage_fastcluster(self): @@ -586,9 +584,8 @@ def _calculate_linkage_fastcluster(self): method=self.method, metric=self.metric) else: - pairwise_dists = distance.pdist(self.array, metric=self.metric) - linkage = fastcluster.linkage(pairwise_dists, method=self.method) - del pairwise_dists + linkage = fastcluster.linkage(self.array, method=self.method, + metric=self.metric) return linkage @property From 12c18941f31dba22ec01d41201409be3bd5e57ba Mon Sep 17 00:00:00 2001 From: Mike Williams Date: Fri, 20 May 2016 17:59:22 -0400 Subject: [PATCH 0279/1738] Fix https://github.com/mwaskom/seaborn/issues/929 Calculates correct number of subplots to draw when unused categories are present --- seaborn/axisgrid.py | 2 +- seaborn/tests/test_axisgrid.py | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 551bbf0c8d..749bb68b34 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -271,7 +271,7 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, err = "Cannot use `row` and `col_wrap` together." raise ValueError(err) ncol = col_wrap - nrow = int(np.ceil(len(data[col].unique()) / col_wrap)) + nrow = int(np.ceil(len(col_names) / col_wrap)) self._ncol = ncol self._nrow = nrow diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 0129141aaf..06de53d0a3 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -713,6 +713,15 @@ def test_unicode_content_no_columns(self): g = ag.FacetGrid(df, row="b") g = g.map(plt.plot, "x") + def test_categorical_column_missing_categories(self): + + df = self.df.copy() + df['a'] = df['a'].astype('category') + + g = ag.FacetGrid(df.query("a != 'a'"), col="a", col_wrap=1) + + nt.assert_equal(g.axes.shape, (len(df['a'].cat.categories),)) + class TestPairGrid(PlotTestCase): From 5fe16dde3a2bfd8e2c45441d7f58eec965cd7c22 Mon Sep 17 00:00:00 2001 From: Mike Williams Date: Sat, 21 May 2016 11:55:22 -0400 Subject: [PATCH 0280/1738] Eliminate numexpr dependency from test --- seaborn/tests/test_axisgrid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 06de53d0a3..47d5897e6b 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -718,7 +718,7 @@ def test_categorical_column_missing_categories(self): df = self.df.copy() df['a'] = df['a'].astype('category') - g = ag.FacetGrid(df.query("a != 'a'"), col="a", col_wrap=1) + g = ag.FacetGrid(df[df['a'] == 'a'], col="a", col_wrap=1) nt.assert_equal(g.axes.shape, (len(df['a'].cat.categories),)) From 0d73d646e2016cdb24a8f02332f3315512442575 Mon Sep 17 00:00:00 2001 From: Mike Williams Date: Sat, 21 May 2016 11:58:56 -0400 Subject: [PATCH 0281/1738] Skip missing cats FacetGrid test if pandas does not have cats --- seaborn/tests/test_axisgrid.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 47d5897e6b..3f1dc5de18 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -24,6 +24,7 @@ rs = np.random.RandomState(0) old_matplotlib = LooseVersion(mpl.__version__) < "1.4" +pandas_has_categoricals = LooseVersion(pd.__version__) >= "0.15" class TestFacetGrid(PlotTestCase): @@ -713,6 +714,7 @@ def test_unicode_content_no_columns(self): g = ag.FacetGrid(df, row="b") g = g.map(plt.plot, "x") + @skipif(not pandas_has_categoricals) def test_categorical_column_missing_categories(self): df = self.df.copy() From 0f79211cd8d25bddecbf5a6a024f0f606e8685e2 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 4 Jun 2016 17:34:10 -0700 Subject: [PATCH 0282/1738] Update release notes --- doc/releases/v0.7.1.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.7.1.txt b/doc/releases/v0.7.1.txt index 9e69035876..aea837fd73 100644 --- a/doc/releases/v0.7.1.txt +++ b/doc/releases/v0.7.1.txt @@ -10,6 +10,8 @@ v0.7.1 (Unreleased) - The ``annot`` parameter of :func:`heatmap` now accepts a rectangular dataset in addition to a boolean value. If a dataset is passed, its values will be used for the annotations, while the main dataset will be used for the heatmap cell colors. +- Fixed a bug in :class:`FacetGrid` that appeared when using ``col_wrap`` with missing ``col`` levels. + - Made it possible to pass a tick locator object to the :func:`heatmap` colorbar. - Made it possible to use different styles (e.g., step) for :class:`PairGrid` histograms when there are multiple hue levels. From 758fad534832ba9bfcc28913f219ed45423e75d1 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 4 Jun 2016 17:40:41 -0700 Subject: [PATCH 0283/1738] Fix scipy univariate KDE bandwidth scale (fixes #918) --- doc/releases/v0.7.1.txt | 2 ++ seaborn/distributions.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/releases/v0.7.1.txt b/doc/releases/v0.7.1.txt index aea837fd73..ff0ca3a38d 100644 --- a/doc/releases/v0.7.1.txt +++ b/doc/releases/v0.7.1.txt @@ -16,6 +16,8 @@ v0.7.1 (Unreleased) - Made it possible to use different styles (e.g., step) for :class:`PairGrid` histograms when there are multiple hue levels. +- Fixed a bug in scipy-based univariate kernel density bandwidth calculation. + - The :func:`reset_orig` function (and, by extension, importing ``seaborn.apionly``) resets matplotlib rcParams to their values at the time seaborn itself was imported, which should work better with rcParams changed by the jupyter notebook backend. - Removed some objects from the top-level ``seaborn`` namespace. diff --git a/seaborn/distributions.py b/seaborn/distributions.py index ee089da465..94e4f5321d 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -345,7 +345,7 @@ def _scipy_univariate_kde(data, bw, gridsize, cut, clip): warnings.warn(msg, UserWarning) if isinstance(bw, string_types): bw = "scotts" if bw == "scott" else bw - bw = getattr(kde, "%s_factor" % bw)() + bw = getattr(kde, "%s_factor" % bw)() * np.std(data) grid = _kde_support(data, bw, gridsize, cut, clip) y = kde(grid) return grid, y From b618cc745060b18f55e3d7140c64535fe0c70c4c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 4 Jun 2016 19:22:35 -0700 Subject: [PATCH 0284/1738] Prepare for 0.7.1 release --- doc/releases/v0.7.1.txt | 2 +- doc/tutorial/aesthetics.ipynb | 2 +- doc/whatsnew.rst | 2 ++ seaborn/__init__.py | 2 +- setup.py | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/releases/v0.7.1.txt b/doc/releases/v0.7.1.txt index ff0ca3a38d..3aacbf66ce 100644 --- a/doc/releases/v0.7.1.txt +++ b/doc/releases/v0.7.1.txt @@ -1,5 +1,5 @@ -v0.7.1 (Unreleased) +v0.7.1 (June 2016) ------------------- - Added the ability to put "caps" on the error bars that are drawn by :func:`barplot` or :func:`pointplot` (and, by extension, :func:`factorplot`). Additionally, the line width of the error bars can now be controlled. These changes involve the new parameters ``capsize`` and ``errwidth``. See the `github pull request `_ for examples of usage. diff --git a/doc/tutorial/aesthetics.ipynb b/doc/tutorial/aesthetics.ipynb index c792829b44..49c7a2a14d 100644 --- a/doc/tutorial/aesthetics.ipynb +++ b/doc/tutorial/aesthetics.ipynb @@ -465,4 +465,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file diff --git a/doc/whatsnew.rst b/doc/whatsnew.rst index 0be879984a..902d861e90 100644 --- a/doc/whatsnew.rst +++ b/doc/whatsnew.rst @@ -7,6 +7,8 @@ What's new in the package A catalog of new features, improvements, and bug-fixes in each release. +.. include:: releases/v0.7.1.txt + .. include:: releases/v0.7.0.txt .. include:: releases/v0.6.0.txt diff --git a/seaborn/__init__.py b/seaborn/__init__.py index 9aa7c5cb8e..49657f17c9 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -20,4 +20,4 @@ # Set default aesthetics set() -__version__ = "0.8.dev" +__version__ = "0.7.1" diff --git a/setup.py b/setup.py index c3c9515523..76ec635718 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ URL = 'http://stanford.edu/~mwaskom/software/seaborn/' LICENSE = 'BSD (3-clause)' DOWNLOAD_URL = 'https://github.com/mwaskom/seaborn/' -VERSION = '0.8.dev' +VERSION = '0.7.1' try: from setuptools import setup From 7e6bbe7612f34580978f667f5564751d1ee658d1 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 4 Jun 2016 19:42:02 -0700 Subject: [PATCH 0285/1738] Bump version back to dev --- seaborn/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/__init__.py b/seaborn/__init__.py index 49657f17c9..9aa7c5cb8e 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -20,4 +20,4 @@ # Set default aesthetics set() -__version__ = "0.7.1" +__version__ = "0.8.dev" diff --git a/setup.py b/setup.py index 76ec635718..c3c9515523 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ URL = 'http://stanford.edu/~mwaskom/software/seaborn/' LICENSE = 'BSD (3-clause)' DOWNLOAD_URL = 'https://github.com/mwaskom/seaborn/' -VERSION = '0.7.1' +VERSION = '0.8.dev' try: from setuptools import setup From d36c533089b12321f2afcbb83dd576083382df60 Mon Sep 17 00:00:00 2001 From: Chris Fonnesbeck Date: Thu, 16 Jun 2016 22:19:57 -0500 Subject: [PATCH 0286/1738] Replaced next methods with next functions --- doc/sphinxext/plot_generator.py | 2 +- seaborn/external/six.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/sphinxext/plot_generator.py b/doc/sphinxext/plot_generator.py index 46fbdf01d9..5bd64b9a22 100644 --- a/doc/sphinxext/plot_generator.py +++ b/doc/sphinxext/plot_generator.py @@ -235,7 +235,7 @@ def extract_docstring(self): docstring = '' first_par = '' - tokens = tokenize.generate_tokens(lines.__iter__().next) + tokens = tokenize.generate_tokens(lambda: next(lines.__iter__())) for tok_type, tok_content, _, (erow, _), _ in tokens: tok_type = token.tok_name[tok_type] if tok_type in ('NEWLINE', 'COMMENT', 'NL', 'INDENT', 'DEDENT'): diff --git a/seaborn/external/six.py b/seaborn/external/six.py index 7ec7f1bec1..09edee7391 100644 --- a/seaborn/external/six.py +++ b/seaborn/external/six.py @@ -428,7 +428,7 @@ def remove_move(name): advance_iterator = next except NameError: def advance_iterator(it): - return it.next() + return next(it) next = advance_iterator From b846d6e688be7026e4282c283f70eae1a3964dd1 Mon Sep 17 00:00:00 2001 From: Pete Bachant Date: Fri, 17 Jun 2016 12:54:00 -0400 Subject: [PATCH 0287/1738] Fix typo --- doc/tutorial/distributions.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorial/distributions.ipynb b/doc/tutorial/distributions.ipynb index 524430e163..33da4be7a7 100644 --- a/doc/tutorial/distributions.ipynb +++ b/doc/tutorial/distributions.ipynb @@ -20,7 +20,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "When dealing with a set of data, often the first thing you'll want to do is get a sense for how the variables are distributed. This chapter of the tutorial will give a brief introduction to some of the tools in seborn for examining univariate and bivariate distributions. You may also want to look at the :ref:`categorical plots ` chapter for examples of functions that make it easy to compare the distribution of a variable across levels of other variables." + "When dealing with a set of data, often the first thing you'll want to do is get a sense for how the variables are distributed. This chapter of the tutorial will give a brief introduction to some of the tools in seaborn for examining univariate and bivariate distributions. You may also want to look at the :ref:`categorical plots ` chapter for examples of functions that make it easy to compare the distribution of a variable across levels of other variables." ] }, { @@ -507,4 +507,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} From 6689100e860dc1f792e07f4522137554652731cd Mon Sep 17 00:00:00 2001 From: Chris Fonnesbeck Date: Fri, 17 Jun 2016 15:54:09 -0500 Subject: [PATCH 0288/1738] Added Py3 compatibility to docs generation --- doc/sphinxext/plot_generator.py | 5 +++++ examples/many_pairwise_correlations.py | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/doc/sphinxext/plot_generator.py b/doc/sphinxext/plot_generator.py index 5bd64b9a22..5fe3138375 100644 --- a/doc/sphinxext/plot_generator.py +++ b/doc/sphinxext/plot_generator.py @@ -7,6 +7,7 @@ from __future__ import division import os import os.path as op +import sys import re import glob import token @@ -20,6 +21,10 @@ from matplotlib import image +if sys.version_info >= (3,0): + execfile = (lambda filename, globals=None, locals=None: + exec(compile(open(filename, "rb").read(), filename, 'exec'), globals, locals)) + RST_TEMPLATE = """ .. _{sphinx_tag}: diff --git a/examples/many_pairwise_correlations.py b/examples/many_pairwise_correlations.py index f43fc089be..6d45c3b669 100644 --- a/examples/many_pairwise_correlations.py +++ b/examples/many_pairwise_correlations.py @@ -4,7 +4,10 @@ _thumb: .3, .6 """ -from string import letters +try: + from string import letters +except ImportError: + from string import ascii_letters as letters import numpy as np import pandas as pd import seaborn as sns From 5451b7c0e1463236608f651c4ffb9e1dbc38b91f Mon Sep 17 00:00:00 2001 From: Tobias Knuth Date: Fri, 1 Jul 2016 11:54:28 +0200 Subject: [PATCH 0289/1738] Fixed typo paletes -> palettes --- seaborn/palettes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/palettes.py b/seaborn/palettes.py index ac5edbe246..36608858b9 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -68,7 +68,7 @@ def color_palette(palette=None, n_colors=None, desat=None): Calling this function with ``palette=None`` will return the current matplotlib color cycle. - Matplotlib paletes can be specified as reversed palettes by appending + Matplotlib palettes can be specified as reversed palettes by appending "_r" to the name or as dark palettes by appending "_d" to the name. (These options are mutually exclusive, but the resulting list of colors can also be reversed). From 1f4cfa9f3441b2b8b87f821d584af75b7f0e11fc Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Mon, 12 Sep 2016 21:32:01 -0400 Subject: [PATCH 0290/1738] BF(TST): explicitly request .values for sorter to stay compatible with older numpys while indexing hue_vals --- seaborn/tests/test_categorical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index be54281a5d..7fedcf80bc 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1751,7 +1751,7 @@ def test_unsplit_nested_swarmplot_vertical(self): npt.assert_array_almost_equal(y, vals.iloc[sorter]) _, hue_vals = grouped_hues[i] - for hue, fc in zip(hue_vals.values[sorter], + for hue, fc in zip(hue_vals.values[sorter.values], points.get_facecolors()): npt.assert_equal(fc[:3], pal[hue_names.index(hue)]) @@ -1772,7 +1772,7 @@ def test_unsplit_nested_swarmplot_horizontal(self): npt.assert_array_almost_equal(x, vals.iloc[sorter]) _, hue_vals = grouped_hues[i] - for hue, fc in zip(hue_vals.values[sorter], + for hue, fc in zip(hue_vals.values[sorter.values], points.get_facecolors()): npt.assert_equal(fc[:3], pal[hue_names.index(hue)]) From d112e35f8637c4068b3e34faceffbbece1a02184 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 18 Sep 2016 16:43:51 -0400 Subject: [PATCH 0291/1738] Allow use of hue without nesting bars/boxes/violins --- doc/releases/v0.8.0.txt | 5 ++ seaborn/categorical.py | 116 +++++++++++++++++++++++------- seaborn/tests/test_categorical.py | 23 ++++-- 3 files changed, 113 insertions(+), 31 deletions(-) create mode 100644 doc/releases/v0.8.0.txt diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt new file mode 100644 index 0000000000..81c8019bd7 --- /dev/null +++ b/doc/releases/v0.8.0.txt @@ -0,0 +1,5 @@ + +v0.8.0 (Unreleased) +------------------- + +- Added the ``dodge`` argument to :func:`boxplot`, :func:`violinplot`, and :func:`barplot` to allow use of ``hue`` without changing the position or width of the plot elements, as when the ``hue`` varible is not nested within the main categorical variable. diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 7229e9387c..47d7df47fb 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -356,16 +356,23 @@ def is_not_numeric(s): def hue_offsets(self): """A list of center positions for plots when hue nesting is used.""" n_levels = len(self.hue_names) - each_width = self.width / n_levels - offsets = np.linspace(0, self.width - each_width, n_levels) - offsets -= offsets.mean() + if self.dodge: + each_width = self.width / n_levels + offsets = np.linspace(0, self.width - each_width, n_levels) + offsets -= offsets.mean() + else: + offsets = np.zeros(n_levels) return offsets @property def nested_width(self): """A float with the width of plot elements when hue nesting is used.""" - return self.width / len(self.hue_names) * .98 + if self.dodge: + width = self.width / len(self.hue_names) * .98 + else: + width = self.width + return width def annotate_axes(self, ax): """Add descriptive labels to an Axes object.""" @@ -421,11 +428,12 @@ class _BoxPlotter(_CategoricalPlotter): def __init__(self, x, y, hue, data, order, hue_order, orient, color, palette, saturation, - width, fliersize, linewidth): + width, dodge, fliersize, linewidth): self.establish_variables(x, y, hue, data, orient, order, hue_order) self.establish_colors(color, palette, saturation) + self.dodge = dodge self.width = width self.fliersize = fliersize @@ -535,7 +543,7 @@ class _ViolinPlotter(_CategoricalPlotter): def __init__(self, x, y, hue, data, order, hue_order, bw, cut, scale, scale_hue, gridsize, - width, inner, split, orient, linewidth, + width, inner, split, dodge, orient, linewidth, color, palette, saturation): self.establish_variables(x, y, hue, data, orient, order, hue_order) @@ -544,6 +552,7 @@ def __init__(self, x, y, hue, data, order, hue_order, self.gridsize = gridsize self.width = width + self.dodge = dodge if inner is not None: if not any([inner.startswith("quart"), @@ -768,7 +777,7 @@ def scale_count(self, density, counts, scale_hue): @property def dwidth(self): - if self.hue_names is None: + if self.hue_names is None or not self.dodge: return self.width / 2 elif self.split: return self.width / 2 @@ -1067,6 +1076,8 @@ def plot(self, ax): class _CategoricalScatterPlotter(_CategoricalPlotter): + dodge = True + @property def point_colors(self): """Return a color for each scatter point based on group and hue.""" @@ -1406,7 +1417,11 @@ class _CategoricalStatPlotter(_CategoricalPlotter): @property def nested_width(self): """A float with the width of plot elements when hue nesting is used.""" - return self.width / len(self.hue_names) + if self.dodge: + width = self.width / len(self.hue_names) + else: + width = self.width + return width def estimate_statistic(self, estimator, ci, n_boot): @@ -1536,14 +1551,16 @@ class _BarPlotter(_CategoricalStatPlotter): def __init__(self, x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, - orient, color, palette, saturation, errcolor, errwidth=None, - capsize=None): + orient, color, palette, saturation, errcolor, + errwidth, capsize, dodge): """Initialize the plotter.""" self.establish_variables(x, y, hue, data, orient, order, hue_order, units) self.establish_colors(color, palette, saturation) self.estimate_statistic(estimator, ci, n_boot) + self.dodge = dodge + self.errcolor = errcolor self.errwidth = errwidth self.capsize = capsize @@ -1642,8 +1659,11 @@ def __init__(self, x, y, hue, data, order, hue_order, @property def hue_offsets(self): """Offsets relative to the center position for each hue level.""" - offset = np.linspace(0, self.dodge, len(self.hue_names)) - offset -= offset.mean() + if self.dodge: + offset = np.linspace(0, self.dodge, len(self.hue_names)) + offset -= offset.mean() + else: + offset = np.zeros(len(self.hue_names)) return offset def draw_points(self, ax): @@ -1739,12 +1759,15 @@ class _LVPlotter(_CategoricalPlotter): def __init__(self, x, y, hue, data, order, hue_order, orient, color, palette, saturation, - width, k_depth, linewidth, scale, outlier_prop): + width, dodge, k_depth, linewidth, scale, outlier_prop): + # TODO assigning variables for None is unceccesary if width is None: width = .8 self.width = width + self.dodge = dodge + if saturation is None: saturation = .75 self.saturation = saturation @@ -2075,6 +2098,11 @@ def plot(self, ax, boxplot_kws): Width of a full element when not using hue nesting, or width of all the elements for one level of the major grouping variable.\ """), + dodge=dedent("""\ + dodge : bool, optional + When hue nesting is used, whether elements should be shifted along the + categorical axis.\ + """), linewidth=dedent("""\ linewidth : float, optional Width of the gray lines that frame the plot elements.\ @@ -2127,8 +2155,8 @@ def plot(self, ax, boxplot_kws): def boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, orient=None, color=None, palette=None, saturation=.75, - width=.8, fliersize=5, linewidth=None, whis=1.5, notch=False, - ax=None, **kwargs): + width=.8, dodge=True, fliersize=5, linewidth=None, + whis=1.5, notch=False, ax=None, **kwargs): # Try to handle broken backwards-compatability # This should help with the lack of a smooth deprecation, @@ -2172,7 +2200,7 @@ def boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, plotter = _BoxPlotter(x, y, hue, data, order, hue_order, orient, color, palette, saturation, - width, fliersize, linewidth) + width, dodge, fliersize, linewidth) if ax is None: ax = plt.gca() @@ -2203,6 +2231,7 @@ def boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, {palette} {saturation} {width} + {dodge} fliersize : float, optional Size of the markers used to indicate outlier observations. {linewidth} @@ -2282,6 +2311,15 @@ def boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> iris = sns.load_dataset("iris") >>> ax = sns.boxplot(data=iris, orient="h", palette="Set2") + Use ``hue`` without changing box position or width: + + .. plot:: + :context: close-figs + + >>> tips["weekend"] = tips["day"].isin(["Sat", "Sun"]) + >>> ax = sns.boxplot(x="day", y="total_bill", hue="weekend", + ... data=tips, dodge=False) + Use :func:`swarmplot` to show the datapoints on top of the boxes: .. plot:: @@ -2307,8 +2345,9 @@ def boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def violinplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, bw="scott", cut=2, scale="area", scale_hue=True, gridsize=100, - width=.8, inner="box", split=False, orient=None, linewidth=None, - color=None, palette=None, saturation=.75, ax=None, **kwargs): + width=.8, inner="box", split=False, dodge=True, orient=None, + linewidth=None, color=None, palette=None, saturation=.75, + ax=None, **kwargs): # Try to handle broken backwards-compatability # This should help with the lack of a smooth deprecation, @@ -2343,7 +2382,7 @@ def violinplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, plotter = _ViolinPlotter(x, y, hue, data, order, hue_order, bw, cut, scale, scale_hue, gridsize, - width, inner, split, orient, linewidth, + width, inner, split, dodge, orient, linewidth, color, palette, saturation) if ax is None: @@ -2407,6 +2446,7 @@ def violinplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, When using hue nesting with a variable that takes two levels, setting ``split`` to True will draw half of a violin for each level. This can make it easier to directly compare the distributions. + {dodge} {orient} {linewidth} {color} @@ -2514,6 +2554,15 @@ def violinplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, ... scale="count", inner="stick", ... scale_hue=False, bw=.2) + Use ``hue`` without changing violin position or width: + + .. plot:: + :context: close-figs + + >>> tips["weekend"] = tips["day"].isin(["Sat", "Sun"]) + >>> ax = sns.violinplot(x="day", y="total_bill", hue="weekend", + ... data=tips, dodge=False) + Draw horizontal violins: .. plot:: @@ -2877,7 +2926,8 @@ def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, estimator=np.mean, ci=95, n_boot=1000, units=None, orient=None, color=None, palette=None, saturation=.75, - errcolor=".26", errwidth=None, capsize=None, ax=None, **kwargs): + errcolor=".26", errwidth=None, capsize=None, dodge=True, + ax=None, **kwargs): # Handle some deprecated arguments if "hline" in kwargs: @@ -2896,7 +2946,7 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, plotter = _BarPlotter(x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, orient, color, palette, saturation, - errcolor, errwidth, capsize) + errcolor, errwidth, capsize, dodge) if ax is None: ax = plt.gca() @@ -2942,6 +2992,7 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, {ax_in} {errwidth} {capsize} + {dodge} kwargs : key, value mappings Other keyword arguments are passed through to ``plt.bar`` at draw time. @@ -3021,6 +3072,15 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.barplot("size", y="total_bill", data=tips, ... palette="Blues_d") + Use ``hue`` without changing bar position or width: + + .. plot:: + :context: close-figs + + >>> tips["weekend"] = tips["day"].isin(["Sat", "Sun"]) + >>> ax = sns.barplot(x="day", y="total_bill", hue="weekend", + ... data=tips, dodge=False) + Plot all bars in a single color: .. plot:: @@ -3233,13 +3293,15 @@ def pointplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def countplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, orient=None, color=None, palette=None, saturation=.75, - ax=None, **kwargs): + dodge=True, ax=None, **kwargs): estimator = len ci = None n_boot = 0 units = None errcolor = None + errwidth = None + capsize = None if x is None and y is not None: orient = "h" @@ -3255,7 +3317,7 @@ def countplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, plotter = _BarPlotter(x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, orient, color, palette, saturation, - errcolor) + errcolor, errwidth, capsize, dodge) plotter.value_label = "count" @@ -3284,6 +3346,7 @@ def countplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, {color} {palette} {saturation} + {dodge} {ax_in} kwargs : key, value mappings Other keyword arguments are passed to ``plt.bar``. @@ -3588,12 +3651,12 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, orient=None, color=None, palette=None, saturation=.75, - width=.8, k_depth='proportion', linewidth=None, scale='exponential', - outlier_prop=None, ax=None, **kwargs): + width=.8, dodge=True, k_depth='proportion', linewidth=None, + scale='exponential', outlier_prop=None, ax=None, **kwargs): plotter = _LVPlotter(x, y, hue, data, order, hue_order, orient, color, palette, saturation, - width, k_depth, linewidth, scale, outlier_prop) + width, dodge, k_depth, linewidth, scale, outlier_prop) if ax is None: ax = plt.gca() @@ -3626,6 +3689,7 @@ def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, {palette} {saturation} {width} + {dodge} k_depth : "proportion" | "tukey" | "trustworthy", optional The number of boxes, and by extension number of percentiles, to draw. All methods are detailed in Wickham's paper. Each makes different diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index be54281a5d..1f3b093b1c 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -699,12 +699,13 @@ class TestBoxPlotter(CategoricalFixture): default_kws = dict(x=None, y=None, hue=None, data=None, order=None, hue_order=None, orient=None, color=None, palette=None, - saturation=.75, width=.8, + saturation=.75, width=.8, dodge=True, fliersize=5, linewidth=None) def test_nested_width(self): - p = cat._BoxPlotter(**self.default_kws) + kws = self.default_kws.copy() + p = cat._BoxPlotter(**kws) p.establish_variables("g", "y", "h", data=self.df) nt.assert_equal(p.nested_width, .4 * .98) @@ -714,6 +715,12 @@ def test_nested_width(self): p.establish_variables("g", "y", "h", data=self.df) nt.assert_equal(p.nested_width, .3 * .98) + kws = self.default_kws.copy() + kws["dodge"] = False + p = cat._BoxPlotter(**kws) + p.establish_variables("g", "y", "h", data=self.df) + nt.assert_equal(p.nested_width, .8) + def test_hue_offsets(self): p = cat._BoxPlotter(**self.default_kws) @@ -850,7 +857,7 @@ class TestViolinPlotter(CategoricalFixture): order=None, hue_order=None, bw="scott", cut=2, scale="area", scale_hue=True, gridsize=100, width=.8, inner="box", split=False, - orient=None, linewidth=None, + dodge=True, orient=None, linewidth=None, color=None, palette=None, saturation=.75) def test_split_error(self): @@ -1784,7 +1791,8 @@ class TestBarPlotter(CategoricalFixture): estimator=np.mean, ci=95, n_boot=100, units=None, order=None, hue_order=None, orient=None, color=None, palette=None, - saturation=.75, errcolor=".26") + saturation=.75, errcolor=".26", errwidth=None, + capsize=None, dodge=True) def test_nested_width(self): @@ -1798,6 +1806,11 @@ def test_nested_width(self): p.establish_variables("h", "y", "g", data=self.df) nt.assert_equal(p.nested_width, .8 / 3) + kws["dodge"] = False + p = cat._BarPlotter(**kws) + p.establish_variables("h", "y", "g", data=self.df) + nt.assert_equal(p.nested_width, .8) + def test_draw_vertical_bars(self): kws = self.default_kws.copy() @@ -2410,7 +2423,7 @@ def edge_calc(n, data): self.default_kws = dict(x=None, y=None, hue=None, data=None, order=None, hue_order=None, orient=None, color=None, palette=None, - saturation=.75, width=.8, + saturation=.75, width=.8, dodge=True, k_depth='proportion', linewidth=None, scale='exponential', outlier_prop=None) self.linear_data = np.arange(101) From f95dc5560d0445bde437d250418f2c215e9ef959 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 18 Sep 2016 17:13:01 -0400 Subject: [PATCH 0292/1738] Py3 compat in example (closes #939) --- examples/many_pairwise_correlations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/many_pairwise_correlations.py b/examples/many_pairwise_correlations.py index f43fc089be..8f9caa700a 100644 --- a/examples/many_pairwise_correlations.py +++ b/examples/many_pairwise_correlations.py @@ -4,7 +4,7 @@ _thumb: .3, .6 """ -from string import letters +from string import ascii_letters import numpy as np import pandas as pd import seaborn as sns @@ -15,7 +15,7 @@ # Generate a large random dataset rs = np.random.RandomState(33) d = pd.DataFrame(data=rs.normal(size=(100, 26)), - columns=list(letters[:26])) + columns=list(ascii_letters[26:])) # Compute the correlation matrix corr = d.corr() From b3aa513964ced467343bb0a8eb20b4e2c6b78cd4 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 21 Sep 2016 07:20:42 -0700 Subject: [PATCH 0293/1738] Update data range in license --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index c6b4209658..82f25ce9ef 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2012-2013, Michael L. Waskom +Copyright (c) 2012-2016, Michael L. Waskom All rights reserved. Redistribution and use in source and binary forms, with or without From 61230fde4e737f9ad29b3b9ab488f1e92afc2989 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 21 Sep 2016 07:21:24 -0700 Subject: [PATCH 0294/1738] Update third clause --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 82f25ce9ef..aff43d5c72 100644 --- a/LICENSE +++ b/LICENSE @@ -11,7 +11,7 @@ modification, are permitted provided that the following conditions are met: this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -* Neither the name of the {organization} nor the names of its +* Neither the name of the project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. From a2c4602c9569c17d256dae97c25aa69da63c48ef Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 21 Sep 2016 21:37:35 -0400 Subject: [PATCH 0295/1738] Fix tsplot with non-default data index (closes #1019) --- seaborn/timeseries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/timeseries.py b/seaborn/timeseries.py index 45be35552c..cdc394ea30 100644 --- a/seaborn/timeseries.py +++ b/seaborn/timeseries.py @@ -181,7 +181,7 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, # Condition is optional if condition is None: - condition = pd.Series(np.ones(len(data))) + condition = pd.Series(1, index=data.index) legend = False legend_name = None n_cond = 1 From f415e57bddf0e4f5e98529f237d42e546240e13a Mon Sep 17 00:00:00 2001 From: Joris Vankerschaver Date: Wed, 12 Oct 2016 13:36:28 +0100 Subject: [PATCH 0296/1738] Use six.exec_ to mimic execfile on Python 3 --- doc/sphinxext/plot_generator.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/sphinxext/plot_generator.py b/doc/sphinxext/plot_generator.py index 5fe3138375..4de49d6f36 100644 --- a/doc/sphinxext/plot_generator.py +++ b/doc/sphinxext/plot_generator.py @@ -7,13 +7,13 @@ from __future__ import division import os import os.path as op -import sys import re import glob import token import tokenize import shutil -import json + +from seaborn.external import six import matplotlib matplotlib.use('Agg') @@ -21,9 +21,11 @@ from matplotlib import image -if sys.version_info >= (3,0): - execfile = (lambda filename, globals=None, locals=None: - exec(compile(open(filename, "rb").read(), filename, 'exec'), globals, locals)) +if six.PY3: + # Python 3 has no execfile + def execfile(filename, globals=None, locals=None): + with open(filename, "rb") as fp: + six.exec_(compile(fp.read(), filename, 'exec'), globals, locals) RST_TEMPLATE = """ From b4bcb38de751c9ab8bd531f3160a23b2c3110aca Mon Sep 17 00:00:00 2001 From: wordsforthewise Date: Thu, 13 Oct 2016 16:01:53 -0600 Subject: [PATCH 0297/1738] DOCS: update docs links and remove dev docs link (couldnt find the dev docs on the github pages) #958 --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d9b81f438d..47fb61b956 100644 --- a/README.md +++ b/README.md @@ -30,14 +30,12 @@ Seaborn is a Python visualization library based on matplotlib. It provides a hig Documentation ------------- -Online documentation is available [here](http://stanford.edu/~mwaskom/software/seaborn/). It includes a high-level tutorial, detailed API documentation, and other useful info. - -There are docs for the development version [here](http://stanford.edu/~mwaskom/software/seaborn-dev/). These should more or less correspond with the github master branch, but they're not built automatically and thus may fall out of sync at times. +Online documentation is available [here](https://seaborn.github.io/). It includes a high-level tutorial, detailed API documentation, and other useful info. Examples -------- -The documentation has an [example gallery](http://stanford.edu/~mwaskom/software/seaborn/examples/index.html) with short scripts showing how to use different parts of the package. +The documentation has an [example gallery](https://seaborn.github.io/examples/index.html) with short scripts showing how to use different parts of the package. Citing ------ From 2757d664b57070dc3351c4c3c5d996790e057d16 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 17 Oct 2016 09:38:00 -0400 Subject: [PATCH 0298/1738] Update doc info in setup file --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index c3c9515523..aa61f8a87c 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ #! /usr/bin/env python # -# Copyright (C) 2012-2014 Michael Waskom +# Copyright (C) 2012-2016 Michael Waskom import os # temporarily redirect config directory to prevent matplotlib importing # testing that for writeable directory which results in sandbox error in @@ -25,7 +25,7 @@ DISTNAME = 'seaborn' MAINTAINER = 'Michael Waskom' MAINTAINER_EMAIL = 'mwaskom@stanford.edu' -URL = 'http://stanford.edu/~mwaskom/software/seaborn/' +URL = 'http://seaborn.stanford.edu' LICENSE = 'BSD (3-clause)' DOWNLOAD_URL = 'https://github.com/mwaskom/seaborn/' VERSION = '0.8.dev' From be63455b4533593301d54e4fec24fe6f372e5d8c Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 20 Oct 2016 08:00:41 -0700 Subject: [PATCH 0299/1738] Update README links to pydata.org --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 47fb61b956..74e7ecab30 100644 --- a/README.md +++ b/README.md @@ -30,12 +30,12 @@ Seaborn is a Python visualization library based on matplotlib. It provides a hig Documentation ------------- -Online documentation is available [here](https://seaborn.github.io/). It includes a high-level tutorial, detailed API documentation, and other useful info. +Online documentation is available [here](https://seaborn.pydata.org/). It includes a high-level tutorial, detailed API documentation, and other useful info. Examples -------- -The documentation has an [example gallery](https://seaborn.github.io/examples/index.html) with short scripts showing how to use different parts of the package. +The documentation has an [example gallery](https://seaborn.pydata.org/examples/index.html) with short scripts showing how to use different parts of the package. Citing ------ From 065fd9fd91c319c056b925befcf82a6189dc6841 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 20 Oct 2016 14:23:48 -0400 Subject: [PATCH 0300/1738] Fix thummbnail links in README and update examples shown --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 47fb61b956..5e4930e1d2 100644 --- a/README.md +++ b/README.md @@ -2,24 +2,24 @@ Seaborn: statistical data visualization ======================================= From 0849ab9a88be4743939b749b91796ab5e6b41e22 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 20 Oct 2016 14:26:56 -0400 Subject: [PATCH 0301/1738] Fix thumnbnail image links --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fbf3fcd7e5..ac5c82810f 100644 --- a/README.md +++ b/README.md @@ -7,26 +7,29 @@ Seaborn: statistical data visualization - + - - + + - + - + + + + +
Seaborn is a Python visualization library based on matplotlib. It provides a high-level interface for drawing attractive statistical graphics. - Documentation ------------- From 1bda5bc463222eca7887aed720f7bf2104b47939 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 20 Oct 2016 14:29:02 -0400 Subject: [PATCH 0302/1738] Update DOI in README and a few other small edits --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ac5c82810f..e4bc06c50d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Seaborn: statistical data visualization +seaborn: statistical data visualization =======================================
@@ -43,12 +43,12 @@ The documentation has an [example gallery](https://seaborn.pydata.org/examples/i Citing ------ -Seaborn can be cited using a DOI provided through Zenodo: [![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.45133.svg)](http://dx.doi.org/10.5281/zenodo.45133) +Seaborn can be cited using a DOI provided through Zenodo: [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.54844.svg)](https://doi.org/10.5281/zenodo.54844) Dependencies ------------ -- Python 2.7 or 3.3+ +- Python 2.7 or 3.4+ ### Mandatory @@ -56,7 +56,7 @@ Dependencies - [scipy](http://www.scipy.org/) -- [matplotlib](http://matplotlib.sourceforge.net) +- [matplotlib](http://matplotlib.org/) - [pandas](http://pandas.pydata.org/) From 46bef7c18a4ede69d834deb2b36f4fa895e9b2a7 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 20 Oct 2016 14:30:10 -0400 Subject: [PATCH 0303/1738] Use http links to seaborn docs --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e4bc06c50d..d27079dd52 100644 --- a/README.md +++ b/README.md @@ -33,12 +33,12 @@ Seaborn is a Python visualization library based on matplotlib. It provides a hig Documentation ------------- -Online documentation is available [here](https://seaborn.pydata.org/). It includes a high-level tutorial, detailed API documentation, and other useful info. +Online documentation is available [here](http://seaborn.pydata.org/). It includes a high-level tutorial, detailed API documentation, and other useful info. Examples -------- -The documentation has an [example gallery](https://seaborn.pydata.org/examples/index.html) with short scripts showing how to use different parts of the package. +The documentation has an [example gallery](http://seaborn.pydata.org/examples/index.html) with short scripts showing how to use different parts of the package. Citing ------ From b3b5681efc20854c2ea60f7de8fafaeacf0ae878 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 20 Oct 2016 15:59:13 -0400 Subject: [PATCH 0304/1738] Add favicon file --- doc/_static/favicon.ico | Bin 0 -> 4286 bytes doc/conf.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 doc/_static/favicon.ico diff --git a/doc/_static/favicon.ico b/doc/_static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e928e78b3a6c2d78dfad594a8d798fe93e18c207 GIT binary patch literal 4286 zcmb`Le@s?Y7{~7`Zxa<0GqKVX%`M%mW~S?pN@HuLA>_*o3X1VdK?ev|O^q=?&`eEb zfrg?f!Lit^fg!?@W7yHN~vy9qy59qF)OZGp4u7(9O%Hho#4Lb?kB;Cia%DNb>Kq(ERZ#LwkKma z7{6xpmWZsYUq)}do0GV*EpuJm(IVZqh`dM8?E{r?O6uso2nyb%e*m;W$bab{(o+UL z$_IN;QG7~UZp;Riy*#xPZB?u%-!0ZxcNFRKKb9DDUj_%vlQ!DbkSlex%OLMjxlJ1y zUd8b#4QAUcu`dL*F??sEzi&kWIdkA-k-osYSYILIXrh0D_@kWzwP3|38ty=Uv{%D* zc;q=Geg?E$hnx0 z&oGepkw3nZLH2-ioDXSCBKB^6?E1qtpGvwOeE+bGhuh4_F6=erlxOz=S=SNdDuq0J zo`MT-0{Y^Y2j@WcK^YX_C;PA&V$J+g;y(;z>__dsKNb>GW7r2`I}ayR#G5N`N8$BO@U{9ZErWvo%q0h2!{ofUH3PzA@f z8sidswA8U3f}g=cjy$LmJ2|Rhr>Sd}#L1kNuxD;W_?Mg}?mJ>4p(Q5Q?nVq}AS@f7 zHrQ-vY{l;?$ozU?k2ma-u*tK_bN2^1OEQOpK)&m9P2J8V@v?WSd0#a}1sqlZA%!!E zYlomBJ>qz=p437AK5!wooSAzNN54E{t)Xqf_5+**vCjorNBO4sg3RO4&bpoJG6v@0 z5Gc-cW5&9r7jqL<-NerOVhLjp9AN0ky4QhxGo}D$=e+x4lV^WJ+SrdkiPX`}0OvW8 z_Txayk{Ow?YFlK6%8yTN$V$^4xe2l1#@MHmTlSXh`CvmM6OHq+bN9$MrU9}I`$+5# zkUcDS+>5a8ak>xN$&UxO`iTxSs|gdm`)wta;@%>?0(O+~IH^hAo+s z>(GOCj5X{7vE76uX`}lgG=lRCi|zk_&QL#FSv94ssS33pREY~V&6HSj%f0D0$T2iz zZwDHEsn})Sh8TT(cf7xldRIZ$5|$oc5|+O7&jQ^??xhKmgWRDYdwaaJGuQgo&{s@b z_M^;&z)OD&H>WUG}|; zp`SvGd_$k;j zjKt4vw^A}^|B8mYp&@7Mlu=(py$PN++B$Xl-VN`GZs%BXg@Eie=ekqA54bqj`bp|? zZ~h+S{?G!co#T62C!ZBg!!O_s_3-ZYYO@BeW2Uwa9+T8Q*qY>Uu_mcLu1ck`sj5;?s6go- D`%ydS literal 0 HcmV?d00001 diff --git a/doc/conf.py b/doc/conf.py index 96351fe710..c8d02f3775 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -150,7 +150,7 @@ # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +html_favicon = "favicon.ico" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, From 8bfacd5da2b03578b770cb2a29da45783852b011 Mon Sep 17 00:00:00 2001 From: Ben Congdon Date: Sun, 23 Oct 2016 06:39:43 -0500 Subject: [PATCH 0305/1738] Fix typo 'to just the jointplot function' -> 'to just use the jointplot function' --- doc/tutorial/distributions.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial/distributions.ipynb b/doc/tutorial/distributions.ipynb index 33da4be7a7..770ac75369 100644 --- a/doc/tutorial/distributions.ipynb +++ b/doc/tutorial/distributions.ipynb @@ -291,7 +291,7 @@ "Plotting bivariate distributions\n", "--------------------------------\n", "\n", - "It can also be useful to visualize a bivariate distribution of two variables. The easiest way to do this in seaborn is to just the :func:`jointplot` function, which creates a multi-panel figure that shows both the bivariate (or joint) relationship between two variables along with the univariate (or marginal) distribution of each on separate axes." + "It can also be useful to visualize a bivariate distribution of two variables. The easiest way to do this in seaborn is to just use the :func:`jointplot` function, which creates a multi-panel figure that shows both the bivariate (or joint) relationship between two variables along with the univariate (or marginal) distribution of each on separate axes." ] }, { From fe7963e3f12e2a69066f003afc15d9abeb6ea7ab Mon Sep 17 00:00:00 2001 From: Timofei Bondarev Date: Wed, 2 Nov 2016 12:08:32 -0400 Subject: [PATCH 0306/1738] fix typo in module attribute: __call__ to __all__ --- seaborn/miscplot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/miscplot.py b/seaborn/miscplot.py index 8eac76b003..afb98218c7 100644 --- a/seaborn/miscplot.py +++ b/seaborn/miscplot.py @@ -4,7 +4,7 @@ import matplotlib.pyplot as plt -__call__ = ["palplot", "puppyplot"] +__all__ = ["palplot", "puppyplot"] def palplot(pal, size=1): From 10a1c4416e62c7c48b37e38cdd6bea9bca1b27d2 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Tue, 29 Nov 2016 06:08:32 -0800 Subject: [PATCH 0307/1738] Update doc copyright dates --- doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index c8d02f3775..f97dcd94a9 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -65,7 +65,7 @@ # General information about the project. project = u'seaborn' -copyright = u'2012-2015, Michael Waskom' +copyright = u'2012-2016, Michael Waskom' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From d5e42b67e19c888eb36bab9f274b47525208b81c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 8 Dec 2016 15:34:51 -0500 Subject: [PATCH 0308/1738] Clean up the travis file and test on python 3.5 --- .travis.yml | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8c8b7cf2dc..0b4a8b1203 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,21 +4,14 @@ env: - PYTHON=2.7 DEPS=modern - PYTHON=2.7 DEPS=minimal - PYTHON=3.4 DEPS=modern - #- PYTHON=3.5 DEPS=modern + - PYTHON=3.5 DEPS=modern install: - conda update conda --yes - conda create -n testenv --yes pip python=$PYTHON - conda update conda --yes - source activate testenv - - if [ ${PYTHON:0:1} == "2" ]; - then conda install --yes imaging; else - pip install pillow; - fi - conda install --yes --file testing/deps_${DEPS}_${PYTHON}.txt - - conda install ipython-notebook=2 --yes - #- pip install sphinx numpydoc sphinx_bootstrap_theme runipy - #- sudo apt-get install pandoc - pip install pep8==1.5 # Later versions get stricter... - pip install https://github.com/dcramer/pyflakes/tarball/master - pip install . @@ -42,12 +35,6 @@ before_script: - if [ ${PYTHON:0:1} == "2" ]; then make lint; fi - #- if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then - # cd doc; - # make notebooks html; - # cd ..; - # fi - script: - if [ $DEPS == 'modern' ]; then nosetests --with-doctest; From 96b3bb66386effa1aa7aaa2a8cd7fc3c94e02def Mon Sep 17 00:00:00 2001 From: Margaret Pearce Date: Fri, 9 Dec 2016 23:06:11 -0500 Subject: [PATCH 0309/1738] Fixed documentation issue on load_dataset --- seaborn/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/utils.py b/seaborn/utils.py index f612905656..d6fddcc139 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -409,7 +409,7 @@ def load_dataset(name, cache=True, data_home=None, **kws): cache : boolean, optional If True, then cache data locally and use the cache on subsequent calls data_home : string, optional - The directory in which to cache data. By default, uses ~/seaborn_data/ + The directory in which to cache data. By default, uses ~/seaborn-data/ kws : dict, optional Passed to pandas.read_csv From 97b51e07960dcc732d4901e48f5235d1a6373ea8 Mon Sep 17 00:00:00 2001 From: Yoav Ram Date: Mon, 12 Dec 2016 12:49:03 +0200 Subject: [PATCH 0310/1738] ignore warnings when importing widgets --- seaborn/widgets.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/seaborn/widgets.py b/seaborn/widgets.py index 9f450f100d..3c203fb15b 100644 --- a/seaborn/widgets.py +++ b/seaborn/widgets.py @@ -7,15 +7,18 @@ try: from ipywidgets import interact, FloatSlider, IntSlider except ImportError: - try: - from IPython.html.widgets import interact, FloatSlider, IntSlider - except ImportError: + import warnings + with warnings.catch_warnings(): + warnings.simplefilter("ignore") # ignore ShimWarning raised by IPython, see #892 try: - from IPython.html.widgets import (interact, - FloatSliderWidget as FloatSlider, - IntSliderWidget as IntSlider) + from IPython.html.widgets import interact, FloatSlider, IntSlider except ImportError: - pass + try: + from IPython.html.widgets import (interact, + FloatSliderWidget as FloatSlider, + IntSliderWidget as IntSlider) + except ImportError: + pass from .miscplot import palplot From 0315ba554b0b27d5ba622c3fff221addd3f14657 Mon Sep 17 00:00:00 2001 From: Chris Holdgraf Date: Mon, 26 Dec 2016 15:59:05 -0600 Subject: [PATCH 0311/1738] fixing husl int bug --- seaborn/external/husl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/external/husl.py b/seaborn/external/husl.py index 37f0a38b00..5ba2d2c75b 100644 --- a/seaborn/external/husl.py +++ b/seaborn/external/husl.py @@ -170,7 +170,7 @@ def rgb_prepare(triple): # instead of Python 2 which is rounded to 5.0 which caused # a couple off by one errors in the tests. Tests now all pass # in Python 2 and Python 3 - ret.append(round(ch * 255 + 0.001, 0)) + ret.append(int(round(ch * 255 + 0.001, 0))) return ret From 2acb45f38d796df9ff4706a460f9ea82e67ff1e3 Mon Sep 17 00:00:00 2001 From: "Stephen W. Thomas" Date: Fri, 18 Nov 2016 15:10:39 -0500 Subject: [PATCH 0312/1738] Better error message when using 'split' with less than 2 hues. --- seaborn/categorical.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 47d7df47fb..5b21d3d5ca 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -564,7 +564,8 @@ def __init__(self, x, y, hue, data, order, hue_order, self.inner = inner if split and self.hue_names is not None and len(self.hue_names) != 2: - raise ValueError("Cannot use `split` with more than 2 hue levels.") + msg = "There must be exactly two hue levels to use `split`.'" + raise ValueError(msg) self.split = split if linewidth is None: From 35d07cf822a32474183777d84792b949113d9837 Mon Sep 17 00:00:00 2001 From: moosekaka Date: Fri, 6 Jan 2017 20:30:34 -0800 Subject: [PATCH 0313/1738] Replace str with to_utf8 methods --- seaborn/axisgrid.py | 4 ++-- seaborn/categorical.py | 4 ++-- seaborn/matrix.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 749bb68b34..c58e85e183 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -64,7 +64,7 @@ def add_legend(self, legend_data=None, title=None, label_order=None, if self.hue_names is None: label_order = np.sort(list(legend_data.keys())) else: - label_order = list(map(str, self.hue_names)) + label_order = list(map(utils.to_utf8, self.hue_names)) blank_handle = mpl.patches.Patch(alpha=0, linewidth=0) handles = [legend_data.get(l, blank_handle) for l in label_order] @@ -711,7 +711,7 @@ def map(self, func, *args, **kwargs): # Insert a label in the keyword arguments for the legend if self._hue_var is not None: - kwargs["label"] = str(self.hue_names[hue_k]) + kwargs["label"] = utils.to_utf8(self.hue_names[hue_k]) # Get the actual data we are going to plot with plot_data = data_ijk[list(args)] diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 47d7df47fb..f099141dae 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1514,7 +1514,7 @@ def estimate_statistic(self, estimator, ci, n_boot): # Rename the value label to reflect the estimation if self.value_label is not None: - self.value_label = "{}({})".format(estimator.__name__, + self.value_label = u"{}({})".format(estimator.__name__, self.value_label) def draw_confints(self, ax, at_group, confint, colors, @@ -3500,7 +3500,7 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, g.set_axis_labels(y_var="count") if legend and (hue is not None) and (hue not in [x, row, col]): - hue_order = list(map(str, hue_order)) + hue_order = list(map(utils.to_utf8, hue_order)) g.add_legend(title=hue, label_order=hue_order) return g diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 5ff9ff9235..7f34442cce 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -12,7 +12,7 @@ from .axisgrid import Grid from .palettes import cubehelix_palette -from .utils import despine, axis_ticklabels_overlap, relative_luminance +from .utils import despine, axis_ticklabels_overlap, relative_luminance, to_utf8 __all__ = ["heatmap", "clustermap"] @@ -21,7 +21,7 @@ def _index_to_label(index): """Convert a pandas index or multiindex to an axis label.""" if isinstance(index, pd.MultiIndex): - return "-".join(map(str, index.names)) + return "-".join(map(to_utf8, index.names)) else: return index.name @@ -29,7 +29,7 @@ def _index_to_label(index): def _index_to_ticklabels(index): """Convert a pandas index or multiindex into ticklabels.""" if isinstance(index, pd.MultiIndex): - return ["-".join(map(str, i)) for i in index.values] + return ["-".join(map(to_utf8, i)) for i in index.values] else: return index.values From c7a14a75d4cc751d70c114eb111b7059f8e16f2d Mon Sep 17 00:00:00 2001 From: moosekaka Date: Sat, 7 Jan 2017 12:11:36 -0800 Subject: [PATCH 0314/1738] fixed pep8 --- seaborn/categorical.py | 2 +- seaborn/matrix.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index f099141dae..d7d6c135be 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1515,7 +1515,7 @@ def estimate_statistic(self, estimator, ci, n_boot): # Rename the value label to reflect the estimation if self.value_label is not None: self.value_label = u"{}({})".format(estimator.__name__, - self.value_label) + self.value_label) def draw_confints(self, ax, at_group, confint, colors, errwidth=None, capsize=None, **kws): diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 7f34442cce..111a679a93 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -12,7 +12,8 @@ from .axisgrid import Grid from .palettes import cubehelix_palette -from .utils import despine, axis_ticklabels_overlap, relative_luminance, to_utf8 +from .utils import (despine, axis_ticklabels_overlap, relative_luminance, + to_utf8) __all__ = ["heatmap", "clustermap"] From 2c0b9ddc3df161bcb5ed1d71bc1f0a914a142396 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 23 Jan 2017 10:16:02 -0500 Subject: [PATCH 0315/1738] Test different subplot keyword Matplotlib changed the axisbg parameter in 2.0. Test the projection instead (which is probably more useful). Fixes #1098 --- seaborn/tests/test_axisgrid.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 3f1dc5de18..00a162d6e4 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -267,9 +267,10 @@ def test_legendout_with_colwrap(self): def test_subplot_kws(self): - g = ag.FacetGrid(self.df, subplot_kws=dict(axisbg="blue")) + g = ag.FacetGrid(self.df, despine=False, + subplot_kws=dict(projection="polar")) for ax in g.axes.flat: - nt.assert_equal(ax.get_axis_bgcolor(), "blue") + nt.assert_true("PolarAxesSubplot" in str(type(ax))) @skipif(old_matplotlib) def test_gridspec_kws(self): From 08186b454cbbcebab0e55db70e0b7eb39731953b Mon Sep 17 00:00:00 2001 From: Patrick Stegmann Date: Tue, 24 Jan 2017 14:44:13 +0100 Subject: [PATCH 0316/1738] Fixed broken link in doc/installing.rst I've also replaced the sourceforge link (that only redirects to matplotlib.org) with it's redirect target --- doc/installing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/installing.rst b/doc/installing.rst index 0127a38fb9..f78baf5294 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -33,7 +33,7 @@ Mandatory dependencies - `scipy `__ -- `matplotlib `__ +- `matplotlib `__ - `pandas `__ From 0c0d6e8524882a7c5c1e96419134b5ea8a78dd1b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 24 Jan 2017 09:45:57 -0500 Subject: [PATCH 0317/1738] More liberal test that allows for interpolation error --- seaborn/tests/test_palettes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/tests/test_palettes.py b/seaborn/tests/test_palettes.py index 87c5ed49b8..4b0ef0e3fe 100644 --- a/seaborn/tests/test_palettes.py +++ b/seaborn/tests/test_palettes.py @@ -163,7 +163,7 @@ def test_mpl_reversal(self): pal_forward = palettes.mpl_palette("BuPu", 6) pal_reverse = palettes.mpl_palette("BuPu_r", 6) - nt.assert_equal(pal_forward, pal_reverse[::-1]) + npt.assert_array_almost_equal(pal_forward, pal_reverse[::-1]) def test_rgb_from_hls(self): From 55c82679e8416d5d220567a03e77f792f3c388f6 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 4 Feb 2017 16:02:14 -0500 Subject: [PATCH 0318/1738] Fix line widths for PEP8 --- seaborn/widgets.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/seaborn/widgets.py b/seaborn/widgets.py index 3c203fb15b..6976f61bf4 100644 --- a/seaborn/widgets.py +++ b/seaborn/widgets.py @@ -8,15 +8,18 @@ from ipywidgets import interact, FloatSlider, IntSlider except ImportError: import warnings + # ignore ShimWarning raised by IPython, see GH #892 with warnings.catch_warnings(): - warnings.simplefilter("ignore") # ignore ShimWarning raised by IPython, see #892 + warnings.simplefilter("ignore") try: from IPython.html.widgets import interact, FloatSlider, IntSlider except ImportError: try: from IPython.html.widgets import (interact, - FloatSliderWidget as FloatSlider, - IntSliderWidget as IntSlider) + FloatSliderWidget, + IntSliderWidget) + FloatSlider = FloatSliderWidget + IntSlider = IntSliderWidget except ImportError: pass From 04368a13bd7e8f1338ce63353672220587397b4c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 4 Feb 2017 16:11:44 -0500 Subject: [PATCH 0319/1738] Put a cap on the default number of bins for hex jointplot --- seaborn/distributions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 94e4f5321d..0d9180b355 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -809,8 +809,8 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, elif kind.startswith("hex"): - x_bins = _freedman_diaconis_bins(grid.x) - y_bins = _freedman_diaconis_bins(grid.y) + x_bins = min(_freedman_diaconis_bins(grid.x), 50) + y_bins = min(_freedman_diaconis_bins(grid.y), 50) gridsize = int(np.mean([x_bins, y_bins])) joint_kws.setdefault("gridsize", gridsize) From 2d4231daccbd4b5c0ec27dd513ca4d0a1ee99e52 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 4 Feb 2017 16:29:44 -0500 Subject: [PATCH 0320/1738] Update release notes --- doc/releases/v0.8.0.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index 81c8019bd7..0b266ce1d9 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -3,3 +3,5 @@ v0.8.0 (Unreleased) ------------------- - Added the ``dodge`` argument to :func:`boxplot`, :func:`violinplot`, and :func:`barplot` to allow use of ``hue`` without changing the position or width of the plot elements, as when the ``hue`` varible is not nested within the main categorical variable. + +- Put a cap on the number of bins used in :func:`jointplot` for ``type=="hex"`` to avoid hanging when the reference rule prescribes too many. From c25b82a4f802e38c7c7902ff27364fdde20edc85 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 4 Feb 2017 16:34:49 -0500 Subject: [PATCH 0321/1738] Remove API stub for vestigal functions --- doc/api.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 1945900a37..ca63439d0c 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -150,5 +150,3 @@ Utility functions desaturate saturate set_hls_values - ci_to_errsize - axlabel From 9be4d59a7cd08885bf915e83d4ffb947b512d779 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 4 Feb 2017 16:35:15 -0500 Subject: [PATCH 0322/1738] Update copyright year --- doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index f97dcd94a9..6fa856a8e4 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -65,7 +65,7 @@ # General information about the project. project = u'seaborn' -copyright = u'2012-2016, Michael Waskom' +copyright = u'2012-2017, Michael Waskom' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From 10a7f140d1ac12fa16f4d7fe536fe66d05ea1f87 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 4 Feb 2017 16:16:04 -0500 Subject: [PATCH 0323/1738] Turn dendrogram axes off instead of using white background --- seaborn/matrix.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 111a679a93..884928c354 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -760,10 +760,10 @@ def __init__(self, data, pivot_kws=None, z_score=None, standard_scale=None, width_ratios=width_ratios, height_ratios=height_ratios) - self.ax_row_dendrogram = self.fig.add_subplot(self.gs[nrows - 1, 0:2], - axisbg="white") - self.ax_col_dendrogram = self.fig.add_subplot(self.gs[0:2, ncols - 1], - axisbg="white") + self.ax_row_dendrogram = self.fig.add_subplot(self.gs[nrows - 1, 0:2]) + self.ax_col_dendrogram = self.fig.add_subplot(self.gs[0:2, ncols - 1]) + self.ax_row_dendrogram.set_axis_off() + self.ax_col_dendrogram.set_axis_off() self.ax_row_colors = None self.ax_col_colors = None From 9559a6d81412447a6269bb3213d54bd8f5f34631 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 4 Feb 2017 16:41:33 -0500 Subject: [PATCH 0324/1738] Update release notes --- doc/releases/v0.8.0.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index 0b266ce1d9..aacd4d2f69 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -5,3 +5,5 @@ v0.8.0 (Unreleased) - Added the ``dodge`` argument to :func:`boxplot`, :func:`violinplot`, and :func:`barplot` to allow use of ``hue`` without changing the position or width of the plot elements, as when the ``hue`` varible is not nested within the main categorical variable. - Put a cap on the number of bins used in :func:`jointplot` for ``type=="hex"`` to avoid hanging when the reference rule prescribes too many. + +- Turn off dendrogram axes in :func:`clustermap` rather than setting the background color to white. From 89249342bca5b4d8dae0cfdd5e5597c22e564b16 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 4 Feb 2017 16:50:11 -0500 Subject: [PATCH 0325/1738] Allow side-specific offsets in despine --- doc/releases/v0.8.0.txt | 2 ++ seaborn/tests/test_utils.py | 13 +++++++++++++ seaborn/utils.py | 14 ++++++++++---- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index aacd4d2f69..eb604464d8 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -4,6 +4,8 @@ v0.8.0 (Unreleased) - Added the ``dodge`` argument to :func:`boxplot`, :func:`violinplot`, and :func:`barplot` to allow use of ``hue`` without changing the position or width of the plot elements, as when the ``hue`` varible is not nested within the main categorical variable. +- Allow side-specific offsets in :func:`despine`. + - Put a cap on the number of bins used in :func:`jointplot` for ``type=="hex"`` to avoid hanging when the reference rule prescribes too many. - Turn off dendrogram axes in :func:`clustermap` rather than setting the background color to white. diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index 82a2ab596a..4c2fb7ca4a 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -174,6 +174,19 @@ def test_despine_with_offset(self): else: nt.assert_equal(new_position, self.original_position) + def test_despine_side_specific_offset(self): + + f, ax = plt.subplots() + utils.despine(ax=ax, offset=dict(left=self.offset)) + + for side in self.sides: + is_visible = ax.spines[side].get_visible() + new_position = ax.spines[side].get_position() + if is_visible and side == "left": + nt.assert_equal(new_position, self.offset_position) + else: + nt.assert_equal(new_position, self.original_position) + def test_despine_with_offset_specific_axes(self): f, (ax1, ax2) = plt.subplots(2, 1) diff --git a/seaborn/utils.py b/seaborn/utils.py index d6fddcc139..36ade224aa 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -173,11 +173,13 @@ def despine(fig=None, ax=None, top=True, right=True, left=False, Specific axes object to despine. top, right, left, bottom : boolean, optional If True, remove that spine. - offset : int or None (default), optional + offset : int or dict, optional Absolute distance, in points, spines should be moved away - from the axes (negative values move spines inward). + from the axes (negative values move spines inward). A single value + applies to all spines; a dict can be used to set offset values per + side. trim : bool, optional - If true, limit spines to the smallest and largest major tick + If True, limit spines to the smallest and largest major tick on each non-despined axis. Returns @@ -199,7 +201,11 @@ def despine(fig=None, ax=None, top=True, right=True, left=False, is_visible = not locals()[side] ax_i.spines[side].set_visible(is_visible) if offset is not None and is_visible: - _set_spine_position(ax_i.spines[side], ('outward', offset)) + try: + val = offset.get(side, 0) + except AttributeError: + val = offset + _set_spine_position(ax_i.spines[side], ('outward', val)) # Set the ticks appropriately if bottom: From edc456df2a59501c44db463a2e8c4d3141b56cc7 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 4 Feb 2017 17:50:22 -0500 Subject: [PATCH 0326/1738] Update internally packaged six version --- seaborn/external/six.py | 373 ++++++++++++++++++++++++++++++++-------- 1 file changed, 305 insertions(+), 68 deletions(-) diff --git a/seaborn/external/six.py b/seaborn/external/six.py index 09edee7391..c374474da1 100644 --- a/seaborn/external/six.py +++ b/seaborn/external/six.py @@ -1,6 +1,6 @@ """Utilities for writing code that runs on Python 2 and 3""" -# Copyright (c) 2010-2014 Benjamin Peterson +# Copyright (c) 2010-2015 Benjamin Peterson # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,17 +20,22 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import + +import functools +import itertools import operator import sys import types __author__ = "Benjamin Peterson " -__version__ = "1.5.2" +__version__ = "1.10.0" # Useful for very coarse version differentiation. PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) if PY3: string_types = str, @@ -53,6 +58,7 @@ else: # It's possible to have sizeof(long) != sizeof(Py_ssize_t). class X(object): + def __len__(self): return 1 << 31 try: @@ -84,9 +90,13 @@ def __init__(self, name): def __get__(self, obj, tp): result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - # This is a bit ugly, but it avoids running this again. - delattr(obj.__class__, self.name) + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass return result @@ -105,14 +115,6 @@ def _resolve(self): return _import_module(self.mod) def __getattr__(self, attr): - # Hack around the Django autoreloader. The reloader tries to get - # __file__ or __name__ of every module in sys.modules. This doesn't work - # well if this MovedModule is for an module that is unavailable on this - # machine (like winreg on Unix systems). Thus, we pretend __file__ and - # __name__ don't exist if the module hasn't been loaded yet. See issues - # #51 and #53. - if attr in ("__file__", "__name__") and self.mod not in sys.modules: - raise AttributeError _module = self._resolve() value = getattr(_module, attr) setattr(self, attr, value) @@ -159,9 +161,75 @@ def _resolve(self): return getattr(module, self.attr) +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + class _MovedItems(_LazyModule): + """Lazy loading of moved objects""" + __path__ = [] # mark as package _moved_attributes = [ @@ -169,26 +237,33 @@ class _MovedItems(_LazyModule): MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "imp", "reload"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), MovedAttribute("UserString", "UserString", "collections"), MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), MovedModule("configparser", "ConfigParser"), MovedModule("copyreg", "copy_reg"), MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), MovedModule("http_cookies", "Cookie", "http.cookies"), MovedModule("html_entities", "htmlentitydefs", "html.entities"), MovedModule("html_parser", "HTMLParser", "html.parser"), MovedModule("http_client", "httplib", "http.client"), MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), @@ -222,25 +297,34 @@ class _MovedItems(_LazyModule): MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("winreg", "_winreg"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), ] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + for attr in _moved_attributes: setattr(_MovedItems, attr.name, attr) if isinstance(attr, MovedModule): - sys.modules[__name__ + ".moves." + attr.name] = attr + _importer._add_module(attr, "moves." + attr.name) del attr _MovedItems._moved_attributes = _moved_attributes -moves = sys.modules[__name__ + ".moves"] = _MovedItems(__name__ + ".moves") +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") class Module_six_moves_urllib_parse(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_parse""" _urllib_parse_moved_attributes = [ MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), MovedAttribute("parse_qs", "urlparse", "urllib.parse"), MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), MovedAttribute("urldefrag", "urlparse", "urllib.parse"), @@ -254,6 +338,14 @@ class Module_six_moves_urllib_parse(_LazyModule): MovedAttribute("unquote", "urllib", "urllib.parse"), MovedAttribute("unquote_plus", "urllib", "urllib.parse"), MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), ] for attr in _urllib_parse_moved_attributes: setattr(Module_six_moves_urllib_parse, attr.name, attr) @@ -261,10 +353,12 @@ class Module_six_moves_urllib_parse(_LazyModule): Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes -sys.modules[__name__ + ".moves.urllib_parse"] = sys.modules[__name__ + ".moves.urllib.parse"] = Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse") +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") class Module_six_moves_urllib_error(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_error""" @@ -279,10 +373,12 @@ class Module_six_moves_urllib_error(_LazyModule): Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes -sys.modules[__name__ + ".moves.urllib_error"] = sys.modules[__name__ + ".moves.urllib.error"] = Module_six_moves_urllib_error(__name__ + ".moves.urllib.error") +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") class Module_six_moves_urllib_request(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_request""" @@ -327,10 +423,12 @@ class Module_six_moves_urllib_request(_LazyModule): Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes -sys.modules[__name__ + ".moves.urllib_request"] = sys.modules[__name__ + ".moves.urllib.request"] = Module_six_moves_urllib_request(__name__ + ".moves.urllib.request") +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") class Module_six_moves_urllib_response(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_response""" @@ -346,10 +444,12 @@ class Module_six_moves_urllib_response(_LazyModule): Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes -sys.modules[__name__ + ".moves.urllib_response"] = sys.modules[__name__ + ".moves.urllib.response"] = Module_six_moves_urllib_response(__name__ + ".moves.urllib.response") +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") class Module_six_moves_urllib_robotparser(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_robotparser""" @@ -362,22 +462,25 @@ class Module_six_moves_urllib_robotparser(_LazyModule): Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes -sys.modules[__name__ + ".moves.urllib_robotparser"] = sys.modules[__name__ + ".moves.urllib.robotparser"] = Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser") +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") class Module_six_moves_urllib(types.ModuleType): + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - parse = sys.modules[__name__ + ".moves.urllib_parse"] - error = sys.modules[__name__ + ".moves.urllib_error"] - request = sys.modules[__name__ + ".moves.urllib_request"] - response = sys.modules[__name__ + ".moves.urllib_response"] - robotparser = sys.modules[__name__ + ".moves.urllib_robotparser"] + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") def __dir__(self): return ['parse', 'error', 'request', 'response', 'robotparser'] - -sys.modules[__name__ + ".moves.urllib"] = Module_six_moves_urllib(__name__ + ".moves.urllib") +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") def add_move(move): @@ -404,11 +507,6 @@ def remove_move(name): _func_code = "__code__" _func_defaults = "__defaults__" _func_globals = "__globals__" - - _iterkeys = "keys" - _itervalues = "values" - _iteritems = "items" - _iterlists = "lists" else: _meth_func = "im_func" _meth_self = "im_self" @@ -418,17 +516,12 @@ def remove_move(name): _func_defaults = "func_defaults" _func_globals = "func_globals" - _iterkeys = "iterkeys" - _itervalues = "itervalues" - _iteritems = "iteritems" - _iterlists = "iterlists" - try: advance_iterator = next except NameError: def advance_iterator(it): - return next(it) + return it.next() next = advance_iterator @@ -445,6 +538,9 @@ def get_unbound_function(unbound): create_bound_method = types.MethodType + def create_unbound_method(func, cls): + return func + Iterator = object else: def get_unbound_function(unbound): @@ -453,6 +549,9 @@ def get_unbound_function(unbound): def create_bound_method(func, obj): return types.MethodType(func, obj, obj.__class__) + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + class Iterator(object): def next(self): @@ -471,66 +570,117 @@ def next(self): get_function_globals = operator.attrgetter(_func_globals) -def iterkeys(d, **kw): - """Return an iterator over the keys of a dictionary.""" - return iter(getattr(d, _iterkeys)(**kw)) +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") -def itervalues(d, **kw): - """Return an iterator over the values of a dictionary.""" - return iter(getattr(d, _itervalues)(**kw)) + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) -def iteritems(d, **kw): - """Return an iterator over the (key, value) pairs of a dictionary.""" - return iter(getattr(d, _iteritems)(**kw)) + def itervalues(d, **kw): + return d.itervalues(**kw) -def iterlists(d, **kw): - """Return an iterator over the (key, [values]) pairs of a dictionary.""" - return iter(getattr(d, _iterlists)(**kw)) + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") if PY3: def b(s): return s.encode("latin-1") + def u(s): return s unichr = chr - if sys.version_info[1] <= 1: - def int2byte(i): - return bytes((i,)) - else: - # This is about 2x faster than the implementation above on 3.2+ - int2byte = operator.methodcaller("to_bytes", 1, "big") + import struct + int2byte = struct.Struct(">B").pack + del struct byte2int = operator.itemgetter(0) indexbytes = operator.getitem iterbytes = iter import io StringIO = io.StringIO BytesIO = io.BytesIO + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" else: def b(s): return s # Workaround for standalone backslash + def u(s): return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") unichr = unichr int2byte = chr + def byte2int(bs): return ord(bs[0]) + def indexbytes(buf, i): return ord(buf[i]) - def iterbytes(buf): - return (ord(byte) for byte in buf) + iterbytes = functools.partial(itertools.imap, ord) import StringIO StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" _add_doc(b, """Byte literal""") _add_doc(u, """Text literal""") +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + if PY3: exec_ = getattr(moves.builtins, "exec") - def reraise(tp, value, tb=None): + if value is None: + value = tp() if value.__traceback__ is not tb: raise value.with_traceback(tb) raise value @@ -548,12 +698,26 @@ def exec_(_code_, _globs_=None, _locs_=None): _locs_ = _globs_ exec("""exec _code_ in _globs_, _locs_""") - exec_("""def reraise(tp, value, tb=None): raise tp, value, tb """) +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + if from_value is None: + raise value + raise value from from_value +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + raise value from from_value +""") +else: + def raise_from(value, from_value): + raise value + + print_ = getattr(moves.builtins, "print", None) if print_ is None: def print_(*args, **kwargs): @@ -561,13 +725,14 @@ def print_(*args, **kwargs): fp = kwargs.pop("file", sys.stdout) if fp is None: return + def write(data): if not isinstance(data, basestring): data = str(data) # If the file has an encoding, encode unicode with it. if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): + isinstance(data, unicode) and + fp.encoding is not None): errors = getattr(fp, "errors", None) if errors is None: errors = "strict" @@ -608,25 +773,97 @@ def write(data): write(sep) write(arg) write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() _add_doc(reraise, """Reraise an exception.""") +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + def with_metaclass(meta, *bases): """Create a base class with a metaclass.""" - return meta("NewBase", bases, {}) + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + def add_metaclass(metaclass): """Class decorator for creating a class with a metaclass.""" def wrapper(cls): orig_vars = cls.__dict__.copy() - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) slots = orig_vars.get('__slots__') if slots is not None: if isinstance(slots, str): slots = [slots] for slots_var in slots: orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) return metaclass(cls.__name__, cls.__bases__, orig_vars) return wrapper + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) + From 8ab1a3a236a4b50759d860aa6de639f5f44cc709 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 4 Feb 2017 18:04:21 -0500 Subject: [PATCH 0327/1738] Don't run pyflakes on six --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1d71ae1874..1320d257f2 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ coverage: lint: - pyflakes -x W seaborn + pyflakes -x W -X seaborn/external/six.py seaborn pep8 --exclude external seaborn hexstrip: From cb351dd0e3aecfe42b3005ee57e1afd4fefb84a9 Mon Sep 17 00:00:00 2001 From: Jimmy Callin Date: Wed, 8 Feb 2017 16:57:24 +0100 Subject: [PATCH 0328/1738] Fixes availible typo --- doc/installing.rst | 8 ++++---- doc/tutorial/distributions.ipynb | 2 +- seaborn/palettes.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/installing.rst b/doc/installing.rst index f78baf5294..63cd61e8b1 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -6,7 +6,7 @@ Installing and getting started To install the released version of seaborn, you can use ``pip`` (i.e. ``pip install seaborn``). It's also possible to install the released version using ``conda`` (i.e. ``conda install seaborn``), although this may lag behind the -version availible from PyPI. +version available from PyPI. Alternatively, you can use ``pip`` to install the development version, with the command ``pip install git+git://github.com/mwaskom/seaborn.git#egg=seaborn``. @@ -21,7 +21,7 @@ are not built automatically and may at times fall out of sync with the actual master branch on github. -Dependencies +Dependencies ~~~~~~~~~~~~ - Python 2.7 or 3.3+ @@ -47,7 +47,7 @@ dependencies if they do not exist at install-time. I recommend using seaborn with the `Anaconda distribution `_, as this makes it easy to manage -the main dependencies, which otherwise can be difficult to install. +the main dependencies, which otherwise can be difficult to install. I attempt to keep seaborn importable and generally functional on the versions available through the stable Debian channels. There may be cases where some @@ -83,7 +83,7 @@ Testing To test seaborn, run ``make test`` in the root directory of the source distribution. This runs the unit test suite (which can also be exercised -separately by running ``nosetests``). It also runs the code in the example +separately by running ``nosetests``). It also runs the code in the example notebooks to smoke-test a broader and more realistic range of example usage. The full set of tests requires an internet connection to download the example diff --git a/doc/tutorial/distributions.ipynb b/doc/tutorial/distributions.ipynb index 770ac75369..22aa12772d 100644 --- a/doc/tutorial/distributions.ipynb +++ b/doc/tutorial/distributions.ipynb @@ -335,7 +335,7 @@ "Hexbin plots\n", "^^^^^^^^^^^^\n", "\n", - "The bivariate analogue of a histogram is known as a \"hexbin\" plot, because it shows the counts of observations that fall within hexagonal bins. This plot works best with relatively large datasets. It's availible through the matplotlib ``plt.hexbin`` function and as a style in :func:`jointplot`. It looks best with a white background:" + "The bivariate analogue of a histogram is known as a \"hexbin\" plot, because it shows the counts of observations that fall within hexagonal bins. This plot works best with relatively large datasets. It's available through the matplotlib ``plt.hexbin`` function and as a style in :func:`jointplot`. It looks best with a white background:" ] }, { diff --git a/seaborn/palettes.py b/seaborn/palettes.py index 36608858b9..7402ca61a1 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -59,7 +59,7 @@ def as_hex(self): def color_palette(palette=None, n_colors=None, desat=None): """Return a list of colors defining a color palette. - Availible seaborn palette names: + Available seaborn palette names: deep, muted, bright, pastel, dark, colorblind Other options: @@ -788,7 +788,7 @@ def cubehelix_palette(n_colors=6, start=0, rot=.4, gamma=1.0, hue=0.8, This produces a colormap with linearly-decreasing (or increasing) brightness. That means that information will be preserved if printed to black and white or viewed by someone who is colorblind. "cubehelix" is - also availible as a matplotlib-based palette, but this function gives the + also available as a matplotlib-based palette, but this function gives the user more control over the look of the palette and has a different set of defaults. From 2bedbc2cf3722709b2ac85ebda6f1ce9b7cc1906 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 16 Feb 2017 05:38:01 -0800 Subject: [PATCH 0329/1738] Change git install prototype in README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d27079dd52..e6c7272701 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ To install the released version, just do You may instead want to use the development version from Github, by running - pip install git+git://github.com/mwaskom/seaborn.git#egg=seaborn + pip install git+https://github.com/mwaskom/seaborn.git#egg=seaborn Testing @@ -85,6 +85,7 @@ Testing To test seaborn, run `make test` in the source directory. This will run the unit-test and doctest suite (using `nose`). + Development ----------- From b9fa2a3d27200ca5003901539ff05770bf708f8c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 16 Feb 2017 08:39:02 -0500 Subject: [PATCH 0330/1738] Change git install protocol in docs --- doc/installing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/installing.rst b/doc/installing.rst index f78baf5294..97a42df670 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -9,7 +9,7 @@ install seaborn``). It's also possible to install the released version using version availible from PyPI. Alternatively, you can use ``pip`` to install the development version, with the -command ``pip install git+git://github.com/mwaskom/seaborn.git#egg=seaborn``. +command ``pip install git+https://github.com/mwaskom/seaborn.git#egg=seaborn``. Another option would be to to clone the `github repository `_ and install with ``pip install .`` from the source directory. Seaborn itself is pure Python, so installation should be From 48215b9e5b3a75a8141d7d722693feec3b29dc72 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 16 Feb 2017 11:04:12 -0500 Subject: [PATCH 0331/1738] Keep copyright statement up to date --- doc/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index 6fa856a8e4..6cb2b7043c 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -65,7 +65,8 @@ # General information about the project. project = u'seaborn' -copyright = u'2012-2017, Michael Waskom' +import time +copyright = u'2012-{}, Michael Waskom'.format(time.strftime("%Y")) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From 484de5fb60a41c2929def09e3260ce37dda984e3 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 16 Feb 2017 21:11:39 -0500 Subject: [PATCH 0332/1738] Ensure that array size is an int --- seaborn/tests/test_categorical.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index a30aefccc7..a04c7cb399 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -23,12 +23,12 @@ class CategoricalFixture(PlotTestCase): """Test boxplot (also base class for things like violinplots).""" rs = np.random.RandomState(30) n_total = 60 - x = rs.randn(n_total / 3, 3) + x = rs.randn(int(n_total / 3), 3) x_df = pd.DataFrame(x, columns=pd.Series(list("XYZ"), name="big")) y = pd.Series(rs.randn(n_total), name="y_data") - g = pd.Series(np.repeat(list("abc"), n_total / 3), name="small") - h = pd.Series(np.tile(list("mn"), n_total / 2), name="medium") - u = pd.Series(np.tile(list("jkh"), n_total / 3)) + g = pd.Series(np.repeat(list("abc"), int(n_total / 3)), name="small") + h = pd.Series(np.tile(list("mn"), int(n_total / 2)), name="medium") + u = pd.Series(np.tile(list("jkh"), int(n_total / 3))) df = pd.DataFrame(dict(y=y, g=g, h=h, u=u)) x_df["W"] = g From 740ac9ed854e620d1a3874869928039c1edef35f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 24 Feb 2017 19:02:16 -0500 Subject: [PATCH 0333/1738] Specify violin facecolor not color (closes #1122) --- seaborn/categorical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index d7d6c135be..90a2ce9553 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -848,7 +848,7 @@ def draw_violins(self, ax): for j, hue_level in enumerate(self.hue_names): support, density = self.support[i][j], self.density[i][j] - kws["color"] = self.colors[j] + kws["facecolor"] = self.colors[j] # Add legend data, but just for one set of violins if not i: From e86067e4f22566900c2438b4e17306fa6b4a5eb6 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 24 Feb 2017 19:15:28 -0500 Subject: [PATCH 0334/1738] Fix test --- seaborn/tests/test_categorical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index a04c7cb399..d085eb745e 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -82,7 +82,7 @@ def test_1d_input_data(self): # Test an object array that looks 1D but isn't x_notreally_1d = np.array([self.x.ravel(), - self.x.ravel()[:self.n_total / 2]]) + self.x.ravel()[:int(self.n_total / 2)]]) p.establish_variables(data=x_notreally_1d) nt.assert_equal(len(p.plot_data), 2) nt.assert_equal(len(p.plot_data[0]), self.n_total) From a6ffdeef743b8c85e7fdde8aac6cf16b7525b8c3 Mon Sep 17 00:00:00 2001 From: Saulius Lukauskas Date: Thu, 2 Mar 2017 15:36:58 +0000 Subject: [PATCH 0335/1738] Changed swarmplot code a little bit. Replaced O(N^2) algorithm used in pruning positions for points into an O(N log N) alrogithm. I wonder if we can get away with an even faster one --- seaborn/categorical.py | 27 +++++++++++++++------------ seaborn/tests/test_categorical.py | 13 +++++++------ 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 90a2ce9553..5f9dbd036c 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1234,19 +1234,22 @@ def position_candidates(self, xy_i, neighbors, d): new_candidates = [cr, cl] candidates.extend(new_candidates) left_first = not left_first - return candidates + return np.array(candidates) - def prune_candidates(self, candidates, neighbors, d): + def first_non_overlapping_candidate(self, candidates, neighbors, d): """Remove candidates from the list if they overlap with the swarm.""" - good_candidates = [] for xy_i in candidates: good_candidate = True for xy_j in neighbors: if self.overlap(xy_i, xy_j, d): good_candidate = False + break + if good_candidate: - good_candidates.append(xy_i) - return np.array(good_candidates) + return xy_i + + # If `position_candidates` works well this should never happen + raise Exception('No non-overlapping candidates found. This should not happen.') def beeswarm(self, orig_xy, d): """Adjust x position of points to avoid overlaps.""" @@ -1268,14 +1271,14 @@ def beeswarm(self, orig_xy, d): # with respect to each of the swarm neighbors candidates = self.position_candidates(xy_i, neighbors, d) - # Remove the positions that overlap with any of the - # other neighbors - candidates = self.prune_candidates(candidates, neighbors, d) - - # Find the most central of the remaining positions + # Sort candidates by their centrality offsets = np.abs(candidates[:, 0] - midline) - best_index = np.argmin(offsets) - new_xy_i = candidates[best_index] + candidates = candidates[np.argsort(offsets)] + + # Find the first candidate that doesn't overlap any neighbours + new_xy_i = self.first_non_overlapping_candidate(candidates, neighbors, d) + + # Place it into the swarm swarm.append(new_xy_i) return np.array(swarm) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index d085eb745e..d31a589a55 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1656,16 +1656,17 @@ def test_position_candidates(self): candidates = p.position_candidates(xy_i, neighbors, 1) dx1 = 1.05 dx2 = np.sqrt(1 - .5 ** 2) * 1.05 - nt.assert_equal(candidates, - [(0, 1), (-dx1, 1), (dx1, 1), (dx2, 1), (-dx2, 1)]) + npt.assert_array_equal(candidates, + [(0, 1), (-dx1, 1), (dx1, 1), (dx2, 1), (-dx2, 1)]) - def test_prune_candidates(self): + def test_find_first_non_overlapping_candidate(self): p = cat._SwarmPlotter(**self.default_kws) - candidates = [(.5, 1), (1, 1)] + candidates = [(.5, 1), (1, 1), (1.5, 1)] neighbors = [(0, 1)] - candidates = p.prune_candidates(candidates, neighbors, 1) - npt.assert_array_equal(candidates, np.array([(1, 1)])) + + first = p.first_non_overlapping_candidate(candidates, neighbors, 1) + npt.assert_array_equal(first, (1, 1)) def test_beeswarm(self): From 4c4a29da0481ca1db44736e0a99aa37830e320d4 Mon Sep 17 00:00:00 2001 From: Saulius Lukauskas Date: Thu, 2 Mar 2017 16:30:02 +0000 Subject: [PATCH 0336/1738] Moved good_point and overlap calculation to numpy. Doesn't look like there's much speed improvement --- seaborn/categorical.py | 34 ++++++++++++++++++++----------- seaborn/tests/test_categorical.py | 10 ++------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 5f9dbd036c..e6c486bd26 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1198,12 +1198,6 @@ def __init__(self, x, y, hue, data, order, hue_order, self.split = split self.width = .8 - def overlap(self, xy_i, xy_j, d): - """Return True if two circles with the same diameter will overlap.""" - x_i, y_i = xy_i - x_j, y_j = xy_j - return ((x_i - x_j) ** 2 + (y_i - y_j) ** 2) < (d ** 2) - def could_overlap(self, xy_i, swarm, d): """Return a list of all swarm points that could overlap with target. @@ -1217,7 +1211,7 @@ def could_overlap(self, xy_i, swarm, d): neighbors.append(xy_j) else: break - return list(reversed(neighbors)) + return np.array(list(reversed(neighbors))) def position_candidates(self, xy_i, neighbors, d): """Return a list of (x, y) coordinates that might be valid.""" @@ -1238,12 +1232,28 @@ def position_candidates(self, xy_i, neighbors, d): def first_non_overlapping_candidate(self, candidates, neighbors, d): """Remove candidates from the list if they overlap with the swarm.""" + + # IF we have no neighbours, all candidates are good. + if len(neighbors) == 0: + return candidates[0] + + neighbors_x = neighbors[:, 0] + neighbors_y = neighbors[:, 1] + + d_square = d ** 2 + for xy_i in candidates: - good_candidate = True - for xy_j in neighbors: - if self.overlap(xy_i, xy_j, d): - good_candidate = False - break + x_i, y_i = xy_i + + dx = neighbors_x - x_i + dy = neighbors_y - y_i + + sq_distances = np.power(dx, 2.0) + np.power(dy, 2.0) + + # good candidate does not overlap any of neighbors + # which means that squared distance between candidate + # and any of the neighbours has to be at least square of the diameter + good_candidate = np.all(sq_distances >= d_square) if good_candidate: return xy_i diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index d31a589a55..33ac88979a 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1636,17 +1636,11 @@ class TestSwarmPlotter(CategoricalFixture): order=None, hue_order=None, split=False, orient=None, color=None, palette=None) - def test_overlap(self): - - p = cat._SwarmPlotter(**self.default_kws) - nt.assert_false(p.overlap((0, 0), (1, 1), np.sqrt(1.999))) - nt.assert_true(p.overlap((0, 0), (1, 1), np.sqrt(2.001))) - def test_could_overlap(self): p = cat._SwarmPlotter(**self.default_kws) neighbors = p.could_overlap((1, 1), [(0, 0), (1, .5), (.5, .5)], 1) - nt.assert_equal(neighbors, [(1, .5), (.5, .5)]) + npt.assert_array_equal(neighbors, [(1, .5), (.5, .5)]) def test_position_candidates(self): @@ -1663,7 +1657,7 @@ def test_find_first_non_overlapping_candidate(self): p = cat._SwarmPlotter(**self.default_kws) candidates = [(.5, 1), (1, 1), (1.5, 1)] - neighbors = [(0, 1)] + neighbors = np.array([(0, 1)]) first = p.first_non_overlapping_candidate(candidates, neighbors, 1) npt.assert_array_equal(first, (1, 1)) From ed06906caadaf8561e0a629bb8622e2dd150cac8 Mon Sep 17 00:00:00 2001 From: Saulius Lukauskas Date: Thu, 2 Mar 2017 16:55:30 +0000 Subject: [PATCH 0337/1738] Prefiltering neighbours before distance computation --- seaborn/categorical.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index e6c486bd26..3c8a13d851 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1237,16 +1237,15 @@ def first_non_overlapping_candidate(self, candidates, neighbors, d): if len(neighbors) == 0: return candidates[0] - neighbors_x = neighbors[:, 0] - neighbors_y = neighbors[:, 1] - d_square = d ** 2 for xy_i in candidates: x_i, y_i = xy_i - dx = neighbors_x - x_i - dy = neighbors_y - y_i + close_neighbors = neighbors[np.abs(neighbors[:, 0] - x_i) < d] + + dx = close_neighbors[:, 0] - x_i + dy = close_neighbors[:, 1] - y_i sq_distances = np.power(dx, 2.0) + np.power(dy, 2.0) From 684725e43186f6de5ee5b17f26b3382c98bb6aab Mon Sep 17 00:00:00 2001 From: Saulius Lukauskas Date: Thu, 2 Mar 2017 17:27:58 +0000 Subject: [PATCH 0338/1738] Trying out sorting neighbors and using binary search --- seaborn/categorical.py | 22 +++++++++++++++++++--- seaborn/tests/test_categorical.py | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 3c8a13d851..53102f08ea 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1202,6 +1202,8 @@ def could_overlap(self, xy_i, swarm, d): """Return a list of all swarm points that could overlap with target. Assumes that swarm is a sorted list of all points below xy_i. + + Returns a sorted list of neigbors """ _, y_i = xy_i neighbors = [] @@ -1211,7 +1213,13 @@ def could_overlap(self, xy_i, swarm, d): neighbors.append(xy_j) else: break - return np.array(list(reversed(neighbors))) + + neighbors = np.array(neighbors) + if len(neighbors) == 0: + return neighbors + + neighbors = neighbors[np.argsort(neighbors[:, 0])] + return neighbors def position_candidates(self, xy_i, neighbors, d): """Return a list of (x, y) coordinates that might be valid.""" @@ -1231,18 +1239,26 @@ def position_candidates(self, xy_i, neighbors, d): return np.array(candidates) def first_non_overlapping_candidate(self, candidates, neighbors, d): - """Remove candidates from the list if they overlap with the swarm.""" + """ + Remove candidates from the list if they overlap with the swarm. + Assumes neighbors is sorted ascending by x coordinate + """ # IF we have no neighbours, all candidates are good. if len(neighbors) == 0: return candidates[0] d_square = d ** 2 + neighbors_x = neighbors[:, 0] for xy_i in candidates: x_i, y_i = xy_i - close_neighbors = neighbors[np.abs(neighbors[:, 0] - x_i) < d] + # Find all neighbors that are within one diameter from the target + # exploit the sorted structure of neighbors array for efficiency + left = np.searchsorted(neighbors_x, x_i-d, side='left') + right = np.searchsorted(neighbors_x, x_i+d, side='right') + close_neighbors = neighbors[left:right] dx = close_neighbors[:, 0] - x_i dy = close_neighbors[:, 1] - y_i diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 33ac88979a..5a8f2efcb0 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1640,7 +1640,7 @@ def test_could_overlap(self): p = cat._SwarmPlotter(**self.default_kws) neighbors = p.could_overlap((1, 1), [(0, 0), (1, .5), (.5, .5)], 1) - npt.assert_array_equal(neighbors, [(1, .5), (.5, .5)]) + npt.assert_array_equal(neighbors, [(.5, .5), (1, .5)]) def test_position_candidates(self): From d51333f543cd453037a7fca3ea83be38c9a9fed0 Mon Sep 17 00:00:00 2001 From: Saulius Lukauskas Date: Thu, 2 Mar 2017 17:30:34 +0000 Subject: [PATCH 0339/1738] Revert "Trying out sorting neighbors and using binary search" This reverts commit 684725e43186f6de5ee5b17f26b3382c98bb6aab. --- seaborn/categorical.py | 22 +++------------------- seaborn/tests/test_categorical.py | 2 +- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 53102f08ea..3c8a13d851 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1202,8 +1202,6 @@ def could_overlap(self, xy_i, swarm, d): """Return a list of all swarm points that could overlap with target. Assumes that swarm is a sorted list of all points below xy_i. - - Returns a sorted list of neigbors """ _, y_i = xy_i neighbors = [] @@ -1213,13 +1211,7 @@ def could_overlap(self, xy_i, swarm, d): neighbors.append(xy_j) else: break - - neighbors = np.array(neighbors) - if len(neighbors) == 0: - return neighbors - - neighbors = neighbors[np.argsort(neighbors[:, 0])] - return neighbors + return np.array(list(reversed(neighbors))) def position_candidates(self, xy_i, neighbors, d): """Return a list of (x, y) coordinates that might be valid.""" @@ -1239,26 +1231,18 @@ def position_candidates(self, xy_i, neighbors, d): return np.array(candidates) def first_non_overlapping_candidate(self, candidates, neighbors, d): - """ - Remove candidates from the list if they overlap with the swarm. - Assumes neighbors is sorted ascending by x coordinate - """ + """Remove candidates from the list if they overlap with the swarm.""" # IF we have no neighbours, all candidates are good. if len(neighbors) == 0: return candidates[0] d_square = d ** 2 - neighbors_x = neighbors[:, 0] for xy_i in candidates: x_i, y_i = xy_i - # Find all neighbors that are within one diameter from the target - # exploit the sorted structure of neighbors array for efficiency - left = np.searchsorted(neighbors_x, x_i-d, side='left') - right = np.searchsorted(neighbors_x, x_i+d, side='right') - close_neighbors = neighbors[left:right] + close_neighbors = neighbors[np.abs(neighbors[:, 0] - x_i) < d] dx = close_neighbors[:, 0] - x_i dy = close_neighbors[:, 1] - y_i diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 5a8f2efcb0..33ac88979a 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1640,7 +1640,7 @@ def test_could_overlap(self): p = cat._SwarmPlotter(**self.default_kws) neighbors = p.could_overlap((1, 1), [(0, 0), (1, .5), (.5, .5)], 1) - npt.assert_array_equal(neighbors, [(.5, .5), (1, .5)]) + npt.assert_array_equal(neighbors, [(1, .5), (.5, .5)]) def test_position_candidates(self): From 41afc3fa952e2fd06e3d5b9ceb71e04294bad5a2 Mon Sep 17 00:00:00 2001 From: Saulius Lukauskas Date: Thu, 2 Mar 2017 17:30:43 +0000 Subject: [PATCH 0340/1738] Revert "Prefiltering neighbours before distance computation" This reverts commit ed06906caadaf8561e0a629bb8622e2dd150cac8. --- seaborn/categorical.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 3c8a13d851..e6c486bd26 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1237,15 +1237,16 @@ def first_non_overlapping_candidate(self, candidates, neighbors, d): if len(neighbors) == 0: return candidates[0] + neighbors_x = neighbors[:, 0] + neighbors_y = neighbors[:, 1] + d_square = d ** 2 for xy_i in candidates: x_i, y_i = xy_i - close_neighbors = neighbors[np.abs(neighbors[:, 0] - x_i) < d] - - dx = close_neighbors[:, 0] - x_i - dy = close_neighbors[:, 1] - y_i + dx = neighbors_x - x_i + dy = neighbors_y - y_i sq_distances = np.power(dx, 2.0) + np.power(dy, 2.0) From f5a6fb362fccddaad35f7ba5f6e74e875188e312 Mon Sep 17 00:00:00 2001 From: Saulius Lukauskas Date: Thu, 2 Mar 2017 18:23:34 +0000 Subject: [PATCH 0341/1738] Shorter lines to make lint happy --- seaborn/categorical.py | 12 ++++++++---- seaborn/tests/test_categorical.py | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index e6c486bd26..e202a9c47b 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1252,14 +1252,17 @@ def first_non_overlapping_candidate(self, candidates, neighbors, d): # good candidate does not overlap any of neighbors # which means that squared distance between candidate - # and any of the neighbours has to be at least square of the diameter + # and any of the neighbours has to be at least + # square of the diameter good_candidate = np.all(sq_distances >= d_square) if good_candidate: return xy_i - # If `position_candidates` works well this should never happen - raise Exception('No non-overlapping candidates found. This should not happen.') + # If `position_candidates` works well + # this should never happen + raise Exception('No non-overlapping candidates found. ' + 'This should not happen.') def beeswarm(self, orig_xy, d): """Adjust x position of points to avoid overlaps.""" @@ -1286,7 +1289,8 @@ def beeswarm(self, orig_xy, d): candidates = candidates[np.argsort(offsets)] # Find the first candidate that doesn't overlap any neighbours - new_xy_i = self.first_non_overlapping_candidate(candidates, neighbors, d) + new_xy_i = self.first_non_overlapping_candidate(candidates, + neighbors, d) # Place it into the swarm swarm.append(new_xy_i) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 33ac88979a..7442bc2bd4 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1651,7 +1651,8 @@ def test_position_candidates(self): dx1 = 1.05 dx2 = np.sqrt(1 - .5 ** 2) * 1.05 npt.assert_array_equal(candidates, - [(0, 1), (-dx1, 1), (dx1, 1), (dx2, 1), (-dx2, 1)]) + [(0, 1), (-dx1, 1), (dx1, 1), + (dx2, 1), (-dx2, 1)]) def test_find_first_non_overlapping_candidate(self): From 5b92fd0a15465f1f38f8e5d0fcd47ff3654dd38b Mon Sep 17 00:00:00 2001 From: Ken Date: Tue, 7 Mar 2017 14:42:47 -0500 Subject: [PATCH 0342/1738] Fix typo --- doc/tutorial/color_palettes.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index 8f6cacbe89..e2430ad084 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -629,7 +629,7 @@ "Custom diverging palettes with :func:`diverging_palette`\n", "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", "\n", - "You can also use the seaborn function :func:`diverging_palette` to create a custom colormap for diverging data. (Naturally there is also a companion interactive widget, :func:`choose_diverging_palette`). This function makes diverging palettes using the ``husl`` color system. You pass it two hues (in degreees) and, optionally, the lightness and saturation values for the extremes. Using ``husl`` means that the extreme values, and the resulting ramps to the midpoint, will be well-balanced" + "You can also use the seaborn function :func:`diverging_palette` to create a custom colormap for diverging data. (Naturally there is also a companion interactive widget, :func:`choose_diverging_palette`). This function makes diverging palettes using the ``husl`` color system. You pass it two hues (in degrees) and, optionally, the lightness and saturation values for the extremes. Using ``husl`` means that the extreme values, and the resulting ramps to the midpoint, will be well-balanced" ] }, { @@ -778,4 +778,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} From ebb7e772e09c6536fb7e6bb2c88798d1e69e529f Mon Sep 17 00:00:00 2001 From: Sandip Chatterjee Date: Fri, 24 Mar 2017 18:02:33 -0700 Subject: [PATCH 0343/1738] Fix typos in regression tutorial docs --- doc/tutorial/regression.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/tutorial/regression.ipynb b/doc/tutorial/regression.ipynb index 5eeb278e89..24c5ac3a2e 100644 --- a/doc/tutorial/regression.ipynb +++ b/doc/tutorial/regression.ipynb @@ -390,7 +390,7 @@ "Conditioning on other variables\n", "-------------------------------\n", "\n", - "The plots above show many ways to explore the relationship between a pair of variables. Often, however, a more interesting question is \"how does the relationship between these two variables change as a function of a third variable?\" This is where the difference between :func:`regplot` and :func:`lmplot` appears. While :func:`regplot` always shows a single relationsihp, :func:`lmplot` combines :func:`regplot` with :class:`FacetGrid` to provide an easy interface to show a linear regression on \"faceted\" plots that allow you to explore interactions with up to three additional categorical variables.\n", + "The plots above show many ways to explore the relationship between a pair of variables. Often, however, a more interesting question is \"how does the relationship between these two variables change as a function of a third variable?\" This is where the difference between :func:`regplot` and :func:`lmplot` appears. While :func:`regplot` always shows a single relationship, :func:`lmplot` combines :func:`regplot` with :class:`FacetGrid` to provide an easy interface to show a linear regression on \"faceted\" plots that allow you to explore interactions with up to three additional categorical variables.\n", "\n", "The best way to separate out a relationship is to plot both levels on the same axes and to use color to distinguish them:" ] @@ -462,7 +462,7 @@ "Controlling the size and shape of the plot\n", "------------------------------------------\n", "\n", - "Before we noted that the default plots made by :func:`regplot` and :func:`lmplot` look the same but on axes that have a different size and shape. This is because func:`regplot` is an \"axes-level\" function draws onto a specific axes. This means that you can make mutli-panel figures yourself and control exactly where the the regression plot goes. If no axes is provided, it simply uses the \"currently active\" axes, which is why the default plot has the same size and shape as most other matplotlib functions. To control the size, you need to create a figure object yourself." + "Before we noted that the default plots made by :func:`regplot` and :func:`lmplot` look the same but on axes that have a different size and shape. This is because :func:`regplot` is an \"axes-level\" function draws onto a specific axes. This means that you can make multi-panel figures yourself and control exactly where the regression plot goes. If no axes object is explictly provided, it simply uses the \"currently active\" axes, which is why the default plot has the same size and shape as most other matplotlib functions. To control the size, you need to create a figure object yourself." ] }, { @@ -590,4 +590,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} From bd3d7cb046945bb563cdfeb803e9d4e0351d1f51 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 28 Mar 2017 15:52:35 -0400 Subject: [PATCH 0344/1738] Don't fail on PerfectSeparationError in regplot logistic regression --- seaborn/linearmodels.py | 8 +++++++- seaborn/tests/test_linearmodels.py | 9 +++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index 2c906178b4..8c79b8b24f 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -246,7 +246,13 @@ def fit_statsmodels(self, grid, model, **kwargs): """More general regression function using statsmodels objects.""" X, y = np.c_[np.ones(len(self.x)), self.x], self.y grid = np.c_[np.ones(len(grid)), grid] - reg_func = lambda _x, _y: model(_y, _x, **kwargs).fit().predict(grid) + import statsmodels.genmod.generalized_linear_model as glm + def reg_func(_x, _y): + try: + yhat = model(_y, _x, **kwargs).fit().predict(grid) + except glm.PerfectSeparationError: + yhat = np.ones(len(grid)) * np.nan + return yhat yhat = reg_func(X, y) if self.ci is None: return yhat, None diff --git a/seaborn/tests/test_linearmodels.py b/seaborn/tests/test_linearmodels.py index 6e6bc40ec3..e0ec194463 100644 --- a/seaborn/tests/test_linearmodels.py +++ b/seaborn/tests/test_linearmodels.py @@ -349,6 +349,15 @@ def test_logistic_regression(self): npt.assert_array_less(yhat, 1) npt.assert_array_less(0, yhat) + @skipif(_no_statsmodels) + def test_logistic_perfect_separation(self): + + y = self.df.x > self.df.x.mean() + p = lm._RegressionPlotter("x", y, data=self.df, + logistic=True, n_boot=10) + _, yhat, _ = p.fit_regression(x_range=(-3, 3)) + nt.assert_true(np.isnan(yhat).all()) + @skipif(_no_statsmodels) def test_robust_regression(self): From a252e93935bf5f503a5d17aad57030210a75bf91 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 28 Mar 2017 16:19:35 -0400 Subject: [PATCH 0345/1738] PEP8 --- seaborn/linearmodels.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index 8c79b8b24f..9f949c00ff 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -247,12 +247,14 @@ def fit_statsmodels(self, grid, model, **kwargs): X, y = np.c_[np.ones(len(self.x)), self.x], self.y grid = np.c_[np.ones(len(grid)), grid] import statsmodels.genmod.generalized_linear_model as glm + def reg_func(_x, _y): try: yhat = model(_y, _x, **kwargs).fit().predict(grid) except glm.PerfectSeparationError: yhat = np.ones(len(grid)) * np.nan return yhat + yhat = reg_func(X, y) if self.ci is None: return yhat, None From 311b671f5f1e2b08ef1d63c72565a7ec1f4e6a16 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 30 Mar 2017 11:22:51 -0400 Subject: [PATCH 0346/1738] Increase performance of nanarray creation --- seaborn/linearmodels.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index 9f949c00ff..910d14bfa1 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -252,7 +252,8 @@ def reg_func(_x, _y): try: yhat = model(_y, _x, **kwargs).fit().predict(grid) except glm.PerfectSeparationError: - yhat = np.ones(len(grid)) * np.nan + yhat = np.empty(len(grid)) + yhat.fill(np.nan) return yhat yhat = reg_func(X, y) From 7c92a7fe7b86fc6a670db4d5df0bf27894ca0440 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 2 Apr 2017 14:00:21 -0400 Subject: [PATCH 0347/1738] Change target path for datasets to reflect github change (closes #1121) --- seaborn/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seaborn/utils.py b/seaborn/utils.py index 36ade224aa..e8e7278e41 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -420,7 +420,8 @@ def load_dataset(name, cache=True, data_home=None, **kws): Passed to pandas.read_csv """ - path = "https://github.com/mwaskom/seaborn-data/raw/master/{0}.csv" + path = ("https://raw.githubusercontent.com/" + "mwaskom/seabon-data/master/{}.csv") full_path = path.format(name) if cache: From e8c342622417dee1a966b0cab09d4275fe0e18f3 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 2 Apr 2017 14:12:13 -0400 Subject: [PATCH 0348/1738] Fix typo in dataset url --- seaborn/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/utils.py b/seaborn/utils.py index e8e7278e41..fdb2311098 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -421,7 +421,7 @@ def load_dataset(name, cache=True, data_home=None, **kws): """ path = ("https://raw.githubusercontent.com/" - "mwaskom/seabon-data/master/{}.csv") + "mwaskom/seaborn-data/master/{}.csv") full_path = path.format(name) if cache: From 179881fdb224279500eda9968fae3e5d1ed5c735 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 2 Apr 2017 15:12:16 -0400 Subject: [PATCH 0349/1738] Add Zenodo badges to release notes (closes #1127) --- doc/releases/v0.6.0.txt | 3 +++ doc/releases/v0.7.0.txt | 3 +++ doc/releases/v0.7.1.txt | 3 +++ 3 files changed, 9 insertions(+) diff --git a/doc/releases/v0.6.0.txt b/doc/releases/v0.6.0.txt index 0cfce7bec5..7bbd7ae52c 100644 --- a/doc/releases/v0.6.0.txt +++ b/doc/releases/v0.6.0.txt @@ -2,6 +2,9 @@ v0.6.0 (June 2015) ------------------ +.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.19108.svg + :target: https://doi.org/10.5281/zenodo.19108 + This is a major release from 0.5. The main objective of this release was to unify the API for categorical plots, which means that there are some relatively large API changes in some of the older functions. See below for details of those changes, which may break code written for older versions of seaborn. There are also some new functions (:func:`stripplot`, and :func:`countplot`), numerous enhancements to existing functions, and bug fixes. Additionally, the documentation has been completely revamped and expanded for the 0.6 release. Now, the API docs page for each function has multiple examples with embedded plots showing how to use the various options. These pages should be considered the most comprehensive resource for examples, and the tutorial pages are now streamlined and oriented towards a higher-level overview of the various features. diff --git a/doc/releases/v0.7.0.txt b/doc/releases/v0.7.0.txt index d809c10320..0a6abbe08c 100644 --- a/doc/releases/v0.7.0.txt +++ b/doc/releases/v0.7.0.txt @@ -2,6 +2,9 @@ v0.7.0 (January 2016) --------------------- +.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.45133.svg + :target: https://doi.org/10.5281/zenodo.45133 + This is a major release from 0.6. The main new feature is :func:`swarmplot` which implements the beeswarm approach for drawing categorical scatterplots. There are also some performance improvements, bug fixes, and updates for compatibility with new versions of dependencies. - Added the :func:`swarmplot` function, which draws beeswarm plots. These are categorical scatterplots, similar to those produced by :func:`stripplot`, but position of the points on the categorical axis is chosen to avoid overlapping points. See the :ref:`categorical plot tutorial ` for more information. diff --git a/doc/releases/v0.7.1.txt b/doc/releases/v0.7.1.txt index 3aacbf66ce..27dc99b020 100644 --- a/doc/releases/v0.7.1.txt +++ b/doc/releases/v0.7.1.txt @@ -2,6 +2,9 @@ v0.7.1 (June 2016) ------------------- +.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.54844.svg + :target: https://doi.org/10.5281/zenodo.54844 + - Added the ability to put "caps" on the error bars that are drawn by :func:`barplot` or :func:`pointplot` (and, by extension, :func:`factorplot`). Additionally, the line width of the error bars can now be controlled. These changes involve the new parameters ``capsize`` and ``errwidth``. See the `github pull request `_ for examples of usage. - Improved the row and column colors display in :func:`clustermap`. It is now possible to pass Pandas objects for these elements and, when possible, the semantic information in the Pandas objects will be used to add labels to the plot. When Pandas objects are used, the color data is matched against the main heatmap based on the index, not on position. This is more accurate, but it may lead to different results if current code assumed positional matching. From 81619abfac7b0ad3875e5fa46fcf1f48b41b779a Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 22 Apr 2017 15:37:18 -0400 Subject: [PATCH 0350/1738] Emphasize factorplot over direct usage of FacetGrid (closes #805) --- doc/api.rst | 1 + seaborn/categorical.py | 119 ++++++++++++++++++++++++++++++++--------- 2 files changed, 95 insertions(+), 25 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index ca63439d0c..ad6f562083 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -44,6 +44,7 @@ Categorical plots factorplot boxplot violinplot + lvplot stripplot swarmplot pointplot diff --git a/seaborn/categorical.py b/seaborn/categorical.py index e202a9c47b..946e6d72d4 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2345,17 +2345,18 @@ def boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.boxplot(x="day", y="total_bill", data=tips) >>> ax = sns.swarmplot(x="day", y="total_bill", data=tips, color=".25") - Draw a box plot on to a :class:`FacetGrid` to group within an additional - categorical variable: + Use :func:`factorplot` to combine a :func:`boxplot` and a + :class:`FacetGrid`. This allows grouping within additional categorical + variables. Using :func:`factorplot` is safer than using :class:`FacetGrid` + directly, as it ensures synchronization of variable order across facets: .. plot:: :context: close-figs - >>> g = sns.FacetGrid(tips, col="time", size=4, aspect=.7) - >>> (g.map(sns.boxplot, "sex", "total_bill", "smoker") - ... .despine(left=True) - ... .add_legend(title="smoker")) #doctest: +ELLIPSIS - + >>> g = sns.factorplot(x="sex", y="total_bill", + ... hue="smoker", col="time", + ... data=tips, kind="box", + ... size=4, aspect=.7); """).format(**_categorical_docs) @@ -2590,17 +2591,18 @@ def violinplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, ... data=planets[planets.orbital_period < 1000], ... scale="width", palette="Set3") - Draw a violin plot on to a :class:`FacetGrid` to group within an additional - categorical variable: + Use :func:`factorplot` to combine a :func:`violinplot` and a + :class:`FacetGrid`. This allows grouping within additional categorical + variables. Using :func:`factorplot` is safer than using :class:`FacetGrid` + directly, as it ensures synchronization of variable order across facets: .. plot:: :context: close-figs - >>> g = sns.FacetGrid(tips, col="time", size=4, aspect=.7) - >>> (g.map(sns.violinplot, "sex", "total_bill", "smoker", split=True) - ... .despine(left=True) - ... .add_legend(title="smoker")) # doctest: +ELLIPSIS - + >>> g = sns.factorplot(x="sex", y="total_bill", + ... hue="smoker", col="time", + ... data=tips, kind="violin", split=True, + ... size=4, aspect=.7); """).format(**_categorical_docs) @@ -2780,6 +2782,20 @@ def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, ... inner=None, color=".8") >>> ax = sns.stripplot(x="day", y="total_bill", data=tips, jitter=True) + Use :func:`factorplot` to combine a :func:`stripplot` and a + :class:`FacetGrid`. This allows grouping within additional categorical + variables. Using :func:`factorplot` is safer than using :class:`FacetGrid` + directly, as it ensures synchronization of variable order across facets: + + .. plot:: + :context: close-figs + + >>> g = sns.factorplot(x="sex", y="total_bill", + ... hue="smoker", col="time", + ... data=tips, kind="strip", + ... jitter=True, + ... size=4, aspect=.7); + """).format(**_categorical_docs) @@ -2919,14 +2935,13 @@ def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.swarmplot(x="time", y="tip", data=tips, size=6) - Draw swarms of observations on top of a box plot: .. plot:: :context: close-figs >>> ax = sns.boxplot(x="tip", y="day", data=tips, whis=np.inf) - >>> ax = sns.swarmplot(x="tip", y="day", data=tips) + >>> ax = sns.swarmplot(x="tip", y="day", data=tips, color=".2") Draw swarms of observations on top of a violin plot: @@ -2937,6 +2952,19 @@ def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.swarmplot(x="day", y="total_bill", data=tips, ... color="white", edgecolor="gray") + Use :func:`factorplot` to combine a :func:`swarmplot` and a + :class:`FacetGrid`. This allows grouping within additional categorical + variables. Using :func:`factorplot` is safer than using :class:`FacetGrid` + directly, as it ensures synchronization of variable order across facets: + + .. plot:: + :context: close-figs + + >>> g = sns.factorplot(x="sex", y="total_bill", + ... hue="smoker", col="time", + ... data=tips, kind="swarm", + ... size=4, aspect=.7); + """).format(**_categorical_docs) @@ -3115,6 +3143,19 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, ... linewidth=2.5, facecolor=(1, 1, 1, 0), ... errcolor=".2", edgecolor=".2") + Use :func:`factorplot` to combine a :func:`barplot` and a + :class:`FacetGrid`. This allows grouping within additional categorical + variables. Using :func:`factorplot` is safer than using :class:`FacetGrid` + directly, as it ensures synchronization of variable order across facets: + + .. plot:: + :context: close-figs + + >>> g = sns.factorplot(x="sex", y="total_bill", + ... hue="smoker", col="time", + ... data=tips, kind="bar", + ... size=4, aspect=.7); + """).format(**_categorical_docs) @@ -3305,6 +3346,20 @@ def pointplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.pointplot(x="day", y="tip", data=tips, capsize=.2) + Use :func:`factorplot` to combine a :func:`barplot` and a + :class:`FacetGrid`. This allows grouping within additional categorical + variables. Using :func:`factorplot` is safer than using :class:`FacetGrid` + directly, as it ensures synchronization of variable order across facets: + + .. plot:: + :context: close-figs + + >>> g = sns.factorplot(x="sex", y="total_bill", + ... hue="smoker", col="time", + ... data=tips, kind="point", + ... dodge=True, + ... size=4, aspect=.7); + """).format(**_categorical_docs) @@ -3421,6 +3476,19 @@ def countplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, ... linewidth=5, ... edgecolor=sns.color_palette("dark", 3)) + Use :func:`factorplot` to combine a :func:`countplot` and a + :class:`FacetGrid`. This allows grouping within additional categorical + variables. Using :func:`factorplot` is safer than using :class:`FacetGrid` + directly, as it ensures synchronization of variable order across facets: + + .. plot:: + :context: close-figs + + >>> g = sns.factorplot(x="sex", y="total_bill", + ... hue="smoker", col="time", + ... data=tips, kind="count", + ... size=4, aspect=.7); + """).format(**_categorical_docs) @@ -3682,7 +3750,7 @@ def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, return ax lvplot.__doc__ = dedent("""\ - Create a letter value plot + Draw a letter value plot to show distributions of large datasets. Letter value (LV) plots are non-parametric estimates of the distribution of a dataset, similar to boxplots. LV plots are also similar to violin plots @@ -3795,18 +3863,19 @@ def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.lvplot(x="day", y="total_bill", data=tips) >>> ax = sns.stripplot(x="day", y="total_bill", data=tips, - ... size=4, jitter=True, edgecolor="gray") + ... size=4, jitter=True, color="gray") - Draw a letter value plot on to a :class:`FacetGrid` to group within an - additional categorical variable: + Use :func:`factorplot` to combine a :func:`lvplot` and a + :class:`FacetGrid`. This allows grouping within additional categorical + variables. Using :func:`factorplot` is safer than using :class:`FacetGrid` + directly, as it ensures synchronization of variable order across facets: .. plot:: :context: close-figs - >>> g = sns.FacetGrid(tips, col="time", size=4, aspect=.7) - >>> (g.map(sns.lvplot, "sex", "total_bill", "smoker") - ... .despine(left=True) - ... .add_legend(title="smoker")) #doctest: +ELLIPSIS - + >>> g = sns.factorplot(x="sex", y="total_bill", + ... hue="smoker", col="time", + ... data=tips, kind="lv", + ... size=4, aspect=.7); """).format(**_categorical_docs) From 46e948bffcceff7fac72e22acf15fbf1f774a43d Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 22 Apr 2017 15:43:02 -0400 Subject: [PATCH 0351/1738] Add errwidth and capsize to pointplot docstring (closes #1058) --- seaborn/categorical.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 946e6d72d4..d91cb642a8 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -3162,8 +3162,8 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def pointplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, estimator=np.mean, ci=95, n_boot=1000, units=None, markers="o", linestyles="-", dodge=False, join=True, scale=1, - orient=None, color=None, palette=None, ax=None, errwidth=None, - capsize=None, **kwargs): + orient=None, color=None, palette=None, errwidth=None, + capsize=None, ax=None, **kwargs): # Handle some deprecated arguments if "hline" in kwargs: @@ -3236,6 +3236,8 @@ def pointplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, {orient} {color} {palette} + {errwidth} + {capsize} {ax_in} Returns From f1854bb3c298ff89bdd4d46c7eca49b9561e93eb Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 22 Apr 2017 16:08:02 -0400 Subject: [PATCH 0352/1738] Fix countplot example --- seaborn/categorical.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index d91cb642a8..210be8cb5a 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -3486,9 +3486,8 @@ def countplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, .. plot:: :context: close-figs - >>> g = sns.factorplot(x="sex", y="total_bill", - ... hue="smoker", col="time", - ... data=tips, kind="count", + >>> g = sns.factorplot(x="class", hue="who", col="survived", + ... data=titanic, kind="count", ... size=4, aspect=.7); """).format(**_categorical_docs) From c3fe5096d7de6d67552a9add2a1862f9a2ee7589 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 22 Apr 2017 16:08:10 -0400 Subject: [PATCH 0353/1738] Fix example script parsing (closes #1149) --- doc/sphinxext/plot_generator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/sphinxext/plot_generator.py b/doc/sphinxext/plot_generator.py index 4de49d6f36..78abddb46c 100644 --- a/doc/sphinxext/plot_generator.py +++ b/doc/sphinxext/plot_generator.py @@ -242,7 +242,8 @@ def extract_docstring(self): docstring = '' first_par = '' - tokens = tokenize.generate_tokens(lambda: next(lines.__iter__())) + line_iter = lines.__iter__() + tokens = tokenize.generate_tokens(lambda: next(line_iter)) for tok_type, tok_content, _, (erow, _), _ in tokens: tok_type = token.tok_name[tok_type] if tok_type in ('NEWLINE', 'COMMENT', 'NL', 'INDENT', 'DEDENT'): From 20ae4ab6913ed30c16a0b622b420051176cc7212 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 22 Apr 2017 19:11:27 -0400 Subject: [PATCH 0354/1738] Add information about dodge in categorical tutorial --- doc/tutorial/categorical.ipynb | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/doc/tutorial/categorical.ipynb b/doc/tutorial/categorical.ipynb index 82bb9e652c..4064b46992 100644 --- a/doc/tutorial/categorical.ipynb +++ b/doc/tutorial/categorical.ipynb @@ -227,6 +227,25 @@ "sns.boxplot(x=\"day\", y=\"total_bill\", hue=\"time\", data=tips);" ] }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "For boxplots, the assumption when using a ``hue`` variable is that it is nested within the ``x`` or ``y`` variable. This means that by default, the boxes for different levels of ``hue`` will be offset, as you can see above. If your ``hue`` variable is not nested, you can set the ``dodge`` parameter to disable offsetting:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "tips[\"weekend\"] = tips[\"day\"].isin([\"Sat\", \"Sun\"])\n", + "sns.boxplot(x=\"day\", y=\"total_bill\", hue=\"weekend\", data=tips, dodge=False);" + ] + }, { "cell_type": "raw", "metadata": {}, @@ -613,9 +632,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", - "version": "2.7.11" + "version": "2.7.12" } }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} From 2812e77d9b688e8e3c85e4371e85faa7353c1fcf Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 22 Apr 2017 19:29:58 -0400 Subject: [PATCH 0355/1738] Add optional colorbar for bivariate kdeplot --- seaborn/distributions.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 0d9180b355..a38ced5062 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -353,7 +353,7 @@ def _scipy_univariate_kde(data, bw, gridsize, cut, clip): def _bivariate_kdeplot(x, y, filled, fill_lowest, kernel, bw, gridsize, cut, clip, - axlabel, ax, **kwargs): + axlabel, cbar, cbar_ax, cbar_kws, ax, **kwargs): """Plot a joint KDE estimate as a bivariate contour plot.""" # Determine the clipping if clip is None: @@ -385,6 +385,10 @@ def _bivariate_kdeplot(x, y, filled, fill_lowest, cset.collections[0].set_alpha(0) kwargs["n_levels"] = n_levels + if cbar: + cbar_kws = {} if cbar_kws is None else cbar_kws + ax.figure.colorbar(cset, cbar_ax, ax, **cbar_kws) + # Label the axes if hasattr(x, "name") and axlabel: ax.set_xlabel(x.name) @@ -441,7 +445,8 @@ def _scipy_bivariate_kde(x, y, bw, gridsize, cut, clip): def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", bw="scott", gridsize=100, cut=3, clip=None, legend=True, - cumulative=False, shade_lowest=True, ax=None, **kwargs): + cumulative=False, shade_lowest=True, cbar=False, cbar_ax=None, + cbar_kws=None, ax=None, **kwargs): """Fit and plot a univariate or bivariate kernel density estimate. Parameters @@ -557,6 +562,13 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", >>> ax = sns.kdeplot(x, cut=0) + Add a colorbar for the contours: + + .. plot:: + :context: close-figs + + >>> ax = sns.kdeplot(x, y, cbar=True) + Plot two shaded bivariate densities: .. plot:: @@ -597,7 +609,7 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", if bivariate: ax = _bivariate_kdeplot(x, y, shade, shade_lowest, kernel, bw, gridsize, cut, clip, legend, - ax, **kwargs) + cbar, cbar_ax, cbar_kws, ax, **kwargs) else: ax = _univariate_kdeplot(data, shade, vertical, kernel, bw, gridsize, cut, clip, legend, ax, From b8f5c50dd85337b7083fd24e2fbab67ef5002dc2 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 22 Apr 2017 19:33:01 -0400 Subject: [PATCH 0356/1738] Add test for kde colorbar --- seaborn/tests/test_distributions.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/seaborn/tests/test_distributions.py b/seaborn/tests/test_distributions.py index 042b934ebe..9c97a807f2 100644 --- a/seaborn/tests/test_distributions.py +++ b/seaborn/tests/test_distributions.py @@ -110,6 +110,15 @@ def test_bivariate_kde_series(self): nt.assert_equal(ax_series.collections[0].get_paths(), ax_values.collections[0].get_paths()) + def test_bivariate_kde_colorbar(self): + + f, ax = plt.subplots() + dist.kdeplot(self.x, self.y, + cbar=True, cbar_kws=dict(label="density"), + ax=ax) + nt.assert_equal(len(f.axes), 2) + nt.assert_equal(f.axes[1].get_ylabel(), "density") + class TestJointPlot(PlotTestCase): From 36c845f616df8e7deb96e5847f872de31e55a7ff Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 22 Apr 2017 19:48:42 -0400 Subject: [PATCH 0357/1738] Skip test on old matplotlib --- seaborn/tests/test_distributions.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/seaborn/tests/test_distributions.py b/seaborn/tests/test_distributions.py index 9c97a807f2..49066e17a0 100644 --- a/seaborn/tests/test_distributions.py +++ b/seaborn/tests/test_distributions.py @@ -3,6 +3,7 @@ import matplotlib as mpl import matplotlib.pyplot as plt +from distutils.version import LooseVersion import nose.tools as nt import numpy.testing as npt from numpy.testing.decorators import skipif @@ -18,6 +19,9 @@ _no_statsmodels = True +_old_matplotlib = LooseVersion(mpl.__version__) < "1.5" + + class TestKDE(PlotTestCase): rs = np.random.RandomState(0) @@ -110,6 +114,7 @@ def test_bivariate_kde_series(self): nt.assert_equal(ax_series.collections[0].get_paths(), ax_values.collections[0].get_paths()) + @skipif(_old_matplotlib) def test_bivariate_kde_colorbar(self): f, ax = plt.subplots() From 61dfa0b5606938c3526fbbb463c2d63b3fcabc6a Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 22 Apr 2017 20:12:43 -0400 Subject: [PATCH 0358/1738] Update release notes --- doc/releases/v0.8.0.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index eb604464d8..2cedf3dd0d 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -4,6 +4,8 @@ v0.8.0 (Unreleased) - Added the ``dodge`` argument to :func:`boxplot`, :func:`violinplot`, and :func:`barplot` to allow use of ``hue`` without changing the position or width of the plot elements, as when the ``hue`` varible is not nested within the main categorical variable. +- Added the ability to draw a colorbar for a bivariate :func:`kdeplot` with the ``cbar`` parameter (and related ``cbar_ax`` and ``cbar_kws`` parameters). + - Allow side-specific offsets in :func:`despine`. - Put a cap on the number of bins used in :func:`jointplot` for ``type=="hex"`` to avoid hanging when the reference rule prescribes too many. From 4d89197e94dd65d0ab80f77fc380ca3307950ba7 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Mon, 24 Apr 2017 06:56:57 -0500 Subject: [PATCH 0359/1738] COMPAT: Pandas 0.20 compat Pandas is now using pytest for its test runner. The only affect that has here is through the `@network` decorator seaborn was reusing. It's possible that the network tests are no longer skipped (not sure if / how they were being skipped before). Getting the skip to work again is a bit more effort. The other major change is around pandas module privacy. The `pandas.util.testing` module is now private. Common methods like `assert_frame_equal` are now available in the public `pandas.testing` module. Seaaborn is also using a few now-private methods from pandas.core. I'll followup with fixes for those later (shouldn't see any deprecation warnings in the meantime). --- devel_requirements_py2.txt | 1 + devel_requirements_py3.txt | 1 + seaborn/tests/test_axisgrid.py | 5 ++++- seaborn/tests/test_linearmodels.py | 5 ++++- seaborn/tests/test_matrix.py | 5 ++++- seaborn/tests/test_utils.py | 5 ++++- testing/deps_minimal_2.7.txt | 1 + testing/deps_modern_2.7.txt | 1 + testing/deps_modern_3.4.txt | 1 + testing/deps_modern_3.5.txt | 1 + 10 files changed, 22 insertions(+), 4 deletions(-) diff --git a/devel_requirements_py2.txt b/devel_requirements_py2.txt index c86433fdce..9e727e953f 100644 --- a/devel_requirements_py2.txt +++ b/devel_requirements_py2.txt @@ -4,3 +4,4 @@ sphinx_bootstrap_theme numpydoc PIL nose +pytest diff --git a/devel_requirements_py3.txt b/devel_requirements_py3.txt index 3cdd2bb287..9d70e49753 100644 --- a/devel_requirements_py3.txt +++ b/devel_requirements_py3.txt @@ -4,3 +4,4 @@ sphinx_bootstrap_theme numpydoc pillow nose +pytest diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 00a162d6e4..71b935f89e 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -10,7 +10,10 @@ import nose.tools as nt import numpy.testing as npt from numpy.testing.decorators import skipif -import pandas.util.testing as tm +try: + import pandas.testing as tm +except ImportError: + import pandas.util.testing as tm from . import PlotTestCase from .. import axisgrid as ag diff --git a/seaborn/tests/test_linearmodels.py b/seaborn/tests/test_linearmodels.py index e0ec194463..9d6b0ee57e 100644 --- a/seaborn/tests/test_linearmodels.py +++ b/seaborn/tests/test_linearmodels.py @@ -5,7 +5,10 @@ import nose.tools as nt import numpy.testing as npt -import pandas.util.testing as pdt +try: + import pandas.testing as pdt +except ImportError: + import pandas.util.testing as pdt from numpy.testing.decorators import skipif from nose import SkipTest diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 140f652aa7..831e1b91f9 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -10,7 +10,10 @@ import nose.tools as nt import numpy.testing as npt -import pandas.util.testing as pdt +try: + import pandas.testing as pdt +except ImportError: + import pandas.util.testing as pdt from numpy.testing.decorators import skipif from . import PlotTestCase diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index 4c2fb7ca4a..f92adecc53 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -11,7 +11,10 @@ import nose.tools as nt from nose.tools import assert_equal, raises import numpy.testing as npt -import pandas.util.testing as pdt +try: + import pandas.testing as pdt +except ImportError: + import pandas.util.testing as pdt from distutils.version import LooseVersion pandas_has_categoricals = LooseVersion(pd.__version__) >= "0.15" diff --git a/testing/deps_minimal_2.7.txt b/testing/deps_minimal_2.7.txt index 3b6c70cc3e..3ccac79ad4 100644 --- a/testing/deps_minimal_2.7.txt +++ b/testing/deps_minimal_2.7.txt @@ -1,5 +1,6 @@ ipython-notebook nose +pytest numpy=1.6.2 scipy=0.11.0 matplotlib=1.1.1 diff --git a/testing/deps_modern_2.7.txt b/testing/deps_modern_2.7.txt index 500bb9fbd1..f48432a5ae 100644 --- a/testing/deps_modern_2.7.txt +++ b/testing/deps_modern_2.7.txt @@ -1,5 +1,6 @@ ipython-notebook nose +pytest numpy scipy matplotlib diff --git a/testing/deps_modern_3.4.txt b/testing/deps_modern_3.4.txt index a63b15f21e..34889c9c53 100644 --- a/testing/deps_modern_3.4.txt +++ b/testing/deps_modern_3.4.txt @@ -1,5 +1,6 @@ ipython-notebook nose +pytest numpy scipy matplotlib diff --git a/testing/deps_modern_3.5.txt b/testing/deps_modern_3.5.txt index 500bb9fbd1..f48432a5ae 100644 --- a/testing/deps_modern_3.5.txt +++ b/testing/deps_modern_3.5.txt @@ -1,5 +1,6 @@ ipython-notebook nose +pytest numpy scipy matplotlib From 88ad75aea85a7c4646216007231eb41d95e62344 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Mon, 1 May 2017 13:55:29 -0500 Subject: [PATCH 0360/1738] Remove pytest dependency for CI Added a stripped-down verison of pandas network decorator. --- devel_requirements_py2.txt | 1 - devel_requirements_py3.txt | 1 - seaborn/tests/test_utils.py | 9 ++++----- seaborn/utils.py | 30 ++++++++++++++++++++++++++++++ testing/deps_minimal_2.7.txt | 1 - testing/deps_modern_2.7.txt | 1 - testing/deps_modern_3.4.txt | 1 - testing/deps_modern_3.5.txt | 1 - 8 files changed, 34 insertions(+), 11 deletions(-) diff --git a/devel_requirements_py2.txt b/devel_requirements_py2.txt index 9e727e953f..c86433fdce 100644 --- a/devel_requirements_py2.txt +++ b/devel_requirements_py2.txt @@ -4,4 +4,3 @@ sphinx_bootstrap_theme numpydoc PIL nose -pytest diff --git a/devel_requirements_py3.txt b/devel_requirements_py3.txt index 9d70e49753..3cdd2bb287 100644 --- a/devel_requirements_py3.txt +++ b/devel_requirements_py3.txt @@ -4,4 +4,3 @@ sphinx_bootstrap_theme numpydoc pillow nose -pytest diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index f92adecc53..f94d2c0015 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -19,7 +19,6 @@ from distutils.version import LooseVersion pandas_has_categoricals = LooseVersion(pd.__version__) >= "0.15" -from pandas.util.testing import network try: from bs4 import BeautifulSoup @@ -28,7 +27,7 @@ from . import PlotTestCase from .. import utils, rcmod -from ..utils import get_dataset_names, load_dataset +from ..utils import get_dataset_names, load_dataset, _network a_norm = np.random.randn(100) @@ -362,7 +361,7 @@ def check_load_cached_dataset(name): finally: shutil.rmtree(tmpdir) - @network(url="https://github.com/mwaskom/seaborn-data") + @_network(url="https://github.com/mwaskom/seaborn-data") def test_get_dataset_names(): if not BeautifulSoup: raise nose.SkipTest("No BeautifulSoup available for parsing html") @@ -370,7 +369,7 @@ def test_get_dataset_names(): assert(len(names) > 0) assert(u"titanic" in names) - @network(url="https://github.com/mwaskom/seaborn-data") + @_network(url="https://github.com/mwaskom/seaborn-data") def test_load_datasets(): if not BeautifulSoup: raise nose.SkipTest("No BeautifulSoup available for parsing html") @@ -382,7 +381,7 @@ def test_load_datasets(): # yield check_load_dataset, name check_load_dataset(name) - @network(url="https://github.com/mwaskom/seaborn-data") + @_network(url="https://github.com/mwaskom/seaborn-data") def test_load_cached_datasets(): if not BeautifulSoup: raise nose.SkipTest("No BeautifulSoup available for parsing html") diff --git a/seaborn/utils.py b/seaborn/utils.py index fdb2311098..c32860b693 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -3,6 +3,7 @@ import colorsys import warnings import os +from functools import partial import numpy as np from scipy import stats @@ -15,6 +16,8 @@ pandas_has_categoricals = LooseVersion(pd.__version__) >= "0.15" mpl_ge_150 = LooseVersion(mpl.__version__) >= "1.5.0" from .external.six.moves.urllib.request import urlopen, urlretrieve +from .external.six.moves.http_client import HTTPException +from .external.six import wraps __all__ = ["desaturate", "saturate", "set_hls_values", @@ -624,3 +627,30 @@ def to_utf8(obj): return obj.decode("utf-8") else: return obj.__str__() + + +def _network(t=None, url='http://google.com'): + """ + Decorator that will skip a test if `url` is unreachable. + + Parameters + ---------- + t : function, optional + url : str, optional + """ + import nose + + if t is None: + return partial(_network, url=url) + + @wraps(t) + def wrapper(*args, **kwargs): + # attempt to connect + try: + with urlopen(url): + pass + except (IOError, HTTPException): + raise nose.SkipTest() + else: + return t(*args, **kwargs) + return wrapper diff --git a/testing/deps_minimal_2.7.txt b/testing/deps_minimal_2.7.txt index 3ccac79ad4..3b6c70cc3e 100644 --- a/testing/deps_minimal_2.7.txt +++ b/testing/deps_minimal_2.7.txt @@ -1,6 +1,5 @@ ipython-notebook nose -pytest numpy=1.6.2 scipy=0.11.0 matplotlib=1.1.1 diff --git a/testing/deps_modern_2.7.txt b/testing/deps_modern_2.7.txt index f48432a5ae..500bb9fbd1 100644 --- a/testing/deps_modern_2.7.txt +++ b/testing/deps_modern_2.7.txt @@ -1,6 +1,5 @@ ipython-notebook nose -pytest numpy scipy matplotlib diff --git a/testing/deps_modern_3.4.txt b/testing/deps_modern_3.4.txt index 34889c9c53..a63b15f21e 100644 --- a/testing/deps_modern_3.4.txt +++ b/testing/deps_modern_3.4.txt @@ -1,6 +1,5 @@ ipython-notebook nose -pytest numpy scipy matplotlib diff --git a/testing/deps_modern_3.5.txt b/testing/deps_modern_3.5.txt index f48432a5ae..500bb9fbd1 100644 --- a/testing/deps_modern_3.5.txt +++ b/testing/deps_modern_3.5.txt @@ -1,6 +1,5 @@ ipython-notebook nose -pytest numpy scipy matplotlib From cd482cf44587db51fffaa9e25f12cb4a3aeda75b Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Mon, 1 May 2017 14:09:53 -0500 Subject: [PATCH 0361/1738] PY2 compat --- seaborn/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/utils.py b/seaborn/utils.py index c32860b693..241f6ee4e1 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -3,7 +3,7 @@ import colorsys import warnings import os -from functools import partial +import functools import numpy as np from scipy import stats @@ -641,7 +641,7 @@ def _network(t=None, url='http://google.com'): import nose if t is None: - return partial(_network, url=url) + return functools.partial(_network, url=url) @wraps(t) def wrapper(*args, **kwargs): From b68206561a1c27fee7713b991f78d3816b71facd Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Mon, 1 May 2017 14:41:05 -0500 Subject: [PATCH 0362/1738] PY2 compat not a contextmanager --- seaborn/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/utils.py b/seaborn/utils.py index 241f6ee4e1..6c73f7ceae 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -647,10 +647,10 @@ def _network(t=None, url='http://google.com'): def wrapper(*args, **kwargs): # attempt to connect try: - with urlopen(url): - pass + f = urlopen(url) except (IOError, HTTPException): raise nose.SkipTest() else: + f.close() return t(*args, **kwargs) return wrapper From 6e39fcba91a30fdc60deb4de98b352ceecd9b419 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Mon, 1 May 2017 15:07:28 -0500 Subject: [PATCH 0363/1738] Avoid functools entirely --- seaborn/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/seaborn/utils.py b/seaborn/utils.py index 6c73f7ceae..edc748cabf 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -3,7 +3,6 @@ import colorsys import warnings import os -import functools import numpy as np from scipy import stats @@ -641,7 +640,7 @@ def _network(t=None, url='http://google.com'): import nose if t is None: - return functools.partial(_network, url=url) + return lambda x: _network(x, url=url) @wraps(t) def wrapper(*args, **kwargs): From bc495bab76d857551a8d6676ee27e182229bc622 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Mon, 1 May 2017 15:20:19 -0500 Subject: [PATCH 0364/1738] No wraps? --- seaborn/utils.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/seaborn/utils.py b/seaborn/utils.py index edc748cabf..d77e79d8c4 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -16,7 +16,6 @@ mpl_ge_150 = LooseVersion(mpl.__version__) >= "1.5.0" from .external.six.moves.urllib.request import urlopen, urlretrieve from .external.six.moves.http_client import HTTPException -from .external.six import wraps __all__ = ["desaturate", "saturate", "set_hls_values", @@ -642,7 +641,6 @@ def _network(t=None, url='http://google.com'): if t is None: return lambda x: _network(x, url=url) - @wraps(t) def wrapper(*args, **kwargs): # attempt to connect try: From 3e45b02b5c6a2bdbb7241adf7f255bb0db8ea7a3 Mon Sep 17 00:00:00 2001 From: lincolnfrias Date: Sat, 13 May 2017 20:43:04 -0300 Subject: [PATCH 0365/1738] default factorplot is point not pairplot, right? --- doc/tutorial/categorical.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial/categorical.ipynb b/doc/tutorial/categorical.ipynb index 4064b46992..d540ab2c1a 100644 --- a/doc/tutorial/categorical.ipynb +++ b/doc/tutorial/categorical.ipynb @@ -512,7 +512,7 @@ "Drawing multi-panel categorical plots\n", "-------------------------------------\n", "\n", - "As we mentioned above, there are two ways to draw categorical plots in seaborn. Similar to the duality in the regression plots, you can either use the functions introduced above, or the higher-level function :func:`factorplot`, which combines these functions with a :func:`FacetGrid` to add the ability to examine additional categories through the larger structure of the figure. By default, :func:`factorplot` produces a :func:`pairplot`:" + "As we mentioned above, there are two ways to draw categorical plots in seaborn. Similar to the duality in the regression plots, you can either use the functions introduced above, or the higher-level function :func:`factorplot`, which combines these functions with a :func:`FacetGrid` to add the ability to examine additional categories through the larger structure of the figure. By default, :func:`factorplot` produces a :func:`pointplot`:" ] }, { From aa3952b117038d056f158bdc0441cd6567626128 Mon Sep 17 00:00:00 2001 From: lincolnfrias Date: Sat, 13 May 2017 20:46:28 -0300 Subject: [PATCH 0366/1738] fixing an incomplete phrase How the is drawn? Created? I am not sure what was your intent :) --- doc/tutorial/axis_grids.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorial/axis_grids.ipynb b/doc/tutorial/axis_grids.ipynb index 2f2cb0496f..c955e615ee 100644 --- a/doc/tutorial/axis_grids.ipynb +++ b/doc/tutorial/axis_grids.ipynb @@ -24,7 +24,7 @@ "\n", "To use these features, your data has to be in a Pandas DataFrame and it must take the form of what Hadley Whickam calls `\"tidy\" data `_. In brief, that means your dataframe should be structured such that each column is a variable and each row is an observation.\n", "\n", - "For advanced use, you can use the objects discussed in this part of the tutorial directly, which will provide maximum flexibility. Some seaborn functions (such as :func:`lmplot`, :func:`factorplot`, and :func:`pairplot`) also use them behind the scenes. Unlike other seaborn functions that are \"Axes-level\" and draw onto specific (possibly already-existing) matplotlib ``Axes`` without otherwise manipulating the figure, these higher-level functions create a figure when called and are generally more strict about how it gets set up. In some cases, arguments either to those functions or to the constructor of the class they rely on will provide a different interface attributes like the figure size, as in the case of :func:`lmplot` where you can set the height and aspect ratio for each facet rather than the overall size of the figure. Any function that uses one of these objects will always return it after plotting, though, and most of these objects have convenience methods for changing how the plot, often in a more abstract and easy way." + "For advanced use, you can use the objects discussed in this part of the tutorial directly, which will provide maximum flexibility. Some seaborn functions (such as :func:`lmplot`, :func:`factorplot`, and :func:`pairplot`) also use them behind the scenes. Unlike other seaborn functions that are \"Axes-level\" and draw onto specific (possibly already-existing) matplotlib ``Axes`` without otherwise manipulating the figure, these higher-level functions create a figure when called and are generally more strict about how it gets set up. In some cases, arguments either to those functions or to the constructor of the class they rely on will provide a different interface attributes like the figure size, as in the case of :func:`lmplot` where you can set the height and aspect ratio for each facet rather than the overall size of the figure. Any function that uses one of these objects will always return it after plotting, though, and most of these objects have convenience methods for changing how the plot is drawn, often in a more abstract and easy way." ] }, { @@ -665,4 +665,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} From b3cd2476309eee193a984551e46966727780f43f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 20 May 2017 19:19:49 -0400 Subject: [PATCH 0367/1738] Update expected bar position attributes --- seaborn/tests/test_categorical.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 7442bc2bd4..cf322f9263 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1825,8 +1825,8 @@ def test_draw_vertical_bars(self): positions = np.arange(len(p.plot_data)) - p.width / 2 for bar, pos, stat in zip(ax.patches, positions, p.statistic): nt.assert_equal(bar.get_x(), pos) - nt.assert_equal(bar.get_y(), min(0, stat)) - nt.assert_equal(bar.get_height(), abs(stat)) + nt.assert_equal(bar.get_y(), 0) + nt.assert_equal(bar.get_height(), stat) nt.assert_equal(bar.get_width(), p.width) def test_draw_horizontal_bars(self): @@ -1846,10 +1846,10 @@ def test_draw_horizontal_bars(self): positions = np.arange(len(p.plot_data)) - p.width / 2 for bar, pos, stat in zip(ax.patches, positions, p.statistic): - nt.assert_equal(bar.get_x(), min(0, stat)) + nt.assert_equal(bar.get_x(), 0) nt.assert_equal(bar.get_y(), pos) nt.assert_equal(bar.get_height(), p.width) - nt.assert_equal(bar.get_width(), abs(stat)) + nt.assert_equal(bar.get_width(), stat) def test_draw_nested_vertical_bars(self): @@ -1870,8 +1870,8 @@ def test_draw_nested_vertical_bars(self): nt.assert_equal(bar.get_facecolor()[:-1], p.colors[1]) for bar, stat in zip(ax.patches, p.statistic.T.flat): - nt.assert_almost_equal(bar.get_y(), min(0, stat)) - nt.assert_almost_equal(bar.get_height(), abs(stat)) + nt.assert_almost_equal(bar.get_y(), 0) + nt.assert_almost_equal(bar.get_height(), stat) positions = np.arange(len(p.plot_data)) for bar, pos in zip(ax.patches[:n_groups], positions): @@ -1902,8 +1902,8 @@ def test_draw_nested_horizontal_bars(self): nt.assert_almost_equal(bar.get_height(), p.nested_width) for bar, stat in zip(ax.patches, p.statistic.T.flat): - nt.assert_almost_equal(bar.get_x(), min(0, stat)) - nt.assert_almost_equal(bar.get_width(), abs(stat)) + nt.assert_almost_equal(bar.get_x(), 0) + nt.assert_almost_equal(bar.get_width(), stat) def test_draw_missing_bars(self): From c0ddc4c68ebe8151870418a6c77c1df130b4a1b2 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 20 May 2017 19:29:36 -0400 Subject: [PATCH 0368/1738] Skip barplot quantitative axis tests on older matplotlibs --- seaborn/tests/test_categorical.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index cf322f9263..4b764311e7 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1825,9 +1825,10 @@ def test_draw_vertical_bars(self): positions = np.arange(len(p.plot_data)) - p.width / 2 for bar, pos, stat in zip(ax.patches, positions, p.statistic): nt.assert_equal(bar.get_x(), pos) - nt.assert_equal(bar.get_y(), 0) - nt.assert_equal(bar.get_height(), stat) nt.assert_equal(bar.get_width(), p.width) + if LooseVersion(mpl.__version__) >= "2.0.2": + nt.assert_equal(bar.get_y(), 0) + nt.assert_equal(bar.get_height(), stat) def test_draw_horizontal_bars(self): @@ -1846,10 +1847,11 @@ def test_draw_horizontal_bars(self): positions = np.arange(len(p.plot_data)) - p.width / 2 for bar, pos, stat in zip(ax.patches, positions, p.statistic): - nt.assert_equal(bar.get_x(), 0) nt.assert_equal(bar.get_y(), pos) nt.assert_equal(bar.get_height(), p.width) - nt.assert_equal(bar.get_width(), stat) + if LooseVersion(mpl.__version__) >= "2.0.2": + nt.assert_equal(bar.get_x(), 0) + nt.assert_equal(bar.get_width(), stat) def test_draw_nested_vertical_bars(self): @@ -1869,15 +1871,16 @@ def test_draw_nested_vertical_bars(self): for bar in ax.patches[n_groups:]: nt.assert_equal(bar.get_facecolor()[:-1], p.colors[1]) - for bar, stat in zip(ax.patches, p.statistic.T.flat): - nt.assert_almost_equal(bar.get_y(), 0) - nt.assert_almost_equal(bar.get_height(), stat) - positions = np.arange(len(p.plot_data)) for bar, pos in zip(ax.patches[:n_groups], positions): nt.assert_almost_equal(bar.get_x(), pos - p.width / 2) nt.assert_almost_equal(bar.get_width(), p.nested_width) + if LooseVersion(mpl.__version__) >= "2.0.2": + for bar, stat in zip(ax.patches, p.statistic.T.flat): + nt.assert_almost_equal(bar.get_y(), 0) + nt.assert_almost_equal(bar.get_height(), stat) + def test_draw_nested_horizontal_bars(self): kws = self.default_kws.copy() @@ -1901,9 +1904,10 @@ def test_draw_nested_horizontal_bars(self): nt.assert_almost_equal(bar.get_y(), pos - p.width / 2) nt.assert_almost_equal(bar.get_height(), p.nested_width) - for bar, stat in zip(ax.patches, p.statistic.T.flat): - nt.assert_almost_equal(bar.get_x(), 0) - nt.assert_almost_equal(bar.get_width(), stat) + if LooseVersion(mpl.__version__) >= "2.0.2": + for bar, stat in zip(ax.patches, p.statistic.T.flat): + nt.assert_almost_equal(bar.get_x(), 0) + nt.assert_almost_equal(bar.get_width(), stat) def test_draw_missing_bars(self): From 1bb368545b90f6685ad009db1d0ddaf17bc253d8 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 20 May 2017 20:54:51 -0400 Subject: [PATCH 0369/1738] Revive bar tests on older matplotlibs --- seaborn/tests/test_categorical.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 4b764311e7..8ccc418c2a 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1829,6 +1829,9 @@ def test_draw_vertical_bars(self): if LooseVersion(mpl.__version__) >= "2.0.2": nt.assert_equal(bar.get_y(), 0) nt.assert_equal(bar.get_height(), stat) + else: + nt.assert_equal(bar.get_y(), min(0, stat)) + nt.assert_equal(bar.get_height(), abs(stat)) def test_draw_horizontal_bars(self): @@ -1852,6 +1855,9 @@ def test_draw_horizontal_bars(self): if LooseVersion(mpl.__version__) >= "2.0.2": nt.assert_equal(bar.get_x(), 0) nt.assert_equal(bar.get_width(), stat) + else: + nt.assert_equal(bar.get_x(), min(0, stat)) + nt.assert_equal(bar.get_width(), abs(stat)) def test_draw_nested_vertical_bars(self): @@ -1876,10 +1882,13 @@ def test_draw_nested_vertical_bars(self): nt.assert_almost_equal(bar.get_x(), pos - p.width / 2) nt.assert_almost_equal(bar.get_width(), p.nested_width) - if LooseVersion(mpl.__version__) >= "2.0.2": - for bar, stat in zip(ax.patches, p.statistic.T.flat): + for bar, stat in zip(ax.patches, p.statistic.T.flat): + if LooseVersion(mpl.__version__) >= "2.0.2": nt.assert_almost_equal(bar.get_y(), 0) nt.assert_almost_equal(bar.get_height(), stat) + else: + nt.assert_almost_equal(bar.get_y(), min(0, stat)) + nt.assert_almost_equal(bar.get_height(), abs(stat)) def test_draw_nested_horizontal_bars(self): @@ -1904,10 +1913,13 @@ def test_draw_nested_horizontal_bars(self): nt.assert_almost_equal(bar.get_y(), pos - p.width / 2) nt.assert_almost_equal(bar.get_height(), p.nested_width) - if LooseVersion(mpl.__version__) >= "2.0.2": - for bar, stat in zip(ax.patches, p.statistic.T.flat): + for bar, stat in zip(ax.patches, p.statistic.T.flat): + if LooseVersion(mpl.__version__) >= "2.0.2": nt.assert_almost_equal(bar.get_x(), 0) nt.assert_almost_equal(bar.get_width(), stat) + else: + nt.assert_almost_equal(bar.get_x(), min(0, stat)) + nt.assert_almost_equal(bar.get_width(), abs(stat)) def test_draw_missing_bars(self): From 0222df1a8c770339f8f52e2fef95f19e1a2da9f3 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 11 May 2017 11:44:58 -0400 Subject: [PATCH 0370/1738] Remove 'robust' from kwargs before plotting clustermap colors (#1173) --- seaborn/matrix.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 884928c354..eb9a5b6b26 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -796,6 +796,7 @@ def _preprocess_colors(self, data, colors, axis): colors = colors.ix[data.columns] # Replace na's with background color + # TODO We should set these to transparent instead colors = colors.fillna('white') # Extract color values and labels from frame/series @@ -1018,8 +1019,11 @@ def plot_colors(self, xind, yind, **kws): kws.pop('center', None) kws.pop('vmin', None) kws.pop('vmax', None) + kws.pop('robust', None) kws.pop('xticklabels', None) kws.pop('yticklabels', None) + + # Plot the row colors if self.row_colors is not None: matrix, cmap = self.color_list_to_matrix_and_cmap( self.row_colors, yind, axis=0) @@ -1039,6 +1043,7 @@ def plot_colors(self, xind, yind, **kws): else: despine(self.ax_row_colors, left=True, bottom=True) + # Plot the column colors if self.col_colors is not None: matrix, cmap = self.color_list_to_matrix_and_cmap( self.col_colors, xind, axis=1) From 9f589d9bf33aa39be345a656d42f12b79f3755c8 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 11 May 2017 13:16:41 -0400 Subject: [PATCH 0371/1738] Don't label clustermap colors when Series has no name (closes #1177) --- seaborn/matrix.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index eb9a5b6b26..37987b4672 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -804,7 +804,10 @@ def _preprocess_colors(self, data, colors, axis): labels = list(colors.columns) colors = colors.T.values else: - labels = [colors.name] + if colors.name is None: + labels = [""] + else: + labels = [colors.name] colors = colors.values colors = _convert_colors(colors) From 3af5d807d8943baf17ff439f0123a1c761afce8b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 22 Apr 2017 22:34:56 -0400 Subject: [PATCH 0372/1738] Document kdeplot colorbar in docstring --- seaborn/distributions.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index a38ced5062..bd0e8b9504 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -482,8 +482,15 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", relevant when drawing a univariate plot or when ``shade=False``. Setting this to ``False`` can be useful when you want multiple densities on the same Axes. - ax : matplotlib axis, optional - Axis to plot on, otherwise uses current axis. + cbar : bool, optional + If True and drawing a bivariate KDE plot, add a colorbar. + cbar_ax : matplotlib axes, optional + Existing axes to draw the colorbar onto, otherwise space is taken + from the main axes. + cbar_kws : dict, optional + Keyword arguments for ``fig.colorbar()``. + ax : matplotlib axes, optional + Axes to plot on, otherwise uses current axes. kwargs : key, value pairings Other keyword arguments are passed to ``plt.plot()`` or ``plt.contour{f}`` depending on whether a univariate or bivariate From 2623bd116fd4e02223f4b8a922c3c2da7bc4d0b3 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 21 May 2017 11:07:59 -0400 Subject: [PATCH 0373/1738] Restore clustermap label rotation (closes #870) --- seaborn/matrix.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 37987b4672..f1435a20ff 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -1086,9 +1086,16 @@ def plot_matrix(self, colorbar_kws, xind, yind, **kws): heatmap(self.data2d, ax=self.ax_heatmap, cbar_ax=self.cax, cbar_kws=colorbar_kws, mask=self.mask, xticklabels=xtl, yticklabels=ytl, **kws) + + xtl_rot = self.ax_heatmap.get_xticklabels()[0].get_rotation() + ytl_rot = self.ax_heatmap.get_yticklabels()[0].get_rotation() + self.ax_heatmap.yaxis.set_ticks_position('right') self.ax_heatmap.yaxis.set_label_position('right') + plt.setp(self.ax_heatmap.get_xticklabels(), rotation=xtl_rot) + plt.setp(self.ax_heatmap.get_yticklabels(), rotation=ytl_rot) + def plot(self, metric, method, colorbar_kws, row_cluster, col_cluster, row_linkage, col_linkage, **kws): colorbar_kws = {} if colorbar_kws is None else colorbar_kws From 061ac2f2cf885de0abf50a642241eb45771c8472 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 21 May 2017 11:51:10 -0400 Subject: [PATCH 0374/1738] Handle new qualitative mpl palettes Closes #1170 --- doc/releases/v0.8.0.txt | 2 ++ seaborn/palettes.py | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index 2cedf3dd0d..0aa7615c90 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -11,3 +11,5 @@ v0.8.0 (Unreleased) - Put a cap on the number of bins used in :func:`jointplot` for ``type=="hex"`` to avoid hanging when the reference rule prescribes too many. - Turn off dendrogram axes in :func:`clustermap` rather than setting the background color to white. + +- New matplotlib qualitative palettes (e.g. "tab10") are now handled correctly. diff --git a/seaborn/palettes.py b/seaborn/palettes.py index 7402ca61a1..b43d39e50b 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -394,9 +394,10 @@ def mpl_palette(name, n_colors=6): >>> sns.palplot(sns.mpl_palette("GnBu_d")) """ - brewer_qual_pals = {"Accent": 8, "Dark2": 8, "Paired": 12, - "Pastel1": 9, "Pastel2": 8, - "Set1": 9, "Set2": 8, "Set3": 12} + mpl_qual_pals = {"Accent": 8, "Dark2": 8, "Paired": 12, + "Pastel1": 9, "Pastel2": 8, + "Set1": 9, "Set2": 8, "Set3": 12, + "tab10": 10, "tab20": 20, "tab20b": 20, "tab20c": 20} if name.endswith("_d"): pal = ["#333333"] @@ -404,8 +405,8 @@ def mpl_palette(name, n_colors=6): cmap = blend_palette(pal, n_colors, as_cmap=True) else: cmap = getattr(mpl.cm, name) - if name in brewer_qual_pals: - bins = np.linspace(0, 1, brewer_qual_pals[name])[:n_colors] + if name in mpl_qual_pals: + bins = np.linspace(0, 1, mpl_qual_pals[name])[:n_colors] else: bins = np.linspace(0, 1, n_colors + 2)[1:-1] palette = list(map(tuple, cmap(bins)[:, :3])) From 4de2b92bd3085ebd3288f1ec97f5927d071dc1d8 Mon Sep 17 00:00:00 2001 From: Cameron Pye Date: Fri, 26 May 2017 11:48:41 -0700 Subject: [PATCH 0375/1738] added list input support for kdeplot --- seaborn/distributions.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index bd0e8b9504..7c67256857 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -593,10 +593,16 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", if ax is None: ax = plt.gca() + if isinstance(data, list): + data = np.asarray(data) + data = data.astype(np.float64) if data2 is not None: + if isinstance(data2, list): + data2 = np.asarray(data2) data2 = data2.astype(np.float64) + bivariate = False if isinstance(data, np.ndarray) and np.ndim(data) > 1: bivariate = True @@ -621,7 +627,6 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", ax = _univariate_kdeplot(data, shade, vertical, kernel, bw, gridsize, cut, clip, legend, ax, cumulative=cumulative, **kwargs) - return ax From 7a2c7d53d3d4a412dde84fe49e30de843c27752a Mon Sep 17 00:00:00 2001 From: pyeguy Date: Fri, 26 May 2017 14:46:59 -0700 Subject: [PATCH 0376/1738] Update distributions.py fixed white space --- seaborn/distributions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 7c67256857..279f1e6f04 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -602,7 +602,6 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", data2 = np.asarray(data2) data2 = data2.astype(np.float64) - bivariate = False if isinstance(data, np.ndarray) and np.ndim(data) > 1: bivariate = True @@ -627,6 +626,7 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", ax = _univariate_kdeplot(data, shade, vertical, kernel, bw, gridsize, cut, clip, legend, ax, cumulative=cumulative, **kwargs) + return ax From e0a5679dd91e9bce31736bbbb2afcac2f0eb0dff Mon Sep 17 00:00:00 2001 From: pyeguy Date: Fri, 26 May 2017 15:10:11 -0700 Subject: [PATCH 0377/1738] Update distributions.py more white space (pep8) issues --- seaborn/distributions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 279f1e6f04..7325b3af86 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -626,7 +626,7 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", ax = _univariate_kdeplot(data, shade, vertical, kernel, bw, gridsize, cut, clip, legend, ax, cumulative=cumulative, **kwargs) - + return ax From 13434f687b5fb4863640d9aed3180272279339b4 Mon Sep 17 00:00:00 2001 From: Cameron Pye Date: Mon, 29 May 2017 17:12:39 -0700 Subject: [PATCH 0378/1738] added warning to matrix input for kdeplot. --- seaborn/distributions.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 7325b3af86..9763f7aa62 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -602,11 +602,14 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", data2 = np.asarray(data2) data2 = data2.astype(np.float64) + warn = False bivariate = False if isinstance(data, np.ndarray) and np.ndim(data) > 1: + warn = True bivariate = True x, y = data.T elif isinstance(data, pd.DataFrame) and np.ndim(data) > 1: + warn = True bivariate = True x = data.iloc[:, 0].values y = data.iloc[:, 1].values @@ -615,6 +618,12 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", x = data y = data2 + if warn: + warn_msg = ("This 'matrix' style of input has been deprecated" + "in favor of kdeplot(x,y) and will be removed in " + "a forthcoming release. Please update your code.") + warnings.warn(warn_msg, UserWarning) + if bivariate and cumulative: raise TypeError("Cumulative distribution plots are not" "supported for bivariate distributions.") From 0659b820248fbd665a1b3c170f2e97c89ac5bbc0 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 24 Jun 2017 14:04:57 -0400 Subject: [PATCH 0379/1738] Fix matplotlib barplot conditional in categorical tests --- seaborn/tests/test_categorical.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 8ccc418c2a..bc11c9b700 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -17,6 +17,7 @@ pandas_has_categoricals = LooseVersion(pd.__version__) >= "0.15" +mpl_barplot_change = LooseVersion("2.0.1") class CategoricalFixture(PlotTestCase): @@ -1826,7 +1827,7 @@ def test_draw_vertical_bars(self): for bar, pos, stat in zip(ax.patches, positions, p.statistic): nt.assert_equal(bar.get_x(), pos) nt.assert_equal(bar.get_width(), p.width) - if LooseVersion(mpl.__version__) >= "2.0.2": + if mpl.__version__ >= mpl_barplot_change: nt.assert_equal(bar.get_y(), 0) nt.assert_equal(bar.get_height(), stat) else: @@ -1852,7 +1853,7 @@ def test_draw_horizontal_bars(self): for bar, pos, stat in zip(ax.patches, positions, p.statistic): nt.assert_equal(bar.get_y(), pos) nt.assert_equal(bar.get_height(), p.width) - if LooseVersion(mpl.__version__) >= "2.0.2": + if mpl.__version__ >= mpl_barplot_change: nt.assert_equal(bar.get_x(), 0) nt.assert_equal(bar.get_width(), stat) else: @@ -1883,7 +1884,7 @@ def test_draw_nested_vertical_bars(self): nt.assert_almost_equal(bar.get_width(), p.nested_width) for bar, stat in zip(ax.patches, p.statistic.T.flat): - if LooseVersion(mpl.__version__) >= "2.0.2": + if LooseVersion(mpl.__version__) >= mpl_barplot_change: nt.assert_almost_equal(bar.get_y(), 0) nt.assert_almost_equal(bar.get_height(), stat) else: @@ -1914,7 +1915,7 @@ def test_draw_nested_horizontal_bars(self): nt.assert_almost_equal(bar.get_height(), p.nested_width) for bar, stat in zip(ax.patches, p.statistic.T.flat): - if LooseVersion(mpl.__version__) >= "2.0.2": + if LooseVersion(mpl.__version__) >= mpl_barplot_change: nt.assert_almost_equal(bar.get_x(), 0) nt.assert_almost_equal(bar.get_width(), stat) else: From 8caffb050eef1d1f4c28084f26606cc9b89fafd5 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 24 Jun 2017 14:12:16 -0400 Subject: [PATCH 0380/1738] Implement standard deviation error bars in categorical plots --- seaborn/categorical.py | 41 +++++++++++++++++++-------- seaborn/tests/test_categorical.py | 46 +++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 12 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 210be8cb5a..91fc4af680 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1478,10 +1478,18 @@ def estimate_statistic(self, estimator, ci, n_boot): confint.append([np.nan, np.nan]) continue - boots = bootstrap(stat_data, func=estimator, - n_boot=n_boot, - units=unit_data) - confint.append(utils.ci(boots, ci)) + if ci == "std": + + estimate = estimator(stat_data) + sd = np.std(stat_data) + confint.append((estimate - sd, estimate + sd)) + + else: + + boots = bootstrap(stat_data, func=estimator, + n_boot=n_boot, + units=unit_data) + confint.append(utils.ci(boots, ci)) # Option 2: we are grouping by a hue layer # ---------------------------------------- @@ -1520,10 +1528,18 @@ def estimate_statistic(self, estimator, ci, n_boot): confint[i].append([np.nan, np.nan]) continue - boots = bootstrap(stat_data, func=estimator, - n_boot=n_boot, - units=unit_data) - confint[i].append(utils.ci(boots, ci)) + if ci == "std": + + estimate = estimator(stat_data) + sd = np.std(stat_data) + confint[i].append((estimate - sd, estimate + sd)) + + else: + + boots = bootstrap(stat_data, func=estimator, + n_boot=n_boot, + units=unit_data) + confint[i].append(utils.ci(boots, ci)) # Save the resulting values for plotting self.statistic = np.array(statistic) @@ -2066,10 +2082,11 @@ def plot(self, ax, boxplot_kws): stat_api_params=dedent("""\ estimator : callable that maps vector -> scalar, optional Statistical function to estimate within each categorical bin. - ci : float or None, optional - Size of confidence intervals to draw around estimated values. If - ``None``, no bootstrapping will be performed, and error bars will - not be drawn. + ci : float or "std" or None, optional + Size of confidence intervals to draw around estimated values. If + "std", skip bootstrapping and draw the standard deviation of the + observerations. If ``None``, no bootstrapping will be performed, and + error bars will not be drawn. n_boot : int, optional Number of bootstrap iterations to use when computing confidence intervals. diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index bc11c9b700..6ec3082787 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -602,6 +602,52 @@ def test_nested_stats_with_missing_data(self): npt.assert_array_equal(p.confint[2], np.zeros((3, 2)) * np.nan) + def test_std_error_bars(self): + + p = cat._CategoricalStatPlotter() + + g = pd.Series(np.repeat(list("abc"), 100)) + y = pd.Series(np.random.RandomState(0).randn(300)) + + p.establish_variables(g, y) + p.estimate_statistic(np.mean, "std", None) + + nt.assert_equal(p.statistic.shape, (3,)) + nt.assert_equal(p.confint.shape, (3, 2)) + + npt.assert_array_almost_equal(p.statistic, + y.groupby(g).mean()) + + for ci, (_, grp_y) in zip(p.confint, y.groupby(g)): + mean = grp_y.mean() + half_ci = np.std(grp_y) + ci_want = mean - half_ci, mean + half_ci + npt.assert_array_almost_equal(ci_want, ci, 2) + + def test_nested_std_error_bars(self): + + p = cat._CategoricalStatPlotter() + + g = pd.Series(np.repeat(list("abc"), 100)) + h = pd.Series(np.tile(list("xy"), 150)) + y = pd.Series(np.random.RandomState(0).randn(300)) + + p.establish_variables(g, y, h) + p.estimate_statistic(np.mean, "std", None) + + nt.assert_equal(p.statistic.shape, (3, 2)) + nt.assert_equal(p.confint.shape, (3, 2, 2)) + + npt.assert_array_almost_equal(p.statistic, + y.groupby([g, h]).mean().unstack()) + + for ci_g, (_, grp_y) in zip(p.confint, y.groupby(g)): + for ci, hue_y in zip(ci_g, [grp_y[::2], grp_y[1::2]]): + mean = hue_y.mean() + half_ci = np.std(hue_y) + ci_want = mean - half_ci, mean + half_ci + npt.assert_array_almost_equal(ci_want, ci, 2) + def test_estimator_value_label(self): p = cat._CategoricalStatPlotter() From 1e0093bdc97d2414e54285eec0121168da87b9e2 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 24 Jun 2017 14:39:33 -0400 Subject: [PATCH 0381/1738] Add standard deviation error bars in tsplot --- seaborn/linearmodels.py | 23 ++++++++++++++--------- seaborn/tests/test_linearmodels.py | 4 ++++ seaborn/timeseries.py | 18 +++++++++++------- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index 910d14bfa1..d4cf689c2f 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -166,11 +166,15 @@ def estimate_data(self): cis.append(None) else: units = None - if self.units is not None: - units = self.units[x == val] - boots = algo.bootstrap(_y, func=self.x_estimator, - n_boot=self.n_boot, units=units) - _ci = utils.ci(boots, self.x_ci) + if self.x_ci == "std": + std = np.std(_y) + _ci = est - std, est + std + else: + if self.units is not None: + units = self.units[x == val] + boots = algo.bootstrap(_y, func=self.x_estimator, + n_boot=self.n_boot, units=units) + _ci = utils.ci(boots, self.x_ci) cis.append(_ci) return vals, points, cis @@ -424,7 +428,7 @@ def lineplot(self, ax, kws): x_estimator : callable that maps vector -> scalar, optional Apply this function to each unique value of ``x`` and plot the resulting estimate. This is useful when ``x`` is a discrete variable. - If ``x_ci`` is not ``None``, this estimate will be bootstrapped and a + If ``x_ci`` is given, this estimate will be bootstrapped and a confidence interval will be drawn.\ """), x_bins=dedent("""\ @@ -438,10 +442,11 @@ def lineplot(self, ax, kws): ``x_estimator`` is ``numpy.mean``.\ """), x_ci=dedent("""\ - x_ci : "ci", int in [0, 100] or None, optional + x_ci : "ci", "std", int in [0, 100] or None, optional Size of the confidence interval used when plotting a central tendency - for discrete values of ``x``. If "ci", defer to the value of the``ci`` - parameter.\ + for discrete values of ``x``. If ``"ci"``, defer to the value of the + ``ci`` parameter. If ``"std"``, skip bootstrappig and show the standard + deviation of the observations in each bin.\ """), scatter=dedent("""\ scatter : bool, optional diff --git a/seaborn/tests/test_linearmodels.py b/seaborn/tests/test_linearmodels.py index 9d6b0ee57e..74cf827be6 100644 --- a/seaborn/tests/test_linearmodels.py +++ b/seaborn/tests/test_linearmodels.py @@ -158,6 +158,10 @@ def test_ci(self): nt.assert_equal(p.ci, 95) nt.assert_equal(p.x_ci, 68) + p = lm._RegressionPlotter("x", "y", data=self.df, ci=95, x_ci="std") + nt.assert_equal(p.ci, 95) + nt.assert_equal(p.x_ci, "std") + @skipif(_no_statsmodels) def test_fast_regression(self): diff --git a/seaborn/timeseries.py b/seaborn/timeseries.py index cdc394ea30..371c75585c 100644 --- a/seaborn/timeseries.py +++ b/seaborn/timeseries.py @@ -61,10 +61,11 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, Names of ways to plot uncertainty across units from set of {ci_band, ci_bars, boot_traces, boot_kde, unit_traces, unit_points}. Can use one or more than one method. - ci : float or list of floats in [0, 100] - Confidence interval size(s). If a list, it will stack the error - plots for each confidence interval. Only relevant for error styles - with "ci" in the name. + ci : float or list of floats in [0, 100] or "std" or None + Confidence interval size(s). If a list, it will stack the error plots + for each confidence interval. If ``"std"``, show standard deviation of + the observations instead of boostrapped confidence intervals. Only + relevant for error styles with "ci" in the name. interpolate : boolean Whether to do a linear interpolation between each timepoint when plotting. The value of this parameter also determines the marker @@ -280,9 +281,12 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, x = df_c.columns.values.astype(np.float) # Bootstrap the data for confidence intervals - boot_data = algo.bootstrap(df_c.values, n_boot=n_boot, - axis=0, func=estimator) - cis = [utils.ci(boot_data, v, axis=0) for v in ci] + if ci == "std": + cis = [np.std(df_c.values, axis=0)] + else: + boot_data = algo.bootstrap(df_c.values, n_boot=n_boot, + axis=0, func=estimator) + cis = [utils.ci(boot_data, v, axis=0) for v in ci] central_data = estimator(df_c.values, axis=0) # Get the color for this condition From 825f7ef80e33593bd262f4e72ec465f2150e892a Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 24 Jun 2017 14:39:45 -0400 Subject: [PATCH 0382/1738] Update release notes --- doc/releases/v0.8.0.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index 0aa7615c90..fa55ab92c4 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -6,6 +6,8 @@ v0.8.0 (Unreleased) - Added the ability to draw a colorbar for a bivariate :func:`kdeplot` with the ``cbar`` parameter (and related ``cbar_ax`` and ``cbar_kws`` parameters). +- Added the ability to use error bars to show standard deviations rather than bootstrip confidence intervals in most statistical functions. + - Allow side-specific offsets in :func:`despine`. - Put a cap on the number of bins used in :func:`jointplot` for ``type=="hex"`` to avoid hanging when the reference rule prescribes too many. From 7cacfa47e3700ee181235920e02346c3a2634356 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 24 Jun 2017 14:44:32 -0400 Subject: [PATCH 0383/1738] Fix tsplot standard deviation error bars --- seaborn/timeseries.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/seaborn/timeseries.py b/seaborn/timeseries.py index 371c75585c..ca08702c7b 100644 --- a/seaborn/timeseries.py +++ b/seaborn/timeseries.py @@ -281,8 +281,11 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, x = df_c.columns.values.astype(np.float) # Bootstrap the data for confidence intervals - if ci == "std": - cis = [np.std(df_c.values, axis=0)] + if "std" in ci: + est = estimator(df_c.values, axis=0) + std = np.std(df_c.values, axis=0) + cis = [(est - std, est + std)] + boot_data = df_c.values else: boot_data = algo.bootstrap(df_c.values, n_boot=n_boot, axis=0, func=estimator) From da57cccbe8855e3bcd01c6ba9239572a3c14d390 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 24 Jun 2017 15:43:29 -0400 Subject: [PATCH 0384/1738] Fix typo --- doc/releases/v0.8.0.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index fa55ab92c4..cf523ea66e 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -6,7 +6,7 @@ v0.8.0 (Unreleased) - Added the ability to draw a colorbar for a bivariate :func:`kdeplot` with the ``cbar`` parameter (and related ``cbar_ax`` and ``cbar_kws`` parameters). -- Added the ability to use error bars to show standard deviations rather than bootstrip confidence intervals in most statistical functions. +- Added the ability to use error bars to show standard deviations rather than bootstrap confidence intervals in most statistical functions. - Allow side-specific offsets in :func:`despine`. From 03476a850f5aeabe57a1fa0b7c3988e6caa25680 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 25 Jun 2017 12:50:13 -0400 Subject: [PATCH 0385/1738] Demonstrate standard deviation error bars in api examples --- seaborn/categorical.py | 14 ++++++++++++++ seaborn/timeseries.py | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 91fc4af680..520324ee14 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -3119,6 +3119,13 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.barplot(x="day", y="tip", data=tips, ci=68) + Show standard deviation of observations instead of a confidence interval: + + .. plot:: + :context: close-figs + + >>> ax = sns.barplot(x="day", y="tip", data=tips, ci="std") + Add "caps" to the error bars: .. plot:: @@ -3358,6 +3365,13 @@ def pointplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.pointplot(x="day", y="tip", data=tips, ci=68) + Show standard deviation of observations instead of a confidence interval: + + .. plot:: + :context: close-figs + + >>> ax = sns.pointplot(x="day", y="tip", data=tips, ci="std") + Add "caps" to the error bars: .. plot:: diff --git a/seaborn/timeseries.py b/seaborn/timeseries.py index ca08702c7b..ddebb4e3ec 100644 --- a/seaborn/timeseries.py +++ b/seaborn/timeseries.py @@ -144,6 +144,13 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, >>> ax = sns.tsplot(data=data, ci=[68, 95], color="m") + Show the standard deviation of the observations: + + .. plot:: + :context: close-figs + + >>> ax = sns.tsplot(data=data, ci="std") + Use a different estimator: .. plot:: From 4d6843e003dd9c64a5d99cb874052ad5972c21a5 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 26 Jun 2017 11:09:57 -0400 Subject: [PATCH 0386/1738] Change 'std' to 'sd' Less consistent with the numpy function; more consistent with statistics --- doc/releases/v0.8.0.txt | 2 +- seaborn/categorical.py | 12 ++++++------ seaborn/linearmodels.py | 10 +++++----- seaborn/tests/test_categorical.py | 8 ++++---- seaborn/tests/test_linearmodels.py | 4 ++-- seaborn/timeseries.py | 12 ++++++------ 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index cf523ea66e..aa67d18729 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -6,7 +6,7 @@ v0.8.0 (Unreleased) - Added the ability to draw a colorbar for a bivariate :func:`kdeplot` with the ``cbar`` parameter (and related ``cbar_ax`` and ``cbar_kws`` parameters). -- Added the ability to use error bars to show standard deviations rather than bootstrap confidence intervals in most statistical functions. +- Added the ability to use error bars to show standard deviations rather than bootstrap confidence intervals in most statistical functions by putting `ci="sd"`. - Allow side-specific offsets in :func:`despine`. diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 520324ee14..e70a66b0e8 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1478,7 +1478,7 @@ def estimate_statistic(self, estimator, ci, n_boot): confint.append([np.nan, np.nan]) continue - if ci == "std": + if ci == "sd": estimate = estimator(stat_data) sd = np.std(stat_data) @@ -1528,7 +1528,7 @@ def estimate_statistic(self, estimator, ci, n_boot): confint[i].append([np.nan, np.nan]) continue - if ci == "std": + if ci == "sd": estimate = estimator(stat_data) sd = np.std(stat_data) @@ -2082,9 +2082,9 @@ def plot(self, ax, boxplot_kws): stat_api_params=dedent("""\ estimator : callable that maps vector -> scalar, optional Statistical function to estimate within each categorical bin. - ci : float or "std" or None, optional + ci : float or "sd" or None, optional Size of confidence intervals to draw around estimated values. If - "std", skip bootstrapping and draw the standard deviation of the + "sd", skip bootstrapping and draw the standard deviation of the observerations. If ``None``, no bootstrapping will be performed, and error bars will not be drawn. n_boot : int, optional @@ -3124,7 +3124,7 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, .. plot:: :context: close-figs - >>> ax = sns.barplot(x="day", y="tip", data=tips, ci="std") + >>> ax = sns.barplot(x="day", y="tip", data=tips, ci="sd") Add "caps" to the error bars: @@ -3370,7 +3370,7 @@ def pointplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, .. plot:: :context: close-figs - >>> ax = sns.pointplot(x="day", y="tip", data=tips, ci="std") + >>> ax = sns.pointplot(x="day", y="tip", data=tips, ci="sd") Add "caps" to the error bars: diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index d4cf689c2f..3e117f2584 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -166,9 +166,9 @@ def estimate_data(self): cis.append(None) else: units = None - if self.x_ci == "std": - std = np.std(_y) - _ci = est - std, est + std + if self.x_ci == "sd": + sd = np.std(_y) + _ci = est - sd, est + sd else: if self.units is not None: units = self.units[x == val] @@ -442,10 +442,10 @@ def lineplot(self, ax, kws): ``x_estimator`` is ``numpy.mean``.\ """), x_ci=dedent("""\ - x_ci : "ci", "std", int in [0, 100] or None, optional + x_ci : "ci", "sd", int in [0, 100] or None, optional Size of the confidence interval used when plotting a central tendency for discrete values of ``x``. If ``"ci"``, defer to the value of the - ``ci`` parameter. If ``"std"``, skip bootstrappig and show the standard + ``ci`` parameter. If ``"sd"``, skip bootstrappig and show the standard deviation of the observations in each bin.\ """), scatter=dedent("""\ diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 6ec3082787..59cb6f3b04 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -602,7 +602,7 @@ def test_nested_stats_with_missing_data(self): npt.assert_array_equal(p.confint[2], np.zeros((3, 2)) * np.nan) - def test_std_error_bars(self): + def test_sd_error_bars(self): p = cat._CategoricalStatPlotter() @@ -610,7 +610,7 @@ def test_std_error_bars(self): y = pd.Series(np.random.RandomState(0).randn(300)) p.establish_variables(g, y) - p.estimate_statistic(np.mean, "std", None) + p.estimate_statistic(np.mean, "sd", None) nt.assert_equal(p.statistic.shape, (3,)) nt.assert_equal(p.confint.shape, (3, 2)) @@ -624,7 +624,7 @@ def test_std_error_bars(self): ci_want = mean - half_ci, mean + half_ci npt.assert_array_almost_equal(ci_want, ci, 2) - def test_nested_std_error_bars(self): + def test_nested_sd_error_bars(self): p = cat._CategoricalStatPlotter() @@ -633,7 +633,7 @@ def test_nested_std_error_bars(self): y = pd.Series(np.random.RandomState(0).randn(300)) p.establish_variables(g, y, h) - p.estimate_statistic(np.mean, "std", None) + p.estimate_statistic(np.mean, "sd", None) nt.assert_equal(p.statistic.shape, (3, 2)) nt.assert_equal(p.confint.shape, (3, 2, 2)) diff --git a/seaborn/tests/test_linearmodels.py b/seaborn/tests/test_linearmodels.py index 74cf827be6..19559fc0d7 100644 --- a/seaborn/tests/test_linearmodels.py +++ b/seaborn/tests/test_linearmodels.py @@ -158,9 +158,9 @@ def test_ci(self): nt.assert_equal(p.ci, 95) nt.assert_equal(p.x_ci, 68) - p = lm._RegressionPlotter("x", "y", data=self.df, ci=95, x_ci="std") + p = lm._RegressionPlotter("x", "y", data=self.df, ci=95, x_ci="sd") nt.assert_equal(p.ci, 95) - nt.assert_equal(p.x_ci, "std") + nt.assert_equal(p.x_ci, "sd") @skipif(_no_statsmodels) def test_fast_regression(self): diff --git a/seaborn/timeseries.py b/seaborn/timeseries.py index ddebb4e3ec..1b1aab9389 100644 --- a/seaborn/timeseries.py +++ b/seaborn/timeseries.py @@ -61,9 +61,9 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, Names of ways to plot uncertainty across units from set of {ci_band, ci_bars, boot_traces, boot_kde, unit_traces, unit_points}. Can use one or more than one method. - ci : float or list of floats in [0, 100] or "std" or None + ci : float or list of floats in [0, 100] or "sd" or None Confidence interval size(s). If a list, it will stack the error plots - for each confidence interval. If ``"std"``, show standard deviation of + for each confidence interval. If ``"sd"``, show standard deviation of the observations instead of boostrapped confidence intervals. Only relevant for error styles with "ci" in the name. interpolate : boolean @@ -149,7 +149,7 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, .. plot:: :context: close-figs - >>> ax = sns.tsplot(data=data, ci="std") + >>> ax = sns.tsplot(data=data, ci="sd") Use a different estimator: @@ -288,10 +288,10 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, x = df_c.columns.values.astype(np.float) # Bootstrap the data for confidence intervals - if "std" in ci: + if "sd" in ci: est = estimator(df_c.values, axis=0) - std = np.std(df_c.values, axis=0) - cis = [(est - std, est + std)] + sd = np.std(df_c.values, axis=0) + cis = [(est - sd, est + sd)] boot_data = df_c.values else: boot_data = algo.bootstrap(df_c.values, n_boot=n_boot, From fdf504ed124054d0542974e5dddfd730fe3d0169 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 21 May 2017 20:03:45 -0400 Subject: [PATCH 0387/1738] Recenter divergent colormaps rather than exappanding limits --- seaborn/matrix.py | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index f1435a20ff..57a76414d6 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -1,4 +1,5 @@ """Functions to visualize matrices of data.""" +from __future__ import division import itertools import matplotlib as mpl @@ -11,10 +12,12 @@ from scipy.cluster import hierarchy from .axisgrid import Grid -from .palettes import cubehelix_palette +from .palettes import cubehelix_palette, diverging_palette from .utils import (despine, axis_ticklabels_overlap, relative_luminance, to_utf8) +from .external.six import string_types + __all__ = ["heatmap", "clustermap"] @@ -207,35 +210,34 @@ def _determine_cmap_params(self, plot_data, vmin, vmax, vmin = np.percentile(calc_data, 2) if robust else calc_data.min() if vmax is None: vmax = np.percentile(calc_data, 98) if robust else calc_data.max() + self.vmin, self.vmax = vmin, vmax # Simple heuristics for whether these data should have a divergent map divergent = ((vmin < 0) and (vmax > 0)) or center is not None - # Now set center to 0 so math below makes sense - if center is None: - center = 0 - - # A divergent map should be symmetric around the center value - if divergent: - vlim = max(abs(vmin - center), abs(vmax - center)) - vmin, vmax = -vlim, vlim - self.divergent = divergent - - # Now add in the centering value and set the limits - vmin += center - vmax += center - self.vmin = vmin - self.vmax = vmax - # Choose default colormaps if not provided if cmap is None: if divergent: - self.cmap = "RdBu_r" + self.cmap = mpl.cm.RdBu_r else: self.cmap = cubehelix_palette(light=.95, as_cmap=True) + elif isinstance(cmap, string_types): + self.cmap = mpl.cm.get_cmap(cmap) + elif isinstance(cmap, list): + self.cmap = mpl.colors.ListedColormap(cmap) else: self.cmap = cmap + # Recenter a divergent colormap + if divergent: + + center = 0 if center is None else center + vrange = 2 * max(vmax - center, center - vmin) + cmax = vmax / vrange + (.5 - center) + cmin = vmin / vrange + (.5 - center) + cc = np.linspace(cmin, cmax, 256) + self.cmap = mpl.colors.ListedColormap(self.cmap(cc)) + def _annotate_heatmap(self, ax, mesh): """Add textual labels with the value in each cell.""" mesh.update_scalarmappable() From a72bf624b5159aea5ba4bc30f30390e6b97f2c03 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 22 May 2017 10:11:06 -0400 Subject: [PATCH 0388/1738] Remove heuristic selection of diverging colormap in matrix plots --- seaborn/matrix.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 57a76414d6..18302980d1 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -212,12 +212,9 @@ def _determine_cmap_params(self, plot_data, vmin, vmax, vmax = np.percentile(calc_data, 98) if robust else calc_data.max() self.vmin, self.vmax = vmin, vmax - # Simple heuristics for whether these data should have a divergent map - divergent = ((vmin < 0) and (vmax > 0)) or center is not None - # Choose default colormaps if not provided if cmap is None: - if divergent: + if center is not None: self.cmap = mpl.cm.RdBu_r else: self.cmap = cubehelix_palette(light=.95, as_cmap=True) From e3dcef75ff1dbf601496f770830a6118b5ee9c4c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 22 May 2017 10:11:20 -0400 Subject: [PATCH 0389/1738] Fix logic for rescaling divergent colormap around a center point --- seaborn/matrix.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 18302980d1..9d8bb95201 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -226,12 +226,10 @@ def _determine_cmap_params(self, plot_data, vmin, vmax, self.cmap = cmap # Recenter a divergent colormap - if divergent: - - center = 0 if center is None else center - vrange = 2 * max(vmax - center, center - vmin) - cmax = vmax / vrange + (.5 - center) - cmin = vmin / vrange + (.5 - center) + if center is not None: + vrange = max(vmax - center, center - vmin) + normlize = mpl.colors.Normalize(center - vrange, center + vrange) + cmin, cmax = normlize([vmin, vmax]) cc = np.linspace(cmin, cmax, 256) self.cmap = mpl.colors.ListedColormap(self.cmap(cc)) From dd7bdc654fb5e69e2c81eeafed74be13c2ba2dfa Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 23 May 2017 14:14:37 -0400 Subject: [PATCH 0390/1738] Update tests to cover new heatmap colormap behavior --- seaborn/tests/test_matrix.py | 47 ++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 831e1b91f9..40859e4432 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -97,22 +97,13 @@ def test_mask_input(self): npt.assert_array_equal(p.plot_data, plot_data[::-1]) - def test_default_sequential_vlims(self): + def test_default_vlims(self): p = mat._HeatMapper(self.df_unif, **self.default_kws) nt.assert_equal(p.vmin, self.x_unif.min()) nt.assert_equal(p.vmax, self.x_unif.max()) - nt.assert_true(not p.divergent) - def test_default_diverging_vlims(self): - - p = mat._HeatMapper(self.df_norm, **self.default_kws) - vlim = max(abs(self.x_norm.min()), abs(self.x_norm.max())) - nt.assert_equal(p.vmin, -vlim) - nt.assert_equal(p.vmax, vlim) - nt.assert_true(p.divergent) - - def test_robust_sequential_vlims(self): + def test_robust_vlims(self): kws = self.default_kws.copy() kws["robust"] = True @@ -136,9 +127,10 @@ def test_custom_diverging_vlims(self): kws = self.default_kws.copy() kws["vmin"] = -4 kws["vmax"] = 5 + kws["center"] = 0 p = mat._HeatMapper(self.df_norm, **kws) - nt.assert_equal(p.vmin, -5) + nt.assert_equal(p.vmin, -4) nt.assert_equal(p.vmax, 5) def test_array_with_nans(self): @@ -172,7 +164,7 @@ def test_custom_cmap(self): kws = self.default_kws.copy() kws["cmap"] = "BuGn" p = mat._HeatMapper(self.df_unif, **kws) - nt.assert_equal(p.cmap, "BuGn") + nt.assert_equal(p.cmap, mpl.cm.BuGn) def test_centered_vlims(self): @@ -181,8 +173,33 @@ def test_centered_vlims(self): p = mat._HeatMapper(self.df_unif, **kws) - nt.assert_true(p.divergent) - nt.assert_equal(p.vmax - .5, .5 - p.vmin) + nt.assert_equal(p.vmin, self.df_unif.values.min()) + nt.assert_equal(p.vmax, self.df_unif.values.max()) + + def test_default_colors(self): + + vals = np.linspace(.2, 1, 9) + cmap = mpl.cm.binary + ax = mat.heatmap([vals], cmap=cmap) + fc = ax.collections[0].get_facecolors() + cvals = np.linspace(0, 1, 9) + npt.assert_array_almost_equal(fc, cmap(cvals), 2) + + def test_custom_vlim_colors(self): + + vals = np.linspace(.2, 1, 9) + cmap = mpl.cm.binary + ax = mat.heatmap([vals], vmin=0, cmap=cmap) + fc = ax.collections[0].get_facecolors() + npt.assert_array_almost_equal(fc, cmap(vals), 2) + + def test_custom_center_colors(self): + + vals = np.linspace(.2, 1, 9) + cmap = mpl.cm.binary + ax = mat.heatmap([vals], center=.5, cmap=cmap) + fc = ax.collections[0].get_facecolors() + npt.assert_array_almost_equal(fc, cmap(vals), 2) def test_tickabels_off(self): kws = self.default_kws.copy() From fc705ca9836de74951fb2d0e739eed8cf910768f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 23 May 2017 14:14:49 -0400 Subject: [PATCH 0391/1738] Add information about changes in heatmap to release notes --- doc/releases/v0.8.0.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index aa67d18729..4576ff1668 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -2,6 +2,10 @@ v0.8.0 (Unreleased) ------------------- +- Changed the behavior of :func:`heatmap` (and by extension :func:`clustermap`) when plotting divergent dataesets (i.e. when the ``center`` parameter is used). Instead of extending the lower and upper limits of the colormap to be symettrical around the ``center`` value, the colormap is modified so that its middle color corresponds to ``center``. This means that the full range of the colormap will not be used (unless the data or specified ``vmin`` and ``vmax`` are symettric), but the upper and lower limits of the colorbar will correspond to the range of the data. See the Github pull request `(#1184) `_ for examples of the behavior. + +- Removed automatic detection of diverging data in :func:`heatmap` (and by extension :func:`clustermap`). If you want the colormap to be treated as diverging (see above), it is now necessary to specify the ``center`` value. When no colormap is specified, specifying ``center`` will still change the default to be one that is more appropriate for displaying diverging data. + - Added the ``dodge`` argument to :func:`boxplot`, :func:`violinplot`, and :func:`barplot` to allow use of ``hue`` without changing the position or width of the plot elements, as when the ``hue`` varible is not nested within the main categorical variable. - Added the ability to draw a colorbar for a bivariate :func:`kdeplot` with the ``cbar`` parameter (and related ``cbar_ax`` and ``cbar_kws`` parameters). From 54c4af29f7e49c8b7a85600f87af53d0d1d3b8e3 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 24 May 2017 12:49:01 -0400 Subject: [PATCH 0392/1738] Update heatmap docstring --- seaborn/matrix.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 9d8bb95201..0897df4e6f 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -298,10 +298,6 @@ def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, **kwargs): """Plot rectangular data as a color-encoded matrix. - This function tries to infer a good colormap to use from the data, but - this is not guaranteed to work, so take care to make sure the kind of - colormap (sequential or diverging) and its limits are appropriate. - This is an Axes-level function and will draw the heatmap into the currently-active Axes if none is provided to the ``ax`` argument. Part of this Axes space will be taken and used to plot a colormap, unless ``cbar`` @@ -315,15 +311,15 @@ def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, columns and rows. vmin, vmax : floats, optional Values to anchor the colormap, otherwise they are inferred from the - data and other keyword arguments. When a diverging dataset is inferred, - one of these values may be ignored. - cmap : matplotlib colormap name or object, optional + data and other keyword arguments. + cmap : matplotlib colormap name or object, or list of colors, optional The mapping from data values to color space. If not provided, this will be either a cubehelix map (if the function infers a sequential dataset) or ``RdBu_r`` (if the function infers a diverging dataset). center : float, optional - The value at which to center the colormap. Passing this value implies - use of a diverging colormap. + The value at which to center the colormap when plotting divergant data. + Using this parameter will change the default ``cmap`` if none is + specified. robust : bool, optional If True and ``vmin`` or ``vmax`` are absent, the colormap range is computed with robust quantiles instead of the extreme values. @@ -352,12 +348,12 @@ def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, ax : matplotlib Axes, optional Axes in which to draw the plot, otherwise use the currently-active Axes. - xticklabels : list-like, int, or bool, optional + xticklabels : bool, list-like, or int, optional If True, plot the column names of the dataframe. If False, don't plot the column names. If list-like, plot these alternate labels as the xticklabels. If an integer, use the column names but plot only every n label. - yticklabels : list-like, int, or bool, optional + yticklabels : bool, list-like, or int, optional If True, plot the row names of the dataframe. If False, don't plot the row names. If list-like, plot these alternate labels as the yticklabels. If an integer, use the index names but plot only every @@ -373,6 +369,11 @@ def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, ax : matplotlib Axes Axes object with the heatmap. + See also + -------- + clustermap : Plot a matrix using hierachical clustering to arrange the + rows and columns. + Examples -------- @@ -393,13 +394,13 @@ def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, >>> ax = sns.heatmap(uniform_data, vmin=0, vmax=1) - Plot a heatmap for data centered on 0: + Plot a heatmap for data centered on 0 with a diverging colormap: .. plot:: :context: close-figs >>> normal_data = np.random.randn(10, 12) - >>> ax = sns.heatmap(normal_data) + >>> ax = sns.heatmap(normal_data, center=0) Plot a dataframe with meaningful row and column labels: From 1645dc4de70fe54d9297249d1b30a8b9848e2ce0 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 20 Jun 2017 21:03:48 -0400 Subject: [PATCH 0393/1738] Add new seaborn colormaps --- seaborn/__init__.py | 1 + seaborn/cm.py | 1052 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1053 insertions(+) create mode 100644 seaborn/cm.py diff --git a/seaborn/__init__.py b/seaborn/__init__.py index 9aa7c5cb8e..733ff8eb47 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -16,6 +16,7 @@ from .widgets import * from .xkcd_rgb import xkcd_rgb from .crayons import crayons +import cm # Set default aesthetics set() diff --git a/seaborn/cm.py b/seaborn/cm.py new file mode 100644 index 0000000000..552bd3ba33 --- /dev/null +++ b/seaborn/cm.py @@ -0,0 +1,1052 @@ +from matplotlib import colors, cm as mpl_cm + + +__all__ = ["rocket", "mako", "vlag", "fmri", + "rocket_r", "mako_r", "vlag_r", "fmri_r"] + + +_rocket_lut = [[ 0.01060815, 0.01808215, 0.10018654], + [ 0.01428972, 0.02048237, 0.10374486], + [ 0.01831941, 0.0229766 , 0.10738511], + [ 0.02275049, 0.02554464, 0.11108639], + [ 0.02759119, 0.02818316, 0.11483751], + [ 0.03285175, 0.03088792, 0.11863035], + [ 0.03853466, 0.03365771, 0.12245873], + [ 0.04447016, 0.03648425, 0.12631831], + [ 0.05032105, 0.03936808, 0.13020508], + [ 0.05611171, 0.04224835, 0.13411624], + [ 0.0618531 , 0.04504866, 0.13804929], + [ 0.06755457, 0.04778179, 0.14200206], + [ 0.0732236 , 0.05045047, 0.14597263], + [ 0.0788708 , 0.05305461, 0.14995981], + [ 0.08450105, 0.05559631, 0.15396203], + [ 0.09011319, 0.05808059, 0.15797687], + [ 0.09572396, 0.06050127, 0.16200507], + [ 0.10132312, 0.06286782, 0.16604287], + [ 0.10692823, 0.06517224, 0.17009175], + [ 0.1125315 , 0.06742194, 0.17414848], + [ 0.11813947, 0.06961499, 0.17821272], + [ 0.12375803, 0.07174938, 0.18228425], + [ 0.12938228, 0.07383015, 0.18636053], + [ 0.13501631, 0.07585609, 0.19044109], + [ 0.14066867, 0.0778224 , 0.19452676], + [ 0.14633406, 0.07973393, 0.1986151 ], + [ 0.15201338, 0.08159108, 0.20270523], + [ 0.15770877, 0.08339312, 0.20679668], + [ 0.16342174, 0.0851396 , 0.21088893], + [ 0.16915387, 0.08682996, 0.21498104], + [ 0.17489524, 0.08848235, 0.2190294 ], + [ 0.18065495, 0.09009031, 0.22303512], + [ 0.18643324, 0.09165431, 0.22699705], + [ 0.19223028, 0.09317479, 0.23091409], + [ 0.19804623, 0.09465217, 0.23478512], + [ 0.20388117, 0.09608689, 0.23860907], + [ 0.20973515, 0.09747934, 0.24238489], + [ 0.21560818, 0.09882993, 0.24611154], + [ 0.22150014, 0.10013944, 0.2497868 ], + [ 0.22741085, 0.10140876, 0.25340813], + [ 0.23334047, 0.10263737, 0.25697736], + [ 0.23928891, 0.10382562, 0.2604936 ], + [ 0.24525608, 0.10497384, 0.26395596], + [ 0.25124182, 0.10608236, 0.26736359], + [ 0.25724602, 0.10715148, 0.27071569], + [ 0.26326851, 0.1081815 , 0.27401148], + [ 0.26930915, 0.1091727 , 0.2772502 ], + [ 0.27536766, 0.11012568, 0.28043021], + [ 0.28144375, 0.11104133, 0.2835489 ], + [ 0.2875374 , 0.11191896, 0.28660853], + [ 0.29364846, 0.11275876, 0.2896085 ], + [ 0.29977678, 0.11356089, 0.29254823], + [ 0.30592213, 0.11432553, 0.29542718], + [ 0.31208435, 0.11505284, 0.29824485], + [ 0.31826327, 0.1157429 , 0.30100076], + [ 0.32445869, 0.11639585, 0.30369448], + [ 0.33067031, 0.11701189, 0.30632563], + [ 0.33689808, 0.11759095, 0.3088938 ], + [ 0.34314168, 0.11813362, 0.31139721], + [ 0.34940101, 0.11863987, 0.3138355 ], + [ 0.355676 , 0.11910909, 0.31620996], + [ 0.36196644, 0.1195413 , 0.31852037], + [ 0.36827206, 0.11993653, 0.32076656], + [ 0.37459292, 0.12029443, 0.32294825], + [ 0.38092887, 0.12061482, 0.32506528], + [ 0.38727975, 0.12089756, 0.3271175 ], + [ 0.39364518, 0.12114272, 0.32910494], + [ 0.40002537, 0.12134964, 0.33102734], + [ 0.40642019, 0.12151801, 0.33288464], + [ 0.41282936, 0.12164769, 0.33467689], + [ 0.41925278, 0.12173833, 0.33640407], + [ 0.42569057, 0.12178916, 0.33806605], + [ 0.43214263, 0.12179973, 0.33966284], + [ 0.43860848, 0.12177004, 0.34119475], + [ 0.44508855, 0.12169883, 0.34266151], + [ 0.45158266, 0.12158557, 0.34406324], + [ 0.45809049, 0.12142996, 0.34540024], + [ 0.46461238, 0.12123063, 0.34667231], + [ 0.47114798, 0.12098721, 0.34787978], + [ 0.47769736, 0.12069864, 0.34902273], + [ 0.48426077, 0.12036349, 0.35010104], + [ 0.49083761, 0.11998161, 0.35111537], + [ 0.49742847, 0.11955087, 0.35206533], + [ 0.50403286, 0.11907081, 0.35295152], + [ 0.51065109, 0.11853959, 0.35377385], + [ 0.51728314, 0.1179558 , 0.35453252], + [ 0.52392883, 0.11731817, 0.35522789], + [ 0.53058853, 0.11662445, 0.35585982], + [ 0.53726173, 0.11587369, 0.35642903], + [ 0.54394898, 0.11506307, 0.35693521], + [ 0.5506426 , 0.11420757, 0.35737863], + [ 0.55734473, 0.11330456, 0.35775059], + [ 0.56405586, 0.11235265, 0.35804813], + [ 0.57077365, 0.11135597, 0.35827146], + [ 0.5774991 , 0.11031233, 0.35841679], + [ 0.58422945, 0.10922707, 0.35848469], + [ 0.59096382, 0.10810205, 0.35847347], + [ 0.59770215, 0.10693774, 0.35838029], + [ 0.60444226, 0.10573912, 0.35820487], + [ 0.61118304, 0.10450943, 0.35794557], + [ 0.61792306, 0.10325288, 0.35760108], + [ 0.62466162, 0.10197244, 0.35716891], + [ 0.63139686, 0.10067417, 0.35664819], + [ 0.63812122, 0.09938212, 0.35603757], + [ 0.64483795, 0.0980891 , 0.35533555], + [ 0.65154562, 0.09680192, 0.35454107], + [ 0.65824241, 0.09552918, 0.3536529 ], + [ 0.66492652, 0.09428017, 0.3526697 ], + [ 0.67159578, 0.09306598, 0.35159077], + [ 0.67824099, 0.09192342, 0.3504148 ], + [ 0.684863 , 0.09085633, 0.34914061], + [ 0.69146268, 0.0898675 , 0.34776864], + [ 0.69803757, 0.08897226, 0.3462986 ], + [ 0.70457834, 0.0882129 , 0.34473046], + [ 0.71108138, 0.08761223, 0.3430635 ], + [ 0.7175507 , 0.08716212, 0.34129974], + [ 0.72398193, 0.08688725, 0.33943958], + [ 0.73035829, 0.0868623 , 0.33748452], + [ 0.73669146, 0.08704683, 0.33543669], + [ 0.74297501, 0.08747196, 0.33329799], + [ 0.74919318, 0.08820542, 0.33107204], + [ 0.75535825, 0.08919792, 0.32876184], + [ 0.76145589, 0.09050716, 0.32637117], + [ 0.76748424, 0.09213602, 0.32390525], + [ 0.77344838, 0.09405684, 0.32136808], + [ 0.77932641, 0.09634794, 0.31876642], + [ 0.78513609, 0.09892473, 0.31610488], + [ 0.79085854, 0.10184672, 0.313391 ], + [ 0.7965014 , 0.10506637, 0.31063031], + [ 0.80205987, 0.10858333, 0.30783 ], + [ 0.80752799, 0.11239964, 0.30499738], + [ 0.81291606, 0.11645784, 0.30213802], + [ 0.81820481, 0.12080606, 0.29926105], + [ 0.82341472, 0.12535343, 0.2963705 ], + [ 0.82852822, 0.13014118, 0.29347474], + [ 0.83355779, 0.13511035, 0.29057852], + [ 0.83850183, 0.14025098, 0.2876878 ], + [ 0.84335441, 0.14556683, 0.28480819], + [ 0.84813096, 0.15099892, 0.281943 ], + [ 0.85281737, 0.15657772, 0.27909826], + [ 0.85742602, 0.1622583 , 0.27627462], + [ 0.86196552, 0.16801239, 0.27346473], + [ 0.86641628, 0.17387796, 0.27070818], + [ 0.87079129, 0.17982114, 0.26797378], + [ 0.87507281, 0.18587368, 0.26529697], + [ 0.87925878, 0.19203259, 0.26268136], + [ 0.8833417 , 0.19830556, 0.26014181], + [ 0.88731387, 0.20469941, 0.25769539], + [ 0.89116859, 0.21121788, 0.2553592 ], + [ 0.89490337, 0.21785614, 0.25314362], + [ 0.8985026 , 0.22463251, 0.25108745], + [ 0.90197527, 0.23152063, 0.24918223], + [ 0.90530097, 0.23854541, 0.24748098], + [ 0.90848638, 0.24568473, 0.24598324], + [ 0.911533 , 0.25292623, 0.24470258], + [ 0.9144225 , 0.26028902, 0.24369359], + [ 0.91717106, 0.26773821, 0.24294137], + [ 0.91978131, 0.27526191, 0.24245973], + [ 0.92223947, 0.28287251, 0.24229568], + [ 0.92456587, 0.29053388, 0.24242622], + [ 0.92676657, 0.29823282, 0.24285536], + [ 0.92882964, 0.30598085, 0.24362274], + [ 0.93078135, 0.31373977, 0.24468803], + [ 0.93262051, 0.3215093 , 0.24606461], + [ 0.93435067, 0.32928362, 0.24775328], + [ 0.93599076, 0.33703942, 0.24972157], + [ 0.93752831, 0.34479177, 0.25199928], + [ 0.93899289, 0.35250734, 0.25452808], + [ 0.94036561, 0.36020899, 0.25734661], + [ 0.94167588, 0.36786594, 0.2603949 ], + [ 0.94291042, 0.37549479, 0.26369821], + [ 0.94408513, 0.3830811 , 0.26722004], + [ 0.94520419, 0.39062329, 0.27094924], + [ 0.94625977, 0.39813168, 0.27489742], + [ 0.94727016, 0.4055909 , 0.27902322], + [ 0.94823505, 0.41300424, 0.28332283], + [ 0.94914549, 0.42038251, 0.28780969], + [ 0.95001704, 0.42771398, 0.29244728], + [ 0.95085121, 0.43500005, 0.29722817], + [ 0.95165009, 0.44224144, 0.30214494], + [ 0.9524044 , 0.44944853, 0.3072105 ], + [ 0.95312556, 0.45661389, 0.31239776], + [ 0.95381595, 0.46373781, 0.31769923], + [ 0.95447591, 0.47082238, 0.32310953], + [ 0.95510255, 0.47787236, 0.32862553], + [ 0.95569679, 0.48489115, 0.33421404], + [ 0.95626788, 0.49187351, 0.33985601], + [ 0.95681685, 0.49882008, 0.34555431], + [ 0.9573439 , 0.50573243, 0.35130912], + [ 0.95784842, 0.51261283, 0.35711942], + [ 0.95833051, 0.51946267, 0.36298589], + [ 0.95879054, 0.52628305, 0.36890904], + [ 0.95922872, 0.53307513, 0.3748895 ], + [ 0.95964538, 0.53983991, 0.38092784], + [ 0.96004345, 0.54657593, 0.3870292 ], + [ 0.96042097, 0.55328624, 0.39319057], + [ 0.96077819, 0.55997184, 0.39941173], + [ 0.9611152 , 0.5666337 , 0.40569343], + [ 0.96143273, 0.57327231, 0.41203603], + [ 0.96173392, 0.57988594, 0.41844491], + [ 0.96201757, 0.58647675, 0.42491751], + [ 0.96228344, 0.59304598, 0.43145271], + [ 0.96253168, 0.5995944 , 0.43805131], + [ 0.96276513, 0.60612062, 0.44471698], + [ 0.96298491, 0.6126247 , 0.45145074], + [ 0.96318967, 0.61910879, 0.45824902], + [ 0.96337949, 0.6255736 , 0.46511271], + [ 0.96355923, 0.63201624, 0.47204746], + [ 0.96372785, 0.63843852, 0.47905028], + [ 0.96388426, 0.64484214, 0.4861196 ], + [ 0.96403203, 0.65122535, 0.4932578 ], + [ 0.96417332, 0.65758729, 0.50046894], + [ 0.9643063 , 0.66393045, 0.5077467 ], + [ 0.96443322, 0.67025402, 0.51509334], + [ 0.96455845, 0.67655564, 0.52251447], + [ 0.96467922, 0.68283846, 0.53000231], + [ 0.96479861, 0.68910113, 0.53756026], + [ 0.96492035, 0.69534192, 0.5451917 ], + [ 0.96504223, 0.7015636 , 0.5528892 ], + [ 0.96516917, 0.70776351, 0.5606593 ], + [ 0.96530224, 0.71394212, 0.56849894], + [ 0.96544032, 0.72010124, 0.57640375], + [ 0.96559206, 0.72623592, 0.58438387], + [ 0.96575293, 0.73235058, 0.59242739], + [ 0.96592829, 0.73844258, 0.60053991], + [ 0.96612013, 0.74451182, 0.60871954], + [ 0.96632832, 0.75055966, 0.61696136], + [ 0.96656022, 0.75658231, 0.62527295], + [ 0.96681185, 0.76258381, 0.63364277], + [ 0.96709183, 0.76855969, 0.64207921], + [ 0.96739773, 0.77451297, 0.65057302], + [ 0.96773482, 0.78044149, 0.65912731], + [ 0.96810471, 0.78634563, 0.66773889], + [ 0.96850919, 0.79222565, 0.6764046 ], + [ 0.96893132, 0.79809112, 0.68512266], + [ 0.96935926, 0.80395415, 0.69383201], + [ 0.9698028 , 0.80981139, 0.70252255], + [ 0.97025511, 0.81566605, 0.71120296], + [ 0.97071849, 0.82151775, 0.71987163], + [ 0.97120159, 0.82736371, 0.72851999], + [ 0.97169389, 0.83320847, 0.73716071], + [ 0.97220061, 0.83905052, 0.74578903], + [ 0.97272597, 0.84488881, 0.75440141], + [ 0.97327085, 0.85072354, 0.76299805], + [ 0.97383206, 0.85655639, 0.77158353], + [ 0.97441222, 0.86238689, 0.78015619], + [ 0.97501782, 0.86821321, 0.78871034], + [ 0.97564391, 0.87403763, 0.79725261], + [ 0.97628674, 0.87986189, 0.8057883 ], + [ 0.97696114, 0.88568129, 0.81430324], + [ 0.97765722, 0.89149971, 0.82280948], + [ 0.97837585, 0.89731727, 0.83130786], + [ 0.97912374, 0.90313207, 0.83979337], + [ 0.979891 , 0.90894778, 0.84827858], + [ 0.98067764, 0.91476465, 0.85676611], + [ 0.98137749, 0.92061729, 0.86536915]] + + +_mako_lut = [[ 0.04503935, 0.01482344, 0.02092227], + [ 0.04933018, 0.01709292, 0.02535719], + [ 0.05356262, 0.01950702, 0.03018802], + [ 0.05774337, 0.02205989, 0.03545515], + [ 0.06188095, 0.02474764, 0.04115287], + [ 0.06598247, 0.0275665 , 0.04691409], + [ 0.07005374, 0.03051278, 0.05264306], + [ 0.07409947, 0.03358324, 0.05834631], + [ 0.07812339, 0.03677446, 0.06403249], + [ 0.08212852, 0.0400833 , 0.06970862], + [ 0.08611731, 0.04339148, 0.07538208], + [ 0.09009161, 0.04664706, 0.08105568], + [ 0.09405308, 0.04985685, 0.08673591], + [ 0.09800301, 0.05302279, 0.09242646], + [ 0.10194255, 0.05614641, 0.09813162], + [ 0.10587261, 0.05922941, 0.103854 ], + [ 0.1097942 , 0.06227277, 0.10959847], + [ 0.11370826, 0.06527747, 0.11536893], + [ 0.11761516, 0.06824548, 0.12116393], + [ 0.12151575, 0.07117741, 0.12698763], + [ 0.12541095, 0.07407363, 0.1328442 ], + [ 0.12930083, 0.07693611, 0.13873064], + [ 0.13317849, 0.07976988, 0.14465095], + [ 0.13701138, 0.08259683, 0.15060265], + [ 0.14079223, 0.08542126, 0.15659379], + [ 0.14452486, 0.08824175, 0.16262484], + [ 0.14820351, 0.09106304, 0.16869476], + [ 0.15183185, 0.09388372, 0.17480366], + [ 0.15540398, 0.09670855, 0.18094993], + [ 0.15892417, 0.09953561, 0.18713384], + [ 0.16238588, 0.10236998, 0.19335329], + [ 0.16579435, 0.10520905, 0.19960847], + [ 0.16914226, 0.10805832, 0.20589698], + [ 0.17243586, 0.11091443, 0.21221911], + [ 0.17566717, 0.11378321, 0.21857219], + [ 0.17884322, 0.11666074, 0.2249565 ], + [ 0.18195582, 0.11955283, 0.23136943], + [ 0.18501213, 0.12245547, 0.23781116], + [ 0.18800459, 0.12537395, 0.24427914], + [ 0.19093944, 0.1283047 , 0.25077369], + [ 0.19381092, 0.13125179, 0.25729255], + [ 0.19662307, 0.13421303, 0.26383543], + [ 0.19937337, 0.13719028, 0.27040111], + [ 0.20206187, 0.14018372, 0.27698891], + [ 0.20469116, 0.14319196, 0.28359861], + [ 0.20725547, 0.14621882, 0.29022775], + [ 0.20976258, 0.14925954, 0.29687795], + [ 0.21220409, 0.15231929, 0.30354703], + [ 0.21458611, 0.15539445, 0.31023563], + [ 0.21690827, 0.15848519, 0.31694355], + [ 0.21916481, 0.16159489, 0.32366939], + [ 0.2213631 , 0.16471913, 0.33041431], + [ 0.22349947, 0.1678599 , 0.33717781], + [ 0.2255714 , 0.1710185 , 0.34395925], + [ 0.22758415, 0.17419169, 0.35075983], + [ 0.22953569, 0.17738041, 0.35757941], + [ 0.23142077, 0.18058733, 0.3644173 ], + [ 0.2332454 , 0.18380872, 0.37127514], + [ 0.2350092 , 0.18704459, 0.3781528 ], + [ 0.23670785, 0.190297 , 0.38504973], + [ 0.23834119, 0.19356547, 0.39196711], + [ 0.23991189, 0.19684817, 0.39890581], + [ 0.24141903, 0.20014508, 0.4058667 ], + [ 0.24286214, 0.20345642, 0.4128484 ], + [ 0.24423453, 0.20678459, 0.41985299], + [ 0.24554109, 0.21012669, 0.42688124], + [ 0.2467815 , 0.21348266, 0.43393244], + [ 0.24795393, 0.21685249, 0.4410088 ], + [ 0.24905614, 0.22023618, 0.448113 ], + [ 0.25007383, 0.22365053, 0.45519562], + [ 0.25098926, 0.22710664, 0.46223892], + [ 0.25179696, 0.23060342, 0.46925447], + [ 0.25249346, 0.23414353, 0.47623196], + [ 0.25307401, 0.23772973, 0.48316271], + [ 0.25353152, 0.24136961, 0.49001976], + [ 0.25386167, 0.24506548, 0.49679407], + [ 0.25406082, 0.2488164 , 0.50348932], + [ 0.25412435, 0.25262843, 0.51007843], + [ 0.25404842, 0.25650743, 0.51653282], + [ 0.25383134, 0.26044852, 0.52286845], + [ 0.2534705 , 0.26446165, 0.52903422], + [ 0.25296722, 0.2685428 , 0.53503572], + [ 0.2523226 , 0.27269346, 0.54085315], + [ 0.25153974, 0.27691629, 0.54645752], + [ 0.25062402, 0.28120467, 0.55185939], + [ 0.24958205, 0.28556371, 0.55701246], + [ 0.24842386, 0.28998148, 0.56194601], + [ 0.24715928, 0.29446327, 0.56660884], + [ 0.24580099, 0.29899398, 0.57104399], + [ 0.24436202, 0.30357852, 0.57519929], + [ 0.24285591, 0.30819938, 0.57913247], + [ 0.24129828, 0.31286235, 0.58278615], + [ 0.23970131, 0.3175495 , 0.5862272 ], + [ 0.23807973, 0.32226344, 0.58941872], + [ 0.23644557, 0.32699241, 0.59240198], + [ 0.2348113 , 0.33173196, 0.59518282], + [ 0.23318874, 0.33648036, 0.59775543], + [ 0.2315855 , 0.34122763, 0.60016456], + [ 0.23001121, 0.34597357, 0.60240251], + [ 0.2284748 , 0.35071512, 0.6044784 ], + [ 0.22698081, 0.35544612, 0.60642528], + [ 0.22553305, 0.36016515, 0.60825252], + [ 0.22413977, 0.36487341, 0.60994938], + [ 0.22280246, 0.36956728, 0.61154118], + [ 0.22152555, 0.37424409, 0.61304472], + [ 0.22030752, 0.37890437, 0.61446646], + [ 0.2191538 , 0.38354668, 0.61581561], + [ 0.21806257, 0.38817169, 0.61709794], + [ 0.21703799, 0.39277882, 0.61831922], + [ 0.21607792, 0.39736958, 0.61948028], + [ 0.21518463, 0.40194196, 0.62059763], + [ 0.21435467, 0.40649717, 0.62167507], + [ 0.21358663, 0.41103579, 0.62271724], + [ 0.21288172, 0.41555771, 0.62373011], + [ 0.21223835, 0.42006355, 0.62471794], + [ 0.21165312, 0.42455441, 0.62568371], + [ 0.21112526, 0.42903064, 0.6266318 ], + [ 0.21065161, 0.43349321, 0.62756504], + [ 0.21023306, 0.43794288, 0.62848279], + [ 0.20985996, 0.44238227, 0.62938329], + [ 0.20951045, 0.44680966, 0.63030696], + [ 0.20916709, 0.45122981, 0.63124483], + [ 0.20882976, 0.45564335, 0.63219599], + [ 0.20849798, 0.46005094, 0.63315928], + [ 0.20817199, 0.46445309, 0.63413391], + [ 0.20785149, 0.46885041, 0.63511876], + [ 0.20753716, 0.47324327, 0.63611321], + [ 0.20722876, 0.47763224, 0.63711608], + [ 0.20692679, 0.48201774, 0.63812656], + [ 0.20663156, 0.48640018, 0.63914367], + [ 0.20634336, 0.49078002, 0.64016638], + [ 0.20606303, 0.49515755, 0.6411939 ], + [ 0.20578999, 0.49953341, 0.64222457], + [ 0.20552612, 0.50390766, 0.64325811], + [ 0.20527189, 0.50828072, 0.64429331], + [ 0.20502868, 0.51265277, 0.64532947], + [ 0.20479718, 0.51702417, 0.64636539], + [ 0.20457804, 0.52139527, 0.64739979], + [ 0.20437304, 0.52576622, 0.64843198], + [ 0.20418396, 0.53013715, 0.64946117], + [ 0.20401238, 0.53450825, 0.65048638], + [ 0.20385896, 0.53887991, 0.65150606], + [ 0.20372653, 0.54325208, 0.65251978], + [ 0.20361709, 0.5476249 , 0.6535266 ], + [ 0.20353258, 0.55199854, 0.65452542], + [ 0.20347472, 0.55637318, 0.655515 ], + [ 0.20344718, 0.56074869, 0.65649508], + [ 0.20345161, 0.56512531, 0.65746419], + [ 0.20349089, 0.56950304, 0.65842151], + [ 0.20356842, 0.57388184, 0.65936642], + [ 0.20368663, 0.57826181, 0.66029768], + [ 0.20384884, 0.58264293, 0.6612145 ], + [ 0.20405904, 0.58702506, 0.66211645], + [ 0.20431921, 0.59140842, 0.66300179], + [ 0.20463464, 0.59579264, 0.66387079], + [ 0.20500731, 0.60017798, 0.66472159], + [ 0.20544449, 0.60456387, 0.66555409], + [ 0.20596097, 0.60894927, 0.66636568], + [ 0.20654832, 0.61333521, 0.66715744], + [ 0.20721003, 0.61772167, 0.66792838], + [ 0.20795035, 0.62210845, 0.66867802], + [ 0.20877302, 0.62649546, 0.66940555], + [ 0.20968223, 0.63088252, 0.6701105 ], + [ 0.21068163, 0.63526951, 0.67079211], + [ 0.21177544, 0.63965621, 0.67145005], + [ 0.21298582, 0.64404072, 0.67208182], + [ 0.21430361, 0.64842404, 0.67268861], + [ 0.21572716, 0.65280655, 0.67326978], + [ 0.21726052, 0.65718791, 0.6738255 ], + [ 0.21890636, 0.66156803, 0.67435491], + [ 0.220668 , 0.66594665, 0.67485792], + [ 0.22255447, 0.67032297, 0.67533374], + [ 0.22458372, 0.67469531, 0.67578061], + [ 0.22673713, 0.67906542, 0.67620044], + [ 0.22901625, 0.6834332 , 0.67659251], + [ 0.23142316, 0.68779836, 0.67695703], + [ 0.23395924, 0.69216072, 0.67729378], + [ 0.23663857, 0.69651881, 0.67760151], + [ 0.23946645, 0.70087194, 0.67788018], + [ 0.24242624, 0.70522162, 0.67813088], + [ 0.24549008, 0.70957083, 0.67835215], + [ 0.24863372, 0.71392166, 0.67854868], + [ 0.25187832, 0.71827158, 0.67872193], + [ 0.25524083, 0.72261873, 0.67887024], + [ 0.25870947, 0.72696469, 0.67898912], + [ 0.26229238, 0.73130855, 0.67907645], + [ 0.26604085, 0.73564353, 0.67914062], + [ 0.26993099, 0.73997282, 0.67917264], + [ 0.27397488, 0.74429484, 0.67917096], + [ 0.27822463, 0.74860229, 0.67914468], + [ 0.28264201, 0.75290034, 0.67907959], + [ 0.2873016 , 0.75717817, 0.67899164], + [ 0.29215894, 0.76144162, 0.67886578], + [ 0.29729823, 0.76567816, 0.67871894], + [ 0.30268199, 0.76989232, 0.67853896], + [ 0.30835665, 0.77407636, 0.67833512], + [ 0.31435139, 0.77822478, 0.67811118], + [ 0.3206671 , 0.78233575, 0.67786729], + [ 0.32733158, 0.78640315, 0.67761027], + [ 0.33437168, 0.79042043, 0.67734882], + [ 0.34182112, 0.79437948, 0.67709394], + [ 0.34968889, 0.79827511, 0.67685638], + [ 0.35799244, 0.80210037, 0.67664969], + [ 0.36675371, 0.80584651, 0.67649539], + [ 0.3759816 , 0.80950627, 0.67641393], + [ 0.38566792, 0.81307432, 0.67642947], + [ 0.39579804, 0.81654592, 0.67656899], + [ 0.40634556, 0.81991799, 0.67686215], + [ 0.41730243, 0.82318339, 0.67735255], + [ 0.4285828 , 0.82635051, 0.6780564 ], + [ 0.44012728, 0.82942353, 0.67900049], + [ 0.45189421, 0.83240398, 0.68021733], + [ 0.46378379, 0.83530763, 0.6817062 ], + [ 0.47573199, 0.83814472, 0.68347352], + [ 0.48769865, 0.84092197, 0.68552698], + [ 0.49962354, 0.84365379, 0.68783929], + [ 0.5114027 , 0.8463718 , 0.69029789], + [ 0.52301693, 0.84908401, 0.69288545], + [ 0.53447549, 0.85179048, 0.69561066], + [ 0.54578602, 0.8544913 , 0.69848331], + [ 0.55695565, 0.85718723, 0.70150427], + [ 0.56798832, 0.85987893, 0.70468261], + [ 0.57888639, 0.86256715, 0.70802931], + [ 0.5896541 , 0.8652532 , 0.71154204], + [ 0.60028928, 0.86793835, 0.71523675], + [ 0.61079441, 0.87062438, 0.71910895], + [ 0.62116633, 0.87331311, 0.72317003], + [ 0.63140509, 0.87600675, 0.72741689], + [ 0.64150735, 0.87870746, 0.73185717], + [ 0.65147219, 0.8814179 , 0.73648495], + [ 0.66129632, 0.8841403 , 0.74130658], + [ 0.67097934, 0.88687758, 0.74631123], + [ 0.68051833, 0.88963189, 0.75150483], + [ 0.68991419, 0.89240612, 0.75687187], + [ 0.69916533, 0.89520211, 0.76241714], + [ 0.70827373, 0.89802257, 0.76812286], + [ 0.71723995, 0.90086891, 0.77399039], + [ 0.72606665, 0.90374337, 0.7800041 ], + [ 0.73475675, 0.90664718, 0.78615802], + [ 0.74331358, 0.90958151, 0.79244474], + [ 0.75174143, 0.91254787, 0.79884925], + [ 0.76004473, 0.91554656, 0.80536823], + [ 0.76827704, 0.91856549, 0.81196513], + [ 0.77647029, 0.921603 , 0.81855729], + [ 0.78462009, 0.92466151, 0.82514119], + [ 0.79273542, 0.92773848, 0.83172131], + [ 0.8008109 , 0.93083672, 0.83829355], + [ 0.80885107, 0.93395528, 0.84485982], + [ 0.81685878, 0.9370938 , 0.85142101], + [ 0.82483206, 0.94025378, 0.8579751 ], + [ 0.83277661, 0.94343371, 0.86452477], + [ 0.84069127, 0.94663473, 0.87106853], + [ 0.84857662, 0.9498573 , 0.8776059 ], + [ 0.8564431 , 0.95309792, 0.88414253], + [ 0.86429066, 0.95635719, 0.89067759], + [ 0.87218969, 0.95960708, 0.89725384]] + + +_vlag_lut = [[ 0.13850039, 0.41331206, 0.74052025], + [ 0.15077609, 0.41762684, 0.73970427], + [ 0.16235219, 0.4219191 , 0.7389667 ], + [ 0.1733322 , 0.42619024, 0.73832537], + [ 0.18382538, 0.43044226, 0.73776764], + [ 0.19394034, 0.4346772 , 0.73725867], + [ 0.20367115, 0.43889576, 0.73685314], + [ 0.21313625, 0.44310003, 0.73648045], + [ 0.22231173, 0.44729079, 0.73619681], + [ 0.23125148, 0.45146945, 0.73597803], + [ 0.23998101, 0.45563715, 0.7358223 ], + [ 0.24853358, 0.45979489, 0.73571524], + [ 0.25691416, 0.4639437 , 0.73566943], + [ 0.26513894, 0.46808455, 0.73568319], + [ 0.27322194, 0.47221835, 0.73575497], + [ 0.28117543, 0.47634598, 0.73588332], + [ 0.28901021, 0.48046826, 0.73606686], + [ 0.2967358 , 0.48458597, 0.73630433], + [ 0.30436071, 0.48869986, 0.73659451], + [ 0.3118955 , 0.49281055, 0.73693255], + [ 0.31935389, 0.49691847, 0.73730851], + [ 0.32672701, 0.5010247 , 0.73774013], + [ 0.33402607, 0.50512971, 0.73821941], + [ 0.34125337, 0.50923419, 0.73874905], + [ 0.34840921, 0.51333892, 0.73933402], + [ 0.35551826, 0.51744353, 0.73994642], + [ 0.3625676 , 0.52154929, 0.74060763], + [ 0.36956356, 0.52565656, 0.74131327], + [ 0.37649902, 0.52976642, 0.74207698], + [ 0.38340273, 0.53387791, 0.74286286], + [ 0.39025859, 0.53799253, 0.7436962 ], + [ 0.39706821, 0.54211081, 0.744578 ], + [ 0.40384046, 0.54623277, 0.74549872], + [ 0.41058241, 0.55035849, 0.74645094], + [ 0.41728385, 0.55448919, 0.74745174], + [ 0.42395178, 0.55862494, 0.74849357], + [ 0.4305964 , 0.56276546, 0.74956387], + [ 0.4372044 , 0.56691228, 0.75068412], + [ 0.4437909 , 0.57106468, 0.75183427], + [ 0.45035117, 0.5752235 , 0.75302312], + [ 0.45687824, 0.57938983, 0.75426297], + [ 0.46339713, 0.58356191, 0.75551816], + [ 0.46988778, 0.58774195, 0.75682037], + [ 0.47635605, 0.59192986, 0.75816245], + [ 0.48281101, 0.5961252 , 0.75953212], + [ 0.4892374 , 0.60032986, 0.76095418], + [ 0.49566225, 0.60454154, 0.76238852], + [ 0.50206137, 0.60876307, 0.76387371], + [ 0.50845128, 0.61299312, 0.76538551], + [ 0.5148258 , 0.61723272, 0.76693475], + [ 0.52118385, 0.62148236, 0.76852436], + [ 0.52753571, 0.62574126, 0.77013939], + [ 0.53386831, 0.63001125, 0.77180152], + [ 0.54020159, 0.63429038, 0.7734803 ], + [ 0.54651272, 0.63858165, 0.77521306], + [ 0.55282975, 0.64288207, 0.77695608], + [ 0.55912585, 0.64719519, 0.77875327], + [ 0.56542599, 0.65151828, 0.78056551], + [ 0.57170924, 0.65585426, 0.78242747], + [ 0.57799572, 0.6602009 , 0.78430751], + [ 0.58426817, 0.66456073, 0.78623458], + [ 0.590544 , 0.66893178, 0.78818117], + [ 0.59680758, 0.67331643, 0.79017369], + [ 0.60307553, 0.67771273, 0.79218572], + [ 0.60934065, 0.68212194, 0.79422987], + [ 0.61559495, 0.68654548, 0.7963202 ], + [ 0.62185554, 0.69098125, 0.79842918], + [ 0.62810662, 0.69543176, 0.80058381], + [ 0.63436425, 0.69989499, 0.80275812], + [ 0.64061445, 0.70437326, 0.80497621], + [ 0.6468706 , 0.70886488, 0.80721641], + [ 0.65312213, 0.7133717 , 0.80949719], + [ 0.65937818, 0.71789261, 0.81180392], + [ 0.66563334, 0.72242871, 0.81414642], + [ 0.67189155, 0.72697967, 0.81651872], + [ 0.67815314, 0.73154569, 0.81892097], + [ 0.68441395, 0.73612771, 0.82136094], + [ 0.69068321, 0.74072452, 0.82382353], + [ 0.69694776, 0.7453385 , 0.82633199], + [ 0.70322431, 0.74996721, 0.8288583 ], + [ 0.70949595, 0.75461368, 0.83143221], + [ 0.7157774 , 0.75927574, 0.83402904], + [ 0.72206299, 0.76395461, 0.83665922], + [ 0.72835227, 0.76865061, 0.8393242 ], + [ 0.73465238, 0.7733628 , 0.84201224], + [ 0.74094862, 0.77809393, 0.84474951], + [ 0.74725683, 0.78284158, 0.84750915], + [ 0.75357103, 0.78760701, 0.85030217], + [ 0.75988961, 0.79239077, 0.85313207], + [ 0.76621987, 0.79719185, 0.85598668], + [ 0.77255045, 0.8020125 , 0.85888658], + [ 0.77889241, 0.80685102, 0.86181298], + [ 0.78524572, 0.81170768, 0.86476656], + [ 0.79159841, 0.81658489, 0.86776906], + [ 0.79796459, 0.82148036, 0.8707962 ], + [ 0.80434168, 0.82639479, 0.87385315], + [ 0.8107221 , 0.83132983, 0.87695392], + [ 0.81711301, 0.8362844 , 0.88008641], + [ 0.82351479, 0.84125863, 0.88325045], + [ 0.82992772, 0.84625263, 0.88644594], + [ 0.83634359, 0.85126806, 0.8896878 ], + [ 0.84277295, 0.85630293, 0.89295721], + [ 0.84921192, 0.86135782, 0.89626076], + [ 0.85566206, 0.866432 , 0.89959467], + [ 0.86211514, 0.87152627, 0.90297183], + [ 0.86857483, 0.87663856, 0.90638248], + [ 0.87504231, 0.88176648, 0.90981938], + [ 0.88151194, 0.88690782, 0.91328493], + [ 0.88797938, 0.89205857, 0.91677544], + [ 0.89443865, 0.89721298, 0.9202854 ], + [ 0.90088204, 0.90236294, 0.92380601], + [ 0.90729768, 0.90749778, 0.92732797], + [ 0.91367037, 0.91260329, 0.93083814], + [ 0.91998105, 0.91766106, 0.93431861], + [ 0.92620596, 0.92264789, 0.93774647], + [ 0.93231683, 0.9275351 , 0.94109192], + [ 0.93827772, 0.9322888 , 0.94432312], + [ 0.94404755, 0.93686925, 0.94740137], + [ 0.94958284, 0.94123072, 0.95027696], + [ 0.95482682, 0.9453245 , 0.95291103], + [ 0.9597248 , 0.94909728, 0.95525103], + [ 0.96422552, 0.95249273, 0.95723271], + [ 0.96826161, 0.95545812, 0.95882188], + [ 0.97178458, 0.95793984, 0.95995705], + [ 0.97474105, 0.95989142, 0.96059997], + [ 0.97708604, 0.96127366, 0.96071853], + [ 0.97877855, 0.96205832, 0.96030095], + [ 0.97978484, 0.96222949, 0.95935496], + [ 0.9805997 , 0.96155216, 0.95813083], + [ 0.98152619, 0.95993719, 0.95639322], + [ 0.9819726 , 0.95766608, 0.95399269], + [ 0.98191855, 0.9547873 , 0.95098107], + [ 0.98138514, 0.95134771, 0.94740644], + [ 0.98040845, 0.94739906, 0.94332125], + [ 0.97902107, 0.94300131, 0.93878672], + [ 0.97729348, 0.93820409, 0.93385135], + [ 0.9752533 , 0.933073 , 0.92858252], + [ 0.97297834, 0.92765261, 0.92302309], + [ 0.97049104, 0.92200317, 0.91723505], + [ 0.96784372, 0.91616744, 0.91126063], + [ 0.96507281, 0.91018664, 0.90514124], + [ 0.96222034, 0.90409203, 0.89890756], + [ 0.9593079 , 0.89791478, 0.89259122], + [ 0.95635626, 0.89167908, 0.88621654], + [ 0.95338303, 0.88540373, 0.87980238], + [ 0.95040174, 0.87910333, 0.87336339], + [ 0.94742246, 0.87278899, 0.86691076], + [ 0.94445249, 0.86646893, 0.86045277], + [ 0.94150476, 0.86014606, 0.85399191], + [ 0.93857394, 0.85382798, 0.84753642], + [ 0.93566206, 0.84751766, 0.84108935], + [ 0.93277194, 0.8412164 , 0.83465197], + [ 0.92990106, 0.83492672, 0.82822708], + [ 0.92704736, 0.82865028, 0.82181656], + [ 0.92422703, 0.82238092, 0.81541333], + [ 0.92142581, 0.81612448, 0.80902415], + [ 0.91864501, 0.80988032, 0.80264838], + [ 0.91587578, 0.80365187, 0.79629001], + [ 0.9131367 , 0.79743115, 0.78994 ], + [ 0.91041602, 0.79122265, 0.78360361], + [ 0.90771071, 0.78502727, 0.77728196], + [ 0.90501581, 0.77884674, 0.7709771 ], + [ 0.90235365, 0.77267117, 0.76467793], + [ 0.8997019 , 0.76650962, 0.75839484], + [ 0.89705346, 0.76036481, 0.752131 ], + [ 0.89444021, 0.75422253, 0.74587047], + [ 0.89183355, 0.74809474, 0.73962689], + [ 0.88923216, 0.74198168, 0.73340061], + [ 0.88665892, 0.73587283, 0.72717995], + [ 0.88408839, 0.72977904, 0.72097718], + [ 0.88153537, 0.72369332, 0.71478461], + [ 0.87899389, 0.7176179 , 0.70860487], + [ 0.87645157, 0.71155805, 0.7024439 ], + [ 0.8739399 , 0.70549893, 0.6962854 ], + [ 0.87142626, 0.6994551 , 0.69014561], + [ 0.8689268 , 0.69341868, 0.68401597], + [ 0.86643562, 0.687392 , 0.67789917], + [ 0.86394434, 0.68137863, 0.67179927], + [ 0.86147586, 0.67536728, 0.665704 ], + [ 0.85899928, 0.66937226, 0.6596292 ], + [ 0.85654668, 0.66337773, 0.6535577 ], + [ 0.85408818, 0.65739772, 0.64750494], + [ 0.85164413, 0.65142189, 0.64145983], + [ 0.84920091, 0.6454565 , 0.63542932], + [ 0.84676427, 0.63949827, 0.62941 ], + [ 0.84433231, 0.63354773, 0.62340261], + [ 0.84190106, 0.62760645, 0.61740899], + [ 0.83947935, 0.62166951, 0.61142404], + [ 0.8370538 , 0.61574332, 0.60545478], + [ 0.83463975, 0.60981951, 0.59949247], + [ 0.83221877, 0.60390724, 0.593547 ], + [ 0.82980985, 0.59799607, 0.58760751], + [ 0.82740268, 0.59209095, 0.58167944], + [ 0.82498638, 0.5861973 , 0.57576866], + [ 0.82258181, 0.5803034 , 0.56986307], + [ 0.82016611, 0.57442123, 0.56397539], + [ 0.81776305, 0.56853725, 0.55809173], + [ 0.81534551, 0.56266602, 0.55222741], + [ 0.81294293, 0.55679056, 0.5463651 ], + [ 0.81052113, 0.55092973, 0.54052443], + [ 0.80811509, 0.54506305, 0.53468464], + [ 0.80568952, 0.53921036, 0.52886622], + [ 0.80327506, 0.53335335, 0.52305077], + [ 0.80084727, 0.52750583, 0.51725256], + [ 0.79842217, 0.5216578 , 0.51146173], + [ 0.79599382, 0.51581223, 0.50568155], + [ 0.79355781, 0.50997127, 0.49991444], + [ 0.79112596, 0.50412707, 0.49415289], + [ 0.78867442, 0.49829386, 0.48841129], + [ 0.7862306 , 0.49245398, 0.48267247], + [ 0.7837687 , 0.48662309, 0.47695216], + [ 0.78130809, 0.4807883 , 0.47123805], + [ 0.77884467, 0.47495151, 0.46553236], + [ 0.77636283, 0.46912235, 0.45984473], + [ 0.77388383, 0.46328617, 0.45416141], + [ 0.77138912, 0.45745466, 0.44849398], + [ 0.76888874, 0.45162042, 0.44283573], + [ 0.76638802, 0.44577901, 0.43718292], + [ 0.76386116, 0.43994762, 0.43155211], + [ 0.76133542, 0.43410655, 0.42592523], + [ 0.75880631, 0.42825801, 0.42030488], + [ 0.75624913, 0.42241905, 0.41470727], + [ 0.7536919 , 0.41656866, 0.40911347], + [ 0.75112748, 0.41071104, 0.40352792], + [ 0.74854331, 0.40485474, 0.3979589 ], + [ 0.74594723, 0.39899309, 0.39240088], + [ 0.74334332, 0.39312199, 0.38685075], + [ 0.74073277, 0.38723941, 0.3813074 ], + [ 0.73809409, 0.38136133, 0.37578553], + [ 0.73544692, 0.37547129, 0.37027123], + [ 0.73278943, 0.36956954, 0.36476549], + [ 0.73011829, 0.36365761, 0.35927038], + [ 0.72743485, 0.35773314, 0.35378465], + [ 0.72472722, 0.35180504, 0.34831662], + [ 0.72200473, 0.34586421, 0.34285937], + [ 0.71927052, 0.33990649, 0.33741033], + [ 0.71652049, 0.33393396, 0.33197219], + [ 0.71375362, 0.32794602, 0.32654545], + [ 0.71096951, 0.32194148, 0.32113016], + [ 0.70816772, 0.31591904, 0.31572637], + [ 0.70534784, 0.30987734, 0.31033414], + [ 0.70250944, 0.30381489, 0.30495353], + [ 0.69965211, 0.2977301 , 0.2995846 ], + [ 0.6967754 , 0.29162126, 0.29422741], + [ 0.69388446, 0.28548074, 0.28887769], + [ 0.69097561, 0.2793096 , 0.28353795], + [ 0.68803513, 0.27311993, 0.27821876], + [ 0.6850794 , 0.26689144, 0.27290694], + [ 0.682108 , 0.26062114, 0.26760246], + [ 0.67911013, 0.2543177 , 0.26231367], + [ 0.67609393, 0.24796818, 0.25703372], + [ 0.67305921, 0.24156846, 0.25176238], + [ 0.67000176, 0.23511902, 0.24650278], + [ 0.66693423, 0.22859879, 0.24124404], + [ 0.6638441 , 0.22201742, 0.2359961 ], + [ 0.66080672, 0.21526712, 0.23069468]] + + +_fmri_lut = [[ 0.73936227, 0.90443867, 0.85757238], + [ 0.72888063, 0.89639109, 0.85488394], + [ 0.71834255, 0.88842162, 0.8521605 ], + [ 0.70773866, 0.88052939, 0.849422 ], + [ 0.69706215, 0.87271313, 0.84668315], + [ 0.68629021, 0.86497329, 0.84398721], + [ 0.67543654, 0.85730617, 0.84130969], + [ 0.66448539, 0.84971123, 0.83868005], + [ 0.65342679, 0.84218728, 0.83611512], + [ 0.64231804, 0.83471867, 0.83358584], + [ 0.63117745, 0.827294 , 0.83113431], + [ 0.62000484, 0.81991069, 0.82876741], + [ 0.60879435, 0.81256797, 0.82648905], + [ 0.59754118, 0.80526458, 0.82430414], + [ 0.58624247, 0.79799884, 0.82221573], + [ 0.57489525, 0.7907688 , 0.82022901], + [ 0.56349779, 0.78357215, 0.81834861], + [ 0.55204294, 0.77640827, 0.81657563], + [ 0.54052516, 0.76927562, 0.81491462], + [ 0.52894085, 0.76217215, 0.81336913], + [ 0.51728854, 0.75509528, 0.81194156], + [ 0.50555676, 0.74804469, 0.81063503], + [ 0.49373871, 0.7410187 , 0.80945242], + [ 0.48183174, 0.73401449, 0.80839675], + [ 0.46982587, 0.72703075, 0.80747097], + [ 0.45770893, 0.72006648, 0.80667756], + [ 0.44547249, 0.71311941, 0.80601991], + [ 0.43318643, 0.70617126, 0.80549278], + [ 0.42110294, 0.69916972, 0.80506683], + [ 0.40925101, 0.69211059, 0.80473246], + [ 0.3976693 , 0.68498786, 0.80448272], + [ 0.38632002, 0.67781125, 0.80431024], + [ 0.37523981, 0.67057537, 0.80420832], + [ 0.36442578, 0.66328229, 0.80417474], + [ 0.35385939, 0.65593699, 0.80420591], + [ 0.34358916, 0.64853177, 0.8043 ], + [ 0.33355526, 0.64107876, 0.80445484], + [ 0.32383062, 0.63356578, 0.80467091], + [ 0.31434372, 0.62600624, 0.8049475 ], + [ 0.30516161, 0.618389 , 0.80528692], + [ 0.29623491, 0.61072284, 0.80569021], + [ 0.28759072, 0.60300319, 0.80616055], + [ 0.27923924, 0.59522877, 0.80669803], + [ 0.27114651, 0.5874047 , 0.80730545], + [ 0.26337153, 0.57952055, 0.80799113], + [ 0.25588696, 0.57157984, 0.80875922], + [ 0.248686 , 0.56358255, 0.80961366], + [ 0.24180668, 0.55552289, 0.81055123], + [ 0.23526251, 0.54739477, 0.8115939 ], + [ 0.22921445, 0.53918506, 0.81267292], + [ 0.22397687, 0.53086094, 0.8137141 ], + [ 0.21977058, 0.52241482, 0.81457651], + [ 0.21658989, 0.51384321, 0.81528511], + [ 0.21452772, 0.50514155, 0.81577278], + [ 0.21372783, 0.49630865, 0.81589566], + [ 0.21409503, 0.48734861, 0.81566163], + [ 0.2157176 , 0.47827123, 0.81487615], + [ 0.21842857, 0.46909168, 0.81351614], + [ 0.22211705, 0.45983212, 0.81146983], + [ 0.22665681, 0.45052233, 0.80860217], + [ 0.23176013, 0.44119137, 0.80494325], + [ 0.23727775, 0.43187704, 0.80038017], + [ 0.24298285, 0.42261123, 0.79493267], + [ 0.24865068, 0.41341842, 0.78869164], + [ 0.25423116, 0.40433127, 0.78155831], + [ 0.25950239, 0.39535521, 0.77376848], + [ 0.2644736 , 0.38651212, 0.76524809], + [ 0.26901584, 0.37779582, 0.75621942], + [ 0.27318141, 0.36922056, 0.746605 ], + [ 0.27690355, 0.3607736 , 0.73659374], + [ 0.28023585, 0.35244234, 0.72622103], + [ 0.28306009, 0.34438449, 0.71500731], + [ 0.28535896, 0.33660243, 0.70303975], + [ 0.28708711, 0.32912157, 0.69034504], + [ 0.28816354, 0.32200604, 0.67684067], + [ 0.28862749, 0.31519824, 0.66278813], + [ 0.28847904, 0.30869064, 0.6482815 ], + [ 0.28770912, 0.30250126, 0.63331265], + [ 0.28640325, 0.29655509, 0.61811374], + [ 0.28458943, 0.29082155, 0.60280913], + [ 0.28233561, 0.28527482, 0.58742866], + [ 0.27967038, 0.2798938 , 0.57204225], + [ 0.27665361, 0.27465357, 0.55667809], + [ 0.27332564, 0.2695165 , 0.54145387], + [ 0.26973851, 0.26447054, 0.52634916], + [ 0.2659204 , 0.25949691, 0.511417 ], + [ 0.26190145, 0.25458123, 0.49668768], + [ 0.2577151 , 0.24971691, 0.48214874], + [ 0.25337618, 0.24490494, 0.46778758], + [ 0.24890842, 0.24013332, 0.45363816], + [ 0.24433654, 0.23539226, 0.4397245 ], + [ 0.23967922, 0.23067729, 0.4260591 ], + [ 0.23495608, 0.22598894, 0.41262952], + [ 0.23018113, 0.22132414, 0.39945577], + [ 0.22534609, 0.21670847, 0.38645794], + [ 0.22048761, 0.21211723, 0.37372555], + [ 0.2156198 , 0.20755389, 0.36125301], + [ 0.21074637, 0.20302717, 0.34903192], + [ 0.20586893, 0.19855368, 0.33701661], + [ 0.20101757, 0.19411573, 0.32529173], + [ 0.19619947, 0.18972425, 0.31383846], + [ 0.19140726, 0.18540157, 0.30260777], + [ 0.1866769 , 0.1811332 , 0.29166583], + [ 0.18201285, 0.17694992, 0.28088776], + [ 0.17745228, 0.17282141, 0.27044211], + [ 0.17300684, 0.16876921, 0.26024893], + [ 0.16868273, 0.16479861, 0.25034479], + [ 0.16448691, 0.16091728, 0.24075373], + [ 0.16043195, 0.15714351, 0.23141745], + [ 0.15652427, 0.15348248, 0.22238175], + [ 0.15277065, 0.14994111, 0.21368395], + [ 0.14918274, 0.14653431, 0.20529486], + [ 0.14577095, 0.14327403, 0.19720829], + [ 0.14254381, 0.14016944, 0.18944326], + [ 0.13951035, 0.13723063, 0.18201072], + [ 0.13667798, 0.13446606, 0.17493774], + [ 0.13405762, 0.13188822, 0.16820842], + [ 0.13165767, 0.12950667, 0.16183275], + [ 0.12948748, 0.12733187, 0.15580631], + [ 0.12755435, 0.1253723 , 0.15014098], + [ 0.12586516, 0.12363617, 0.1448459 ], + [ 0.12442647, 0.12213143, 0.13992571], + [ 0.12324241, 0.12086419, 0.13539995], + [ 0.12232067, 0.11984278, 0.13124644], + [ 0.12166209, 0.11907077, 0.12749671], + [ 0.12126982, 0.11855309, 0.12415079], + [ 0.12114244, 0.11829179, 0.1212385 ], + [ 0.12127766, 0.11828837, 0.11878534], + [ 0.12284806, 0.1179729 , 0.11772022], + [ 0.12619498, 0.11721796, 0.11770203], + [ 0.129968 , 0.11663788, 0.11792377], + [ 0.13410011, 0.11625146, 0.11839138], + [ 0.13855459, 0.11606618, 0.11910584], + [ 0.14333775, 0.11607038, 0.1200606 ], + [ 0.148417 , 0.11626929, 0.12125453], + [ 0.15377389, 0.11666192, 0.12268364], + [ 0.15941427, 0.11723486, 0.12433911], + [ 0.16533376, 0.11797856, 0.12621303], + [ 0.17152547, 0.11888403, 0.12829735], + [ 0.17797765, 0.11994436, 0.13058435], + [ 0.18468769, 0.12114722, 0.13306426], + [ 0.19165663, 0.12247737, 0.13572616], + [ 0.19884415, 0.12394381, 0.1385669 ], + [ 0.20627181, 0.12551883, 0.14157124], + [ 0.21394877, 0.12718055, 0.14472604], + [ 0.22184572, 0.12893119, 0.14802579], + [ 0.22994394, 0.13076731, 0.15146314], + [ 0.23823937, 0.13267611, 0.15502793], + [ 0.24676041, 0.13462172, 0.15870321], + [ 0.25546457, 0.13661751, 0.16248722], + [ 0.26433628, 0.13865956, 0.16637301], + [ 0.27341345, 0.14070412, 0.17034221], + [ 0.28264773, 0.14277192, 0.1743957 ], + [ 0.29202272, 0.14486161, 0.17852793], + [ 0.30159648, 0.14691224, 0.1827169 ], + [ 0.31129002, 0.14897583, 0.18695213], + [ 0.32111555, 0.15103351, 0.19119629], + [ 0.33107961, 0.1530674 , 0.19543758], + [ 0.34119892, 0.15504762, 0.1996803 ], + [ 0.35142388, 0.15701131, 0.20389086], + [ 0.36178937, 0.1589124 , 0.20807639], + [ 0.37229381, 0.16073993, 0.21223189], + [ 0.38288348, 0.16254006, 0.2163249 ], + [ 0.39359592, 0.16426336, 0.22036577], + [ 0.40444332, 0.16588767, 0.22434027], + [ 0.41537995, 0.16745325, 0.2282297 ], + [ 0.42640867, 0.16894939, 0.23202755], + [ 0.43754706, 0.17034847, 0.23572899], + [ 0.44878564, 0.1716535 , 0.23932344], + [ 0.4601126 , 0.17287365, 0.24278607], + [ 0.47151732, 0.17401641, 0.24610337], + [ 0.48300689, 0.17506676, 0.2492737 ], + [ 0.49458302, 0.17601892, 0.25227688], + [ 0.50623876, 0.17687777, 0.255096 ], + [ 0.5179623 , 0.17765528, 0.2577162 ], + [ 0.52975234, 0.17835232, 0.2601134 ], + [ 0.54159776, 0.17898292, 0.26226847], + [ 0.55348804, 0.17956232, 0.26416003], + [ 0.56541729, 0.18010175, 0.26575971], + [ 0.57736669, 0.180631 , 0.26704888], + [ 0.58932081, 0.18117827, 0.26800409], + [ 0.60127582, 0.18175888, 0.26858488], + [ 0.61319563, 0.1824336 , 0.2687872 ], + [ 0.62506376, 0.18324015, 0.26858301], + [ 0.63681202, 0.18430173, 0.26795276], + [ 0.64842603, 0.18565472, 0.26689463], + [ 0.65988195, 0.18734638, 0.26543435], + [ 0.67111966, 0.18948885, 0.26357955], + [ 0.68209194, 0.19216636, 0.26137175], + [ 0.69281185, 0.19535326, 0.25887063], + [ 0.70335022, 0.19891271, 0.25617971], + [ 0.71375229, 0.20276438, 0.25331365], + [ 0.72401436, 0.20691287, 0.25027366], + [ 0.73407638, 0.21145051, 0.24710661], + [ 0.74396983, 0.21631913, 0.24380715], + [ 0.75361506, 0.22163653, 0.24043996], + [ 0.7630579 , 0.22731637, 0.23700095], + [ 0.77222228, 0.23346231, 0.23356628], + [ 0.78115441, 0.23998404, 0.23013825], + [ 0.78979746, 0.24694858, 0.22678822], + [ 0.79819286, 0.25427223, 0.22352658], + [ 0.80630444, 0.26198807, 0.22040877], + [ 0.81417437, 0.27001406, 0.21744645], + [ 0.82177364, 0.27837336, 0.21468316], + [ 0.82915955, 0.28696963, 0.21210766], + [ 0.83628628, 0.2958499 , 0.20977813], + [ 0.84322168, 0.30491136, 0.20766435], + [ 0.84995458, 0.31415945, 0.2057863 ], + [ 0.85648867, 0.32358058, 0.20415327], + [ 0.86286243, 0.33312058, 0.20274969], + [ 0.86908321, 0.34276705, 0.20157271], + [ 0.87512876, 0.3525416 , 0.20064949], + [ 0.88100349, 0.36243385, 0.19999078], + [ 0.8866469 , 0.37249496, 0.1997976 ], + [ 0.89203964, 0.38273475, 0.20013431], + [ 0.89713496, 0.39318156, 0.20121514], + [ 0.90195099, 0.40380687, 0.20301555], + [ 0.90648379, 0.41460191, 0.20558847], + [ 0.9106967 , 0.42557857, 0.20918529], + [ 0.91463791, 0.43668557, 0.21367954], + [ 0.91830723, 0.44790913, 0.21916352], + [ 0.92171507, 0.45922856, 0.22568002], + [ 0.92491786, 0.4705936 , 0.23308207], + [ 0.92790792, 0.48200153, 0.24145932], + [ 0.93073701, 0.49341219, 0.25065486], + [ 0.93343918, 0.5048017 , 0.26056148], + [ 0.93602064, 0.51616486, 0.27118485], + [ 0.93850535, 0.52748892, 0.28242464], + [ 0.94092933, 0.53875462, 0.29416042], + [ 0.94330011, 0.5499628 , 0.30634189], + [ 0.94563159, 0.56110987, 0.31891624], + [ 0.94792955, 0.57219822, 0.33184256], + [ 0.95020929, 0.5832232 , 0.34508419], + [ 0.95247324, 0.59419035, 0.35859866], + [ 0.95471709, 0.60510869, 0.37236035], + [ 0.95698411, 0.61595766, 0.38629631], + [ 0.95923863, 0.62676473, 0.40043317], + [ 0.9615041 , 0.6375203 , 0.41474106], + [ 0.96371553, 0.64826619, 0.42928335], + [ 0.96591497, 0.65899621, 0.44380444], + [ 0.96809871, 0.66971662, 0.45830232], + [ 0.9702495 , 0.6804394 , 0.47280492], + [ 0.9723881 , 0.69115622, 0.48729272], + [ 0.97450723, 0.70187358, 0.50178034], + [ 0.9766108 , 0.712592 , 0.51626837], + [ 0.97871716, 0.72330511, 0.53074053], + [ 0.98082222, 0.73401769, 0.54520694], + [ 0.9829001 , 0.74474445, 0.5597019 ], + [ 0.98497466, 0.75547635, 0.57420239], + [ 0.98705581, 0.76621129, 0.58870185], + [ 0.98913325, 0.77695637, 0.60321626], + [ 0.99119918, 0.78771716, 0.61775821], + [ 0.9932672 , 0.79848979, 0.63231691], + [ 0.99535958, 0.80926704, 0.64687278], + [ 0.99740544, 0.82008078, 0.66150571], + [ 0.9992197 , 0.83100723, 0.6764127 ]] + + +_luts = [_rocket_lut, _mako_lut, _vlag_lut, _fmri_lut] +_names = ["rocket", "mako", "vlag", "fmri"] + +for _lut, _name in zip(_luts, _names): + + _cmap = colors.ListedColormap(_lut, _name) + locals()[_name] = _cmap + + _cmap_r = colors.ListedColormap(_lut[::-1], _name + "_r") + locals()[_name + "_r"] = _cmap_r + + mpl_cm.register_cmap(_name, _cmap) + mpl_cm.register_cmap(_name + "_r", _cmap_r) From 25781e489aeda82022cf2bdf8773733be2fbce56 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 20 Jun 2017 21:04:01 -0400 Subject: [PATCH 0394/1738] Change default heatmap colormaps --- seaborn/matrix.py | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 0897df4e6f..31d189a0bb 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -11,8 +11,8 @@ from scipy.spatial import distance from scipy.cluster import hierarchy +from . import cm from .axisgrid import Grid -from .palettes import cubehelix_palette, diverging_palette from .utils import (despine, axis_ticklabels_overlap, relative_luminance, to_utf8) @@ -214,10 +214,10 @@ def _determine_cmap_params(self, plot_data, vmin, vmax, # Choose default colormaps if not provided if cmap is None: - if center is not None: - self.cmap = mpl.cm.RdBu_r + if center is None: + self.cmap = cm.rocket else: - self.cmap = cubehelix_palette(light=.95, as_cmap=True) + self.cmap = cm.fmri elif isinstance(cmap, string_types): self.cmap = mpl.cm.get_cmap(cmap) elif isinstance(cmap, list): @@ -313,9 +313,8 @@ def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, Values to anchor the colormap, otherwise they are inferred from the data and other keyword arguments. cmap : matplotlib colormap name or object, or list of colors, optional - The mapping from data values to color space. If not provided, this - will be either a cubehelix map (if the function infers a sequential - dataset) or ``RdBu_r`` (if the function infers a diverging dataset). + The mapping from data values to color space. If not provided, the + default will depend on whether ``center`` is set. center : float, optional The value at which to center the colormap when plotting divergant data. Using this parameter will change the default ``cmap`` if none is @@ -1211,15 +1210,14 @@ def clustermap(data, pivot_kws=None, method='average', metric='euclidean', .. plot:: :context: close-figs - >>> cmap = sns.cubehelix_palette(as_cmap=True, rot=-.3, light=1) - >>> g = sns.clustermap(flights, cmap=cmap, linewidths=.5) + >>> g = sns.clustermap(flights, cmap="mako", linewidths=.5) Use a different figure size: .. plot:: :context: close-figs - >>> g = sns.clustermap(flights, cmap=cmap, figsize=(7, 5)) + >>> g = sns.clustermap(flights, figsize=(7, 5)) Standardize the data across the columns: @@ -1247,11 +1245,8 @@ def clustermap(data, pivot_kws=None, method='average', metric='euclidean', .. plot:: :context: close-figs - >>> season_colors = (sns.color_palette("BuPu", 3) + - ... sns.color_palette("RdPu", 3) + - ... sns.color_palette("YlGn", 3) + - ... sns.color_palette("OrRd", 3)) - >>> g = sns.clustermap(flights, row_colors=season_colors) + >>> month_colors = sns.hls_palette(12, h=.7)[::-1] + >>> g = sns.clustermap(flights, row_colors=month_colors) """ plotter = ClusterGrid(data, pivot_kws=pivot_kws, figsize=figsize, From e8183a35800ed1cb28fce226259f69aaf0364735 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 20 Jun 2017 21:09:55 -0400 Subject: [PATCH 0395/1738] Remove __all__ definition in cm.py --- seaborn/cm.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/seaborn/cm.py b/seaborn/cm.py index 552bd3ba33..2f4d72c60f 100644 --- a/seaborn/cm.py +++ b/seaborn/cm.py @@ -1,10 +1,6 @@ from matplotlib import colors, cm as mpl_cm -__all__ = ["rocket", "mako", "vlag", "fmri", - "rocket_r", "mako_r", "vlag_r", "fmri_r"] - - _rocket_lut = [[ 0.01060815, 0.01808215, 0.10018654], [ 0.01428972, 0.02048237, 0.10374486], [ 0.01831941, 0.0229766 , 0.10738511], From cd96955ed1ae077b7d098cc7ead98a4add0221a3 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 21 Jun 2017 09:51:37 -0400 Subject: [PATCH 0396/1738] Exclude colormap data from pep8 check --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1320d257f2..f9d5aa7fda 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ coverage: lint: pyflakes -x W -X seaborn/external/six.py seaborn - pep8 --exclude external seaborn + pep8 --exclude external,cm.py seaborn hexstrip: From a0cd1d04c30379e029c41fa15cf1ee6299c901ce Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 21 Jun 2017 10:08:28 -0400 Subject: [PATCH 0397/1738] Relative import of cm module for Python 3 --- seaborn/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/__init__.py b/seaborn/__init__.py index 733ff8eb47..c1fede1285 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -16,7 +16,7 @@ from .widgets import * from .xkcd_rgb import xkcd_rgb from .crayons import crayons -import cm +from . import cm # Set default aesthetics set() From dcd5e9bb5e0e7fa49cafe9f6c4117460f489115f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 21 Jun 2017 11:38:23 -0400 Subject: [PATCH 0398/1738] Update release notes --- doc/releases/v0.8.0.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index 4576ff1668..688f64a21a 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -6,6 +6,10 @@ v0.8.0 (Unreleased) - Removed automatic detection of diverging data in :func:`heatmap` (and by extension :func:`clustermap`). If you want the colormap to be treated as diverging (see above), it is now necessary to specify the ``center`` value. When no colormap is specified, specifying ``center`` will still change the default to be one that is more appropriate for displaying diverging data. +- Added four new colormaps, created using `viscm `_ for perceptual uniformity. The new colormaps include two sequential colormaps ("rocket" and "mako") and two diverging colormaps ("vlag", and "fmri"). These colormaps are registered with matplotlib on seaborn input and the colormap objects can be accessed in the `seaborn.cm` namespace. + +- Changed the default :func:`heatmap` colormaps to be "rocket" (in the case of sequential data) or "fmri" (in the case of diverging data). Note that this change reverses the direction of the luminance ramp from the previous defaults. While potentially confusing and disruptive, this change better aligns the seaborn defaults with the new matplotlib default colormap ("viridis") and arguably better aligns the semantics of a "heat" map with the appearance of the colormap. + - Added the ``dodge`` argument to :func:`boxplot`, :func:`violinplot`, and :func:`barplot` to allow use of ``hue`` without changing the position or width of the plot elements, as when the ``hue`` varible is not nested within the main categorical variable. - Added the ability to draw a colorbar for a bivariate :func:`kdeplot` with the ``cbar`` parameter (and related ``cbar_ax`` and ``cbar_kws`` parameters). From eb68c39cff8638b99968b30485af7c2b32165f2e Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 22 Jun 2017 10:19:54 -0400 Subject: [PATCH 0399/1738] Update the example gallery to reflect new heatmap behavior --- doc/index.rst | 4 ++-- examples/network_correlations.py | 2 +- examples/structured_heatmap.py | 8 +++----- seaborn/matrix.py | 4 ++-- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index a3e0f7bea7..3756e9bf91 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -33,9 +33,9 @@ Seaborn: statistical data visualization
- +
- +
diff --git a/examples/network_correlations.py b/examples/network_correlations.py index c2f6dafb2c..dd394ab6e8 100644 --- a/examples/network_correlations.py +++ b/examples/network_correlations.py @@ -15,7 +15,7 @@ f, ax = plt.subplots(figsize=(12, 9)) # Draw the heatmap using seaborn -sns.heatmap(corrmat, vmax=.8, square=True) +sns.heatmap(corrmat, center=0, vmax=.8, square=True) # Use matplotlib directly to emphasize known networks networks = corrmat.columns.get_level_values("network") diff --git a/examples/structured_heatmap.py b/examples/structured_heatmap.py index 4f1d9ac598..5fe55b42bb 100644 --- a/examples/structured_heatmap.py +++ b/examples/structured_heatmap.py @@ -28,9 +28,7 @@ networks = df.columns.get_level_values("network") network_colors = pd.Series(networks, index=df.columns).map(network_lut) -# Create a custom colormap for the heatmap values -cmap = sns.diverging_palette(h_neg=210, h_pos=350, s=90, l=30, as_cmap=True) - # Draw the full plot -sns.clustermap(df.corr(), row_colors=network_colors, linewidths=.5, - col_colors=network_colors, figsize=(13, 13), cmap=cmap) +sns.clustermap(df.corr(), center=0, cmap="vlag", + row_colors=network_colors, col_colors=network_colors, + figsize=(13, 13)) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 31d189a0bb..c66df670c9 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -1117,11 +1117,11 @@ def clustermap(data, pivot_kws=None, method='average', metric='euclidean', row_cluster=True, col_cluster=True, row_linkage=None, col_linkage=None, row_colors=None, col_colors=None, mask=None, **kwargs): - """Plot a hierarchically clustered heatmap of a pandas DataFrame + """Plot a matrix dataset as a hierarchically-clustered heatmap. Parameters ---------- - data: pandas.DataFrame + data: 2D array-like Rectangular data for clustering. Cannot contain NAs. pivot_kws : dict, optional If `data` is a tidy dataframe, can provide keyword arguments for From a5b2fe3d99fa995b2931e9ec775e22dd97b74511 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 22 Jun 2017 11:10:48 -0400 Subject: [PATCH 0400/1738] Change name for black-centered diverging colormap --- doc/releases/v0.8.0.txt | 4 +- seaborn/cm.py | 516 ++++++++++++++++++++-------------------- seaborn/matrix.py | 2 +- 3 files changed, 261 insertions(+), 261 deletions(-) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index 688f64a21a..a3897e5e45 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -6,9 +6,9 @@ v0.8.0 (Unreleased) - Removed automatic detection of diverging data in :func:`heatmap` (and by extension :func:`clustermap`). If you want the colormap to be treated as diverging (see above), it is now necessary to specify the ``center`` value. When no colormap is specified, specifying ``center`` will still change the default to be one that is more appropriate for displaying diverging data. -- Added four new colormaps, created using `viscm `_ for perceptual uniformity. The new colormaps include two sequential colormaps ("rocket" and "mako") and two diverging colormaps ("vlag", and "fmri"). These colormaps are registered with matplotlib on seaborn input and the colormap objects can be accessed in the `seaborn.cm` namespace. +- Added four new colormaps, created using `viscm `_ for perceptual uniformity. The new colormaps include two sequential colormaps ("rocket" and "mako") and two diverging colormaps ("vlag", and "icefire"). These colormaps are registered with matplotlib on seaborn input and the colormap objects can be accessed in the `seaborn.cm` namespace. -- Changed the default :func:`heatmap` colormaps to be "rocket" (in the case of sequential data) or "fmri" (in the case of diverging data). Note that this change reverses the direction of the luminance ramp from the previous defaults. While potentially confusing and disruptive, this change better aligns the seaborn defaults with the new matplotlib default colormap ("viridis") and arguably better aligns the semantics of a "heat" map with the appearance of the colormap. +- Changed the default :func:`heatmap` colormaps to be "rocket" (in the case of sequential data) or "icefire" (in the case of diverging data). Note that this change reverses the direction of the luminance ramp from the previous defaults. While potentially confusing and disruptive, this change better aligns the seaborn defaults with the new matplotlib default colormap ("viridis") and arguably better aligns the semantics of a "heat" map with the appearance of the colormap. - Added the ``dodge`` argument to :func:`boxplot`, :func:`violinplot`, and :func:`barplot` to allow use of ``hue`` without changing the position or width of the plot elements, as when the ``hue`` varible is not nested within the main categorical variable. diff --git a/seaborn/cm.py b/seaborn/cm.py index 2f4d72c60f..0f6076980f 100644 --- a/seaborn/cm.py +++ b/seaborn/cm.py @@ -775,266 +775,266 @@ [ 0.66080672, 0.21526712, 0.23069468]] -_fmri_lut = [[ 0.73936227, 0.90443867, 0.85757238], - [ 0.72888063, 0.89639109, 0.85488394], - [ 0.71834255, 0.88842162, 0.8521605 ], - [ 0.70773866, 0.88052939, 0.849422 ], - [ 0.69706215, 0.87271313, 0.84668315], - [ 0.68629021, 0.86497329, 0.84398721], - [ 0.67543654, 0.85730617, 0.84130969], - [ 0.66448539, 0.84971123, 0.83868005], - [ 0.65342679, 0.84218728, 0.83611512], - [ 0.64231804, 0.83471867, 0.83358584], - [ 0.63117745, 0.827294 , 0.83113431], - [ 0.62000484, 0.81991069, 0.82876741], - [ 0.60879435, 0.81256797, 0.82648905], - [ 0.59754118, 0.80526458, 0.82430414], - [ 0.58624247, 0.79799884, 0.82221573], - [ 0.57489525, 0.7907688 , 0.82022901], - [ 0.56349779, 0.78357215, 0.81834861], - [ 0.55204294, 0.77640827, 0.81657563], - [ 0.54052516, 0.76927562, 0.81491462], - [ 0.52894085, 0.76217215, 0.81336913], - [ 0.51728854, 0.75509528, 0.81194156], - [ 0.50555676, 0.74804469, 0.81063503], - [ 0.49373871, 0.7410187 , 0.80945242], - [ 0.48183174, 0.73401449, 0.80839675], - [ 0.46982587, 0.72703075, 0.80747097], - [ 0.45770893, 0.72006648, 0.80667756], - [ 0.44547249, 0.71311941, 0.80601991], - [ 0.43318643, 0.70617126, 0.80549278], - [ 0.42110294, 0.69916972, 0.80506683], - [ 0.40925101, 0.69211059, 0.80473246], - [ 0.3976693 , 0.68498786, 0.80448272], - [ 0.38632002, 0.67781125, 0.80431024], - [ 0.37523981, 0.67057537, 0.80420832], - [ 0.36442578, 0.66328229, 0.80417474], - [ 0.35385939, 0.65593699, 0.80420591], - [ 0.34358916, 0.64853177, 0.8043 ], - [ 0.33355526, 0.64107876, 0.80445484], - [ 0.32383062, 0.63356578, 0.80467091], - [ 0.31434372, 0.62600624, 0.8049475 ], - [ 0.30516161, 0.618389 , 0.80528692], - [ 0.29623491, 0.61072284, 0.80569021], - [ 0.28759072, 0.60300319, 0.80616055], - [ 0.27923924, 0.59522877, 0.80669803], - [ 0.27114651, 0.5874047 , 0.80730545], - [ 0.26337153, 0.57952055, 0.80799113], - [ 0.25588696, 0.57157984, 0.80875922], - [ 0.248686 , 0.56358255, 0.80961366], - [ 0.24180668, 0.55552289, 0.81055123], - [ 0.23526251, 0.54739477, 0.8115939 ], - [ 0.22921445, 0.53918506, 0.81267292], - [ 0.22397687, 0.53086094, 0.8137141 ], - [ 0.21977058, 0.52241482, 0.81457651], - [ 0.21658989, 0.51384321, 0.81528511], - [ 0.21452772, 0.50514155, 0.81577278], - [ 0.21372783, 0.49630865, 0.81589566], - [ 0.21409503, 0.48734861, 0.81566163], - [ 0.2157176 , 0.47827123, 0.81487615], - [ 0.21842857, 0.46909168, 0.81351614], - [ 0.22211705, 0.45983212, 0.81146983], - [ 0.22665681, 0.45052233, 0.80860217], - [ 0.23176013, 0.44119137, 0.80494325], - [ 0.23727775, 0.43187704, 0.80038017], - [ 0.24298285, 0.42261123, 0.79493267], - [ 0.24865068, 0.41341842, 0.78869164], - [ 0.25423116, 0.40433127, 0.78155831], - [ 0.25950239, 0.39535521, 0.77376848], - [ 0.2644736 , 0.38651212, 0.76524809], - [ 0.26901584, 0.37779582, 0.75621942], - [ 0.27318141, 0.36922056, 0.746605 ], - [ 0.27690355, 0.3607736 , 0.73659374], - [ 0.28023585, 0.35244234, 0.72622103], - [ 0.28306009, 0.34438449, 0.71500731], - [ 0.28535896, 0.33660243, 0.70303975], - [ 0.28708711, 0.32912157, 0.69034504], - [ 0.28816354, 0.32200604, 0.67684067], - [ 0.28862749, 0.31519824, 0.66278813], - [ 0.28847904, 0.30869064, 0.6482815 ], - [ 0.28770912, 0.30250126, 0.63331265], - [ 0.28640325, 0.29655509, 0.61811374], - [ 0.28458943, 0.29082155, 0.60280913], - [ 0.28233561, 0.28527482, 0.58742866], - [ 0.27967038, 0.2798938 , 0.57204225], - [ 0.27665361, 0.27465357, 0.55667809], - [ 0.27332564, 0.2695165 , 0.54145387], - [ 0.26973851, 0.26447054, 0.52634916], - [ 0.2659204 , 0.25949691, 0.511417 ], - [ 0.26190145, 0.25458123, 0.49668768], - [ 0.2577151 , 0.24971691, 0.48214874], - [ 0.25337618, 0.24490494, 0.46778758], - [ 0.24890842, 0.24013332, 0.45363816], - [ 0.24433654, 0.23539226, 0.4397245 ], - [ 0.23967922, 0.23067729, 0.4260591 ], - [ 0.23495608, 0.22598894, 0.41262952], - [ 0.23018113, 0.22132414, 0.39945577], - [ 0.22534609, 0.21670847, 0.38645794], - [ 0.22048761, 0.21211723, 0.37372555], - [ 0.2156198 , 0.20755389, 0.36125301], - [ 0.21074637, 0.20302717, 0.34903192], - [ 0.20586893, 0.19855368, 0.33701661], - [ 0.20101757, 0.19411573, 0.32529173], - [ 0.19619947, 0.18972425, 0.31383846], - [ 0.19140726, 0.18540157, 0.30260777], - [ 0.1866769 , 0.1811332 , 0.29166583], - [ 0.18201285, 0.17694992, 0.28088776], - [ 0.17745228, 0.17282141, 0.27044211], - [ 0.17300684, 0.16876921, 0.26024893], - [ 0.16868273, 0.16479861, 0.25034479], - [ 0.16448691, 0.16091728, 0.24075373], - [ 0.16043195, 0.15714351, 0.23141745], - [ 0.15652427, 0.15348248, 0.22238175], - [ 0.15277065, 0.14994111, 0.21368395], - [ 0.14918274, 0.14653431, 0.20529486], - [ 0.14577095, 0.14327403, 0.19720829], - [ 0.14254381, 0.14016944, 0.18944326], - [ 0.13951035, 0.13723063, 0.18201072], - [ 0.13667798, 0.13446606, 0.17493774], - [ 0.13405762, 0.13188822, 0.16820842], - [ 0.13165767, 0.12950667, 0.16183275], - [ 0.12948748, 0.12733187, 0.15580631], - [ 0.12755435, 0.1253723 , 0.15014098], - [ 0.12586516, 0.12363617, 0.1448459 ], - [ 0.12442647, 0.12213143, 0.13992571], - [ 0.12324241, 0.12086419, 0.13539995], - [ 0.12232067, 0.11984278, 0.13124644], - [ 0.12166209, 0.11907077, 0.12749671], - [ 0.12126982, 0.11855309, 0.12415079], - [ 0.12114244, 0.11829179, 0.1212385 ], - [ 0.12127766, 0.11828837, 0.11878534], - [ 0.12284806, 0.1179729 , 0.11772022], - [ 0.12619498, 0.11721796, 0.11770203], - [ 0.129968 , 0.11663788, 0.11792377], - [ 0.13410011, 0.11625146, 0.11839138], - [ 0.13855459, 0.11606618, 0.11910584], - [ 0.14333775, 0.11607038, 0.1200606 ], - [ 0.148417 , 0.11626929, 0.12125453], - [ 0.15377389, 0.11666192, 0.12268364], - [ 0.15941427, 0.11723486, 0.12433911], - [ 0.16533376, 0.11797856, 0.12621303], - [ 0.17152547, 0.11888403, 0.12829735], - [ 0.17797765, 0.11994436, 0.13058435], - [ 0.18468769, 0.12114722, 0.13306426], - [ 0.19165663, 0.12247737, 0.13572616], - [ 0.19884415, 0.12394381, 0.1385669 ], - [ 0.20627181, 0.12551883, 0.14157124], - [ 0.21394877, 0.12718055, 0.14472604], - [ 0.22184572, 0.12893119, 0.14802579], - [ 0.22994394, 0.13076731, 0.15146314], - [ 0.23823937, 0.13267611, 0.15502793], - [ 0.24676041, 0.13462172, 0.15870321], - [ 0.25546457, 0.13661751, 0.16248722], - [ 0.26433628, 0.13865956, 0.16637301], - [ 0.27341345, 0.14070412, 0.17034221], - [ 0.28264773, 0.14277192, 0.1743957 ], - [ 0.29202272, 0.14486161, 0.17852793], - [ 0.30159648, 0.14691224, 0.1827169 ], - [ 0.31129002, 0.14897583, 0.18695213], - [ 0.32111555, 0.15103351, 0.19119629], - [ 0.33107961, 0.1530674 , 0.19543758], - [ 0.34119892, 0.15504762, 0.1996803 ], - [ 0.35142388, 0.15701131, 0.20389086], - [ 0.36178937, 0.1589124 , 0.20807639], - [ 0.37229381, 0.16073993, 0.21223189], - [ 0.38288348, 0.16254006, 0.2163249 ], - [ 0.39359592, 0.16426336, 0.22036577], - [ 0.40444332, 0.16588767, 0.22434027], - [ 0.41537995, 0.16745325, 0.2282297 ], - [ 0.42640867, 0.16894939, 0.23202755], - [ 0.43754706, 0.17034847, 0.23572899], - [ 0.44878564, 0.1716535 , 0.23932344], - [ 0.4601126 , 0.17287365, 0.24278607], - [ 0.47151732, 0.17401641, 0.24610337], - [ 0.48300689, 0.17506676, 0.2492737 ], - [ 0.49458302, 0.17601892, 0.25227688], - [ 0.50623876, 0.17687777, 0.255096 ], - [ 0.5179623 , 0.17765528, 0.2577162 ], - [ 0.52975234, 0.17835232, 0.2601134 ], - [ 0.54159776, 0.17898292, 0.26226847], - [ 0.55348804, 0.17956232, 0.26416003], - [ 0.56541729, 0.18010175, 0.26575971], - [ 0.57736669, 0.180631 , 0.26704888], - [ 0.58932081, 0.18117827, 0.26800409], - [ 0.60127582, 0.18175888, 0.26858488], - [ 0.61319563, 0.1824336 , 0.2687872 ], - [ 0.62506376, 0.18324015, 0.26858301], - [ 0.63681202, 0.18430173, 0.26795276], - [ 0.64842603, 0.18565472, 0.26689463], - [ 0.65988195, 0.18734638, 0.26543435], - [ 0.67111966, 0.18948885, 0.26357955], - [ 0.68209194, 0.19216636, 0.26137175], - [ 0.69281185, 0.19535326, 0.25887063], - [ 0.70335022, 0.19891271, 0.25617971], - [ 0.71375229, 0.20276438, 0.25331365], - [ 0.72401436, 0.20691287, 0.25027366], - [ 0.73407638, 0.21145051, 0.24710661], - [ 0.74396983, 0.21631913, 0.24380715], - [ 0.75361506, 0.22163653, 0.24043996], - [ 0.7630579 , 0.22731637, 0.23700095], - [ 0.77222228, 0.23346231, 0.23356628], - [ 0.78115441, 0.23998404, 0.23013825], - [ 0.78979746, 0.24694858, 0.22678822], - [ 0.79819286, 0.25427223, 0.22352658], - [ 0.80630444, 0.26198807, 0.22040877], - [ 0.81417437, 0.27001406, 0.21744645], - [ 0.82177364, 0.27837336, 0.21468316], - [ 0.82915955, 0.28696963, 0.21210766], - [ 0.83628628, 0.2958499 , 0.20977813], - [ 0.84322168, 0.30491136, 0.20766435], - [ 0.84995458, 0.31415945, 0.2057863 ], - [ 0.85648867, 0.32358058, 0.20415327], - [ 0.86286243, 0.33312058, 0.20274969], - [ 0.86908321, 0.34276705, 0.20157271], - [ 0.87512876, 0.3525416 , 0.20064949], - [ 0.88100349, 0.36243385, 0.19999078], - [ 0.8866469 , 0.37249496, 0.1997976 ], - [ 0.89203964, 0.38273475, 0.20013431], - [ 0.89713496, 0.39318156, 0.20121514], - [ 0.90195099, 0.40380687, 0.20301555], - [ 0.90648379, 0.41460191, 0.20558847], - [ 0.9106967 , 0.42557857, 0.20918529], - [ 0.91463791, 0.43668557, 0.21367954], - [ 0.91830723, 0.44790913, 0.21916352], - [ 0.92171507, 0.45922856, 0.22568002], - [ 0.92491786, 0.4705936 , 0.23308207], - [ 0.92790792, 0.48200153, 0.24145932], - [ 0.93073701, 0.49341219, 0.25065486], - [ 0.93343918, 0.5048017 , 0.26056148], - [ 0.93602064, 0.51616486, 0.27118485], - [ 0.93850535, 0.52748892, 0.28242464], - [ 0.94092933, 0.53875462, 0.29416042], - [ 0.94330011, 0.5499628 , 0.30634189], - [ 0.94563159, 0.56110987, 0.31891624], - [ 0.94792955, 0.57219822, 0.33184256], - [ 0.95020929, 0.5832232 , 0.34508419], - [ 0.95247324, 0.59419035, 0.35859866], - [ 0.95471709, 0.60510869, 0.37236035], - [ 0.95698411, 0.61595766, 0.38629631], - [ 0.95923863, 0.62676473, 0.40043317], - [ 0.9615041 , 0.6375203 , 0.41474106], - [ 0.96371553, 0.64826619, 0.42928335], - [ 0.96591497, 0.65899621, 0.44380444], - [ 0.96809871, 0.66971662, 0.45830232], - [ 0.9702495 , 0.6804394 , 0.47280492], - [ 0.9723881 , 0.69115622, 0.48729272], - [ 0.97450723, 0.70187358, 0.50178034], - [ 0.9766108 , 0.712592 , 0.51626837], - [ 0.97871716, 0.72330511, 0.53074053], - [ 0.98082222, 0.73401769, 0.54520694], - [ 0.9829001 , 0.74474445, 0.5597019 ], - [ 0.98497466, 0.75547635, 0.57420239], - [ 0.98705581, 0.76621129, 0.58870185], - [ 0.98913325, 0.77695637, 0.60321626], - [ 0.99119918, 0.78771716, 0.61775821], - [ 0.9932672 , 0.79848979, 0.63231691], - [ 0.99535958, 0.80926704, 0.64687278], - [ 0.99740544, 0.82008078, 0.66150571], - [ 0.9992197 , 0.83100723, 0.6764127 ]] +_icefire_lut = [[ 0.73936227, 0.90443867, 0.85757238], + [ 0.72888063, 0.89639109, 0.85488394], + [ 0.71834255, 0.88842162, 0.8521605 ], + [ 0.70773866, 0.88052939, 0.849422 ], + [ 0.69706215, 0.87271313, 0.84668315], + [ 0.68629021, 0.86497329, 0.84398721], + [ 0.67543654, 0.85730617, 0.84130969], + [ 0.66448539, 0.84971123, 0.83868005], + [ 0.65342679, 0.84218728, 0.83611512], + [ 0.64231804, 0.83471867, 0.83358584], + [ 0.63117745, 0.827294 , 0.83113431], + [ 0.62000484, 0.81991069, 0.82876741], + [ 0.60879435, 0.81256797, 0.82648905], + [ 0.59754118, 0.80526458, 0.82430414], + [ 0.58624247, 0.79799884, 0.82221573], + [ 0.57489525, 0.7907688 , 0.82022901], + [ 0.56349779, 0.78357215, 0.81834861], + [ 0.55204294, 0.77640827, 0.81657563], + [ 0.54052516, 0.76927562, 0.81491462], + [ 0.52894085, 0.76217215, 0.81336913], + [ 0.51728854, 0.75509528, 0.81194156], + [ 0.50555676, 0.74804469, 0.81063503], + [ 0.49373871, 0.7410187 , 0.80945242], + [ 0.48183174, 0.73401449, 0.80839675], + [ 0.46982587, 0.72703075, 0.80747097], + [ 0.45770893, 0.72006648, 0.80667756], + [ 0.44547249, 0.71311941, 0.80601991], + [ 0.43318643, 0.70617126, 0.80549278], + [ 0.42110294, 0.69916972, 0.80506683], + [ 0.40925101, 0.69211059, 0.80473246], + [ 0.3976693 , 0.68498786, 0.80448272], + [ 0.38632002, 0.67781125, 0.80431024], + [ 0.37523981, 0.67057537, 0.80420832], + [ 0.36442578, 0.66328229, 0.80417474], + [ 0.35385939, 0.65593699, 0.80420591], + [ 0.34358916, 0.64853177, 0.8043 ], + [ 0.33355526, 0.64107876, 0.80445484], + [ 0.32383062, 0.63356578, 0.80467091], + [ 0.31434372, 0.62600624, 0.8049475 ], + [ 0.30516161, 0.618389 , 0.80528692], + [ 0.29623491, 0.61072284, 0.80569021], + [ 0.28759072, 0.60300319, 0.80616055], + [ 0.27923924, 0.59522877, 0.80669803], + [ 0.27114651, 0.5874047 , 0.80730545], + [ 0.26337153, 0.57952055, 0.80799113], + [ 0.25588696, 0.57157984, 0.80875922], + [ 0.248686 , 0.56358255, 0.80961366], + [ 0.24180668, 0.55552289, 0.81055123], + [ 0.23526251, 0.54739477, 0.8115939 ], + [ 0.22921445, 0.53918506, 0.81267292], + [ 0.22397687, 0.53086094, 0.8137141 ], + [ 0.21977058, 0.52241482, 0.81457651], + [ 0.21658989, 0.51384321, 0.81528511], + [ 0.21452772, 0.50514155, 0.81577278], + [ 0.21372783, 0.49630865, 0.81589566], + [ 0.21409503, 0.48734861, 0.81566163], + [ 0.2157176 , 0.47827123, 0.81487615], + [ 0.21842857, 0.46909168, 0.81351614], + [ 0.22211705, 0.45983212, 0.81146983], + [ 0.22665681, 0.45052233, 0.80860217], + [ 0.23176013, 0.44119137, 0.80494325], + [ 0.23727775, 0.43187704, 0.80038017], + [ 0.24298285, 0.42261123, 0.79493267], + [ 0.24865068, 0.41341842, 0.78869164], + [ 0.25423116, 0.40433127, 0.78155831], + [ 0.25950239, 0.39535521, 0.77376848], + [ 0.2644736 , 0.38651212, 0.76524809], + [ 0.26901584, 0.37779582, 0.75621942], + [ 0.27318141, 0.36922056, 0.746605 ], + [ 0.27690355, 0.3607736 , 0.73659374], + [ 0.28023585, 0.35244234, 0.72622103], + [ 0.28306009, 0.34438449, 0.71500731], + [ 0.28535896, 0.33660243, 0.70303975], + [ 0.28708711, 0.32912157, 0.69034504], + [ 0.28816354, 0.32200604, 0.67684067], + [ 0.28862749, 0.31519824, 0.66278813], + [ 0.28847904, 0.30869064, 0.6482815 ], + [ 0.28770912, 0.30250126, 0.63331265], + [ 0.28640325, 0.29655509, 0.61811374], + [ 0.28458943, 0.29082155, 0.60280913], + [ 0.28233561, 0.28527482, 0.58742866], + [ 0.27967038, 0.2798938 , 0.57204225], + [ 0.27665361, 0.27465357, 0.55667809], + [ 0.27332564, 0.2695165 , 0.54145387], + [ 0.26973851, 0.26447054, 0.52634916], + [ 0.2659204 , 0.25949691, 0.511417 ], + [ 0.26190145, 0.25458123, 0.49668768], + [ 0.2577151 , 0.24971691, 0.48214874], + [ 0.25337618, 0.24490494, 0.46778758], + [ 0.24890842, 0.24013332, 0.45363816], + [ 0.24433654, 0.23539226, 0.4397245 ], + [ 0.23967922, 0.23067729, 0.4260591 ], + [ 0.23495608, 0.22598894, 0.41262952], + [ 0.23018113, 0.22132414, 0.39945577], + [ 0.22534609, 0.21670847, 0.38645794], + [ 0.22048761, 0.21211723, 0.37372555], + [ 0.2156198 , 0.20755389, 0.36125301], + [ 0.21074637, 0.20302717, 0.34903192], + [ 0.20586893, 0.19855368, 0.33701661], + [ 0.20101757, 0.19411573, 0.32529173], + [ 0.19619947, 0.18972425, 0.31383846], + [ 0.19140726, 0.18540157, 0.30260777], + [ 0.1866769 , 0.1811332 , 0.29166583], + [ 0.18201285, 0.17694992, 0.28088776], + [ 0.17745228, 0.17282141, 0.27044211], + [ 0.17300684, 0.16876921, 0.26024893], + [ 0.16868273, 0.16479861, 0.25034479], + [ 0.16448691, 0.16091728, 0.24075373], + [ 0.16043195, 0.15714351, 0.23141745], + [ 0.15652427, 0.15348248, 0.22238175], + [ 0.15277065, 0.14994111, 0.21368395], + [ 0.14918274, 0.14653431, 0.20529486], + [ 0.14577095, 0.14327403, 0.19720829], + [ 0.14254381, 0.14016944, 0.18944326], + [ 0.13951035, 0.13723063, 0.18201072], + [ 0.13667798, 0.13446606, 0.17493774], + [ 0.13405762, 0.13188822, 0.16820842], + [ 0.13165767, 0.12950667, 0.16183275], + [ 0.12948748, 0.12733187, 0.15580631], + [ 0.12755435, 0.1253723 , 0.15014098], + [ 0.12586516, 0.12363617, 0.1448459 ], + [ 0.12442647, 0.12213143, 0.13992571], + [ 0.12324241, 0.12086419, 0.13539995], + [ 0.12232067, 0.11984278, 0.13124644], + [ 0.12166209, 0.11907077, 0.12749671], + [ 0.12126982, 0.11855309, 0.12415079], + [ 0.12114244, 0.11829179, 0.1212385 ], + [ 0.12127766, 0.11828837, 0.11878534], + [ 0.12284806, 0.1179729 , 0.11772022], + [ 0.12619498, 0.11721796, 0.11770203], + [ 0.129968 , 0.11663788, 0.11792377], + [ 0.13410011, 0.11625146, 0.11839138], + [ 0.13855459, 0.11606618, 0.11910584], + [ 0.14333775, 0.11607038, 0.1200606 ], + [ 0.148417 , 0.11626929, 0.12125453], + [ 0.15377389, 0.11666192, 0.12268364], + [ 0.15941427, 0.11723486, 0.12433911], + [ 0.16533376, 0.11797856, 0.12621303], + [ 0.17152547, 0.11888403, 0.12829735], + [ 0.17797765, 0.11994436, 0.13058435], + [ 0.18468769, 0.12114722, 0.13306426], + [ 0.19165663, 0.12247737, 0.13572616], + [ 0.19884415, 0.12394381, 0.1385669 ], + [ 0.20627181, 0.12551883, 0.14157124], + [ 0.21394877, 0.12718055, 0.14472604], + [ 0.22184572, 0.12893119, 0.14802579], + [ 0.22994394, 0.13076731, 0.15146314], + [ 0.23823937, 0.13267611, 0.15502793], + [ 0.24676041, 0.13462172, 0.15870321], + [ 0.25546457, 0.13661751, 0.16248722], + [ 0.26433628, 0.13865956, 0.16637301], + [ 0.27341345, 0.14070412, 0.17034221], + [ 0.28264773, 0.14277192, 0.1743957 ], + [ 0.29202272, 0.14486161, 0.17852793], + [ 0.30159648, 0.14691224, 0.1827169 ], + [ 0.31129002, 0.14897583, 0.18695213], + [ 0.32111555, 0.15103351, 0.19119629], + [ 0.33107961, 0.1530674 , 0.19543758], + [ 0.34119892, 0.15504762, 0.1996803 ], + [ 0.35142388, 0.15701131, 0.20389086], + [ 0.36178937, 0.1589124 , 0.20807639], + [ 0.37229381, 0.16073993, 0.21223189], + [ 0.38288348, 0.16254006, 0.2163249 ], + [ 0.39359592, 0.16426336, 0.22036577], + [ 0.40444332, 0.16588767, 0.22434027], + [ 0.41537995, 0.16745325, 0.2282297 ], + [ 0.42640867, 0.16894939, 0.23202755], + [ 0.43754706, 0.17034847, 0.23572899], + [ 0.44878564, 0.1716535 , 0.23932344], + [ 0.4601126 , 0.17287365, 0.24278607], + [ 0.47151732, 0.17401641, 0.24610337], + [ 0.48300689, 0.17506676, 0.2492737 ], + [ 0.49458302, 0.17601892, 0.25227688], + [ 0.50623876, 0.17687777, 0.255096 ], + [ 0.5179623 , 0.17765528, 0.2577162 ], + [ 0.52975234, 0.17835232, 0.2601134 ], + [ 0.54159776, 0.17898292, 0.26226847], + [ 0.55348804, 0.17956232, 0.26416003], + [ 0.56541729, 0.18010175, 0.26575971], + [ 0.57736669, 0.180631 , 0.26704888], + [ 0.58932081, 0.18117827, 0.26800409], + [ 0.60127582, 0.18175888, 0.26858488], + [ 0.61319563, 0.1824336 , 0.2687872 ], + [ 0.62506376, 0.18324015, 0.26858301], + [ 0.63681202, 0.18430173, 0.26795276], + [ 0.64842603, 0.18565472, 0.26689463], + [ 0.65988195, 0.18734638, 0.26543435], + [ 0.67111966, 0.18948885, 0.26357955], + [ 0.68209194, 0.19216636, 0.26137175], + [ 0.69281185, 0.19535326, 0.25887063], + [ 0.70335022, 0.19891271, 0.25617971], + [ 0.71375229, 0.20276438, 0.25331365], + [ 0.72401436, 0.20691287, 0.25027366], + [ 0.73407638, 0.21145051, 0.24710661], + [ 0.74396983, 0.21631913, 0.24380715], + [ 0.75361506, 0.22163653, 0.24043996], + [ 0.7630579 , 0.22731637, 0.23700095], + [ 0.77222228, 0.23346231, 0.23356628], + [ 0.78115441, 0.23998404, 0.23013825], + [ 0.78979746, 0.24694858, 0.22678822], + [ 0.79819286, 0.25427223, 0.22352658], + [ 0.80630444, 0.26198807, 0.22040877], + [ 0.81417437, 0.27001406, 0.21744645], + [ 0.82177364, 0.27837336, 0.21468316], + [ 0.82915955, 0.28696963, 0.21210766], + [ 0.83628628, 0.2958499 , 0.20977813], + [ 0.84322168, 0.30491136, 0.20766435], + [ 0.84995458, 0.31415945, 0.2057863 ], + [ 0.85648867, 0.32358058, 0.20415327], + [ 0.86286243, 0.33312058, 0.20274969], + [ 0.86908321, 0.34276705, 0.20157271], + [ 0.87512876, 0.3525416 , 0.20064949], + [ 0.88100349, 0.36243385, 0.19999078], + [ 0.8866469 , 0.37249496, 0.1997976 ], + [ 0.89203964, 0.38273475, 0.20013431], + [ 0.89713496, 0.39318156, 0.20121514], + [ 0.90195099, 0.40380687, 0.20301555], + [ 0.90648379, 0.41460191, 0.20558847], + [ 0.9106967 , 0.42557857, 0.20918529], + [ 0.91463791, 0.43668557, 0.21367954], + [ 0.91830723, 0.44790913, 0.21916352], + [ 0.92171507, 0.45922856, 0.22568002], + [ 0.92491786, 0.4705936 , 0.23308207], + [ 0.92790792, 0.48200153, 0.24145932], + [ 0.93073701, 0.49341219, 0.25065486], + [ 0.93343918, 0.5048017 , 0.26056148], + [ 0.93602064, 0.51616486, 0.27118485], + [ 0.93850535, 0.52748892, 0.28242464], + [ 0.94092933, 0.53875462, 0.29416042], + [ 0.94330011, 0.5499628 , 0.30634189], + [ 0.94563159, 0.56110987, 0.31891624], + [ 0.94792955, 0.57219822, 0.33184256], + [ 0.95020929, 0.5832232 , 0.34508419], + [ 0.95247324, 0.59419035, 0.35859866], + [ 0.95471709, 0.60510869, 0.37236035], + [ 0.95698411, 0.61595766, 0.38629631], + [ 0.95923863, 0.62676473, 0.40043317], + [ 0.9615041 , 0.6375203 , 0.41474106], + [ 0.96371553, 0.64826619, 0.42928335], + [ 0.96591497, 0.65899621, 0.44380444], + [ 0.96809871, 0.66971662, 0.45830232], + [ 0.9702495 , 0.6804394 , 0.47280492], + [ 0.9723881 , 0.69115622, 0.48729272], + [ 0.97450723, 0.70187358, 0.50178034], + [ 0.9766108 , 0.712592 , 0.51626837], + [ 0.97871716, 0.72330511, 0.53074053], + [ 0.98082222, 0.73401769, 0.54520694], + [ 0.9829001 , 0.74474445, 0.5597019 ], + [ 0.98497466, 0.75547635, 0.57420239], + [ 0.98705581, 0.76621129, 0.58870185], + [ 0.98913325, 0.77695637, 0.60321626], + [ 0.99119918, 0.78771716, 0.61775821], + [ 0.9932672 , 0.79848979, 0.63231691], + [ 0.99535958, 0.80926704, 0.64687278], + [ 0.99740544, 0.82008078, 0.66150571], + [ 0.9992197 , 0.83100723, 0.6764127 ]] -_luts = [_rocket_lut, _mako_lut, _vlag_lut, _fmri_lut] -_names = ["rocket", "mako", "vlag", "fmri"] +_luts = [_rocket_lut, _mako_lut, _vlag_lut, _icefire_lut] +_names = ["rocket", "mako", "vlag", "icefire"] for _lut, _name in zip(_luts, _names): diff --git a/seaborn/matrix.py b/seaborn/matrix.py index c66df670c9..b3a885f22a 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -217,7 +217,7 @@ def _determine_cmap_params(self, plot_data, vmin, vmax, if center is None: self.cmap = cm.rocket else: - self.cmap = cm.fmri + self.cmap = cm.icefire elif isinstance(cmap, string_types): self.cmap = mpl.cm.get_cmap(cmap) elif isinstance(cmap, list): From 0833cbb5a56585d64446f1c0b40a6b16aa12c663 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 22 Jun 2017 11:16:43 -0400 Subject: [PATCH 0401/1738] Standardize formatting of luts --- seaborn/cm.py | 2056 +++++++++++++++++++++++++------------------------ 1 file changed, 1032 insertions(+), 1024 deletions(-) diff --git a/seaborn/cm.py b/seaborn/cm.py index 0f6076980f..c250163428 100644 --- a/seaborn/cm.py +++ b/seaborn/cm.py @@ -1,1036 +1,1044 @@ from matplotlib import colors, cm as mpl_cm -_rocket_lut = [[ 0.01060815, 0.01808215, 0.10018654], - [ 0.01428972, 0.02048237, 0.10374486], - [ 0.01831941, 0.0229766 , 0.10738511], - [ 0.02275049, 0.02554464, 0.11108639], - [ 0.02759119, 0.02818316, 0.11483751], - [ 0.03285175, 0.03088792, 0.11863035], - [ 0.03853466, 0.03365771, 0.12245873], - [ 0.04447016, 0.03648425, 0.12631831], - [ 0.05032105, 0.03936808, 0.13020508], - [ 0.05611171, 0.04224835, 0.13411624], - [ 0.0618531 , 0.04504866, 0.13804929], - [ 0.06755457, 0.04778179, 0.14200206], - [ 0.0732236 , 0.05045047, 0.14597263], - [ 0.0788708 , 0.05305461, 0.14995981], - [ 0.08450105, 0.05559631, 0.15396203], - [ 0.09011319, 0.05808059, 0.15797687], - [ 0.09572396, 0.06050127, 0.16200507], - [ 0.10132312, 0.06286782, 0.16604287], - [ 0.10692823, 0.06517224, 0.17009175], - [ 0.1125315 , 0.06742194, 0.17414848], - [ 0.11813947, 0.06961499, 0.17821272], - [ 0.12375803, 0.07174938, 0.18228425], - [ 0.12938228, 0.07383015, 0.18636053], - [ 0.13501631, 0.07585609, 0.19044109], - [ 0.14066867, 0.0778224 , 0.19452676], - [ 0.14633406, 0.07973393, 0.1986151 ], - [ 0.15201338, 0.08159108, 0.20270523], - [ 0.15770877, 0.08339312, 0.20679668], - [ 0.16342174, 0.0851396 , 0.21088893], - [ 0.16915387, 0.08682996, 0.21498104], - [ 0.17489524, 0.08848235, 0.2190294 ], - [ 0.18065495, 0.09009031, 0.22303512], - [ 0.18643324, 0.09165431, 0.22699705], - [ 0.19223028, 0.09317479, 0.23091409], - [ 0.19804623, 0.09465217, 0.23478512], - [ 0.20388117, 0.09608689, 0.23860907], - [ 0.20973515, 0.09747934, 0.24238489], - [ 0.21560818, 0.09882993, 0.24611154], - [ 0.22150014, 0.10013944, 0.2497868 ], - [ 0.22741085, 0.10140876, 0.25340813], - [ 0.23334047, 0.10263737, 0.25697736], - [ 0.23928891, 0.10382562, 0.2604936 ], - [ 0.24525608, 0.10497384, 0.26395596], - [ 0.25124182, 0.10608236, 0.26736359], - [ 0.25724602, 0.10715148, 0.27071569], - [ 0.26326851, 0.1081815 , 0.27401148], - [ 0.26930915, 0.1091727 , 0.2772502 ], - [ 0.27536766, 0.11012568, 0.28043021], - [ 0.28144375, 0.11104133, 0.2835489 ], - [ 0.2875374 , 0.11191896, 0.28660853], - [ 0.29364846, 0.11275876, 0.2896085 ], - [ 0.29977678, 0.11356089, 0.29254823], - [ 0.30592213, 0.11432553, 0.29542718], - [ 0.31208435, 0.11505284, 0.29824485], - [ 0.31826327, 0.1157429 , 0.30100076], - [ 0.32445869, 0.11639585, 0.30369448], - [ 0.33067031, 0.11701189, 0.30632563], - [ 0.33689808, 0.11759095, 0.3088938 ], - [ 0.34314168, 0.11813362, 0.31139721], - [ 0.34940101, 0.11863987, 0.3138355 ], - [ 0.355676 , 0.11910909, 0.31620996], - [ 0.36196644, 0.1195413 , 0.31852037], - [ 0.36827206, 0.11993653, 0.32076656], - [ 0.37459292, 0.12029443, 0.32294825], - [ 0.38092887, 0.12061482, 0.32506528], - [ 0.38727975, 0.12089756, 0.3271175 ], - [ 0.39364518, 0.12114272, 0.32910494], - [ 0.40002537, 0.12134964, 0.33102734], - [ 0.40642019, 0.12151801, 0.33288464], - [ 0.41282936, 0.12164769, 0.33467689], - [ 0.41925278, 0.12173833, 0.33640407], - [ 0.42569057, 0.12178916, 0.33806605], - [ 0.43214263, 0.12179973, 0.33966284], - [ 0.43860848, 0.12177004, 0.34119475], - [ 0.44508855, 0.12169883, 0.34266151], - [ 0.45158266, 0.12158557, 0.34406324], - [ 0.45809049, 0.12142996, 0.34540024], - [ 0.46461238, 0.12123063, 0.34667231], - [ 0.47114798, 0.12098721, 0.34787978], - [ 0.47769736, 0.12069864, 0.34902273], - [ 0.48426077, 0.12036349, 0.35010104], - [ 0.49083761, 0.11998161, 0.35111537], - [ 0.49742847, 0.11955087, 0.35206533], - [ 0.50403286, 0.11907081, 0.35295152], - [ 0.51065109, 0.11853959, 0.35377385], - [ 0.51728314, 0.1179558 , 0.35453252], - [ 0.52392883, 0.11731817, 0.35522789], - [ 0.53058853, 0.11662445, 0.35585982], - [ 0.53726173, 0.11587369, 0.35642903], - [ 0.54394898, 0.11506307, 0.35693521], - [ 0.5506426 , 0.11420757, 0.35737863], - [ 0.55734473, 0.11330456, 0.35775059], - [ 0.56405586, 0.11235265, 0.35804813], - [ 0.57077365, 0.11135597, 0.35827146], - [ 0.5774991 , 0.11031233, 0.35841679], - [ 0.58422945, 0.10922707, 0.35848469], - [ 0.59096382, 0.10810205, 0.35847347], - [ 0.59770215, 0.10693774, 0.35838029], - [ 0.60444226, 0.10573912, 0.35820487], - [ 0.61118304, 0.10450943, 0.35794557], - [ 0.61792306, 0.10325288, 0.35760108], - [ 0.62466162, 0.10197244, 0.35716891], - [ 0.63139686, 0.10067417, 0.35664819], - [ 0.63812122, 0.09938212, 0.35603757], - [ 0.64483795, 0.0980891 , 0.35533555], - [ 0.65154562, 0.09680192, 0.35454107], - [ 0.65824241, 0.09552918, 0.3536529 ], - [ 0.66492652, 0.09428017, 0.3526697 ], - [ 0.67159578, 0.09306598, 0.35159077], - [ 0.67824099, 0.09192342, 0.3504148 ], - [ 0.684863 , 0.09085633, 0.34914061], - [ 0.69146268, 0.0898675 , 0.34776864], - [ 0.69803757, 0.08897226, 0.3462986 ], - [ 0.70457834, 0.0882129 , 0.34473046], - [ 0.71108138, 0.08761223, 0.3430635 ], - [ 0.7175507 , 0.08716212, 0.34129974], - [ 0.72398193, 0.08688725, 0.33943958], - [ 0.73035829, 0.0868623 , 0.33748452], - [ 0.73669146, 0.08704683, 0.33543669], - [ 0.74297501, 0.08747196, 0.33329799], - [ 0.74919318, 0.08820542, 0.33107204], - [ 0.75535825, 0.08919792, 0.32876184], - [ 0.76145589, 0.09050716, 0.32637117], - [ 0.76748424, 0.09213602, 0.32390525], - [ 0.77344838, 0.09405684, 0.32136808], - [ 0.77932641, 0.09634794, 0.31876642], - [ 0.78513609, 0.09892473, 0.31610488], - [ 0.79085854, 0.10184672, 0.313391 ], - [ 0.7965014 , 0.10506637, 0.31063031], - [ 0.80205987, 0.10858333, 0.30783 ], - [ 0.80752799, 0.11239964, 0.30499738], - [ 0.81291606, 0.11645784, 0.30213802], - [ 0.81820481, 0.12080606, 0.29926105], - [ 0.82341472, 0.12535343, 0.2963705 ], - [ 0.82852822, 0.13014118, 0.29347474], - [ 0.83355779, 0.13511035, 0.29057852], - [ 0.83850183, 0.14025098, 0.2876878 ], - [ 0.84335441, 0.14556683, 0.28480819], - [ 0.84813096, 0.15099892, 0.281943 ], - [ 0.85281737, 0.15657772, 0.27909826], - [ 0.85742602, 0.1622583 , 0.27627462], - [ 0.86196552, 0.16801239, 0.27346473], - [ 0.86641628, 0.17387796, 0.27070818], - [ 0.87079129, 0.17982114, 0.26797378], - [ 0.87507281, 0.18587368, 0.26529697], - [ 0.87925878, 0.19203259, 0.26268136], - [ 0.8833417 , 0.19830556, 0.26014181], - [ 0.88731387, 0.20469941, 0.25769539], - [ 0.89116859, 0.21121788, 0.2553592 ], - [ 0.89490337, 0.21785614, 0.25314362], - [ 0.8985026 , 0.22463251, 0.25108745], - [ 0.90197527, 0.23152063, 0.24918223], - [ 0.90530097, 0.23854541, 0.24748098], - [ 0.90848638, 0.24568473, 0.24598324], - [ 0.911533 , 0.25292623, 0.24470258], - [ 0.9144225 , 0.26028902, 0.24369359], - [ 0.91717106, 0.26773821, 0.24294137], - [ 0.91978131, 0.27526191, 0.24245973], - [ 0.92223947, 0.28287251, 0.24229568], - [ 0.92456587, 0.29053388, 0.24242622], - [ 0.92676657, 0.29823282, 0.24285536], - [ 0.92882964, 0.30598085, 0.24362274], - [ 0.93078135, 0.31373977, 0.24468803], - [ 0.93262051, 0.3215093 , 0.24606461], - [ 0.93435067, 0.32928362, 0.24775328], - [ 0.93599076, 0.33703942, 0.24972157], - [ 0.93752831, 0.34479177, 0.25199928], - [ 0.93899289, 0.35250734, 0.25452808], - [ 0.94036561, 0.36020899, 0.25734661], - [ 0.94167588, 0.36786594, 0.2603949 ], - [ 0.94291042, 0.37549479, 0.26369821], - [ 0.94408513, 0.3830811 , 0.26722004], - [ 0.94520419, 0.39062329, 0.27094924], - [ 0.94625977, 0.39813168, 0.27489742], - [ 0.94727016, 0.4055909 , 0.27902322], - [ 0.94823505, 0.41300424, 0.28332283], - [ 0.94914549, 0.42038251, 0.28780969], - [ 0.95001704, 0.42771398, 0.29244728], - [ 0.95085121, 0.43500005, 0.29722817], - [ 0.95165009, 0.44224144, 0.30214494], - [ 0.9524044 , 0.44944853, 0.3072105 ], - [ 0.95312556, 0.45661389, 0.31239776], - [ 0.95381595, 0.46373781, 0.31769923], - [ 0.95447591, 0.47082238, 0.32310953], - [ 0.95510255, 0.47787236, 0.32862553], - [ 0.95569679, 0.48489115, 0.33421404], - [ 0.95626788, 0.49187351, 0.33985601], - [ 0.95681685, 0.49882008, 0.34555431], - [ 0.9573439 , 0.50573243, 0.35130912], - [ 0.95784842, 0.51261283, 0.35711942], - [ 0.95833051, 0.51946267, 0.36298589], - [ 0.95879054, 0.52628305, 0.36890904], - [ 0.95922872, 0.53307513, 0.3748895 ], - [ 0.95964538, 0.53983991, 0.38092784], - [ 0.96004345, 0.54657593, 0.3870292 ], - [ 0.96042097, 0.55328624, 0.39319057], - [ 0.96077819, 0.55997184, 0.39941173], - [ 0.9611152 , 0.5666337 , 0.40569343], - [ 0.96143273, 0.57327231, 0.41203603], - [ 0.96173392, 0.57988594, 0.41844491], - [ 0.96201757, 0.58647675, 0.42491751], - [ 0.96228344, 0.59304598, 0.43145271], - [ 0.96253168, 0.5995944 , 0.43805131], - [ 0.96276513, 0.60612062, 0.44471698], - [ 0.96298491, 0.6126247 , 0.45145074], - [ 0.96318967, 0.61910879, 0.45824902], - [ 0.96337949, 0.6255736 , 0.46511271], - [ 0.96355923, 0.63201624, 0.47204746], - [ 0.96372785, 0.63843852, 0.47905028], - [ 0.96388426, 0.64484214, 0.4861196 ], - [ 0.96403203, 0.65122535, 0.4932578 ], - [ 0.96417332, 0.65758729, 0.50046894], - [ 0.9643063 , 0.66393045, 0.5077467 ], - [ 0.96443322, 0.67025402, 0.51509334], - [ 0.96455845, 0.67655564, 0.52251447], - [ 0.96467922, 0.68283846, 0.53000231], - [ 0.96479861, 0.68910113, 0.53756026], - [ 0.96492035, 0.69534192, 0.5451917 ], - [ 0.96504223, 0.7015636 , 0.5528892 ], - [ 0.96516917, 0.70776351, 0.5606593 ], - [ 0.96530224, 0.71394212, 0.56849894], - [ 0.96544032, 0.72010124, 0.57640375], - [ 0.96559206, 0.72623592, 0.58438387], - [ 0.96575293, 0.73235058, 0.59242739], - [ 0.96592829, 0.73844258, 0.60053991], - [ 0.96612013, 0.74451182, 0.60871954], - [ 0.96632832, 0.75055966, 0.61696136], - [ 0.96656022, 0.75658231, 0.62527295], - [ 0.96681185, 0.76258381, 0.63364277], - [ 0.96709183, 0.76855969, 0.64207921], - [ 0.96739773, 0.77451297, 0.65057302], - [ 0.96773482, 0.78044149, 0.65912731], - [ 0.96810471, 0.78634563, 0.66773889], - [ 0.96850919, 0.79222565, 0.6764046 ], - [ 0.96893132, 0.79809112, 0.68512266], - [ 0.96935926, 0.80395415, 0.69383201], - [ 0.9698028 , 0.80981139, 0.70252255], - [ 0.97025511, 0.81566605, 0.71120296], - [ 0.97071849, 0.82151775, 0.71987163], - [ 0.97120159, 0.82736371, 0.72851999], - [ 0.97169389, 0.83320847, 0.73716071], - [ 0.97220061, 0.83905052, 0.74578903], - [ 0.97272597, 0.84488881, 0.75440141], - [ 0.97327085, 0.85072354, 0.76299805], - [ 0.97383206, 0.85655639, 0.77158353], - [ 0.97441222, 0.86238689, 0.78015619], - [ 0.97501782, 0.86821321, 0.78871034], - [ 0.97564391, 0.87403763, 0.79725261], - [ 0.97628674, 0.87986189, 0.8057883 ], - [ 0.97696114, 0.88568129, 0.81430324], - [ 0.97765722, 0.89149971, 0.82280948], - [ 0.97837585, 0.89731727, 0.83130786], - [ 0.97912374, 0.90313207, 0.83979337], - [ 0.979891 , 0.90894778, 0.84827858], - [ 0.98067764, 0.91476465, 0.85676611], - [ 0.98137749, 0.92061729, 0.86536915]] +_rocket_lut = [ + [ 0.01060815, 0.01808215, 0.10018654], + [ 0.01428972, 0.02048237, 0.10374486], + [ 0.01831941, 0.0229766 , 0.10738511], + [ 0.02275049, 0.02554464, 0.11108639], + [ 0.02759119, 0.02818316, 0.11483751], + [ 0.03285175, 0.03088792, 0.11863035], + [ 0.03853466, 0.03365771, 0.12245873], + [ 0.04447016, 0.03648425, 0.12631831], + [ 0.05032105, 0.03936808, 0.13020508], + [ 0.05611171, 0.04224835, 0.13411624], + [ 0.0618531 , 0.04504866, 0.13804929], + [ 0.06755457, 0.04778179, 0.14200206], + [ 0.0732236 , 0.05045047, 0.14597263], + [ 0.0788708 , 0.05305461, 0.14995981], + [ 0.08450105, 0.05559631, 0.15396203], + [ 0.09011319, 0.05808059, 0.15797687], + [ 0.09572396, 0.06050127, 0.16200507], + [ 0.10132312, 0.06286782, 0.16604287], + [ 0.10692823, 0.06517224, 0.17009175], + [ 0.1125315 , 0.06742194, 0.17414848], + [ 0.11813947, 0.06961499, 0.17821272], + [ 0.12375803, 0.07174938, 0.18228425], + [ 0.12938228, 0.07383015, 0.18636053], + [ 0.13501631, 0.07585609, 0.19044109], + [ 0.14066867, 0.0778224 , 0.19452676], + [ 0.14633406, 0.07973393, 0.1986151 ], + [ 0.15201338, 0.08159108, 0.20270523], + [ 0.15770877, 0.08339312, 0.20679668], + [ 0.16342174, 0.0851396 , 0.21088893], + [ 0.16915387, 0.08682996, 0.21498104], + [ 0.17489524, 0.08848235, 0.2190294 ], + [ 0.18065495, 0.09009031, 0.22303512], + [ 0.18643324, 0.09165431, 0.22699705], + [ 0.19223028, 0.09317479, 0.23091409], + [ 0.19804623, 0.09465217, 0.23478512], + [ 0.20388117, 0.09608689, 0.23860907], + [ 0.20973515, 0.09747934, 0.24238489], + [ 0.21560818, 0.09882993, 0.24611154], + [ 0.22150014, 0.10013944, 0.2497868 ], + [ 0.22741085, 0.10140876, 0.25340813], + [ 0.23334047, 0.10263737, 0.25697736], + [ 0.23928891, 0.10382562, 0.2604936 ], + [ 0.24525608, 0.10497384, 0.26395596], + [ 0.25124182, 0.10608236, 0.26736359], + [ 0.25724602, 0.10715148, 0.27071569], + [ 0.26326851, 0.1081815 , 0.27401148], + [ 0.26930915, 0.1091727 , 0.2772502 ], + [ 0.27536766, 0.11012568, 0.28043021], + [ 0.28144375, 0.11104133, 0.2835489 ], + [ 0.2875374 , 0.11191896, 0.28660853], + [ 0.29364846, 0.11275876, 0.2896085 ], + [ 0.29977678, 0.11356089, 0.29254823], + [ 0.30592213, 0.11432553, 0.29542718], + [ 0.31208435, 0.11505284, 0.29824485], + [ 0.31826327, 0.1157429 , 0.30100076], + [ 0.32445869, 0.11639585, 0.30369448], + [ 0.33067031, 0.11701189, 0.30632563], + [ 0.33689808, 0.11759095, 0.3088938 ], + [ 0.34314168, 0.11813362, 0.31139721], + [ 0.34940101, 0.11863987, 0.3138355 ], + [ 0.355676 , 0.11910909, 0.31620996], + [ 0.36196644, 0.1195413 , 0.31852037], + [ 0.36827206, 0.11993653, 0.32076656], + [ 0.37459292, 0.12029443, 0.32294825], + [ 0.38092887, 0.12061482, 0.32506528], + [ 0.38727975, 0.12089756, 0.3271175 ], + [ 0.39364518, 0.12114272, 0.32910494], + [ 0.40002537, 0.12134964, 0.33102734], + [ 0.40642019, 0.12151801, 0.33288464], + [ 0.41282936, 0.12164769, 0.33467689], + [ 0.41925278, 0.12173833, 0.33640407], + [ 0.42569057, 0.12178916, 0.33806605], + [ 0.43214263, 0.12179973, 0.33966284], + [ 0.43860848, 0.12177004, 0.34119475], + [ 0.44508855, 0.12169883, 0.34266151], + [ 0.45158266, 0.12158557, 0.34406324], + [ 0.45809049, 0.12142996, 0.34540024], + [ 0.46461238, 0.12123063, 0.34667231], + [ 0.47114798, 0.12098721, 0.34787978], + [ 0.47769736, 0.12069864, 0.34902273], + [ 0.48426077, 0.12036349, 0.35010104], + [ 0.49083761, 0.11998161, 0.35111537], + [ 0.49742847, 0.11955087, 0.35206533], + [ 0.50403286, 0.11907081, 0.35295152], + [ 0.51065109, 0.11853959, 0.35377385], + [ 0.51728314, 0.1179558 , 0.35453252], + [ 0.52392883, 0.11731817, 0.35522789], + [ 0.53058853, 0.11662445, 0.35585982], + [ 0.53726173, 0.11587369, 0.35642903], + [ 0.54394898, 0.11506307, 0.35693521], + [ 0.5506426 , 0.11420757, 0.35737863], + [ 0.55734473, 0.11330456, 0.35775059], + [ 0.56405586, 0.11235265, 0.35804813], + [ 0.57077365, 0.11135597, 0.35827146], + [ 0.5774991 , 0.11031233, 0.35841679], + [ 0.58422945, 0.10922707, 0.35848469], + [ 0.59096382, 0.10810205, 0.35847347], + [ 0.59770215, 0.10693774, 0.35838029], + [ 0.60444226, 0.10573912, 0.35820487], + [ 0.61118304, 0.10450943, 0.35794557], + [ 0.61792306, 0.10325288, 0.35760108], + [ 0.62466162, 0.10197244, 0.35716891], + [ 0.63139686, 0.10067417, 0.35664819], + [ 0.63812122, 0.09938212, 0.35603757], + [ 0.64483795, 0.0980891 , 0.35533555], + [ 0.65154562, 0.09680192, 0.35454107], + [ 0.65824241, 0.09552918, 0.3536529 ], + [ 0.66492652, 0.09428017, 0.3526697 ], + [ 0.67159578, 0.09306598, 0.35159077], + [ 0.67824099, 0.09192342, 0.3504148 ], + [ 0.684863 , 0.09085633, 0.34914061], + [ 0.69146268, 0.0898675 , 0.34776864], + [ 0.69803757, 0.08897226, 0.3462986 ], + [ 0.70457834, 0.0882129 , 0.34473046], + [ 0.71108138, 0.08761223, 0.3430635 ], + [ 0.7175507 , 0.08716212, 0.34129974], + [ 0.72398193, 0.08688725, 0.33943958], + [ 0.73035829, 0.0868623 , 0.33748452], + [ 0.73669146, 0.08704683, 0.33543669], + [ 0.74297501, 0.08747196, 0.33329799], + [ 0.74919318, 0.08820542, 0.33107204], + [ 0.75535825, 0.08919792, 0.32876184], + [ 0.76145589, 0.09050716, 0.32637117], + [ 0.76748424, 0.09213602, 0.32390525], + [ 0.77344838, 0.09405684, 0.32136808], + [ 0.77932641, 0.09634794, 0.31876642], + [ 0.78513609, 0.09892473, 0.31610488], + [ 0.79085854, 0.10184672, 0.313391 ], + [ 0.7965014 , 0.10506637, 0.31063031], + [ 0.80205987, 0.10858333, 0.30783 ], + [ 0.80752799, 0.11239964, 0.30499738], + [ 0.81291606, 0.11645784, 0.30213802], + [ 0.81820481, 0.12080606, 0.29926105], + [ 0.82341472, 0.12535343, 0.2963705 ], + [ 0.82852822, 0.13014118, 0.29347474], + [ 0.83355779, 0.13511035, 0.29057852], + [ 0.83850183, 0.14025098, 0.2876878 ], + [ 0.84335441, 0.14556683, 0.28480819], + [ 0.84813096, 0.15099892, 0.281943 ], + [ 0.85281737, 0.15657772, 0.27909826], + [ 0.85742602, 0.1622583 , 0.27627462], + [ 0.86196552, 0.16801239, 0.27346473], + [ 0.86641628, 0.17387796, 0.27070818], + [ 0.87079129, 0.17982114, 0.26797378], + [ 0.87507281, 0.18587368, 0.26529697], + [ 0.87925878, 0.19203259, 0.26268136], + [ 0.8833417 , 0.19830556, 0.26014181], + [ 0.88731387, 0.20469941, 0.25769539], + [ 0.89116859, 0.21121788, 0.2553592 ], + [ 0.89490337, 0.21785614, 0.25314362], + [ 0.8985026 , 0.22463251, 0.25108745], + [ 0.90197527, 0.23152063, 0.24918223], + [ 0.90530097, 0.23854541, 0.24748098], + [ 0.90848638, 0.24568473, 0.24598324], + [ 0.911533 , 0.25292623, 0.24470258], + [ 0.9144225 , 0.26028902, 0.24369359], + [ 0.91717106, 0.26773821, 0.24294137], + [ 0.91978131, 0.27526191, 0.24245973], + [ 0.92223947, 0.28287251, 0.24229568], + [ 0.92456587, 0.29053388, 0.24242622], + [ 0.92676657, 0.29823282, 0.24285536], + [ 0.92882964, 0.30598085, 0.24362274], + [ 0.93078135, 0.31373977, 0.24468803], + [ 0.93262051, 0.3215093 , 0.24606461], + [ 0.93435067, 0.32928362, 0.24775328], + [ 0.93599076, 0.33703942, 0.24972157], + [ 0.93752831, 0.34479177, 0.25199928], + [ 0.93899289, 0.35250734, 0.25452808], + [ 0.94036561, 0.36020899, 0.25734661], + [ 0.94167588, 0.36786594, 0.2603949 ], + [ 0.94291042, 0.37549479, 0.26369821], + [ 0.94408513, 0.3830811 , 0.26722004], + [ 0.94520419, 0.39062329, 0.27094924], + [ 0.94625977, 0.39813168, 0.27489742], + [ 0.94727016, 0.4055909 , 0.27902322], + [ 0.94823505, 0.41300424, 0.28332283], + [ 0.94914549, 0.42038251, 0.28780969], + [ 0.95001704, 0.42771398, 0.29244728], + [ 0.95085121, 0.43500005, 0.29722817], + [ 0.95165009, 0.44224144, 0.30214494], + [ 0.9524044 , 0.44944853, 0.3072105 ], + [ 0.95312556, 0.45661389, 0.31239776], + [ 0.95381595, 0.46373781, 0.31769923], + [ 0.95447591, 0.47082238, 0.32310953], + [ 0.95510255, 0.47787236, 0.32862553], + [ 0.95569679, 0.48489115, 0.33421404], + [ 0.95626788, 0.49187351, 0.33985601], + [ 0.95681685, 0.49882008, 0.34555431], + [ 0.9573439 , 0.50573243, 0.35130912], + [ 0.95784842, 0.51261283, 0.35711942], + [ 0.95833051, 0.51946267, 0.36298589], + [ 0.95879054, 0.52628305, 0.36890904], + [ 0.95922872, 0.53307513, 0.3748895 ], + [ 0.95964538, 0.53983991, 0.38092784], + [ 0.96004345, 0.54657593, 0.3870292 ], + [ 0.96042097, 0.55328624, 0.39319057], + [ 0.96077819, 0.55997184, 0.39941173], + [ 0.9611152 , 0.5666337 , 0.40569343], + [ 0.96143273, 0.57327231, 0.41203603], + [ 0.96173392, 0.57988594, 0.41844491], + [ 0.96201757, 0.58647675, 0.42491751], + [ 0.96228344, 0.59304598, 0.43145271], + [ 0.96253168, 0.5995944 , 0.43805131], + [ 0.96276513, 0.60612062, 0.44471698], + [ 0.96298491, 0.6126247 , 0.45145074], + [ 0.96318967, 0.61910879, 0.45824902], + [ 0.96337949, 0.6255736 , 0.46511271], + [ 0.96355923, 0.63201624, 0.47204746], + [ 0.96372785, 0.63843852, 0.47905028], + [ 0.96388426, 0.64484214, 0.4861196 ], + [ 0.96403203, 0.65122535, 0.4932578 ], + [ 0.96417332, 0.65758729, 0.50046894], + [ 0.9643063 , 0.66393045, 0.5077467 ], + [ 0.96443322, 0.67025402, 0.51509334], + [ 0.96455845, 0.67655564, 0.52251447], + [ 0.96467922, 0.68283846, 0.53000231], + [ 0.96479861, 0.68910113, 0.53756026], + [ 0.96492035, 0.69534192, 0.5451917 ], + [ 0.96504223, 0.7015636 , 0.5528892 ], + [ 0.96516917, 0.70776351, 0.5606593 ], + [ 0.96530224, 0.71394212, 0.56849894], + [ 0.96544032, 0.72010124, 0.57640375], + [ 0.96559206, 0.72623592, 0.58438387], + [ 0.96575293, 0.73235058, 0.59242739], + [ 0.96592829, 0.73844258, 0.60053991], + [ 0.96612013, 0.74451182, 0.60871954], + [ 0.96632832, 0.75055966, 0.61696136], + [ 0.96656022, 0.75658231, 0.62527295], + [ 0.96681185, 0.76258381, 0.63364277], + [ 0.96709183, 0.76855969, 0.64207921], + [ 0.96739773, 0.77451297, 0.65057302], + [ 0.96773482, 0.78044149, 0.65912731], + [ 0.96810471, 0.78634563, 0.66773889], + [ 0.96850919, 0.79222565, 0.6764046 ], + [ 0.96893132, 0.79809112, 0.68512266], + [ 0.96935926, 0.80395415, 0.69383201], + [ 0.9698028 , 0.80981139, 0.70252255], + [ 0.97025511, 0.81566605, 0.71120296], + [ 0.97071849, 0.82151775, 0.71987163], + [ 0.97120159, 0.82736371, 0.72851999], + [ 0.97169389, 0.83320847, 0.73716071], + [ 0.97220061, 0.83905052, 0.74578903], + [ 0.97272597, 0.84488881, 0.75440141], + [ 0.97327085, 0.85072354, 0.76299805], + [ 0.97383206, 0.85655639, 0.77158353], + [ 0.97441222, 0.86238689, 0.78015619], + [ 0.97501782, 0.86821321, 0.78871034], + [ 0.97564391, 0.87403763, 0.79725261], + [ 0.97628674, 0.87986189, 0.8057883 ], + [ 0.97696114, 0.88568129, 0.81430324], + [ 0.97765722, 0.89149971, 0.82280948], + [ 0.97837585, 0.89731727, 0.83130786], + [ 0.97912374, 0.90313207, 0.83979337], + [ 0.979891 , 0.90894778, 0.84827858], + [ 0.98067764, 0.91476465, 0.85676611], + [ 0.98137749, 0.92061729, 0.86536915] +] -_mako_lut = [[ 0.04503935, 0.01482344, 0.02092227], - [ 0.04933018, 0.01709292, 0.02535719], - [ 0.05356262, 0.01950702, 0.03018802], - [ 0.05774337, 0.02205989, 0.03545515], - [ 0.06188095, 0.02474764, 0.04115287], - [ 0.06598247, 0.0275665 , 0.04691409], - [ 0.07005374, 0.03051278, 0.05264306], - [ 0.07409947, 0.03358324, 0.05834631], - [ 0.07812339, 0.03677446, 0.06403249], - [ 0.08212852, 0.0400833 , 0.06970862], - [ 0.08611731, 0.04339148, 0.07538208], - [ 0.09009161, 0.04664706, 0.08105568], - [ 0.09405308, 0.04985685, 0.08673591], - [ 0.09800301, 0.05302279, 0.09242646], - [ 0.10194255, 0.05614641, 0.09813162], - [ 0.10587261, 0.05922941, 0.103854 ], - [ 0.1097942 , 0.06227277, 0.10959847], - [ 0.11370826, 0.06527747, 0.11536893], - [ 0.11761516, 0.06824548, 0.12116393], - [ 0.12151575, 0.07117741, 0.12698763], - [ 0.12541095, 0.07407363, 0.1328442 ], - [ 0.12930083, 0.07693611, 0.13873064], - [ 0.13317849, 0.07976988, 0.14465095], - [ 0.13701138, 0.08259683, 0.15060265], - [ 0.14079223, 0.08542126, 0.15659379], - [ 0.14452486, 0.08824175, 0.16262484], - [ 0.14820351, 0.09106304, 0.16869476], - [ 0.15183185, 0.09388372, 0.17480366], - [ 0.15540398, 0.09670855, 0.18094993], - [ 0.15892417, 0.09953561, 0.18713384], - [ 0.16238588, 0.10236998, 0.19335329], - [ 0.16579435, 0.10520905, 0.19960847], - [ 0.16914226, 0.10805832, 0.20589698], - [ 0.17243586, 0.11091443, 0.21221911], - [ 0.17566717, 0.11378321, 0.21857219], - [ 0.17884322, 0.11666074, 0.2249565 ], - [ 0.18195582, 0.11955283, 0.23136943], - [ 0.18501213, 0.12245547, 0.23781116], - [ 0.18800459, 0.12537395, 0.24427914], - [ 0.19093944, 0.1283047 , 0.25077369], - [ 0.19381092, 0.13125179, 0.25729255], - [ 0.19662307, 0.13421303, 0.26383543], - [ 0.19937337, 0.13719028, 0.27040111], - [ 0.20206187, 0.14018372, 0.27698891], - [ 0.20469116, 0.14319196, 0.28359861], - [ 0.20725547, 0.14621882, 0.29022775], - [ 0.20976258, 0.14925954, 0.29687795], - [ 0.21220409, 0.15231929, 0.30354703], - [ 0.21458611, 0.15539445, 0.31023563], - [ 0.21690827, 0.15848519, 0.31694355], - [ 0.21916481, 0.16159489, 0.32366939], - [ 0.2213631 , 0.16471913, 0.33041431], - [ 0.22349947, 0.1678599 , 0.33717781], - [ 0.2255714 , 0.1710185 , 0.34395925], - [ 0.22758415, 0.17419169, 0.35075983], - [ 0.22953569, 0.17738041, 0.35757941], - [ 0.23142077, 0.18058733, 0.3644173 ], - [ 0.2332454 , 0.18380872, 0.37127514], - [ 0.2350092 , 0.18704459, 0.3781528 ], - [ 0.23670785, 0.190297 , 0.38504973], - [ 0.23834119, 0.19356547, 0.39196711], - [ 0.23991189, 0.19684817, 0.39890581], - [ 0.24141903, 0.20014508, 0.4058667 ], - [ 0.24286214, 0.20345642, 0.4128484 ], - [ 0.24423453, 0.20678459, 0.41985299], - [ 0.24554109, 0.21012669, 0.42688124], - [ 0.2467815 , 0.21348266, 0.43393244], - [ 0.24795393, 0.21685249, 0.4410088 ], - [ 0.24905614, 0.22023618, 0.448113 ], - [ 0.25007383, 0.22365053, 0.45519562], - [ 0.25098926, 0.22710664, 0.46223892], - [ 0.25179696, 0.23060342, 0.46925447], - [ 0.25249346, 0.23414353, 0.47623196], - [ 0.25307401, 0.23772973, 0.48316271], - [ 0.25353152, 0.24136961, 0.49001976], - [ 0.25386167, 0.24506548, 0.49679407], - [ 0.25406082, 0.2488164 , 0.50348932], - [ 0.25412435, 0.25262843, 0.51007843], - [ 0.25404842, 0.25650743, 0.51653282], - [ 0.25383134, 0.26044852, 0.52286845], - [ 0.2534705 , 0.26446165, 0.52903422], - [ 0.25296722, 0.2685428 , 0.53503572], - [ 0.2523226 , 0.27269346, 0.54085315], - [ 0.25153974, 0.27691629, 0.54645752], - [ 0.25062402, 0.28120467, 0.55185939], - [ 0.24958205, 0.28556371, 0.55701246], - [ 0.24842386, 0.28998148, 0.56194601], - [ 0.24715928, 0.29446327, 0.56660884], - [ 0.24580099, 0.29899398, 0.57104399], - [ 0.24436202, 0.30357852, 0.57519929], - [ 0.24285591, 0.30819938, 0.57913247], - [ 0.24129828, 0.31286235, 0.58278615], - [ 0.23970131, 0.3175495 , 0.5862272 ], - [ 0.23807973, 0.32226344, 0.58941872], - [ 0.23644557, 0.32699241, 0.59240198], - [ 0.2348113 , 0.33173196, 0.59518282], - [ 0.23318874, 0.33648036, 0.59775543], - [ 0.2315855 , 0.34122763, 0.60016456], - [ 0.23001121, 0.34597357, 0.60240251], - [ 0.2284748 , 0.35071512, 0.6044784 ], - [ 0.22698081, 0.35544612, 0.60642528], - [ 0.22553305, 0.36016515, 0.60825252], - [ 0.22413977, 0.36487341, 0.60994938], - [ 0.22280246, 0.36956728, 0.61154118], - [ 0.22152555, 0.37424409, 0.61304472], - [ 0.22030752, 0.37890437, 0.61446646], - [ 0.2191538 , 0.38354668, 0.61581561], - [ 0.21806257, 0.38817169, 0.61709794], - [ 0.21703799, 0.39277882, 0.61831922], - [ 0.21607792, 0.39736958, 0.61948028], - [ 0.21518463, 0.40194196, 0.62059763], - [ 0.21435467, 0.40649717, 0.62167507], - [ 0.21358663, 0.41103579, 0.62271724], - [ 0.21288172, 0.41555771, 0.62373011], - [ 0.21223835, 0.42006355, 0.62471794], - [ 0.21165312, 0.42455441, 0.62568371], - [ 0.21112526, 0.42903064, 0.6266318 ], - [ 0.21065161, 0.43349321, 0.62756504], - [ 0.21023306, 0.43794288, 0.62848279], - [ 0.20985996, 0.44238227, 0.62938329], - [ 0.20951045, 0.44680966, 0.63030696], - [ 0.20916709, 0.45122981, 0.63124483], - [ 0.20882976, 0.45564335, 0.63219599], - [ 0.20849798, 0.46005094, 0.63315928], - [ 0.20817199, 0.46445309, 0.63413391], - [ 0.20785149, 0.46885041, 0.63511876], - [ 0.20753716, 0.47324327, 0.63611321], - [ 0.20722876, 0.47763224, 0.63711608], - [ 0.20692679, 0.48201774, 0.63812656], - [ 0.20663156, 0.48640018, 0.63914367], - [ 0.20634336, 0.49078002, 0.64016638], - [ 0.20606303, 0.49515755, 0.6411939 ], - [ 0.20578999, 0.49953341, 0.64222457], - [ 0.20552612, 0.50390766, 0.64325811], - [ 0.20527189, 0.50828072, 0.64429331], - [ 0.20502868, 0.51265277, 0.64532947], - [ 0.20479718, 0.51702417, 0.64636539], - [ 0.20457804, 0.52139527, 0.64739979], - [ 0.20437304, 0.52576622, 0.64843198], - [ 0.20418396, 0.53013715, 0.64946117], - [ 0.20401238, 0.53450825, 0.65048638], - [ 0.20385896, 0.53887991, 0.65150606], - [ 0.20372653, 0.54325208, 0.65251978], - [ 0.20361709, 0.5476249 , 0.6535266 ], - [ 0.20353258, 0.55199854, 0.65452542], - [ 0.20347472, 0.55637318, 0.655515 ], - [ 0.20344718, 0.56074869, 0.65649508], - [ 0.20345161, 0.56512531, 0.65746419], - [ 0.20349089, 0.56950304, 0.65842151], - [ 0.20356842, 0.57388184, 0.65936642], - [ 0.20368663, 0.57826181, 0.66029768], - [ 0.20384884, 0.58264293, 0.6612145 ], - [ 0.20405904, 0.58702506, 0.66211645], - [ 0.20431921, 0.59140842, 0.66300179], - [ 0.20463464, 0.59579264, 0.66387079], - [ 0.20500731, 0.60017798, 0.66472159], - [ 0.20544449, 0.60456387, 0.66555409], - [ 0.20596097, 0.60894927, 0.66636568], - [ 0.20654832, 0.61333521, 0.66715744], - [ 0.20721003, 0.61772167, 0.66792838], - [ 0.20795035, 0.62210845, 0.66867802], - [ 0.20877302, 0.62649546, 0.66940555], - [ 0.20968223, 0.63088252, 0.6701105 ], - [ 0.21068163, 0.63526951, 0.67079211], - [ 0.21177544, 0.63965621, 0.67145005], - [ 0.21298582, 0.64404072, 0.67208182], - [ 0.21430361, 0.64842404, 0.67268861], - [ 0.21572716, 0.65280655, 0.67326978], - [ 0.21726052, 0.65718791, 0.6738255 ], - [ 0.21890636, 0.66156803, 0.67435491], - [ 0.220668 , 0.66594665, 0.67485792], - [ 0.22255447, 0.67032297, 0.67533374], - [ 0.22458372, 0.67469531, 0.67578061], - [ 0.22673713, 0.67906542, 0.67620044], - [ 0.22901625, 0.6834332 , 0.67659251], - [ 0.23142316, 0.68779836, 0.67695703], - [ 0.23395924, 0.69216072, 0.67729378], - [ 0.23663857, 0.69651881, 0.67760151], - [ 0.23946645, 0.70087194, 0.67788018], - [ 0.24242624, 0.70522162, 0.67813088], - [ 0.24549008, 0.70957083, 0.67835215], - [ 0.24863372, 0.71392166, 0.67854868], - [ 0.25187832, 0.71827158, 0.67872193], - [ 0.25524083, 0.72261873, 0.67887024], - [ 0.25870947, 0.72696469, 0.67898912], - [ 0.26229238, 0.73130855, 0.67907645], - [ 0.26604085, 0.73564353, 0.67914062], - [ 0.26993099, 0.73997282, 0.67917264], - [ 0.27397488, 0.74429484, 0.67917096], - [ 0.27822463, 0.74860229, 0.67914468], - [ 0.28264201, 0.75290034, 0.67907959], - [ 0.2873016 , 0.75717817, 0.67899164], - [ 0.29215894, 0.76144162, 0.67886578], - [ 0.29729823, 0.76567816, 0.67871894], - [ 0.30268199, 0.76989232, 0.67853896], - [ 0.30835665, 0.77407636, 0.67833512], - [ 0.31435139, 0.77822478, 0.67811118], - [ 0.3206671 , 0.78233575, 0.67786729], - [ 0.32733158, 0.78640315, 0.67761027], - [ 0.33437168, 0.79042043, 0.67734882], - [ 0.34182112, 0.79437948, 0.67709394], - [ 0.34968889, 0.79827511, 0.67685638], - [ 0.35799244, 0.80210037, 0.67664969], - [ 0.36675371, 0.80584651, 0.67649539], - [ 0.3759816 , 0.80950627, 0.67641393], - [ 0.38566792, 0.81307432, 0.67642947], - [ 0.39579804, 0.81654592, 0.67656899], - [ 0.40634556, 0.81991799, 0.67686215], - [ 0.41730243, 0.82318339, 0.67735255], - [ 0.4285828 , 0.82635051, 0.6780564 ], - [ 0.44012728, 0.82942353, 0.67900049], - [ 0.45189421, 0.83240398, 0.68021733], - [ 0.46378379, 0.83530763, 0.6817062 ], - [ 0.47573199, 0.83814472, 0.68347352], - [ 0.48769865, 0.84092197, 0.68552698], - [ 0.49962354, 0.84365379, 0.68783929], - [ 0.5114027 , 0.8463718 , 0.69029789], - [ 0.52301693, 0.84908401, 0.69288545], - [ 0.53447549, 0.85179048, 0.69561066], - [ 0.54578602, 0.8544913 , 0.69848331], - [ 0.55695565, 0.85718723, 0.70150427], - [ 0.56798832, 0.85987893, 0.70468261], - [ 0.57888639, 0.86256715, 0.70802931], - [ 0.5896541 , 0.8652532 , 0.71154204], - [ 0.60028928, 0.86793835, 0.71523675], - [ 0.61079441, 0.87062438, 0.71910895], - [ 0.62116633, 0.87331311, 0.72317003], - [ 0.63140509, 0.87600675, 0.72741689], - [ 0.64150735, 0.87870746, 0.73185717], - [ 0.65147219, 0.8814179 , 0.73648495], - [ 0.66129632, 0.8841403 , 0.74130658], - [ 0.67097934, 0.88687758, 0.74631123], - [ 0.68051833, 0.88963189, 0.75150483], - [ 0.68991419, 0.89240612, 0.75687187], - [ 0.69916533, 0.89520211, 0.76241714], - [ 0.70827373, 0.89802257, 0.76812286], - [ 0.71723995, 0.90086891, 0.77399039], - [ 0.72606665, 0.90374337, 0.7800041 ], - [ 0.73475675, 0.90664718, 0.78615802], - [ 0.74331358, 0.90958151, 0.79244474], - [ 0.75174143, 0.91254787, 0.79884925], - [ 0.76004473, 0.91554656, 0.80536823], - [ 0.76827704, 0.91856549, 0.81196513], - [ 0.77647029, 0.921603 , 0.81855729], - [ 0.78462009, 0.92466151, 0.82514119], - [ 0.79273542, 0.92773848, 0.83172131], - [ 0.8008109 , 0.93083672, 0.83829355], - [ 0.80885107, 0.93395528, 0.84485982], - [ 0.81685878, 0.9370938 , 0.85142101], - [ 0.82483206, 0.94025378, 0.8579751 ], - [ 0.83277661, 0.94343371, 0.86452477], - [ 0.84069127, 0.94663473, 0.87106853], - [ 0.84857662, 0.9498573 , 0.8776059 ], - [ 0.8564431 , 0.95309792, 0.88414253], - [ 0.86429066, 0.95635719, 0.89067759], - [ 0.87218969, 0.95960708, 0.89725384]] +_mako_lut = [ + [ 0.04503935, 0.01482344, 0.02092227], + [ 0.04933018, 0.01709292, 0.02535719], + [ 0.05356262, 0.01950702, 0.03018802], + [ 0.05774337, 0.02205989, 0.03545515], + [ 0.06188095, 0.02474764, 0.04115287], + [ 0.06598247, 0.0275665 , 0.04691409], + [ 0.07005374, 0.03051278, 0.05264306], + [ 0.07409947, 0.03358324, 0.05834631], + [ 0.07812339, 0.03677446, 0.06403249], + [ 0.08212852, 0.0400833 , 0.06970862], + [ 0.08611731, 0.04339148, 0.07538208], + [ 0.09009161, 0.04664706, 0.08105568], + [ 0.09405308, 0.04985685, 0.08673591], + [ 0.09800301, 0.05302279, 0.09242646], + [ 0.10194255, 0.05614641, 0.09813162], + [ 0.10587261, 0.05922941, 0.103854 ], + [ 0.1097942 , 0.06227277, 0.10959847], + [ 0.11370826, 0.06527747, 0.11536893], + [ 0.11761516, 0.06824548, 0.12116393], + [ 0.12151575, 0.07117741, 0.12698763], + [ 0.12541095, 0.07407363, 0.1328442 ], + [ 0.12930083, 0.07693611, 0.13873064], + [ 0.13317849, 0.07976988, 0.14465095], + [ 0.13701138, 0.08259683, 0.15060265], + [ 0.14079223, 0.08542126, 0.15659379], + [ 0.14452486, 0.08824175, 0.16262484], + [ 0.14820351, 0.09106304, 0.16869476], + [ 0.15183185, 0.09388372, 0.17480366], + [ 0.15540398, 0.09670855, 0.18094993], + [ 0.15892417, 0.09953561, 0.18713384], + [ 0.16238588, 0.10236998, 0.19335329], + [ 0.16579435, 0.10520905, 0.19960847], + [ 0.16914226, 0.10805832, 0.20589698], + [ 0.17243586, 0.11091443, 0.21221911], + [ 0.17566717, 0.11378321, 0.21857219], + [ 0.17884322, 0.11666074, 0.2249565 ], + [ 0.18195582, 0.11955283, 0.23136943], + [ 0.18501213, 0.12245547, 0.23781116], + [ 0.18800459, 0.12537395, 0.24427914], + [ 0.19093944, 0.1283047 , 0.25077369], + [ 0.19381092, 0.13125179, 0.25729255], + [ 0.19662307, 0.13421303, 0.26383543], + [ 0.19937337, 0.13719028, 0.27040111], + [ 0.20206187, 0.14018372, 0.27698891], + [ 0.20469116, 0.14319196, 0.28359861], + [ 0.20725547, 0.14621882, 0.29022775], + [ 0.20976258, 0.14925954, 0.29687795], + [ 0.21220409, 0.15231929, 0.30354703], + [ 0.21458611, 0.15539445, 0.31023563], + [ 0.21690827, 0.15848519, 0.31694355], + [ 0.21916481, 0.16159489, 0.32366939], + [ 0.2213631 , 0.16471913, 0.33041431], + [ 0.22349947, 0.1678599 , 0.33717781], + [ 0.2255714 , 0.1710185 , 0.34395925], + [ 0.22758415, 0.17419169, 0.35075983], + [ 0.22953569, 0.17738041, 0.35757941], + [ 0.23142077, 0.18058733, 0.3644173 ], + [ 0.2332454 , 0.18380872, 0.37127514], + [ 0.2350092 , 0.18704459, 0.3781528 ], + [ 0.23670785, 0.190297 , 0.38504973], + [ 0.23834119, 0.19356547, 0.39196711], + [ 0.23991189, 0.19684817, 0.39890581], + [ 0.24141903, 0.20014508, 0.4058667 ], + [ 0.24286214, 0.20345642, 0.4128484 ], + [ 0.24423453, 0.20678459, 0.41985299], + [ 0.24554109, 0.21012669, 0.42688124], + [ 0.2467815 , 0.21348266, 0.43393244], + [ 0.24795393, 0.21685249, 0.4410088 ], + [ 0.24905614, 0.22023618, 0.448113 ], + [ 0.25007383, 0.22365053, 0.45519562], + [ 0.25098926, 0.22710664, 0.46223892], + [ 0.25179696, 0.23060342, 0.46925447], + [ 0.25249346, 0.23414353, 0.47623196], + [ 0.25307401, 0.23772973, 0.48316271], + [ 0.25353152, 0.24136961, 0.49001976], + [ 0.25386167, 0.24506548, 0.49679407], + [ 0.25406082, 0.2488164 , 0.50348932], + [ 0.25412435, 0.25262843, 0.51007843], + [ 0.25404842, 0.25650743, 0.51653282], + [ 0.25383134, 0.26044852, 0.52286845], + [ 0.2534705 , 0.26446165, 0.52903422], + [ 0.25296722, 0.2685428 , 0.53503572], + [ 0.2523226 , 0.27269346, 0.54085315], + [ 0.25153974, 0.27691629, 0.54645752], + [ 0.25062402, 0.28120467, 0.55185939], + [ 0.24958205, 0.28556371, 0.55701246], + [ 0.24842386, 0.28998148, 0.56194601], + [ 0.24715928, 0.29446327, 0.56660884], + [ 0.24580099, 0.29899398, 0.57104399], + [ 0.24436202, 0.30357852, 0.57519929], + [ 0.24285591, 0.30819938, 0.57913247], + [ 0.24129828, 0.31286235, 0.58278615], + [ 0.23970131, 0.3175495 , 0.5862272 ], + [ 0.23807973, 0.32226344, 0.58941872], + [ 0.23644557, 0.32699241, 0.59240198], + [ 0.2348113 , 0.33173196, 0.59518282], + [ 0.23318874, 0.33648036, 0.59775543], + [ 0.2315855 , 0.34122763, 0.60016456], + [ 0.23001121, 0.34597357, 0.60240251], + [ 0.2284748 , 0.35071512, 0.6044784 ], + [ 0.22698081, 0.35544612, 0.60642528], + [ 0.22553305, 0.36016515, 0.60825252], + [ 0.22413977, 0.36487341, 0.60994938], + [ 0.22280246, 0.36956728, 0.61154118], + [ 0.22152555, 0.37424409, 0.61304472], + [ 0.22030752, 0.37890437, 0.61446646], + [ 0.2191538 , 0.38354668, 0.61581561], + [ 0.21806257, 0.38817169, 0.61709794], + [ 0.21703799, 0.39277882, 0.61831922], + [ 0.21607792, 0.39736958, 0.61948028], + [ 0.21518463, 0.40194196, 0.62059763], + [ 0.21435467, 0.40649717, 0.62167507], + [ 0.21358663, 0.41103579, 0.62271724], + [ 0.21288172, 0.41555771, 0.62373011], + [ 0.21223835, 0.42006355, 0.62471794], + [ 0.21165312, 0.42455441, 0.62568371], + [ 0.21112526, 0.42903064, 0.6266318 ], + [ 0.21065161, 0.43349321, 0.62756504], + [ 0.21023306, 0.43794288, 0.62848279], + [ 0.20985996, 0.44238227, 0.62938329], + [ 0.20951045, 0.44680966, 0.63030696], + [ 0.20916709, 0.45122981, 0.63124483], + [ 0.20882976, 0.45564335, 0.63219599], + [ 0.20849798, 0.46005094, 0.63315928], + [ 0.20817199, 0.46445309, 0.63413391], + [ 0.20785149, 0.46885041, 0.63511876], + [ 0.20753716, 0.47324327, 0.63611321], + [ 0.20722876, 0.47763224, 0.63711608], + [ 0.20692679, 0.48201774, 0.63812656], + [ 0.20663156, 0.48640018, 0.63914367], + [ 0.20634336, 0.49078002, 0.64016638], + [ 0.20606303, 0.49515755, 0.6411939 ], + [ 0.20578999, 0.49953341, 0.64222457], + [ 0.20552612, 0.50390766, 0.64325811], + [ 0.20527189, 0.50828072, 0.64429331], + [ 0.20502868, 0.51265277, 0.64532947], + [ 0.20479718, 0.51702417, 0.64636539], + [ 0.20457804, 0.52139527, 0.64739979], + [ 0.20437304, 0.52576622, 0.64843198], + [ 0.20418396, 0.53013715, 0.64946117], + [ 0.20401238, 0.53450825, 0.65048638], + [ 0.20385896, 0.53887991, 0.65150606], + [ 0.20372653, 0.54325208, 0.65251978], + [ 0.20361709, 0.5476249 , 0.6535266 ], + [ 0.20353258, 0.55199854, 0.65452542], + [ 0.20347472, 0.55637318, 0.655515 ], + [ 0.20344718, 0.56074869, 0.65649508], + [ 0.20345161, 0.56512531, 0.65746419], + [ 0.20349089, 0.56950304, 0.65842151], + [ 0.20356842, 0.57388184, 0.65936642], + [ 0.20368663, 0.57826181, 0.66029768], + [ 0.20384884, 0.58264293, 0.6612145 ], + [ 0.20405904, 0.58702506, 0.66211645], + [ 0.20431921, 0.59140842, 0.66300179], + [ 0.20463464, 0.59579264, 0.66387079], + [ 0.20500731, 0.60017798, 0.66472159], + [ 0.20544449, 0.60456387, 0.66555409], + [ 0.20596097, 0.60894927, 0.66636568], + [ 0.20654832, 0.61333521, 0.66715744], + [ 0.20721003, 0.61772167, 0.66792838], + [ 0.20795035, 0.62210845, 0.66867802], + [ 0.20877302, 0.62649546, 0.66940555], + [ 0.20968223, 0.63088252, 0.6701105 ], + [ 0.21068163, 0.63526951, 0.67079211], + [ 0.21177544, 0.63965621, 0.67145005], + [ 0.21298582, 0.64404072, 0.67208182], + [ 0.21430361, 0.64842404, 0.67268861], + [ 0.21572716, 0.65280655, 0.67326978], + [ 0.21726052, 0.65718791, 0.6738255 ], + [ 0.21890636, 0.66156803, 0.67435491], + [ 0.220668 , 0.66594665, 0.67485792], + [ 0.22255447, 0.67032297, 0.67533374], + [ 0.22458372, 0.67469531, 0.67578061], + [ 0.22673713, 0.67906542, 0.67620044], + [ 0.22901625, 0.6834332 , 0.67659251], + [ 0.23142316, 0.68779836, 0.67695703], + [ 0.23395924, 0.69216072, 0.67729378], + [ 0.23663857, 0.69651881, 0.67760151], + [ 0.23946645, 0.70087194, 0.67788018], + [ 0.24242624, 0.70522162, 0.67813088], + [ 0.24549008, 0.70957083, 0.67835215], + [ 0.24863372, 0.71392166, 0.67854868], + [ 0.25187832, 0.71827158, 0.67872193], + [ 0.25524083, 0.72261873, 0.67887024], + [ 0.25870947, 0.72696469, 0.67898912], + [ 0.26229238, 0.73130855, 0.67907645], + [ 0.26604085, 0.73564353, 0.67914062], + [ 0.26993099, 0.73997282, 0.67917264], + [ 0.27397488, 0.74429484, 0.67917096], + [ 0.27822463, 0.74860229, 0.67914468], + [ 0.28264201, 0.75290034, 0.67907959], + [ 0.2873016 , 0.75717817, 0.67899164], + [ 0.29215894, 0.76144162, 0.67886578], + [ 0.29729823, 0.76567816, 0.67871894], + [ 0.30268199, 0.76989232, 0.67853896], + [ 0.30835665, 0.77407636, 0.67833512], + [ 0.31435139, 0.77822478, 0.67811118], + [ 0.3206671 , 0.78233575, 0.67786729], + [ 0.32733158, 0.78640315, 0.67761027], + [ 0.33437168, 0.79042043, 0.67734882], + [ 0.34182112, 0.79437948, 0.67709394], + [ 0.34968889, 0.79827511, 0.67685638], + [ 0.35799244, 0.80210037, 0.67664969], + [ 0.36675371, 0.80584651, 0.67649539], + [ 0.3759816 , 0.80950627, 0.67641393], + [ 0.38566792, 0.81307432, 0.67642947], + [ 0.39579804, 0.81654592, 0.67656899], + [ 0.40634556, 0.81991799, 0.67686215], + [ 0.41730243, 0.82318339, 0.67735255], + [ 0.4285828 , 0.82635051, 0.6780564 ], + [ 0.44012728, 0.82942353, 0.67900049], + [ 0.45189421, 0.83240398, 0.68021733], + [ 0.46378379, 0.83530763, 0.6817062 ], + [ 0.47573199, 0.83814472, 0.68347352], + [ 0.48769865, 0.84092197, 0.68552698], + [ 0.49962354, 0.84365379, 0.68783929], + [ 0.5114027 , 0.8463718 , 0.69029789], + [ 0.52301693, 0.84908401, 0.69288545], + [ 0.53447549, 0.85179048, 0.69561066], + [ 0.54578602, 0.8544913 , 0.69848331], + [ 0.55695565, 0.85718723, 0.70150427], + [ 0.56798832, 0.85987893, 0.70468261], + [ 0.57888639, 0.86256715, 0.70802931], + [ 0.5896541 , 0.8652532 , 0.71154204], + [ 0.60028928, 0.86793835, 0.71523675], + [ 0.61079441, 0.87062438, 0.71910895], + [ 0.62116633, 0.87331311, 0.72317003], + [ 0.63140509, 0.87600675, 0.72741689], + [ 0.64150735, 0.87870746, 0.73185717], + [ 0.65147219, 0.8814179 , 0.73648495], + [ 0.66129632, 0.8841403 , 0.74130658], + [ 0.67097934, 0.88687758, 0.74631123], + [ 0.68051833, 0.88963189, 0.75150483], + [ 0.68991419, 0.89240612, 0.75687187], + [ 0.69916533, 0.89520211, 0.76241714], + [ 0.70827373, 0.89802257, 0.76812286], + [ 0.71723995, 0.90086891, 0.77399039], + [ 0.72606665, 0.90374337, 0.7800041 ], + [ 0.73475675, 0.90664718, 0.78615802], + [ 0.74331358, 0.90958151, 0.79244474], + [ 0.75174143, 0.91254787, 0.79884925], + [ 0.76004473, 0.91554656, 0.80536823], + [ 0.76827704, 0.91856549, 0.81196513], + [ 0.77647029, 0.921603 , 0.81855729], + [ 0.78462009, 0.92466151, 0.82514119], + [ 0.79273542, 0.92773848, 0.83172131], + [ 0.8008109 , 0.93083672, 0.83829355], + [ 0.80885107, 0.93395528, 0.84485982], + [ 0.81685878, 0.9370938 , 0.85142101], + [ 0.82483206, 0.94025378, 0.8579751 ], + [ 0.83277661, 0.94343371, 0.86452477], + [ 0.84069127, 0.94663473, 0.87106853], + [ 0.84857662, 0.9498573 , 0.8776059 ], + [ 0.8564431 , 0.95309792, 0.88414253], + [ 0.86429066, 0.95635719, 0.89067759], + [ 0.87218969, 0.95960708, 0.89725384] +] -_vlag_lut = [[ 0.13850039, 0.41331206, 0.74052025], - [ 0.15077609, 0.41762684, 0.73970427], - [ 0.16235219, 0.4219191 , 0.7389667 ], - [ 0.1733322 , 0.42619024, 0.73832537], - [ 0.18382538, 0.43044226, 0.73776764], - [ 0.19394034, 0.4346772 , 0.73725867], - [ 0.20367115, 0.43889576, 0.73685314], - [ 0.21313625, 0.44310003, 0.73648045], - [ 0.22231173, 0.44729079, 0.73619681], - [ 0.23125148, 0.45146945, 0.73597803], - [ 0.23998101, 0.45563715, 0.7358223 ], - [ 0.24853358, 0.45979489, 0.73571524], - [ 0.25691416, 0.4639437 , 0.73566943], - [ 0.26513894, 0.46808455, 0.73568319], - [ 0.27322194, 0.47221835, 0.73575497], - [ 0.28117543, 0.47634598, 0.73588332], - [ 0.28901021, 0.48046826, 0.73606686], - [ 0.2967358 , 0.48458597, 0.73630433], - [ 0.30436071, 0.48869986, 0.73659451], - [ 0.3118955 , 0.49281055, 0.73693255], - [ 0.31935389, 0.49691847, 0.73730851], - [ 0.32672701, 0.5010247 , 0.73774013], - [ 0.33402607, 0.50512971, 0.73821941], - [ 0.34125337, 0.50923419, 0.73874905], - [ 0.34840921, 0.51333892, 0.73933402], - [ 0.35551826, 0.51744353, 0.73994642], - [ 0.3625676 , 0.52154929, 0.74060763], - [ 0.36956356, 0.52565656, 0.74131327], - [ 0.37649902, 0.52976642, 0.74207698], - [ 0.38340273, 0.53387791, 0.74286286], - [ 0.39025859, 0.53799253, 0.7436962 ], - [ 0.39706821, 0.54211081, 0.744578 ], - [ 0.40384046, 0.54623277, 0.74549872], - [ 0.41058241, 0.55035849, 0.74645094], - [ 0.41728385, 0.55448919, 0.74745174], - [ 0.42395178, 0.55862494, 0.74849357], - [ 0.4305964 , 0.56276546, 0.74956387], - [ 0.4372044 , 0.56691228, 0.75068412], - [ 0.4437909 , 0.57106468, 0.75183427], - [ 0.45035117, 0.5752235 , 0.75302312], - [ 0.45687824, 0.57938983, 0.75426297], - [ 0.46339713, 0.58356191, 0.75551816], - [ 0.46988778, 0.58774195, 0.75682037], - [ 0.47635605, 0.59192986, 0.75816245], - [ 0.48281101, 0.5961252 , 0.75953212], - [ 0.4892374 , 0.60032986, 0.76095418], - [ 0.49566225, 0.60454154, 0.76238852], - [ 0.50206137, 0.60876307, 0.76387371], - [ 0.50845128, 0.61299312, 0.76538551], - [ 0.5148258 , 0.61723272, 0.76693475], - [ 0.52118385, 0.62148236, 0.76852436], - [ 0.52753571, 0.62574126, 0.77013939], - [ 0.53386831, 0.63001125, 0.77180152], - [ 0.54020159, 0.63429038, 0.7734803 ], - [ 0.54651272, 0.63858165, 0.77521306], - [ 0.55282975, 0.64288207, 0.77695608], - [ 0.55912585, 0.64719519, 0.77875327], - [ 0.56542599, 0.65151828, 0.78056551], - [ 0.57170924, 0.65585426, 0.78242747], - [ 0.57799572, 0.6602009 , 0.78430751], - [ 0.58426817, 0.66456073, 0.78623458], - [ 0.590544 , 0.66893178, 0.78818117], - [ 0.59680758, 0.67331643, 0.79017369], - [ 0.60307553, 0.67771273, 0.79218572], - [ 0.60934065, 0.68212194, 0.79422987], - [ 0.61559495, 0.68654548, 0.7963202 ], - [ 0.62185554, 0.69098125, 0.79842918], - [ 0.62810662, 0.69543176, 0.80058381], - [ 0.63436425, 0.69989499, 0.80275812], - [ 0.64061445, 0.70437326, 0.80497621], - [ 0.6468706 , 0.70886488, 0.80721641], - [ 0.65312213, 0.7133717 , 0.80949719], - [ 0.65937818, 0.71789261, 0.81180392], - [ 0.66563334, 0.72242871, 0.81414642], - [ 0.67189155, 0.72697967, 0.81651872], - [ 0.67815314, 0.73154569, 0.81892097], - [ 0.68441395, 0.73612771, 0.82136094], - [ 0.69068321, 0.74072452, 0.82382353], - [ 0.69694776, 0.7453385 , 0.82633199], - [ 0.70322431, 0.74996721, 0.8288583 ], - [ 0.70949595, 0.75461368, 0.83143221], - [ 0.7157774 , 0.75927574, 0.83402904], - [ 0.72206299, 0.76395461, 0.83665922], - [ 0.72835227, 0.76865061, 0.8393242 ], - [ 0.73465238, 0.7733628 , 0.84201224], - [ 0.74094862, 0.77809393, 0.84474951], - [ 0.74725683, 0.78284158, 0.84750915], - [ 0.75357103, 0.78760701, 0.85030217], - [ 0.75988961, 0.79239077, 0.85313207], - [ 0.76621987, 0.79719185, 0.85598668], - [ 0.77255045, 0.8020125 , 0.85888658], - [ 0.77889241, 0.80685102, 0.86181298], - [ 0.78524572, 0.81170768, 0.86476656], - [ 0.79159841, 0.81658489, 0.86776906], - [ 0.79796459, 0.82148036, 0.8707962 ], - [ 0.80434168, 0.82639479, 0.87385315], - [ 0.8107221 , 0.83132983, 0.87695392], - [ 0.81711301, 0.8362844 , 0.88008641], - [ 0.82351479, 0.84125863, 0.88325045], - [ 0.82992772, 0.84625263, 0.88644594], - [ 0.83634359, 0.85126806, 0.8896878 ], - [ 0.84277295, 0.85630293, 0.89295721], - [ 0.84921192, 0.86135782, 0.89626076], - [ 0.85566206, 0.866432 , 0.89959467], - [ 0.86211514, 0.87152627, 0.90297183], - [ 0.86857483, 0.87663856, 0.90638248], - [ 0.87504231, 0.88176648, 0.90981938], - [ 0.88151194, 0.88690782, 0.91328493], - [ 0.88797938, 0.89205857, 0.91677544], - [ 0.89443865, 0.89721298, 0.9202854 ], - [ 0.90088204, 0.90236294, 0.92380601], - [ 0.90729768, 0.90749778, 0.92732797], - [ 0.91367037, 0.91260329, 0.93083814], - [ 0.91998105, 0.91766106, 0.93431861], - [ 0.92620596, 0.92264789, 0.93774647], - [ 0.93231683, 0.9275351 , 0.94109192], - [ 0.93827772, 0.9322888 , 0.94432312], - [ 0.94404755, 0.93686925, 0.94740137], - [ 0.94958284, 0.94123072, 0.95027696], - [ 0.95482682, 0.9453245 , 0.95291103], - [ 0.9597248 , 0.94909728, 0.95525103], - [ 0.96422552, 0.95249273, 0.95723271], - [ 0.96826161, 0.95545812, 0.95882188], - [ 0.97178458, 0.95793984, 0.95995705], - [ 0.97474105, 0.95989142, 0.96059997], - [ 0.97708604, 0.96127366, 0.96071853], - [ 0.97877855, 0.96205832, 0.96030095], - [ 0.97978484, 0.96222949, 0.95935496], - [ 0.9805997 , 0.96155216, 0.95813083], - [ 0.98152619, 0.95993719, 0.95639322], - [ 0.9819726 , 0.95766608, 0.95399269], - [ 0.98191855, 0.9547873 , 0.95098107], - [ 0.98138514, 0.95134771, 0.94740644], - [ 0.98040845, 0.94739906, 0.94332125], - [ 0.97902107, 0.94300131, 0.93878672], - [ 0.97729348, 0.93820409, 0.93385135], - [ 0.9752533 , 0.933073 , 0.92858252], - [ 0.97297834, 0.92765261, 0.92302309], - [ 0.97049104, 0.92200317, 0.91723505], - [ 0.96784372, 0.91616744, 0.91126063], - [ 0.96507281, 0.91018664, 0.90514124], - [ 0.96222034, 0.90409203, 0.89890756], - [ 0.9593079 , 0.89791478, 0.89259122], - [ 0.95635626, 0.89167908, 0.88621654], - [ 0.95338303, 0.88540373, 0.87980238], - [ 0.95040174, 0.87910333, 0.87336339], - [ 0.94742246, 0.87278899, 0.86691076], - [ 0.94445249, 0.86646893, 0.86045277], - [ 0.94150476, 0.86014606, 0.85399191], - [ 0.93857394, 0.85382798, 0.84753642], - [ 0.93566206, 0.84751766, 0.84108935], - [ 0.93277194, 0.8412164 , 0.83465197], - [ 0.92990106, 0.83492672, 0.82822708], - [ 0.92704736, 0.82865028, 0.82181656], - [ 0.92422703, 0.82238092, 0.81541333], - [ 0.92142581, 0.81612448, 0.80902415], - [ 0.91864501, 0.80988032, 0.80264838], - [ 0.91587578, 0.80365187, 0.79629001], - [ 0.9131367 , 0.79743115, 0.78994 ], - [ 0.91041602, 0.79122265, 0.78360361], - [ 0.90771071, 0.78502727, 0.77728196], - [ 0.90501581, 0.77884674, 0.7709771 ], - [ 0.90235365, 0.77267117, 0.76467793], - [ 0.8997019 , 0.76650962, 0.75839484], - [ 0.89705346, 0.76036481, 0.752131 ], - [ 0.89444021, 0.75422253, 0.74587047], - [ 0.89183355, 0.74809474, 0.73962689], - [ 0.88923216, 0.74198168, 0.73340061], - [ 0.88665892, 0.73587283, 0.72717995], - [ 0.88408839, 0.72977904, 0.72097718], - [ 0.88153537, 0.72369332, 0.71478461], - [ 0.87899389, 0.7176179 , 0.70860487], - [ 0.87645157, 0.71155805, 0.7024439 ], - [ 0.8739399 , 0.70549893, 0.6962854 ], - [ 0.87142626, 0.6994551 , 0.69014561], - [ 0.8689268 , 0.69341868, 0.68401597], - [ 0.86643562, 0.687392 , 0.67789917], - [ 0.86394434, 0.68137863, 0.67179927], - [ 0.86147586, 0.67536728, 0.665704 ], - [ 0.85899928, 0.66937226, 0.6596292 ], - [ 0.85654668, 0.66337773, 0.6535577 ], - [ 0.85408818, 0.65739772, 0.64750494], - [ 0.85164413, 0.65142189, 0.64145983], - [ 0.84920091, 0.6454565 , 0.63542932], - [ 0.84676427, 0.63949827, 0.62941 ], - [ 0.84433231, 0.63354773, 0.62340261], - [ 0.84190106, 0.62760645, 0.61740899], - [ 0.83947935, 0.62166951, 0.61142404], - [ 0.8370538 , 0.61574332, 0.60545478], - [ 0.83463975, 0.60981951, 0.59949247], - [ 0.83221877, 0.60390724, 0.593547 ], - [ 0.82980985, 0.59799607, 0.58760751], - [ 0.82740268, 0.59209095, 0.58167944], - [ 0.82498638, 0.5861973 , 0.57576866], - [ 0.82258181, 0.5803034 , 0.56986307], - [ 0.82016611, 0.57442123, 0.56397539], - [ 0.81776305, 0.56853725, 0.55809173], - [ 0.81534551, 0.56266602, 0.55222741], - [ 0.81294293, 0.55679056, 0.5463651 ], - [ 0.81052113, 0.55092973, 0.54052443], - [ 0.80811509, 0.54506305, 0.53468464], - [ 0.80568952, 0.53921036, 0.52886622], - [ 0.80327506, 0.53335335, 0.52305077], - [ 0.80084727, 0.52750583, 0.51725256], - [ 0.79842217, 0.5216578 , 0.51146173], - [ 0.79599382, 0.51581223, 0.50568155], - [ 0.79355781, 0.50997127, 0.49991444], - [ 0.79112596, 0.50412707, 0.49415289], - [ 0.78867442, 0.49829386, 0.48841129], - [ 0.7862306 , 0.49245398, 0.48267247], - [ 0.7837687 , 0.48662309, 0.47695216], - [ 0.78130809, 0.4807883 , 0.47123805], - [ 0.77884467, 0.47495151, 0.46553236], - [ 0.77636283, 0.46912235, 0.45984473], - [ 0.77388383, 0.46328617, 0.45416141], - [ 0.77138912, 0.45745466, 0.44849398], - [ 0.76888874, 0.45162042, 0.44283573], - [ 0.76638802, 0.44577901, 0.43718292], - [ 0.76386116, 0.43994762, 0.43155211], - [ 0.76133542, 0.43410655, 0.42592523], - [ 0.75880631, 0.42825801, 0.42030488], - [ 0.75624913, 0.42241905, 0.41470727], - [ 0.7536919 , 0.41656866, 0.40911347], - [ 0.75112748, 0.41071104, 0.40352792], - [ 0.74854331, 0.40485474, 0.3979589 ], - [ 0.74594723, 0.39899309, 0.39240088], - [ 0.74334332, 0.39312199, 0.38685075], - [ 0.74073277, 0.38723941, 0.3813074 ], - [ 0.73809409, 0.38136133, 0.37578553], - [ 0.73544692, 0.37547129, 0.37027123], - [ 0.73278943, 0.36956954, 0.36476549], - [ 0.73011829, 0.36365761, 0.35927038], - [ 0.72743485, 0.35773314, 0.35378465], - [ 0.72472722, 0.35180504, 0.34831662], - [ 0.72200473, 0.34586421, 0.34285937], - [ 0.71927052, 0.33990649, 0.33741033], - [ 0.71652049, 0.33393396, 0.33197219], - [ 0.71375362, 0.32794602, 0.32654545], - [ 0.71096951, 0.32194148, 0.32113016], - [ 0.70816772, 0.31591904, 0.31572637], - [ 0.70534784, 0.30987734, 0.31033414], - [ 0.70250944, 0.30381489, 0.30495353], - [ 0.69965211, 0.2977301 , 0.2995846 ], - [ 0.6967754 , 0.29162126, 0.29422741], - [ 0.69388446, 0.28548074, 0.28887769], - [ 0.69097561, 0.2793096 , 0.28353795], - [ 0.68803513, 0.27311993, 0.27821876], - [ 0.6850794 , 0.26689144, 0.27290694], - [ 0.682108 , 0.26062114, 0.26760246], - [ 0.67911013, 0.2543177 , 0.26231367], - [ 0.67609393, 0.24796818, 0.25703372], - [ 0.67305921, 0.24156846, 0.25176238], - [ 0.67000176, 0.23511902, 0.24650278], - [ 0.66693423, 0.22859879, 0.24124404], - [ 0.6638441 , 0.22201742, 0.2359961 ], - [ 0.66080672, 0.21526712, 0.23069468]] +_vlag_lut = [ + [ 0.13850039, 0.41331206, 0.74052025], + [ 0.15077609, 0.41762684, 0.73970427], + [ 0.16235219, 0.4219191 , 0.7389667 ], + [ 0.1733322 , 0.42619024, 0.73832537], + [ 0.18382538, 0.43044226, 0.73776764], + [ 0.19394034, 0.4346772 , 0.73725867], + [ 0.20367115, 0.43889576, 0.73685314], + [ 0.21313625, 0.44310003, 0.73648045], + [ 0.22231173, 0.44729079, 0.73619681], + [ 0.23125148, 0.45146945, 0.73597803], + [ 0.23998101, 0.45563715, 0.7358223 ], + [ 0.24853358, 0.45979489, 0.73571524], + [ 0.25691416, 0.4639437 , 0.73566943], + [ 0.26513894, 0.46808455, 0.73568319], + [ 0.27322194, 0.47221835, 0.73575497], + [ 0.28117543, 0.47634598, 0.73588332], + [ 0.28901021, 0.48046826, 0.73606686], + [ 0.2967358 , 0.48458597, 0.73630433], + [ 0.30436071, 0.48869986, 0.73659451], + [ 0.3118955 , 0.49281055, 0.73693255], + [ 0.31935389, 0.49691847, 0.73730851], + [ 0.32672701, 0.5010247 , 0.73774013], + [ 0.33402607, 0.50512971, 0.73821941], + [ 0.34125337, 0.50923419, 0.73874905], + [ 0.34840921, 0.51333892, 0.73933402], + [ 0.35551826, 0.51744353, 0.73994642], + [ 0.3625676 , 0.52154929, 0.74060763], + [ 0.36956356, 0.52565656, 0.74131327], + [ 0.37649902, 0.52976642, 0.74207698], + [ 0.38340273, 0.53387791, 0.74286286], + [ 0.39025859, 0.53799253, 0.7436962 ], + [ 0.39706821, 0.54211081, 0.744578 ], + [ 0.40384046, 0.54623277, 0.74549872], + [ 0.41058241, 0.55035849, 0.74645094], + [ 0.41728385, 0.55448919, 0.74745174], + [ 0.42395178, 0.55862494, 0.74849357], + [ 0.4305964 , 0.56276546, 0.74956387], + [ 0.4372044 , 0.56691228, 0.75068412], + [ 0.4437909 , 0.57106468, 0.75183427], + [ 0.45035117, 0.5752235 , 0.75302312], + [ 0.45687824, 0.57938983, 0.75426297], + [ 0.46339713, 0.58356191, 0.75551816], + [ 0.46988778, 0.58774195, 0.75682037], + [ 0.47635605, 0.59192986, 0.75816245], + [ 0.48281101, 0.5961252 , 0.75953212], + [ 0.4892374 , 0.60032986, 0.76095418], + [ 0.49566225, 0.60454154, 0.76238852], + [ 0.50206137, 0.60876307, 0.76387371], + [ 0.50845128, 0.61299312, 0.76538551], + [ 0.5148258 , 0.61723272, 0.76693475], + [ 0.52118385, 0.62148236, 0.76852436], + [ 0.52753571, 0.62574126, 0.77013939], + [ 0.53386831, 0.63001125, 0.77180152], + [ 0.54020159, 0.63429038, 0.7734803 ], + [ 0.54651272, 0.63858165, 0.77521306], + [ 0.55282975, 0.64288207, 0.77695608], + [ 0.55912585, 0.64719519, 0.77875327], + [ 0.56542599, 0.65151828, 0.78056551], + [ 0.57170924, 0.65585426, 0.78242747], + [ 0.57799572, 0.6602009 , 0.78430751], + [ 0.58426817, 0.66456073, 0.78623458], + [ 0.590544 , 0.66893178, 0.78818117], + [ 0.59680758, 0.67331643, 0.79017369], + [ 0.60307553, 0.67771273, 0.79218572], + [ 0.60934065, 0.68212194, 0.79422987], + [ 0.61559495, 0.68654548, 0.7963202 ], + [ 0.62185554, 0.69098125, 0.79842918], + [ 0.62810662, 0.69543176, 0.80058381], + [ 0.63436425, 0.69989499, 0.80275812], + [ 0.64061445, 0.70437326, 0.80497621], + [ 0.6468706 , 0.70886488, 0.80721641], + [ 0.65312213, 0.7133717 , 0.80949719], + [ 0.65937818, 0.71789261, 0.81180392], + [ 0.66563334, 0.72242871, 0.81414642], + [ 0.67189155, 0.72697967, 0.81651872], + [ 0.67815314, 0.73154569, 0.81892097], + [ 0.68441395, 0.73612771, 0.82136094], + [ 0.69068321, 0.74072452, 0.82382353], + [ 0.69694776, 0.7453385 , 0.82633199], + [ 0.70322431, 0.74996721, 0.8288583 ], + [ 0.70949595, 0.75461368, 0.83143221], + [ 0.7157774 , 0.75927574, 0.83402904], + [ 0.72206299, 0.76395461, 0.83665922], + [ 0.72835227, 0.76865061, 0.8393242 ], + [ 0.73465238, 0.7733628 , 0.84201224], + [ 0.74094862, 0.77809393, 0.84474951], + [ 0.74725683, 0.78284158, 0.84750915], + [ 0.75357103, 0.78760701, 0.85030217], + [ 0.75988961, 0.79239077, 0.85313207], + [ 0.76621987, 0.79719185, 0.85598668], + [ 0.77255045, 0.8020125 , 0.85888658], + [ 0.77889241, 0.80685102, 0.86181298], + [ 0.78524572, 0.81170768, 0.86476656], + [ 0.79159841, 0.81658489, 0.86776906], + [ 0.79796459, 0.82148036, 0.8707962 ], + [ 0.80434168, 0.82639479, 0.87385315], + [ 0.8107221 , 0.83132983, 0.87695392], + [ 0.81711301, 0.8362844 , 0.88008641], + [ 0.82351479, 0.84125863, 0.88325045], + [ 0.82992772, 0.84625263, 0.88644594], + [ 0.83634359, 0.85126806, 0.8896878 ], + [ 0.84277295, 0.85630293, 0.89295721], + [ 0.84921192, 0.86135782, 0.89626076], + [ 0.85566206, 0.866432 , 0.89959467], + [ 0.86211514, 0.87152627, 0.90297183], + [ 0.86857483, 0.87663856, 0.90638248], + [ 0.87504231, 0.88176648, 0.90981938], + [ 0.88151194, 0.88690782, 0.91328493], + [ 0.88797938, 0.89205857, 0.91677544], + [ 0.89443865, 0.89721298, 0.9202854 ], + [ 0.90088204, 0.90236294, 0.92380601], + [ 0.90729768, 0.90749778, 0.92732797], + [ 0.91367037, 0.91260329, 0.93083814], + [ 0.91998105, 0.91766106, 0.93431861], + [ 0.92620596, 0.92264789, 0.93774647], + [ 0.93231683, 0.9275351 , 0.94109192], + [ 0.93827772, 0.9322888 , 0.94432312], + [ 0.94404755, 0.93686925, 0.94740137], + [ 0.94958284, 0.94123072, 0.95027696], + [ 0.95482682, 0.9453245 , 0.95291103], + [ 0.9597248 , 0.94909728, 0.95525103], + [ 0.96422552, 0.95249273, 0.95723271], + [ 0.96826161, 0.95545812, 0.95882188], + [ 0.97178458, 0.95793984, 0.95995705], + [ 0.97474105, 0.95989142, 0.96059997], + [ 0.97708604, 0.96127366, 0.96071853], + [ 0.97877855, 0.96205832, 0.96030095], + [ 0.97978484, 0.96222949, 0.95935496], + [ 0.9805997 , 0.96155216, 0.95813083], + [ 0.98152619, 0.95993719, 0.95639322], + [ 0.9819726 , 0.95766608, 0.95399269], + [ 0.98191855, 0.9547873 , 0.95098107], + [ 0.98138514, 0.95134771, 0.94740644], + [ 0.98040845, 0.94739906, 0.94332125], + [ 0.97902107, 0.94300131, 0.93878672], + [ 0.97729348, 0.93820409, 0.93385135], + [ 0.9752533 , 0.933073 , 0.92858252], + [ 0.97297834, 0.92765261, 0.92302309], + [ 0.97049104, 0.92200317, 0.91723505], + [ 0.96784372, 0.91616744, 0.91126063], + [ 0.96507281, 0.91018664, 0.90514124], + [ 0.96222034, 0.90409203, 0.89890756], + [ 0.9593079 , 0.89791478, 0.89259122], + [ 0.95635626, 0.89167908, 0.88621654], + [ 0.95338303, 0.88540373, 0.87980238], + [ 0.95040174, 0.87910333, 0.87336339], + [ 0.94742246, 0.87278899, 0.86691076], + [ 0.94445249, 0.86646893, 0.86045277], + [ 0.94150476, 0.86014606, 0.85399191], + [ 0.93857394, 0.85382798, 0.84753642], + [ 0.93566206, 0.84751766, 0.84108935], + [ 0.93277194, 0.8412164 , 0.83465197], + [ 0.92990106, 0.83492672, 0.82822708], + [ 0.92704736, 0.82865028, 0.82181656], + [ 0.92422703, 0.82238092, 0.81541333], + [ 0.92142581, 0.81612448, 0.80902415], + [ 0.91864501, 0.80988032, 0.80264838], + [ 0.91587578, 0.80365187, 0.79629001], + [ 0.9131367 , 0.79743115, 0.78994 ], + [ 0.91041602, 0.79122265, 0.78360361], + [ 0.90771071, 0.78502727, 0.77728196], + [ 0.90501581, 0.77884674, 0.7709771 ], + [ 0.90235365, 0.77267117, 0.76467793], + [ 0.8997019 , 0.76650962, 0.75839484], + [ 0.89705346, 0.76036481, 0.752131 ], + [ 0.89444021, 0.75422253, 0.74587047], + [ 0.89183355, 0.74809474, 0.73962689], + [ 0.88923216, 0.74198168, 0.73340061], + [ 0.88665892, 0.73587283, 0.72717995], + [ 0.88408839, 0.72977904, 0.72097718], + [ 0.88153537, 0.72369332, 0.71478461], + [ 0.87899389, 0.7176179 , 0.70860487], + [ 0.87645157, 0.71155805, 0.7024439 ], + [ 0.8739399 , 0.70549893, 0.6962854 ], + [ 0.87142626, 0.6994551 , 0.69014561], + [ 0.8689268 , 0.69341868, 0.68401597], + [ 0.86643562, 0.687392 , 0.67789917], + [ 0.86394434, 0.68137863, 0.67179927], + [ 0.86147586, 0.67536728, 0.665704 ], + [ 0.85899928, 0.66937226, 0.6596292 ], + [ 0.85654668, 0.66337773, 0.6535577 ], + [ 0.85408818, 0.65739772, 0.64750494], + [ 0.85164413, 0.65142189, 0.64145983], + [ 0.84920091, 0.6454565 , 0.63542932], + [ 0.84676427, 0.63949827, 0.62941 ], + [ 0.84433231, 0.63354773, 0.62340261], + [ 0.84190106, 0.62760645, 0.61740899], + [ 0.83947935, 0.62166951, 0.61142404], + [ 0.8370538 , 0.61574332, 0.60545478], + [ 0.83463975, 0.60981951, 0.59949247], + [ 0.83221877, 0.60390724, 0.593547 ], + [ 0.82980985, 0.59799607, 0.58760751], + [ 0.82740268, 0.59209095, 0.58167944], + [ 0.82498638, 0.5861973 , 0.57576866], + [ 0.82258181, 0.5803034 , 0.56986307], + [ 0.82016611, 0.57442123, 0.56397539], + [ 0.81776305, 0.56853725, 0.55809173], + [ 0.81534551, 0.56266602, 0.55222741], + [ 0.81294293, 0.55679056, 0.5463651 ], + [ 0.81052113, 0.55092973, 0.54052443], + [ 0.80811509, 0.54506305, 0.53468464], + [ 0.80568952, 0.53921036, 0.52886622], + [ 0.80327506, 0.53335335, 0.52305077], + [ 0.80084727, 0.52750583, 0.51725256], + [ 0.79842217, 0.5216578 , 0.51146173], + [ 0.79599382, 0.51581223, 0.50568155], + [ 0.79355781, 0.50997127, 0.49991444], + [ 0.79112596, 0.50412707, 0.49415289], + [ 0.78867442, 0.49829386, 0.48841129], + [ 0.7862306 , 0.49245398, 0.48267247], + [ 0.7837687 , 0.48662309, 0.47695216], + [ 0.78130809, 0.4807883 , 0.47123805], + [ 0.77884467, 0.47495151, 0.46553236], + [ 0.77636283, 0.46912235, 0.45984473], + [ 0.77388383, 0.46328617, 0.45416141], + [ 0.77138912, 0.45745466, 0.44849398], + [ 0.76888874, 0.45162042, 0.44283573], + [ 0.76638802, 0.44577901, 0.43718292], + [ 0.76386116, 0.43994762, 0.43155211], + [ 0.76133542, 0.43410655, 0.42592523], + [ 0.75880631, 0.42825801, 0.42030488], + [ 0.75624913, 0.42241905, 0.41470727], + [ 0.7536919 , 0.41656866, 0.40911347], + [ 0.75112748, 0.41071104, 0.40352792], + [ 0.74854331, 0.40485474, 0.3979589 ], + [ 0.74594723, 0.39899309, 0.39240088], + [ 0.74334332, 0.39312199, 0.38685075], + [ 0.74073277, 0.38723941, 0.3813074 ], + [ 0.73809409, 0.38136133, 0.37578553], + [ 0.73544692, 0.37547129, 0.37027123], + [ 0.73278943, 0.36956954, 0.36476549], + [ 0.73011829, 0.36365761, 0.35927038], + [ 0.72743485, 0.35773314, 0.35378465], + [ 0.72472722, 0.35180504, 0.34831662], + [ 0.72200473, 0.34586421, 0.34285937], + [ 0.71927052, 0.33990649, 0.33741033], + [ 0.71652049, 0.33393396, 0.33197219], + [ 0.71375362, 0.32794602, 0.32654545], + [ 0.71096951, 0.32194148, 0.32113016], + [ 0.70816772, 0.31591904, 0.31572637], + [ 0.70534784, 0.30987734, 0.31033414], + [ 0.70250944, 0.30381489, 0.30495353], + [ 0.69965211, 0.2977301 , 0.2995846 ], + [ 0.6967754 , 0.29162126, 0.29422741], + [ 0.69388446, 0.28548074, 0.28887769], + [ 0.69097561, 0.2793096 , 0.28353795], + [ 0.68803513, 0.27311993, 0.27821876], + [ 0.6850794 , 0.26689144, 0.27290694], + [ 0.682108 , 0.26062114, 0.26760246], + [ 0.67911013, 0.2543177 , 0.26231367], + [ 0.67609393, 0.24796818, 0.25703372], + [ 0.67305921, 0.24156846, 0.25176238], + [ 0.67000176, 0.23511902, 0.24650278], + [ 0.66693423, 0.22859879, 0.24124404], + [ 0.6638441 , 0.22201742, 0.2359961 ], + [ 0.66080672, 0.21526712, 0.23069468] +] -_icefire_lut = [[ 0.73936227, 0.90443867, 0.85757238], - [ 0.72888063, 0.89639109, 0.85488394], - [ 0.71834255, 0.88842162, 0.8521605 ], - [ 0.70773866, 0.88052939, 0.849422 ], - [ 0.69706215, 0.87271313, 0.84668315], - [ 0.68629021, 0.86497329, 0.84398721], - [ 0.67543654, 0.85730617, 0.84130969], - [ 0.66448539, 0.84971123, 0.83868005], - [ 0.65342679, 0.84218728, 0.83611512], - [ 0.64231804, 0.83471867, 0.83358584], - [ 0.63117745, 0.827294 , 0.83113431], - [ 0.62000484, 0.81991069, 0.82876741], - [ 0.60879435, 0.81256797, 0.82648905], - [ 0.59754118, 0.80526458, 0.82430414], - [ 0.58624247, 0.79799884, 0.82221573], - [ 0.57489525, 0.7907688 , 0.82022901], - [ 0.56349779, 0.78357215, 0.81834861], - [ 0.55204294, 0.77640827, 0.81657563], - [ 0.54052516, 0.76927562, 0.81491462], - [ 0.52894085, 0.76217215, 0.81336913], - [ 0.51728854, 0.75509528, 0.81194156], - [ 0.50555676, 0.74804469, 0.81063503], - [ 0.49373871, 0.7410187 , 0.80945242], - [ 0.48183174, 0.73401449, 0.80839675], - [ 0.46982587, 0.72703075, 0.80747097], - [ 0.45770893, 0.72006648, 0.80667756], - [ 0.44547249, 0.71311941, 0.80601991], - [ 0.43318643, 0.70617126, 0.80549278], - [ 0.42110294, 0.69916972, 0.80506683], - [ 0.40925101, 0.69211059, 0.80473246], - [ 0.3976693 , 0.68498786, 0.80448272], - [ 0.38632002, 0.67781125, 0.80431024], - [ 0.37523981, 0.67057537, 0.80420832], - [ 0.36442578, 0.66328229, 0.80417474], - [ 0.35385939, 0.65593699, 0.80420591], - [ 0.34358916, 0.64853177, 0.8043 ], - [ 0.33355526, 0.64107876, 0.80445484], - [ 0.32383062, 0.63356578, 0.80467091], - [ 0.31434372, 0.62600624, 0.8049475 ], - [ 0.30516161, 0.618389 , 0.80528692], - [ 0.29623491, 0.61072284, 0.80569021], - [ 0.28759072, 0.60300319, 0.80616055], - [ 0.27923924, 0.59522877, 0.80669803], - [ 0.27114651, 0.5874047 , 0.80730545], - [ 0.26337153, 0.57952055, 0.80799113], - [ 0.25588696, 0.57157984, 0.80875922], - [ 0.248686 , 0.56358255, 0.80961366], - [ 0.24180668, 0.55552289, 0.81055123], - [ 0.23526251, 0.54739477, 0.8115939 ], - [ 0.22921445, 0.53918506, 0.81267292], - [ 0.22397687, 0.53086094, 0.8137141 ], - [ 0.21977058, 0.52241482, 0.81457651], - [ 0.21658989, 0.51384321, 0.81528511], - [ 0.21452772, 0.50514155, 0.81577278], - [ 0.21372783, 0.49630865, 0.81589566], - [ 0.21409503, 0.48734861, 0.81566163], - [ 0.2157176 , 0.47827123, 0.81487615], - [ 0.21842857, 0.46909168, 0.81351614], - [ 0.22211705, 0.45983212, 0.81146983], - [ 0.22665681, 0.45052233, 0.80860217], - [ 0.23176013, 0.44119137, 0.80494325], - [ 0.23727775, 0.43187704, 0.80038017], - [ 0.24298285, 0.42261123, 0.79493267], - [ 0.24865068, 0.41341842, 0.78869164], - [ 0.25423116, 0.40433127, 0.78155831], - [ 0.25950239, 0.39535521, 0.77376848], - [ 0.2644736 , 0.38651212, 0.76524809], - [ 0.26901584, 0.37779582, 0.75621942], - [ 0.27318141, 0.36922056, 0.746605 ], - [ 0.27690355, 0.3607736 , 0.73659374], - [ 0.28023585, 0.35244234, 0.72622103], - [ 0.28306009, 0.34438449, 0.71500731], - [ 0.28535896, 0.33660243, 0.70303975], - [ 0.28708711, 0.32912157, 0.69034504], - [ 0.28816354, 0.32200604, 0.67684067], - [ 0.28862749, 0.31519824, 0.66278813], - [ 0.28847904, 0.30869064, 0.6482815 ], - [ 0.28770912, 0.30250126, 0.63331265], - [ 0.28640325, 0.29655509, 0.61811374], - [ 0.28458943, 0.29082155, 0.60280913], - [ 0.28233561, 0.28527482, 0.58742866], - [ 0.27967038, 0.2798938 , 0.57204225], - [ 0.27665361, 0.27465357, 0.55667809], - [ 0.27332564, 0.2695165 , 0.54145387], - [ 0.26973851, 0.26447054, 0.52634916], - [ 0.2659204 , 0.25949691, 0.511417 ], - [ 0.26190145, 0.25458123, 0.49668768], - [ 0.2577151 , 0.24971691, 0.48214874], - [ 0.25337618, 0.24490494, 0.46778758], - [ 0.24890842, 0.24013332, 0.45363816], - [ 0.24433654, 0.23539226, 0.4397245 ], - [ 0.23967922, 0.23067729, 0.4260591 ], - [ 0.23495608, 0.22598894, 0.41262952], - [ 0.23018113, 0.22132414, 0.39945577], - [ 0.22534609, 0.21670847, 0.38645794], - [ 0.22048761, 0.21211723, 0.37372555], - [ 0.2156198 , 0.20755389, 0.36125301], - [ 0.21074637, 0.20302717, 0.34903192], - [ 0.20586893, 0.19855368, 0.33701661], - [ 0.20101757, 0.19411573, 0.32529173], - [ 0.19619947, 0.18972425, 0.31383846], - [ 0.19140726, 0.18540157, 0.30260777], - [ 0.1866769 , 0.1811332 , 0.29166583], - [ 0.18201285, 0.17694992, 0.28088776], - [ 0.17745228, 0.17282141, 0.27044211], - [ 0.17300684, 0.16876921, 0.26024893], - [ 0.16868273, 0.16479861, 0.25034479], - [ 0.16448691, 0.16091728, 0.24075373], - [ 0.16043195, 0.15714351, 0.23141745], - [ 0.15652427, 0.15348248, 0.22238175], - [ 0.15277065, 0.14994111, 0.21368395], - [ 0.14918274, 0.14653431, 0.20529486], - [ 0.14577095, 0.14327403, 0.19720829], - [ 0.14254381, 0.14016944, 0.18944326], - [ 0.13951035, 0.13723063, 0.18201072], - [ 0.13667798, 0.13446606, 0.17493774], - [ 0.13405762, 0.13188822, 0.16820842], - [ 0.13165767, 0.12950667, 0.16183275], - [ 0.12948748, 0.12733187, 0.15580631], - [ 0.12755435, 0.1253723 , 0.15014098], - [ 0.12586516, 0.12363617, 0.1448459 ], - [ 0.12442647, 0.12213143, 0.13992571], - [ 0.12324241, 0.12086419, 0.13539995], - [ 0.12232067, 0.11984278, 0.13124644], - [ 0.12166209, 0.11907077, 0.12749671], - [ 0.12126982, 0.11855309, 0.12415079], - [ 0.12114244, 0.11829179, 0.1212385 ], - [ 0.12127766, 0.11828837, 0.11878534], - [ 0.12284806, 0.1179729 , 0.11772022], - [ 0.12619498, 0.11721796, 0.11770203], - [ 0.129968 , 0.11663788, 0.11792377], - [ 0.13410011, 0.11625146, 0.11839138], - [ 0.13855459, 0.11606618, 0.11910584], - [ 0.14333775, 0.11607038, 0.1200606 ], - [ 0.148417 , 0.11626929, 0.12125453], - [ 0.15377389, 0.11666192, 0.12268364], - [ 0.15941427, 0.11723486, 0.12433911], - [ 0.16533376, 0.11797856, 0.12621303], - [ 0.17152547, 0.11888403, 0.12829735], - [ 0.17797765, 0.11994436, 0.13058435], - [ 0.18468769, 0.12114722, 0.13306426], - [ 0.19165663, 0.12247737, 0.13572616], - [ 0.19884415, 0.12394381, 0.1385669 ], - [ 0.20627181, 0.12551883, 0.14157124], - [ 0.21394877, 0.12718055, 0.14472604], - [ 0.22184572, 0.12893119, 0.14802579], - [ 0.22994394, 0.13076731, 0.15146314], - [ 0.23823937, 0.13267611, 0.15502793], - [ 0.24676041, 0.13462172, 0.15870321], - [ 0.25546457, 0.13661751, 0.16248722], - [ 0.26433628, 0.13865956, 0.16637301], - [ 0.27341345, 0.14070412, 0.17034221], - [ 0.28264773, 0.14277192, 0.1743957 ], - [ 0.29202272, 0.14486161, 0.17852793], - [ 0.30159648, 0.14691224, 0.1827169 ], - [ 0.31129002, 0.14897583, 0.18695213], - [ 0.32111555, 0.15103351, 0.19119629], - [ 0.33107961, 0.1530674 , 0.19543758], - [ 0.34119892, 0.15504762, 0.1996803 ], - [ 0.35142388, 0.15701131, 0.20389086], - [ 0.36178937, 0.1589124 , 0.20807639], - [ 0.37229381, 0.16073993, 0.21223189], - [ 0.38288348, 0.16254006, 0.2163249 ], - [ 0.39359592, 0.16426336, 0.22036577], - [ 0.40444332, 0.16588767, 0.22434027], - [ 0.41537995, 0.16745325, 0.2282297 ], - [ 0.42640867, 0.16894939, 0.23202755], - [ 0.43754706, 0.17034847, 0.23572899], - [ 0.44878564, 0.1716535 , 0.23932344], - [ 0.4601126 , 0.17287365, 0.24278607], - [ 0.47151732, 0.17401641, 0.24610337], - [ 0.48300689, 0.17506676, 0.2492737 ], - [ 0.49458302, 0.17601892, 0.25227688], - [ 0.50623876, 0.17687777, 0.255096 ], - [ 0.5179623 , 0.17765528, 0.2577162 ], - [ 0.52975234, 0.17835232, 0.2601134 ], - [ 0.54159776, 0.17898292, 0.26226847], - [ 0.55348804, 0.17956232, 0.26416003], - [ 0.56541729, 0.18010175, 0.26575971], - [ 0.57736669, 0.180631 , 0.26704888], - [ 0.58932081, 0.18117827, 0.26800409], - [ 0.60127582, 0.18175888, 0.26858488], - [ 0.61319563, 0.1824336 , 0.2687872 ], - [ 0.62506376, 0.18324015, 0.26858301], - [ 0.63681202, 0.18430173, 0.26795276], - [ 0.64842603, 0.18565472, 0.26689463], - [ 0.65988195, 0.18734638, 0.26543435], - [ 0.67111966, 0.18948885, 0.26357955], - [ 0.68209194, 0.19216636, 0.26137175], - [ 0.69281185, 0.19535326, 0.25887063], - [ 0.70335022, 0.19891271, 0.25617971], - [ 0.71375229, 0.20276438, 0.25331365], - [ 0.72401436, 0.20691287, 0.25027366], - [ 0.73407638, 0.21145051, 0.24710661], - [ 0.74396983, 0.21631913, 0.24380715], - [ 0.75361506, 0.22163653, 0.24043996], - [ 0.7630579 , 0.22731637, 0.23700095], - [ 0.77222228, 0.23346231, 0.23356628], - [ 0.78115441, 0.23998404, 0.23013825], - [ 0.78979746, 0.24694858, 0.22678822], - [ 0.79819286, 0.25427223, 0.22352658], - [ 0.80630444, 0.26198807, 0.22040877], - [ 0.81417437, 0.27001406, 0.21744645], - [ 0.82177364, 0.27837336, 0.21468316], - [ 0.82915955, 0.28696963, 0.21210766], - [ 0.83628628, 0.2958499 , 0.20977813], - [ 0.84322168, 0.30491136, 0.20766435], - [ 0.84995458, 0.31415945, 0.2057863 ], - [ 0.85648867, 0.32358058, 0.20415327], - [ 0.86286243, 0.33312058, 0.20274969], - [ 0.86908321, 0.34276705, 0.20157271], - [ 0.87512876, 0.3525416 , 0.20064949], - [ 0.88100349, 0.36243385, 0.19999078], - [ 0.8866469 , 0.37249496, 0.1997976 ], - [ 0.89203964, 0.38273475, 0.20013431], - [ 0.89713496, 0.39318156, 0.20121514], - [ 0.90195099, 0.40380687, 0.20301555], - [ 0.90648379, 0.41460191, 0.20558847], - [ 0.9106967 , 0.42557857, 0.20918529], - [ 0.91463791, 0.43668557, 0.21367954], - [ 0.91830723, 0.44790913, 0.21916352], - [ 0.92171507, 0.45922856, 0.22568002], - [ 0.92491786, 0.4705936 , 0.23308207], - [ 0.92790792, 0.48200153, 0.24145932], - [ 0.93073701, 0.49341219, 0.25065486], - [ 0.93343918, 0.5048017 , 0.26056148], - [ 0.93602064, 0.51616486, 0.27118485], - [ 0.93850535, 0.52748892, 0.28242464], - [ 0.94092933, 0.53875462, 0.29416042], - [ 0.94330011, 0.5499628 , 0.30634189], - [ 0.94563159, 0.56110987, 0.31891624], - [ 0.94792955, 0.57219822, 0.33184256], - [ 0.95020929, 0.5832232 , 0.34508419], - [ 0.95247324, 0.59419035, 0.35859866], - [ 0.95471709, 0.60510869, 0.37236035], - [ 0.95698411, 0.61595766, 0.38629631], - [ 0.95923863, 0.62676473, 0.40043317], - [ 0.9615041 , 0.6375203 , 0.41474106], - [ 0.96371553, 0.64826619, 0.42928335], - [ 0.96591497, 0.65899621, 0.44380444], - [ 0.96809871, 0.66971662, 0.45830232], - [ 0.9702495 , 0.6804394 , 0.47280492], - [ 0.9723881 , 0.69115622, 0.48729272], - [ 0.97450723, 0.70187358, 0.50178034], - [ 0.9766108 , 0.712592 , 0.51626837], - [ 0.97871716, 0.72330511, 0.53074053], - [ 0.98082222, 0.73401769, 0.54520694], - [ 0.9829001 , 0.74474445, 0.5597019 ], - [ 0.98497466, 0.75547635, 0.57420239], - [ 0.98705581, 0.76621129, 0.58870185], - [ 0.98913325, 0.77695637, 0.60321626], - [ 0.99119918, 0.78771716, 0.61775821], - [ 0.9932672 , 0.79848979, 0.63231691], - [ 0.99535958, 0.80926704, 0.64687278], - [ 0.99740544, 0.82008078, 0.66150571], - [ 0.9992197 , 0.83100723, 0.6764127 ]] +_icefire_lut = [ + [ 0.73936227, 0.90443867, 0.85757238], + [ 0.72888063, 0.89639109, 0.85488394], + [ 0.71834255, 0.88842162, 0.8521605 ], + [ 0.70773866, 0.88052939, 0.849422 ], + [ 0.69706215, 0.87271313, 0.84668315], + [ 0.68629021, 0.86497329, 0.84398721], + [ 0.67543654, 0.85730617, 0.84130969], + [ 0.66448539, 0.84971123, 0.83868005], + [ 0.65342679, 0.84218728, 0.83611512], + [ 0.64231804, 0.83471867, 0.83358584], + [ 0.63117745, 0.827294 , 0.83113431], + [ 0.62000484, 0.81991069, 0.82876741], + [ 0.60879435, 0.81256797, 0.82648905], + [ 0.59754118, 0.80526458, 0.82430414], + [ 0.58624247, 0.79799884, 0.82221573], + [ 0.57489525, 0.7907688 , 0.82022901], + [ 0.56349779, 0.78357215, 0.81834861], + [ 0.55204294, 0.77640827, 0.81657563], + [ 0.54052516, 0.76927562, 0.81491462], + [ 0.52894085, 0.76217215, 0.81336913], + [ 0.51728854, 0.75509528, 0.81194156], + [ 0.50555676, 0.74804469, 0.81063503], + [ 0.49373871, 0.7410187 , 0.80945242], + [ 0.48183174, 0.73401449, 0.80839675], + [ 0.46982587, 0.72703075, 0.80747097], + [ 0.45770893, 0.72006648, 0.80667756], + [ 0.44547249, 0.71311941, 0.80601991], + [ 0.43318643, 0.70617126, 0.80549278], + [ 0.42110294, 0.69916972, 0.80506683], + [ 0.40925101, 0.69211059, 0.80473246], + [ 0.3976693 , 0.68498786, 0.80448272], + [ 0.38632002, 0.67781125, 0.80431024], + [ 0.37523981, 0.67057537, 0.80420832], + [ 0.36442578, 0.66328229, 0.80417474], + [ 0.35385939, 0.65593699, 0.80420591], + [ 0.34358916, 0.64853177, 0.8043 ], + [ 0.33355526, 0.64107876, 0.80445484], + [ 0.32383062, 0.63356578, 0.80467091], + [ 0.31434372, 0.62600624, 0.8049475 ], + [ 0.30516161, 0.618389 , 0.80528692], + [ 0.29623491, 0.61072284, 0.80569021], + [ 0.28759072, 0.60300319, 0.80616055], + [ 0.27923924, 0.59522877, 0.80669803], + [ 0.27114651, 0.5874047 , 0.80730545], + [ 0.26337153, 0.57952055, 0.80799113], + [ 0.25588696, 0.57157984, 0.80875922], + [ 0.248686 , 0.56358255, 0.80961366], + [ 0.24180668, 0.55552289, 0.81055123], + [ 0.23526251, 0.54739477, 0.8115939 ], + [ 0.22921445, 0.53918506, 0.81267292], + [ 0.22397687, 0.53086094, 0.8137141 ], + [ 0.21977058, 0.52241482, 0.81457651], + [ 0.21658989, 0.51384321, 0.81528511], + [ 0.21452772, 0.50514155, 0.81577278], + [ 0.21372783, 0.49630865, 0.81589566], + [ 0.21409503, 0.48734861, 0.81566163], + [ 0.2157176 , 0.47827123, 0.81487615], + [ 0.21842857, 0.46909168, 0.81351614], + [ 0.22211705, 0.45983212, 0.81146983], + [ 0.22665681, 0.45052233, 0.80860217], + [ 0.23176013, 0.44119137, 0.80494325], + [ 0.23727775, 0.43187704, 0.80038017], + [ 0.24298285, 0.42261123, 0.79493267], + [ 0.24865068, 0.41341842, 0.78869164], + [ 0.25423116, 0.40433127, 0.78155831], + [ 0.25950239, 0.39535521, 0.77376848], + [ 0.2644736 , 0.38651212, 0.76524809], + [ 0.26901584, 0.37779582, 0.75621942], + [ 0.27318141, 0.36922056, 0.746605 ], + [ 0.27690355, 0.3607736 , 0.73659374], + [ 0.28023585, 0.35244234, 0.72622103], + [ 0.28306009, 0.34438449, 0.71500731], + [ 0.28535896, 0.33660243, 0.70303975], + [ 0.28708711, 0.32912157, 0.69034504], + [ 0.28816354, 0.32200604, 0.67684067], + [ 0.28862749, 0.31519824, 0.66278813], + [ 0.28847904, 0.30869064, 0.6482815 ], + [ 0.28770912, 0.30250126, 0.63331265], + [ 0.28640325, 0.29655509, 0.61811374], + [ 0.28458943, 0.29082155, 0.60280913], + [ 0.28233561, 0.28527482, 0.58742866], + [ 0.27967038, 0.2798938 , 0.57204225], + [ 0.27665361, 0.27465357, 0.55667809], + [ 0.27332564, 0.2695165 , 0.54145387], + [ 0.26973851, 0.26447054, 0.52634916], + [ 0.2659204 , 0.25949691, 0.511417 ], + [ 0.26190145, 0.25458123, 0.49668768], + [ 0.2577151 , 0.24971691, 0.48214874], + [ 0.25337618, 0.24490494, 0.46778758], + [ 0.24890842, 0.24013332, 0.45363816], + [ 0.24433654, 0.23539226, 0.4397245 ], + [ 0.23967922, 0.23067729, 0.4260591 ], + [ 0.23495608, 0.22598894, 0.41262952], + [ 0.23018113, 0.22132414, 0.39945577], + [ 0.22534609, 0.21670847, 0.38645794], + [ 0.22048761, 0.21211723, 0.37372555], + [ 0.2156198 , 0.20755389, 0.36125301], + [ 0.21074637, 0.20302717, 0.34903192], + [ 0.20586893, 0.19855368, 0.33701661], + [ 0.20101757, 0.19411573, 0.32529173], + [ 0.19619947, 0.18972425, 0.31383846], + [ 0.19140726, 0.18540157, 0.30260777], + [ 0.1866769 , 0.1811332 , 0.29166583], + [ 0.18201285, 0.17694992, 0.28088776], + [ 0.17745228, 0.17282141, 0.27044211], + [ 0.17300684, 0.16876921, 0.26024893], + [ 0.16868273, 0.16479861, 0.25034479], + [ 0.16448691, 0.16091728, 0.24075373], + [ 0.16043195, 0.15714351, 0.23141745], + [ 0.15652427, 0.15348248, 0.22238175], + [ 0.15277065, 0.14994111, 0.21368395], + [ 0.14918274, 0.14653431, 0.20529486], + [ 0.14577095, 0.14327403, 0.19720829], + [ 0.14254381, 0.14016944, 0.18944326], + [ 0.13951035, 0.13723063, 0.18201072], + [ 0.13667798, 0.13446606, 0.17493774], + [ 0.13405762, 0.13188822, 0.16820842], + [ 0.13165767, 0.12950667, 0.16183275], + [ 0.12948748, 0.12733187, 0.15580631], + [ 0.12755435, 0.1253723 , 0.15014098], + [ 0.12586516, 0.12363617, 0.1448459 ], + [ 0.12442647, 0.12213143, 0.13992571], + [ 0.12324241, 0.12086419, 0.13539995], + [ 0.12232067, 0.11984278, 0.13124644], + [ 0.12166209, 0.11907077, 0.12749671], + [ 0.12126982, 0.11855309, 0.12415079], + [ 0.12114244, 0.11829179, 0.1212385 ], + [ 0.12127766, 0.11828837, 0.11878534], + [ 0.12284806, 0.1179729 , 0.11772022], + [ 0.12619498, 0.11721796, 0.11770203], + [ 0.129968 , 0.11663788, 0.11792377], + [ 0.13410011, 0.11625146, 0.11839138], + [ 0.13855459, 0.11606618, 0.11910584], + [ 0.14333775, 0.11607038, 0.1200606 ], + [ 0.148417 , 0.11626929, 0.12125453], + [ 0.15377389, 0.11666192, 0.12268364], + [ 0.15941427, 0.11723486, 0.12433911], + [ 0.16533376, 0.11797856, 0.12621303], + [ 0.17152547, 0.11888403, 0.12829735], + [ 0.17797765, 0.11994436, 0.13058435], + [ 0.18468769, 0.12114722, 0.13306426], + [ 0.19165663, 0.12247737, 0.13572616], + [ 0.19884415, 0.12394381, 0.1385669 ], + [ 0.20627181, 0.12551883, 0.14157124], + [ 0.21394877, 0.12718055, 0.14472604], + [ 0.22184572, 0.12893119, 0.14802579], + [ 0.22994394, 0.13076731, 0.15146314], + [ 0.23823937, 0.13267611, 0.15502793], + [ 0.24676041, 0.13462172, 0.15870321], + [ 0.25546457, 0.13661751, 0.16248722], + [ 0.26433628, 0.13865956, 0.16637301], + [ 0.27341345, 0.14070412, 0.17034221], + [ 0.28264773, 0.14277192, 0.1743957 ], + [ 0.29202272, 0.14486161, 0.17852793], + [ 0.30159648, 0.14691224, 0.1827169 ], + [ 0.31129002, 0.14897583, 0.18695213], + [ 0.32111555, 0.15103351, 0.19119629], + [ 0.33107961, 0.1530674 , 0.19543758], + [ 0.34119892, 0.15504762, 0.1996803 ], + [ 0.35142388, 0.15701131, 0.20389086], + [ 0.36178937, 0.1589124 , 0.20807639], + [ 0.37229381, 0.16073993, 0.21223189], + [ 0.38288348, 0.16254006, 0.2163249 ], + [ 0.39359592, 0.16426336, 0.22036577], + [ 0.40444332, 0.16588767, 0.22434027], + [ 0.41537995, 0.16745325, 0.2282297 ], + [ 0.42640867, 0.16894939, 0.23202755], + [ 0.43754706, 0.17034847, 0.23572899], + [ 0.44878564, 0.1716535 , 0.23932344], + [ 0.4601126 , 0.17287365, 0.24278607], + [ 0.47151732, 0.17401641, 0.24610337], + [ 0.48300689, 0.17506676, 0.2492737 ], + [ 0.49458302, 0.17601892, 0.25227688], + [ 0.50623876, 0.17687777, 0.255096 ], + [ 0.5179623 , 0.17765528, 0.2577162 ], + [ 0.52975234, 0.17835232, 0.2601134 ], + [ 0.54159776, 0.17898292, 0.26226847], + [ 0.55348804, 0.17956232, 0.26416003], + [ 0.56541729, 0.18010175, 0.26575971], + [ 0.57736669, 0.180631 , 0.26704888], + [ 0.58932081, 0.18117827, 0.26800409], + [ 0.60127582, 0.18175888, 0.26858488], + [ 0.61319563, 0.1824336 , 0.2687872 ], + [ 0.62506376, 0.18324015, 0.26858301], + [ 0.63681202, 0.18430173, 0.26795276], + [ 0.64842603, 0.18565472, 0.26689463], + [ 0.65988195, 0.18734638, 0.26543435], + [ 0.67111966, 0.18948885, 0.26357955], + [ 0.68209194, 0.19216636, 0.26137175], + [ 0.69281185, 0.19535326, 0.25887063], + [ 0.70335022, 0.19891271, 0.25617971], + [ 0.71375229, 0.20276438, 0.25331365], + [ 0.72401436, 0.20691287, 0.25027366], + [ 0.73407638, 0.21145051, 0.24710661], + [ 0.74396983, 0.21631913, 0.24380715], + [ 0.75361506, 0.22163653, 0.24043996], + [ 0.7630579 , 0.22731637, 0.23700095], + [ 0.77222228, 0.23346231, 0.23356628], + [ 0.78115441, 0.23998404, 0.23013825], + [ 0.78979746, 0.24694858, 0.22678822], + [ 0.79819286, 0.25427223, 0.22352658], + [ 0.80630444, 0.26198807, 0.22040877], + [ 0.81417437, 0.27001406, 0.21744645], + [ 0.82177364, 0.27837336, 0.21468316], + [ 0.82915955, 0.28696963, 0.21210766], + [ 0.83628628, 0.2958499 , 0.20977813], + [ 0.84322168, 0.30491136, 0.20766435], + [ 0.84995458, 0.31415945, 0.2057863 ], + [ 0.85648867, 0.32358058, 0.20415327], + [ 0.86286243, 0.33312058, 0.20274969], + [ 0.86908321, 0.34276705, 0.20157271], + [ 0.87512876, 0.3525416 , 0.20064949], + [ 0.88100349, 0.36243385, 0.19999078], + [ 0.8866469 , 0.37249496, 0.1997976 ], + [ 0.89203964, 0.38273475, 0.20013431], + [ 0.89713496, 0.39318156, 0.20121514], + [ 0.90195099, 0.40380687, 0.20301555], + [ 0.90648379, 0.41460191, 0.20558847], + [ 0.9106967 , 0.42557857, 0.20918529], + [ 0.91463791, 0.43668557, 0.21367954], + [ 0.91830723, 0.44790913, 0.21916352], + [ 0.92171507, 0.45922856, 0.22568002], + [ 0.92491786, 0.4705936 , 0.23308207], + [ 0.92790792, 0.48200153, 0.24145932], + [ 0.93073701, 0.49341219, 0.25065486], + [ 0.93343918, 0.5048017 , 0.26056148], + [ 0.93602064, 0.51616486, 0.27118485], + [ 0.93850535, 0.52748892, 0.28242464], + [ 0.94092933, 0.53875462, 0.29416042], + [ 0.94330011, 0.5499628 , 0.30634189], + [ 0.94563159, 0.56110987, 0.31891624], + [ 0.94792955, 0.57219822, 0.33184256], + [ 0.95020929, 0.5832232 , 0.34508419], + [ 0.95247324, 0.59419035, 0.35859866], + [ 0.95471709, 0.60510869, 0.37236035], + [ 0.95698411, 0.61595766, 0.38629631], + [ 0.95923863, 0.62676473, 0.40043317], + [ 0.9615041 , 0.6375203 , 0.41474106], + [ 0.96371553, 0.64826619, 0.42928335], + [ 0.96591497, 0.65899621, 0.44380444], + [ 0.96809871, 0.66971662, 0.45830232], + [ 0.9702495 , 0.6804394 , 0.47280492], + [ 0.9723881 , 0.69115622, 0.48729272], + [ 0.97450723, 0.70187358, 0.50178034], + [ 0.9766108 , 0.712592 , 0.51626837], + [ 0.97871716, 0.72330511, 0.53074053], + [ 0.98082222, 0.73401769, 0.54520694], + [ 0.9829001 , 0.74474445, 0.5597019 ], + [ 0.98497466, 0.75547635, 0.57420239], + [ 0.98705581, 0.76621129, 0.58870185], + [ 0.98913325, 0.77695637, 0.60321626], + [ 0.99119918, 0.78771716, 0.61775821], + [ 0.9932672 , 0.79848979, 0.63231691], + [ 0.99535958, 0.80926704, 0.64687278], + [ 0.99740544, 0.82008078, 0.66150571], + [ 0.9992197 , 0.83100723, 0.6764127 ] +] _luts = [_rocket_lut, _mako_lut, _vlag_lut, _icefire_lut] From 7588d536d6e64e964df6a32472c07e6754daf988 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 27 Jun 2017 11:07:08 -0400 Subject: [PATCH 0402/1738] Invert heatmap y axis instead of alterting the data This is a much better way of achieving 'matrix order' heatmap plot --- seaborn/matrix.py | 11 ++------ seaborn/tests/test_matrix.py | 55 +++++++++++++++++++----------------- 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index b3a885f22a..76e3533d65 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -113,11 +113,6 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, # Validate the mask and convet to DataFrame mask = _matrix_mask(data, mask) - # Reverse the rows so the plot looks like the matrix - plot_data = plot_data[::-1] - data = data.iloc[::-1] - mask = mask.iloc[::-1] - plot_data = np.ma.masked_where(np.asarray(mask), plot_data) # Get good names for the rows and columns @@ -138,8 +133,6 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, yticklabels = _index_to_ticklabels(data.index) elif yticklabels is False: yticklabels = [] - else: - yticklabels = yticklabels[::-1] # Get the positions and used label for the ticks nx, ny = data.T.shape @@ -181,9 +174,9 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, annot_data = None else: try: - annot_data = annot.values[::-1] + annot_data = annot.values except AttributeError: - annot_data = annot[::-1] + annot_data = annot if annot.shape != plot_data.shape: raise ValueError('Data supplied to "annot" must be the same ' 'shape as the data to plot.') diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 40859e4432..7471878b78 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -47,11 +47,11 @@ class TestHeatmap(PlotTestCase): def test_ndarray_input(self): p = mat._HeatMapper(self.x_norm, **self.default_kws) - npt.assert_array_equal(p.plot_data, self.x_norm[::-1]) - pdt.assert_frame_equal(p.data, pd.DataFrame(self.x_norm).ix[::-1]) + npt.assert_array_equal(p.plot_data, self.x_norm) + pdt.assert_frame_equal(p.data, pd.DataFrame(self.x_norm)) npt.assert_array_equal(p.xticklabels, np.arange(8)) - npt.assert_array_equal(p.yticklabels, np.arange(4)[::-1]) + npt.assert_array_equal(p.yticklabels, np.arange(4)) nt.assert_equal(p.xlabel, "") nt.assert_equal(p.ylabel, "") @@ -59,11 +59,11 @@ def test_ndarray_input(self): def test_df_input(self): p = mat._HeatMapper(self.df_norm, **self.default_kws) - npt.assert_array_equal(p.plot_data, self.x_norm[::-1]) - pdt.assert_frame_equal(p.data, self.df_norm.ix[::-1]) + npt.assert_array_equal(p.plot_data, self.x_norm) + pdt.assert_frame_equal(p.data, self.df_norm) npt.assert_array_equal(p.xticklabels, np.arange(8)) - npt.assert_array_equal(p.yticklabels, ["D", "C", "B", "A"]) + npt.assert_array_equal(p.yticklabels, self.letters.values) nt.assert_equal(p.xlabel, "") nt.assert_equal(p.ylabel, "letters") @@ -79,12 +79,13 @@ def test_df_multindex_input(self): p = mat._HeatMapper(df, **self.default_kws) - npt.assert_array_equal(p.yticklabels, ["D-4", "C-3", "B-2", "A-1"]) + combined_tick_labels = ["A-1", "B-2", "C-3", "D-4"] + npt.assert_array_equal(p.yticklabels, combined_tick_labels) nt.assert_equal(p.ylabel, "letter-number") p = mat._HeatMapper(df.T, **self.default_kws) - npt.assert_array_equal(p.xticklabels, ["A-1", "B-2", "C-3", "D-4"]) + npt.assert_array_equal(p.xticklabels, combined_tick_labels) nt.assert_equal(p.xlabel, "letter-number") def test_mask_input(self): @@ -95,7 +96,7 @@ def test_mask_input(self): p = mat._HeatMapper(self.x_norm, **kws) plot_data = np.ma.masked_where(mask, self.x_norm) - npt.assert_array_equal(p.plot_data, plot_data[::-1]) + npt.assert_array_equal(p.plot_data, plot_data) def test_default_vlims(self): @@ -217,29 +218,31 @@ def test_custom_ticklabels(self): kws['yticklabels'] = yticklabels p = mat._HeatMapper(self.df_norm, **kws) nt.assert_equal(p.xticklabels, xticklabels) - nt.assert_equal(p.yticklabels, yticklabels[::-1]) + nt.assert_equal(p.yticklabels, yticklabels) def test_custom_ticklabel_interval(self): kws = self.default_kws.copy() - kws['xticklabels'] = 2 - kws['yticklabels'] = 3 + xstep, ystep = 2, 3 + kws['xticklabels'] = xstep + kws['yticklabels'] = ystep p = mat._HeatMapper(self.df_norm, **kws) nx, ny = self.df_norm.T.shape - ystart = (ny - 1) % 3 - npt.assert_array_equal(p.xticks, np.arange(0, nx, 2) + .5) - npt.assert_array_equal(p.yticks, np.arange(ystart, ny, 3) + .5) + xstart = (nx - 1) % xstep + ystart = (ny - 1) % ystep + npt.assert_array_equal(p.xticks, np.arange(xstart, nx, xstep) + .5) + npt.assert_array_equal(p.yticks, np.arange(ystart, ny, ystep) + .5) npt.assert_array_equal(p.xticklabels, - self.df_norm.columns[::2]) + self.df_norm.columns[xstart:nx:xstep]) npt.assert_array_equal(p.yticklabels, - self.df_norm.index[::-1][ystart:ny:3]) + self.df_norm.index[ystart:ny:ystep]) def test_heatmap_annotation(self): ax = mat.heatmap(self.df_norm, annot=True, fmt=".1f", annot_kws={"fontsize": 14}) - for val, text in zip(self.x_norm[::-1].flat, ax.texts): + for val, text in zip(self.x_norm.flat, ax.texts): nt.assert_equal(text.get_text(), "{:.1f}".format(val)) nt.assert_equal(text.get_fontsize(), 14) @@ -261,8 +264,8 @@ def test_heatmap_annotation_with_mask(self): mask = np.isnan(df.values) df_masked = np.ma.masked_where(mask, df) ax = mat.heatmap(df, annot=True, fmt='.1f', mask=mask) - nt.assert_equal(len(df_masked[::-1].compressed()), len(ax.texts)) - for val, text in zip(df_masked[::-1].compressed(), ax.texts): + nt.assert_equal(len(df_masked.compressed()), len(ax.texts)) + for val, text in zip(df_masked.compressed(), ax.texts): nt.assert_equal("{:.1f}".format(val), text.get_text()) def test_heatmap_annotation_mesh_colors(self): @@ -279,7 +282,7 @@ def test_heatmap_annotation_other_data(self): ax = mat.heatmap(self.df_norm, annot=annot_data, fmt=".1f", annot_kws={"fontsize": 14}) - for val, text in zip(annot_data.values[::-1].flat, ax.texts): + for val, text in zip(annot_data.values.flat, ax.texts): nt.assert_equal(text.get_text(), "{:.1f}".format(val)) nt.assert_equal(text.get_fontsize(), 14) @@ -307,13 +310,13 @@ def test_heatmap_axes(self): xtl = [int(l.get_text()) for l in ax.get_xticklabels()] nt.assert_equal(xtl, list(self.df_norm.columns)) ytl = [l.get_text() for l in ax.get_yticklabels()] - nt.assert_equal(ytl, list(self.df_norm.index[::-1])) + nt.assert_equal(ytl, list(self.df_norm.index)) nt.assert_equal(ax.get_xlabel(), "") nt.assert_equal(ax.get_ylabel(), "letters") nt.assert_equal(ax.get_xlim(), (0, 8)) - nt.assert_equal(ax.get_ylim(), (0, 4)) + nt.assert_equal(ax.get_ylim(), (4, 0)) def test_heatmap_ticklabel_rotation(self): @@ -913,7 +916,7 @@ def test_row_col_colors_df(self): col_labels = [l.get_text() for l in cm.ax_col_colors.get_yticklabels()] nt.assert_equal(cm.col_color_labels, ['col_1', 'col_2']) - nt.assert_equal(col_labels[::-1], cm.col_color_labels) + nt.assert_equal(col_labels, cm.col_color_labels) def test_row_col_colors_df_shuffled(self): # Tests if colors are properly matched, even if given in wrong order @@ -982,7 +985,7 @@ def test_row_col_colors_df_one_axis(self): col_labels = [l.get_text() for l in cm2.ax_col_colors.get_yticklabels()] nt.assert_equal(cm2.col_color_labels, ['col_1', 'col_2']) - nt.assert_equal(col_labels[::-1], cm2.col_color_labels) + nt.assert_equal(col_labels, cm2.col_color_labels) def test_row_col_colors_series(self): kws = self.default_kws.copy() @@ -1073,7 +1076,7 @@ def test_ticklabel_reorganization(self): ytl_actual = [t.get_text() for t in g.ax_heatmap.get_yticklabels()] xtl_want = xtl[g.dendrogram_col.reordered_ind].astype(" Date: Tue, 27 Jun 2017 11:07:33 -0400 Subject: [PATCH 0403/1738] Implement automatic skipping of ticklabels to avoid overlap --- seaborn/matrix.py | 86 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 27 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 76e3533d65..985421b893 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -140,18 +140,22 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, if xticklabels == []: self.xticks = [] self.xticklabels = [] + elif isinstance(xticklabels, string_types) and xticklabels == "auto": + self.xticks = "auto" + self.xticklabels = _index_to_ticklabels(data.columns) else: - xstart, xend, xstep = 0, nx, xtickevery - self.xticks = np.arange(xstart, xend, xstep) + .5 - self.xticklabels = xticklabels[xstart:xend:xstep] + self.xticks, self.xticklabels = self._skip_ticks(xticklabels, + xtickevery) if yticklabels == []: self.yticks = [] self.yticklabels = [] + elif isinstance(yticklabels, string_types) and yticklabels == "auto": + self.yticks = "auto" + self.yticklabels = _index_to_ticklabels(data.index) else: - ystart, yend, ystep = (ny - 1) % ytickevery, ny, ytickevery - self.yticks = np.arange(ystart, yend, ystep) + .5 - self.yticklabels = yticklabels[ystart:yend:ystep] + self.yticks, self.yticklabels = self._skip_ticks(yticklabels, + ytickevery) # Get good names for the axis labels xlabel = _index_to_label(data.columns) @@ -241,6 +245,27 @@ def _annotate_heatmap(self, ax, mesh): text_kwargs.update(self.annot_kws) ax.text(x, y, annotation, **text_kwargs) + def _skip_ticks(self, labels, tickevery): + """Return ticks and labels at evenly spaced intervals.""" + n = len(labels) + start, end, step = (n - 1) % tickevery, n, tickevery + ticks = np.arange(start, end, step) + .5 + ticklabels = labels[start:end:step] + return ticks, ticklabels + + def _auto_ticks(self, ax, labels, axis): + """Determine ticks and ticklabels that minimize overlap.""" + transform = ax.figure.dpi_scale_trans.inverted() + bbox = ax.get_window_extent().transformed(transform) + size = [bbox.width, bbox.height][axis] + axis = [ax.xaxis, ax.yaxis][axis] + fontsize = axis.get_majorticklabels()[0].get_fontsize() + max_ticks = int((size * .85) // (fontsize / 72)) + tick_every = len(labels) // max_ticks + tick_every = 1 if tick_every == 0 else tick_every + ticks, labels = self._skip_ticks(labels, tick_every) + return ticks, labels + def plot(self, ax, cax, kws): """Draw the heatmap on the provided Axes.""" # Remove all the Axes spines @@ -253,10 +278,29 @@ def plot(self, ax, cax, kws): # Set the axis limits ax.set(xlim=(0, self.data.shape[1]), ylim=(0, self.data.shape[0])) + # Possibly add a colorbar + if self.cbar: + cb = ax.figure.colorbar(mesh, cax, ax, **self.cbar_kws) + cb.outline.set_linewidth(0) + # If rasterized is passed to pcolormesh, also rasterize the + # colorbar to avoid white lines on the PDF rendering + if kws.get('rasterized', False): + cb.solids.set_rasterized(True) + # Add row and column labels - ax.set(xticks=self.xticks, yticks=self.yticks) - xtl = ax.set_xticklabels(self.xticklabels) - ytl = ax.set_yticklabels(self.yticklabels, rotation="vertical") + if isinstance(self.xticks, string_types) and self.xticks == "auto": + xticks, xticklabels = self._auto_ticks(ax, self.xticklabels, 0) + else: + xticks, xticklabels = self.xticks, self.xticklabels + + if isinstance(self.yticks, string_types) and self.yticks == "auto": + yticks, yticklabels = self._auto_ticks(ax, self.yticklabels, 1) + else: + yticks, yticklabels = self.yticks, self.yticklabels + + ax.set(xticks=xticks, yticks=yticks) + xtl = ax.set_xticklabels(xticklabels) + ytl = ax.set_yticklabels(yticklabels, rotation="vertical") # Possibly rotate them if they overlap plt.draw() @@ -272,23 +316,16 @@ def plot(self, ax, cax, kws): if self.annot: self._annotate_heatmap(ax, mesh) - # Possibly add a colorbar - if self.cbar: - cb = ax.figure.colorbar(mesh, cax, ax, **self.cbar_kws) - cb.outline.set_linewidth(0) - # If rasterized is passed to pcolormesh, also rasterize the - # colorbar to avoid white lines on the PDF rendering - if kws.get('rasterized', False): - cb.solids.set_rasterized(True) + # Invert the y axis to show the plot in matrix form + ax.invert_yaxis() def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, annot=None, fmt=".2g", annot_kws=None, linewidths=0, linecolor="white", cbar=True, cbar_kws=None, cbar_ax=None, - square=False, ax=None, xticklabels=True, yticklabels=True, - mask=None, - **kwargs): + square=False, ax=None, xticklabels="auto", yticklabels="auto", + mask=None, **kwargs): """Plot rectangular data as a color-encoded matrix. This is an Axes-level function and will draw the heatmap into the @@ -340,16 +377,11 @@ def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, ax : matplotlib Axes, optional Axes in which to draw the plot, otherwise use the currently-active Axes. - xticklabels : bool, list-like, or int, optional + xticklabels, yticklabels : "auto", bool, list-like, or int, optional If True, plot the column names of the dataframe. If False, don't plot the column names. If list-like, plot these alternate labels as the xticklabels. If an integer, use the column names but plot only every - n label. - yticklabels : bool, list-like, or int, optional - If True, plot the row names of the dataframe. If False, don't plot - the row names. If list-like, plot these alternate labels as the - yticklabels. If an integer, use the index names but plot only every - n label. + n label. If "auto", try to densely plot non-overlapping labels. mask : boolean array or DataFrame, optional If passed, data will not be shown in cells where ``mask`` is True. Cells with missing values are automatically masked. From f9f6f3d51957aa3aaf4428ce99c6d55ec020f690 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 27 Jun 2017 19:14:16 -0400 Subject: [PATCH 0404/1738] Validate inputs to JointGrid when using data Closes #1204 --- seaborn/axisgrid.py | 12 ++++++++---- seaborn/tests/test_axisgrid.py | 5 +++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index c58e85e183..a0599c821f 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -11,6 +11,7 @@ from . import utils from .palettes import color_palette +from .external.six import string_types __all__ = ["FacetGrid", "PairGrid", "JointGrid"] @@ -1649,10 +1650,13 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, # Possibly extract the variables from a DataFrame if data is not None: - if x in data: - x = data[x] - if y in data: - y = data[y] + x = data.get(x, x) + y = data.get(y, y) + + for var in [x, y]: + if isinstance(var, string_types): + err = "Could not interpret input '{}'".format(var) + raise ValueError(err) # Possibly drop NA if dropna: diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 71b935f89e..ec392d92ce 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -1257,6 +1257,11 @@ def test_margin_grid_from_dataframe(self): npt.assert_array_equal(g.x, self.x) npt.assert_array_equal(g.y, self.y) + def test_margin_grid_from_dataframe_bad_variable(self): + + with nt.assert_raises(ValueError): + g = ag.JointGrid("x", "bad_column", self.data) + def test_margin_grid_axis_labels(self): g = ag.JointGrid("x", "y", self.data) From 98e248ae861bdebf77ecdf57d47d53b4e6e14639 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 27 Jun 2017 20:15:02 -0400 Subject: [PATCH 0405/1738] Fix failing doctests on auto ticks But I am not convinced the code is working properly yet --- seaborn/matrix.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 985421b893..82bf8beec3 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -259,8 +259,11 @@ def _auto_ticks(self, ax, labels, axis): bbox = ax.get_window_extent().transformed(transform) size = [bbox.width, bbox.height][axis] axis = [ax.xaxis, ax.yaxis][axis] - fontsize = axis.get_majorticklabels()[0].get_fontsize() - max_ticks = int((size * .85) // (fontsize / 72)) + tick, = axis.set_ticks([0]) + fontsize = tick.label.get_size() + max_ticks = int(size // (fontsize / 72)) + if max_ticks == 0: + return [], [] tick_every = len(labels) // max_ticks tick_every = 1 if tick_every == 0 else tick_every ticks, labels = self._skip_ticks(labels, tick_every) From f27c2f271003fe52493755b3c88e322f36ef9f28 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 27 Jun 2017 20:32:26 -0400 Subject: [PATCH 0406/1738] Improve auto ticking --- seaborn/matrix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 82bf8beec3..5889919451 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -248,7 +248,7 @@ def _annotate_heatmap(self, ax, mesh): def _skip_ticks(self, labels, tickevery): """Return ticks and labels at evenly spaced intervals.""" n = len(labels) - start, end, step = (n - 1) % tickevery, n, tickevery + start, end, step = 0, n, tickevery ticks = np.arange(start, end, step) + .5 ticklabels = labels[start:end:step] return ticks, ticklabels @@ -264,7 +264,7 @@ def _auto_ticks(self, ax, labels, axis): max_ticks = int(size // (fontsize / 72)) if max_ticks == 0: return [], [] - tick_every = len(labels) // max_ticks + tick_every = len(labels) // max_ticks + 1 tick_every = 1 if tick_every == 0 else tick_every ticks, labels = self._skip_ticks(labels, tick_every) return ticks, labels From 708dae0e154c29d057885bd290ffa1066791bca0 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 27 Jun 2017 22:00:24 -0400 Subject: [PATCH 0407/1738] Adjustments to automatic ticking --- seaborn/matrix.py | 19 ++++++++++++------- seaborn/tests/test_matrix.py | 14 ++++++-------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 5889919451..aa03dc623b 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -117,7 +117,7 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, # Get good names for the rows and columns xtickevery = 1 - if isinstance(xticklabels, int) and xticklabels > 1: + if isinstance(xticklabels, int): xtickevery = xticklabels xticklabels = _index_to_ticklabels(data.columns) elif xticklabels is True: @@ -126,7 +126,7 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, xticklabels = [] ytickevery = 1 - if isinstance(yticklabels, int) and yticklabels > 1: + if isinstance(yticklabels, int): ytickevery = yticklabels yticklabels = _index_to_ticklabels(data.index) elif yticklabels is True: @@ -248,10 +248,15 @@ def _annotate_heatmap(self, ax, mesh): def _skip_ticks(self, labels, tickevery): """Return ticks and labels at evenly spaced intervals.""" n = len(labels) - start, end, step = 0, n, tickevery - ticks = np.arange(start, end, step) + .5 - ticklabels = labels[start:end:step] - return ticks, ticklabels + if tickevery == 0: + ticks, labels = [], [] + elif tickevery == 1: + ticks, labels = np.arange(n) + .5, labels + else: + start, end, step = 0, n, tickevery + ticks = np.arange(start, end, step) + .5 + labels = labels[start:end:step] + return ticks, labels def _auto_ticks(self, ax, labels, axis): """Determine ticks and ticklabels that minimize overlap.""" @@ -262,7 +267,7 @@ def _auto_ticks(self, ax, labels, axis): tick, = axis.set_ticks([0]) fontsize = tick.label.get_size() max_ticks = int(size // (fontsize / 72)) - if max_ticks == 0: + if max_ticks < 1: return [], [] tick_every = len(labels) // max_ticks + 1 tick_every = 1 if tick_every == 0 else tick_every diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 7471878b78..346c948450 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -229,14 +229,12 @@ def test_custom_ticklabel_interval(self): p = mat._HeatMapper(self.df_norm, **kws) nx, ny = self.df_norm.T.shape - xstart = (nx - 1) % xstep - ystart = (ny - 1) % ystep - npt.assert_array_equal(p.xticks, np.arange(xstart, nx, xstep) + .5) - npt.assert_array_equal(p.yticks, np.arange(ystart, ny, ystep) + .5) + npt.assert_array_equal(p.xticks, np.arange(0, nx, xstep) + .5) + npt.assert_array_equal(p.yticks, np.arange(0, ny, ystep) + .5) npt.assert_array_equal(p.xticklabels, - self.df_norm.columns[xstart:nx:xstep]) + self.df_norm.columns[0:nx:xstep]) npt.assert_array_equal(p.yticklabels, - self.df_norm.index[ystart:ny:ystep]) + self.df_norm.index[0:ny:ystep]) def test_heatmap_annotation(self): @@ -321,7 +319,7 @@ def test_heatmap_axes(self): def test_heatmap_ticklabel_rotation(self): f, ax = plt.subplots(figsize=(2, 2)) - mat.heatmap(self.df_norm, ax=ax) + mat.heatmap(self.df_norm, xticklabels=1, yticklabels=1, ax=ax) for t in ax.get_xticklabels(): nt.assert_equal(t.get_rotation(), 0) @@ -336,7 +334,7 @@ def test_heatmap_ticklabel_rotation(self): df.index = [i * 10 for i in df.index] f, ax = plt.subplots(figsize=(2, 2)) - mat.heatmap(df, ax=ax) + mat.heatmap(df, xticklabels=1, yticklabels=1, ax=ax) for t in ax.get_xticklabels(): nt.assert_equal(t.get_rotation(), 90) From 4a207886c6d39d0443dc1055e4042d8ab2e6fcf5 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 28 Jun 2017 20:56:31 -0400 Subject: [PATCH 0408/1738] Change default tick labeling in clustermap --- seaborn/matrix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index aa03dc623b..f88183ca75 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -1102,12 +1102,12 @@ def plot_matrix(self, colorbar_kws, xind, yind, **kws): self.mask = self.mask.iloc[yind, xind] # Try to reorganize specified tick labels, if provided - xtl = kws.pop("xticklabels", True) + xtl = kws.pop("xticklabels", "auto") try: xtl = np.asarray(xtl)[xind] except (TypeError, IndexError): pass - ytl = kws.pop("yticklabels", True) + ytl = kws.pop("yticklabels", "auto") try: ytl = np.asarray(ytl)[yind] except (TypeError, IndexError): From 906f6367983729e77d04a0da5fda8e6aeec4f67a Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 28 Jun 2017 20:57:53 -0400 Subject: [PATCH 0409/1738] Update release notes --- doc/releases/v0.8.0.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index a3897e5e45..427b00704a 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -10,6 +10,8 @@ v0.8.0 (Unreleased) - Changed the default :func:`heatmap` colormaps to be "rocket" (in the case of sequential data) or "icefire" (in the case of diverging data). Note that this change reverses the direction of the luminance ramp from the previous defaults. While potentially confusing and disruptive, this change better aligns the seaborn defaults with the new matplotlib default colormap ("viridis") and arguably better aligns the semantics of a "heat" map with the appearance of the colormap. +- Added `"auto"` as a (default) option for tick labels in :func:`heatmap` and :func:`clustermap`. This will try to estimate how many ticks can be labeled without the text objects overlapping, which should improve performance for larger matrices. + - Added the ``dodge`` argument to :func:`boxplot`, :func:`violinplot`, and :func:`barplot` to allow use of ``hue`` without changing the position or width of the plot elements, as when the ``hue`` varible is not nested within the main categorical variable. - Added the ability to draw a colorbar for a bivariate :func:`kdeplot` with the ``cbar`` parameter (and related ``cbar_ax`` and ``cbar_kws`` parameters). From 899f0914fb4da2614637be5e64c9ddbe1a4310f4 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 10:54:12 -0400 Subject: [PATCH 0410/1738] Move ax kwarg to end of function signature --- seaborn/matrix.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index f88183ca75..80a9b536ed 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -332,8 +332,8 @@ def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, annot=None, fmt=".2g", annot_kws=None, linewidths=0, linecolor="white", cbar=True, cbar_kws=None, cbar_ax=None, - square=False, ax=None, xticklabels="auto", yticklabels="auto", - mask=None, **kwargs): + square=False, xticklabels="auto", yticklabels="auto", + mask=None, ax=None, **kwargs): """Plot rectangular data as a color-encoded matrix. This is an Axes-level function and will draw the heatmap into the @@ -382,9 +382,6 @@ def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, square : boolean, optional If True, set the Axes aspect to "equal" so each cell will be square-shaped. - ax : matplotlib Axes, optional - Axes in which to draw the plot, otherwise use the currently-active - Axes. xticklabels, yticklabels : "auto", bool, list-like, or int, optional If True, plot the column names of the dataframe. If False, don't plot the column names. If list-like, plot these alternate labels as the @@ -393,6 +390,9 @@ def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, mask : boolean array or DataFrame, optional If passed, data will not be shown in cells where ``mask`` is True. Cells with missing values are automatically masked. + ax : matplotlib Axes, optional + Axes in which to draw the plot, otherwise use the currently-active + Axes. kwargs : other keyword arguments All other keyword arguments are passed to ``ax.pcolormesh``. From c81a7e8baaa7d869e95d90c20913a184a2f50ca9 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 11:25:50 -0400 Subject: [PATCH 0411/1738] Improve clustermap API examples (closes #1212) --- seaborn/matrix.py | 50 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 80a9b536ed..32a934a5dd 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -1227,59 +1227,77 @@ def clustermap(data, pivot_kws=None, method='average', metric='euclidean', :context: close-figs >>> import seaborn as sns; sns.set() - >>> flights = sns.load_dataset("flights") - >>> flights = flights.pivot("month", "year", "passengers") - >>> g = sns.clustermap(flights) + >>> networks = sns.load_dataset("brain_networks", + ... index_col=0, header=[0, 1, 2]) + >>> networks = networks.loc[:200].T + >>> g = sns.clustermap(networks) + + Use a different similarity metric: + + .. plot:: + :context: close-figs + + >>> g = sns.clustermap(networks, metric="correlation") - Don't cluster one of the axes: + Use a different clustering method: .. plot:: :context: close-figs - >>> g = sns.clustermap(flights, col_cluster=False) + >>> g = sns.clustermap(networks, method="single") - Use a different colormap and add lines to separate the cells: + Use a different colormap and ignore outliers in colormap limits: .. plot:: :context: close-figs - >>> g = sns.clustermap(flights, cmap="mako", linewidths=.5) + >>> g = sns.clustermap(networks, cmap="mako", robust=True) - Use a different figure size: + Change the size of the figure: .. plot:: :context: close-figs - >>> g = sns.clustermap(flights, figsize=(7, 5)) + >>> g = sns.clustermap(networks, figsize=(9, 5)) Standardize the data across the columns: .. plot:: :context: close-figs - >>> g = sns.clustermap(flights, standard_scale=1) + >>> g = sns.clustermap(networks, standard_scale=1) Normalize the data across the rows: .. plot:: :context: close-figs - >>> g = sns.clustermap(flights, z_score=0) + >>> g = sns.clustermap(networks, z_score=0) - Use a different clustering method: + Plot one of the axes in its original organization: + + .. plot:: + :context: close-figs + + >>> g = sns.clustermap(networks, col_cluster=False) + + Add colored labels: .. plot:: :context: close-figs - >>> g = sns.clustermap(flights, method="single", metric="cosine") + >>> from matplotlib.colors import Normalize + >>> labels = networks.index.get_level_values("network").astype(int) + >>> colors = sns.cm.vlag(Normalize(1, 17)(labels)) + >>> g = sns.clustermap(networks, row_colors=colors) - Add colored labels on one of the axes: + Show data with a divergent colormap: .. plot:: :context: close-figs - >>> month_colors = sns.hls_palette(12, h=.7)[::-1] - >>> g = sns.clustermap(flights, row_colors=month_colors) + >>> corrmat = networks.T.corr() + >>> g = sns.clustermap(corrmat, center=0) """ plotter = ClusterGrid(data, pivot_kws=pivot_kws, figsize=figsize, From 4e09cb1e3462e7b1c7ff566f1598c28c4c44be41 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 14:48:57 -0400 Subject: [PATCH 0412/1738] Change the default seaborn cmap, figsize, and add new sans serif font --- seaborn/rcmod.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index 7c9bf2f0c6..f1d77c0dd6 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -186,9 +186,9 @@ def axes_style(style=None, rc=None): "xtick.color": dark_gray, "ytick.color": dark_gray, "axes.axisbelow": True, - "image.cmap": "Greys", + "image.cmap": "rocket", "font.family": ["sans-serif"], - "font.sans-serif": ["Arial", "Liberation Sans", + "font.sans-serif": ["DejaVu Sans", "Arial", "Liberation Sans", "Bitstream Vera Sans", "sans-serif"], "grid.linestyle": "-", "lines.solid_capstyle": "round", @@ -348,7 +348,7 @@ def plotting_context(context=None, font_scale=1, rc=None): # Set up dictionary of default parameters base_context = { - "figure.figsize": np.array([8, 5.5]), + "figure.figsize": np.array([6.4, 4.8]), "font.size": 12, "axes.labelsize": 11, "axes.titlesize": 12, From 65c5d8f2be9fb7b662ce36d7bf68769518641ac5 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 14:49:23 -0400 Subject: [PATCH 0413/1738] Don't set seaborn style on import --- seaborn/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/seaborn/__init__.py b/seaborn/__init__.py index c1fede1285..c788470253 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -18,7 +18,4 @@ from .crayons import crayons from . import cm -# Set default aesthetics -set() - __version__ = "0.8.dev" From 228cc572f6595bc65264fc1ca79513a377d0bffa Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 14:49:37 -0400 Subject: [PATCH 0414/1738] Deprecate seaborn.apionly --- seaborn/apionly.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/seaborn/apionly.py b/seaborn/apionly.py index 03679a596c..3c2ea2d6f7 100644 --- a/seaborn/apionly.py +++ b/seaborn/apionly.py @@ -1,2 +1,9 @@ +import warnings +msg = ( +"The seaborn.apionly module is deprecated, as seaborn no longer sets a default " +"style on import. It will be removed in a future version." +) +warnings.warn(msg, UserWarning) + from seaborn import * reset_orig() From b0bb36b58124da3039b28073eca3ae687f62b3fa Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 14:49:51 -0400 Subject: [PATCH 0415/1738] Update docs to reflect new default style behvior --- doc/installing.rst | 26 +------------------------- doc/releases/v0.8.0.txt | 2 ++ doc/tutorial/aesthetics.ipynb | 11 ++++++----- 3 files changed, 9 insertions(+), 30 deletions(-) diff --git a/doc/installing.rst b/doc/installing.rst index e444ac5260..79872db4a9 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -60,24 +60,6 @@ matplotlibs will work fine. Seaborn is tested on the most recent versions offered through ``conda``. - -Importing seaborn -~~~~~~~~~~~~~~~~~ - -Seaborn will apply its default style parameters to the global matplotlib style -dictionary when you import it. This will change the look of all plots, -including those created by using matplotlib functions directly. To avoid this -behavior and use the default matplotlib aesthetics (along with any -customization in your ``matplotlibrc``), you can import the ``seaborn.apionly`` -namespace. - -Seaborn has several other pre-packaged styles along with high-level :ref:`tools -` for managing them, so you should not limit yourself to the -default aesthetics. - -By convention, ``seaborn`` is abbreviated to ``sns`` on import. - - Testing ~~~~~~~ @@ -105,14 +87,8 @@ report. Known issues ~~~~~~~~~~~~ -There is a `bug `_ in the -matplotlib OSX backend that causes unavoidable problems with some of the -seaborn functions (particularly those that draw multi-panel figures). If you -encounter this, you will want to try a `different backend -`_. In particular, this bug affects any multi-panel figure that internally calls the matplotlib ``tight_layout`` function. - An unfortunate consequence of how the matplotlib marker styles work is that line-art markers (e.g. ``"+"``) or markers with ``facecolor`` set to ``"none"`` will be invisible when the default seaborn style is in effect. This can be changed by using a different ``markeredgewidth`` (aliased to ``mew``) either in -the function call or globally in the `rcParams`. +the function call or globally in the ``rcParams``. diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index 427b00704a..53a885b876 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -2,6 +2,8 @@ v0.8.0 (Unreleased) ------------------- +- The default style is no longer applied when seaborn is imported. It is now necessary to explicitly call :func:`set` or one or more of :func:`set_style`, :func:`set_context`, and :func:`set_palette`. Correspondingly, the ``seaborn.apionly`` module has been deprecated. + - Changed the behavior of :func:`heatmap` (and by extension :func:`clustermap`) when plotting divergent dataesets (i.e. when the ``center`` parameter is used). Instead of extending the lower and upper limits of the colormap to be symettrical around the ``center`` value, the colormap is modified so that its middle color corresponds to ``center``. This means that the full range of the colormap will not be used (unless the data or specified ``vmin`` and ``vmax`` are symettric), but the upper and lower limits of the colorbar will correspond to the range of the data. See the Github pull request `(#1184) `_ for examples of the behavior. - Removed automatic detection of diverging data in :func:`heatmap` (and by extension :func:`clustermap`). If you want the colormap to be treated as diverging (see above), it is now necessary to specify the ``center`` value. When no colormap is specified, specifying ``center`` will still change the default to be one that is more appropriate for displaying diverging data. diff --git a/doc/tutorial/aesthetics.ipynb b/doc/tutorial/aesthetics.ipynb index 49c7a2a14d..d158844aa8 100644 --- a/doc/tutorial/aesthetics.ipynb +++ b/doc/tutorial/aesthetics.ipynb @@ -47,6 +47,7 @@ "import numpy as np\n", "import matplotlib as mpl\n", "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", "np.random.seed(sum(map(ord, \"aesthetics\")))" ] }, @@ -93,7 +94,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "To switch to seaborn defaults, simply import the package." + "To switch to seaborn defaults, simply call the :func:`set` function." ] }, { @@ -104,7 +105,7 @@ }, "outputs": [], "source": [ - "import seaborn as sns\n", + "sns.set()\n", "sinplot()" ] }, @@ -112,7 +113,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "The seaborn defaults break from the MATLAB inspired aesthetic of matplotlib to plot in more muted colors over a light gray background with white grid lines. We find that the grid aids in the use of figures for conveying quantitative information – in almost all cases, figures should be preferred to tables. The white-on-gray grid that is used by default avoids being obtrusive. The grid is particularly useful in giving structure to figures with multiple facets, which is central to some of the more complex tools in the library.\n", + "(Note that in versions of seaborn prior to 0.8, :func:`set` was called on import. On later versions, it must be explicitly invoked).\n", "\n", "Seaborn splits matplotlib parameters into two independent groups. The first group sets the aesthetic style of the plot, and the second scales various elements of the figure so that it can be easily incorporated into different contexts.\n", "\n", @@ -460,9 +461,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", - "version": "2.7.11" + "version": "2.7.12" } }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} From b016775c7f78e0ef51ad70810b6feb5851cc083e Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 15:00:40 -0400 Subject: [PATCH 0416/1738] PEP8 --- seaborn/apionly.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/apionly.py b/seaborn/apionly.py index 3c2ea2d6f7..686e4776ba 100644 --- a/seaborn/apionly.py +++ b/seaborn/apionly.py @@ -1,7 +1,7 @@ import warnings msg = ( -"The seaborn.apionly module is deprecated, as seaborn no longer sets a default " -"style on import. It will be removed in a future version." + "As seaborn no longer sets a default style on import, the seaborn.apionly " + "module is deprecated. It will be removed in a future version." ) warnings.warn(msg, UserWarning) From bbcba3a18c20cde208ae1537153d828d3899de04 Mon Sep 17 00:00:00 2001 From: Christopher Roberts Date: Sun, 2 Jul 2017 03:06:46 -0500 Subject: [PATCH 0417/1738] Don't depend on ticks for heatmap annotations Will now show annotations on all cells regardless of what value x/yticklabels take. Fixes #837. --- seaborn/matrix.py | 3 ++- seaborn/tests/test_matrix.py | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 32a934a5dd..041d6cf7f5 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -233,7 +233,8 @@ def _determine_cmap_params(self, plot_data, vmin, vmax, def _annotate_heatmap(self, ax, mesh): """Add textual labels with the value in each cell.""" mesh.update_scalarmappable() - xpos, ypos = np.meshgrid(ax.get_xticks(), ax.get_yticks()) + height, width = self.annot_data.shape + xpos, ypos = np.meshgrid(np.arange(width) + .5, np.arange(height) + .5) for x, y, m, color, val in zip(xpos.flat, ypos.flat, mesh.get_array(), mesh.get_facecolors(), self.annot_data.flat): diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 346c948450..9da9c7f4e7 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -284,6 +284,12 @@ def test_heatmap_annotation_other_data(self): nt.assert_equal(text.get_text(), "{:.1f}".format(val)) nt.assert_equal(text.get_fontsize(), 14) + def test_heatmap_annotation_with_limited_ticklabels(self): + ax = mat.heatmap(self.df_norm, fmt=".2f", annot=True, + xticklabels=False, yticklabels=False) + for val, text in zip(self.x_norm.flat, ax.texts): + nt.assert_equal(text.get_text(), "{:.2f}".format(val)) + def test_heatmap_cbar(self): f = plt.figure() From ca111b7adb7e89b70ea90bf1ec043586c37e3fd3 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 3 Jul 2017 13:43:00 -0400 Subject: [PATCH 0418/1738] Set density axis minimum to 0 in kdeplot (closes #1218) --- seaborn/distributions.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 7325b3af86..bb80b50aed 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -309,9 +309,15 @@ def _univariate_kdeplot(data, shade, vertical, kernel, bw, gridsize, cut, alpha = kwargs.get("alpha", 0.25) if shade: if vertical: - ax.fill_betweenx(y, 1e-12, x, facecolor=color, alpha=alpha) + ax.fill_betweenx(y, 0, x, facecolor=color, alpha=alpha) else: - ax.fill_between(x, 1e-12, y, facecolor=color, alpha=alpha) + ax.fill_between(x, 0, y, facecolor=color, alpha=alpha) + + # Set the density axis minimum to 0 + if vertical: + ax.set_xlim(0, None) + else: + ax.set_ylim(0, None) # Draw the legend here if legend: From 054e07ed02fdb3054f0c825fddf1c0359f58206f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 3 Jul 2017 15:27:20 -0400 Subject: [PATCH 0419/1738] Account for DPI in swarmplot spacing (closes #1008) --- seaborn/categorical.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index e70a66b0e8..4e4827b205 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1315,7 +1315,8 @@ def swarm_points(self, ax, points, center, width, s, **kws): # Convert from point size (area) to diameter default_lw = mpl.rcParams["patch.linewidth"] lw = kws.get("linewidth", kws.get("lw", default_lw)) - d = np.sqrt(s) + lw + dpi = ax.figure.dpi + d = (np.sqrt(s) + lw) * (dpi / 72) # Transform the data coordinates to point coordinates. # We'll figure out the swarm positions in the latter From f05ad4233fd8ffb7d803ca92cb21a435ee94463c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 15:01:50 -0400 Subject: [PATCH 0420/1738] Final removal of corrplot and symmatplot --- seaborn/linearmodels.py | 182 ---------------------------------------- 1 file changed, 182 deletions(-) diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index 3e117f2584..61ec621ad0 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -1257,188 +1257,6 @@ def interactplot(x1, x2, y, data=None, filled=False, cmap="RdBu_r", return ax -def corrplot(data, names=None, annot=True, sig_stars=True, sig_tail="both", - sig_corr=True, cmap=None, cmap_range=None, cbar=True, - diag_names=True, method=None, ax=None, **kwargs): - """Plot a correlation matrix with colormap and r values. - - NOTE: This function is deprecated in favor of :func:`heatmap` and will - be removed in a forthcoming release. - - Parameters - ---------- - data : Dataframe or nobs x nvars array - Rectangular input data with variabes in the columns. - names : sequence of strings - Names to associate with variables if `data` is not a DataFrame. - annot : bool - Whether to annotate the upper triangle with correlation coefficients. - sig_stars : bool - If True, get significance with permutation test and denote with stars. - sig_tail : both | upper | lower - Direction for significance test. Also controls the default colorbar. - sig_corr : bool - If True, use FWE-corrected p values for the sig stars. - cmap : colormap - Colormap name as string or colormap object. - cmap_range : None, "full", (low, high) - Either truncate colormap at (-max(abs(r)), max(abs(r))), use the - full range (-1, 1), or specify (min, max) values for the colormap. - cbar : bool - If true, plot the colorbar legend. - method: None (pearson) | kendall | spearman - Correlation method to compute pairwise correlations. Methods other - than the default pearson correlation will not have a significance - computed. - ax : matplotlib axis - Axis to draw plot in. - kwargs : other keyword arguments - Passed to ax.matshow() - - Returns - ------- - ax : matplotlib axis - Axis object with plot. - - """ - warnings.warn(("The `corrplot` function has been deprecated in favor " - "of `heatmap` and will be removed in a forthcoming " - "release. Please update your code.")) - - if not isinstance(data, pd.DataFrame): - if names is None: - names = ["var_%d" % i for i in range(data.shape[1])] - data = pd.DataFrame(data, columns=names, dtype=np.float) - - # Calculate the correlation matrix of the dataframe - if method is None: - corrmat = data.corr() - else: - corrmat = data.corr(method=method) - - # Pandas will drop non-numeric columns; let's keep track of that operation - names = corrmat.columns - data = data[names] - - # Get p values with a permutation test - if annot and sig_stars and method is None: - p_mat = algo.randomize_corrmat(data.values.T, sig_tail, sig_corr) - else: - p_mat = None - - # Sort out the color range - if cmap_range is None: - triu = np.triu_indices(len(corrmat), 1) - vmax = min(1, np.max(np.abs(corrmat.values[triu])) * 1.15) - vmin = -vmax - if sig_tail == "both": - cmap_range = vmin, vmax - elif sig_tail == "upper": - cmap_range = 0, vmax - elif sig_tail == "lower": - cmap_range = vmin, 0 - elif cmap_range == "full": - cmap_range = (-1, 1) - - # Find a colormapping, somewhat intelligently - if cmap is None: - if min(cmap_range) >= 0: - cmap = "OrRd" - elif max(cmap_range) <= 0: - cmap = "PuBu_r" - else: - cmap = "coolwarm" - if cmap == "jet": - # Paternalism - raise ValueError("Never use the 'jet' colormap!") - - # Plot using the more general symmatplot function - ax = symmatplot(corrmat, p_mat, names, cmap, cmap_range, - cbar, annot, diag_names, ax, **kwargs) - - return ax - - -def symmatplot(mat, p_mat=None, names=None, cmap="Greys", cmap_range=None, - cbar=True, annot=True, diag_names=True, ax=None, **kwargs): - """Plot a symmetric matrix with colormap and statistic values. - - NOTE: This function is deprecated in favor of :func:`heatmap` and will - be removed in a forthcoming release. - - """ - warnings.warn(("The `symmatplot` function has been deprecated in favor " - "of `heatmap` and will be removed in a forthcoming " - "release. Please update your code.")) - - if ax is None: - ax = plt.gca() - - nvars = len(mat) - if isinstance(mat, pd.DataFrame): - plotmat = mat.values.copy() - mat = mat.values - else: - plotmat = mat.copy() - plotmat[np.triu_indices(nvars)] = np.nan - - if cmap_range is None: - vmax = np.nanmax(plotmat) * 1.15 - vmin = np.nanmin(plotmat) * 1.15 - elif len(cmap_range) == 2: - vmin, vmax = cmap_range - else: - raise ValueError("cmap_range argument not understood") - - mat_img = ax.matshow(plotmat, cmap=cmap, vmin=vmin, vmax=vmax, **kwargs) - - if cbar: - plt.colorbar(mat_img, shrink=.75) - - if p_mat is None: - p_mat = np.ones((nvars, nvars)) - - if annot: - for i, j in zip(*np.triu_indices(nvars, 1)): - val = mat[i, j] - stars = utils.sig_stars(p_mat[i, j]) - ax.text(j, i, "\n%.2g\n%s" % (val, stars), - fontdict=dict(ha="center", va="center")) - else: - fill = np.ones_like(plotmat) - fill[np.tril_indices_from(fill, -1)] = np.nan - ax.matshow(fill, cmap="Greys", vmin=0, vmax=0, zorder=2) - - if names is None: - names = ["var%d" % i for i in range(nvars)] - - if diag_names: - for i, name in enumerate(names): - ax.text(i, i, name, fontdict=dict(ha="center", va="center", - weight="bold", rotation=45)) - ax.set_xticklabels(()) - ax.set_yticklabels(()) - else: - ax.xaxis.set_ticks_position("bottom") - xnames = names if annot else names[:-1] - ax.set_xticklabels(xnames, rotation=90) - ynames = names if annot else names[1:] - ax.set_yticklabels(ynames) - - minor_ticks = np.linspace(-.5, nvars - 1.5, nvars) - ax.set_xticks(minor_ticks, True) - ax.set_yticks(minor_ticks, True) - major_ticks = np.linspace(0, nvars - 1, nvars) - xticks = major_ticks if annot else major_ticks[:-1] - ax.set_xticks(xticks) - yticks = major_ticks if annot else major_ticks[1:] - ax.set_yticks(yticks) - ax.grid(False, which="major") - ax.grid(True, which="minor", linestyle="-") - - return ax - - def pairplot(data, hue=None, hue_order=None, palette=None, vars=None, x_vars=None, y_vars=None, kind="scatter", diag_kind="hist", markers=None, From 6f6c19ed36e3edd6b7a72592593ee45ec7cbdf67 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 15:03:53 -0400 Subject: [PATCH 0421/1738] Final removal of offset_spines --- seaborn/tests/test_utils.py | 37 ------------------------------------ seaborn/utils.py | 38 ------------------------------------- 2 files changed, 75 deletions(-) diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index f94d2c0015..351b55d4f3 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -234,43 +234,6 @@ def test_despine_trim_noticks(self): utils.despine(trim=True) nt.assert_equal(ax.get_yticks().size, 0) - def test_offset_spines_warns(self): - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always", category=UserWarning) - - f, ax = plt.subplots() - utils.offset_spines(offset=self.offset) - nt.assert_true('deprecated' in str(w[0].message)) - nt.assert_true(issubclass(w[0].category, UserWarning)) - - def test_offset_spines(self): - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always", category=UserWarning) - f, ax = plt.subplots() - - for side in self.sides: - nt.assert_equal(ax.spines[side].get_position(), - self.original_position) - - utils.offset_spines(offset=self.offset) - - for side in self.sides: - nt.assert_equal(ax.spines[side].get_position(), - self.offset_position) - - def test_offset_spines_specific_axes(self): - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always", category=UserWarning) - f, (ax1, ax2) = plt.subplots(2, 1) - - utils.offset_spines(offset=self.offset, ax=ax2) - - for side in self.sides: - nt.assert_equal(ax1.spines[side].get_position(), - self.original_position) - nt.assert_equal(ax2.spines[side].get_position(), - self.offset_position) - def test_ticklabels_overlap(): diff --git a/seaborn/utils.py b/seaborn/utils.py index d77e79d8c4..1e3089550d 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -245,44 +245,6 @@ def despine(fig=None, ax=None, top=True, right=True, left=False, ax_i.set_yticks(newticks) -def offset_spines(offset=10, fig=None, ax=None): - """Simple function to offset spines away from axes. - - Use this immediately after creating figure and axes objects. - Offsetting spines after plotting or manipulating the axes - objects may result in loss of labels, ticks, and formatting. - - Parameters - ---------- - offset : int, optional - Absolute distance, in points, spines should be moved away - from the axes (negative values move spines inward). - fig : matplotlib figure, optional - Figure to despine all axes of, default uses current figure. - ax : matplotlib axes, optional - Specific axes object to despine - - Returns - ------- - None - - """ - warn_msg = "`offset_spines` is deprecated and will be removed in v0.5" - warnings.warn(warn_msg, UserWarning) - - # Get references to the axes we want - if fig is None and ax is None: - axes = plt.gcf().axes - elif fig is not None: - axes = fig.axes - elif ax is not None: - axes = [ax] - - for ax_i in axes: - for spine in ax_i.spines.values(): - _set_spine_position(spine, ('outward', offset)) - - def _set_spine_position(spine, position): """ Set the spine's position without resetting an associated axis. From 261f32e698d3836a9fb31e1c05ca7b5207ad7741 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 15:05:24 -0400 Subject: [PATCH 0422/1738] Remove backwards-compatability layer from 0.6 categorical plot changes --- seaborn/categorical.py | 113 ----------------------------------------- 1 file changed, 113 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 4e4827b205..64fad354b4 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2193,46 +2193,6 @@ def boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, width=.8, dodge=True, fliersize=5, linewidth=None, whis=1.5, notch=False, ax=None, **kwargs): - # Try to handle broken backwards-compatability - # This should help with the lack of a smooth deprecation, - # but won't catch everything - warn = False - if isinstance(x, pd.DataFrame): - data = x - x = None - warn = True - - if "vals" in kwargs: - x = kwargs.pop("vals") - warn = True - - if "groupby" in kwargs: - y = x - x = kwargs.pop("groupby") - warn = True - - if "vert" in kwargs: - vert = kwargs.pop("vert", True) - if not vert: - x, y = y, x - orient = "v" if vert else "h" - warn = True - - if "names" in kwargs: - kwargs.pop("names") - warn = True - - if "join_rm" in kwargs: - kwargs.pop("join_rm") - warn = True - - msg = ("The boxplot API has been changed. Attempting to adjust your " - "arguments for the new API (which might not work). Please update " - "your code. See the version 0.6 release notes for more info.") - - if warn: - warnings.warn(msg, UserWarning) - plotter = _BoxPlotter(x, y, hue, data, order, hue_order, orient, color, palette, saturation, width, dodge, fliersize, linewidth) @@ -2385,37 +2345,6 @@ def violinplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, linewidth=None, color=None, palette=None, saturation=.75, ax=None, **kwargs): - # Try to handle broken backwards-compatability - # This should help with the lack of a smooth deprecation, - # but won't catch everything - warn = False - if isinstance(x, pd.DataFrame): - data = x - x = None - warn = True - - if "vals" in kwargs: - x = kwargs.pop("vals") - warn = True - - if "groupby" in kwargs: - y = x - x = kwargs.pop("groupby") - warn = True - - if "vert" in kwargs: - vert = kwargs.pop("vert", True) - if not vert: - x, y = y, x - orient = "v" if vert else "h" - warn = True - - msg = ("The violinplot API has been changed. Attempting to adjust your " - "arguments for the new API (which might not work). Please update " - "your code. See the version 0.6 release notes for more info.") - if warn: - warnings.warn(msg, UserWarning) - plotter = _ViolinPlotter(x, y, hue, data, order, hue_order, bw, cut, scale, scale_hue, gridsize, width, inner, split, dodge, orient, linewidth, @@ -2992,20 +2921,6 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, errcolor=".26", errwidth=None, capsize=None, dodge=True, ax=None, **kwargs): - # Handle some deprecated arguments - if "hline" in kwargs: - kwargs.pop("hline") - warnings.warn("The `hline` parameter has been removed", UserWarning) - - if "dropna" in kwargs: - kwargs.pop("dropna") - warnings.warn("The `dropna` parameter has been removed", UserWarning) - - if "x_order" in kwargs: - order = kwargs.pop("x_order") - warnings.warn("The `x_order` parameter has been renamed `order`", - UserWarning) - plotter = _BarPlotter(x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, orient, color, palette, saturation, @@ -3190,20 +3105,6 @@ def pointplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, orient=None, color=None, palette=None, errwidth=None, capsize=None, ax=None, **kwargs): - # Handle some deprecated arguments - if "hline" in kwargs: - kwargs.pop("hline") - warnings.warn("The `hline` parameter has been removed", UserWarning) - - if "dropna" in kwargs: - kwargs.pop("dropna") - warnings.warn("The `dropna` parameter has been removed", UserWarning) - - if "x_order" in kwargs: - order = kwargs.pop("x_order") - warnings.warn("The `x_order` parameter has been renamed `order`", - UserWarning) - plotter = _PointPlotter(x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, markers, linestyles, dodge, join, scale, @@ -3533,20 +3434,6 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, legend=True, legend_out=True, sharex=True, sharey=True, margin_titles=False, facet_kws=None, **kwargs): - # Handle some deprecated arguments - if "hline" in kwargs: - kwargs.pop("hline") - warnings.warn("The `hline` parameter has been removed", UserWarning) - - if "dropna" in kwargs: - kwargs.pop("dropna") - warnings.warn("The `dropna` parameter has been removed", UserWarning) - - if "x_order" in kwargs: - order = kwargs.pop("x_order") - warnings.warn("The `x_order` parameter has been renamed `order`", - UserWarning) - # Determine the plotting function try: plot_func = globals()[kind + "plot"] From 0ab865b3aa6b89da391befac6058c939904b3e2f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 15:07:36 -0400 Subject: [PATCH 0423/1738] Removal of externally deprecated functionality --- seaborn/miscplot.py | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/seaborn/miscplot.py b/seaborn/miscplot.py index afb98218c7..8b70040940 100644 --- a/seaborn/miscplot.py +++ b/seaborn/miscplot.py @@ -4,7 +4,7 @@ import matplotlib.pyplot as plt -__all__ = ["palplot", "puppyplot"] +__all__ = ["palplot"] def palplot(pal, size=1): @@ -27,23 +27,3 @@ def palplot(pal, size=1): ax.set_yticks([-.5, .5]) ax.set_xticklabels([]) ax.set_yticklabels([]) - - -def puppyplot(grown_up=False): - """Plot today's daily puppy. Only works in the IPython notebook.""" - from .external.six.moves.urllib.request import urlopen - from IPython.display import HTML - try: - from bs4 import BeautifulSoup - url = "http://www.dailypuppy.com" - if grown_up: - url += "/dogs" - html_doc = urlopen(url) - soup = BeautifulSoup(html_doc) - puppy = soup.find("div", {"class": "daily_puppy"}) - return HTML(str(puppy.img)) - except ImportError: - html = ('') - return HTML(html) From eb24fb372f3020dcc4196f6961d26f61a3e97c38 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 15:11:08 -0400 Subject: [PATCH 0424/1738] Remove handling of deprecated rcparam --- seaborn/tests/test_rcmod.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/seaborn/tests/test_rcmod.py b/seaborn/tests/test_rcmod.py index 73f13ee4ba..585d10583f 100644 --- a/seaborn/tests/test_rcmod.py +++ b/seaborn/tests/test_rcmod.py @@ -21,10 +21,7 @@ def flatten_list(self, orig_list): def assert_rc_params(self, params): for k, v in params.items(): - if k == "svg.embed_char_paths": - # This param causes test issues and is deprecated anyway - continue - elif isinstance(v, np.ndarray): + if isinstance(v, np.ndarray): npt.assert_array_equal(mpl.rcParams[k], v) else: nt.assert_equal((k, mpl.rcParams[k]), (k, v)) From 69b5b9ee1c2a22fc94e8b2e9e479a38c3fc51b9e Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 15:13:13 -0400 Subject: [PATCH 0425/1738] Rename linearmodels submodule to regression --- seaborn/__init__.py | 2 +- seaborn/distributions.py | 4 +- seaborn/linearmodels.py | 1466 +---------------- seaborn/regression.py | 1462 ++++++++++++++++ seaborn/tests/test_axisgrid.py | 2 +- ...est_linearmodels.py => test_regression.py} | 4 +- 6 files changed, 1472 insertions(+), 1468 deletions(-) create mode 100644 seaborn/regression.py rename seaborn/tests/{test_linearmodels.py => test_regression.py} (99%) diff --git a/seaborn/__init__.py b/seaborn/__init__.py index c788470253..27ac4fcc09 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -6,7 +6,7 @@ from .rcmod import * from .utils import * from .palettes import * -from .linearmodels import * +from .regression import * from .categorical import * from .distributions import * from .timeseries import * diff --git a/seaborn/distributions.py b/seaborn/distributions.py index bb80b50aed..eb5996bb58 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -863,7 +863,7 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, elif kind.startswith("reg"): - from .linearmodels import regplot + from .regression import regplot marginal_kws.setdefault("color", color) grid.plot_marginals(distplot, **marginal_kws) @@ -873,7 +873,7 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, elif kind.startswith("resid"): - from .linearmodels import residplot + from .regression import residplot joint_kws.setdefault("color", color) grid.plot_joint(residplot, **joint_kws) diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index 61ec621ad0..4b0493575e 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -1,1462 +1,6 @@ -"""Plotting functions for linear models (broadly construed).""" -from __future__ import division -import copy -import itertools -from textwrap import dedent -import numpy as np -import pandas as pd -from scipy.spatial import distance -import matplotlib as mpl -import matplotlib.pyplot as plt - import warnings - -try: - import statsmodels - assert statsmodels - _has_statsmodels = True -except ImportError: - _has_statsmodels = False - -from .external.six import string_types -from .external.six.moves import range - -from . import utils -from . import algorithms as algo -from .palettes import color_palette -from .axisgrid import FacetGrid, PairGrid, _facet_docs -from .distributions import kdeplot - - -__all__ = ["lmplot", "regplot", "residplot", - "coefplot", "interactplot", - "pairplot"] - - -class _LinearPlotter(object): - """Base class for plotting relational data in tidy format. - - To get anything useful done you'll have to inherit from this, but setup - code that can be abstracted out should be put here. - - """ - def establish_variables(self, data, **kws): - """Extract variables from data or use directly.""" - self.data = data - - # Validate the inputs - any_strings = any([isinstance(v, string_types) for v in kws.values()]) - if any_strings and data is None: - raise ValueError("Must pass `data` if using named variables.") - - # Set the variables - for var, val in kws.items(): - if isinstance(val, string_types): - setattr(self, var, data[val]) - else: - setattr(self, var, val) - - def dropna(self, *vars): - """Remove observations with missing data.""" - vals = [getattr(self, var) for var in vars] - vals = [v for v in vals if v is not None] - not_na = np.all(np.column_stack([pd.notnull(v) for v in vals]), axis=1) - for var in vars: - val = getattr(self, var) - if val is not None: - setattr(self, var, val[not_na]) - - def plot(self, ax): - raise NotImplementedError - - -class _RegressionPlotter(_LinearPlotter): - """Plotter for numeric independent variables with regression model. - - This does the computations and drawing for the `regplot` function, and - is thus also used indirectly by `lmplot`. - """ - def __init__(self, x, y, data=None, x_estimator=None, x_bins=None, - x_ci="ci", scatter=True, fit_reg=True, ci=95, n_boot=1000, - units=None, order=1, logistic=False, lowess=False, - robust=False, logx=False, x_partial=None, y_partial=None, - truncate=False, dropna=True, x_jitter=None, y_jitter=None, - color=None, label=None): - - # Set member attributes - self.x_estimator = x_estimator - self.ci = ci - self.x_ci = ci if x_ci == "ci" else x_ci - self.n_boot = n_boot - self.scatter = scatter - self.fit_reg = fit_reg - self.order = order - self.logistic = logistic - self.lowess = lowess - self.robust = robust - self.logx = logx - self.truncate = truncate - self.x_jitter = x_jitter - self.y_jitter = y_jitter - self.color = color - self.label = label - - # Validate the regression options: - if sum((order > 1, logistic, robust, lowess, logx)) > 1: - raise ValueError("Mutually exclusive regression options.") - - # Extract the data vals from the arguments or passed dataframe - self.establish_variables(data, x=x, y=y, units=units, - x_partial=x_partial, y_partial=y_partial) - - # Drop null observations - if dropna: - self.dropna("x", "y", "units", "x_partial", "y_partial") - - # Regress nuisance variables out of the data - if self.x_partial is not None: - self.x = self.regress_out(self.x, self.x_partial) - if self.y_partial is not None: - self.y = self.regress_out(self.y, self.y_partial) - - # Possibly bin the predictor variable, which implies a point estimate - if x_bins is not None: - self.x_estimator = np.mean if x_estimator is None else x_estimator - x_discrete, x_bins = self.bin_predictor(x_bins) - self.x_discrete = x_discrete - else: - self.x_discrete = self.x - - # Save the range of the x variable for the grid later - self.x_range = self.x.min(), self.x.max() - - @property - def scatter_data(self): - """Data where each observation is a point.""" - x_j = self.x_jitter - if x_j is None: - x = self.x - else: - x = self.x + np.random.uniform(-x_j, x_j, len(self.x)) - - y_j = self.y_jitter - if y_j is None: - y = self.y - else: - y = self.y + np.random.uniform(-y_j, y_j, len(self.y)) - - return x, y - - @property - def estimate_data(self): - """Data with a point estimate and CI for each discrete x value.""" - x, y = self.x_discrete, self.y - vals = sorted(np.unique(x)) - points, cis = [], [] - - for val in vals: - - # Get the point estimate of the y variable - _y = y[x == val] - est = self.x_estimator(_y) - points.append(est) - - # Compute the confidence interval for this estimate - if self.x_ci is None: - cis.append(None) - else: - units = None - if self.x_ci == "sd": - sd = np.std(_y) - _ci = est - sd, est + sd - else: - if self.units is not None: - units = self.units[x == val] - boots = algo.bootstrap(_y, func=self.x_estimator, - n_boot=self.n_boot, units=units) - _ci = utils.ci(boots, self.x_ci) - cis.append(_ci) - - return vals, points, cis - - def fit_regression(self, ax=None, x_range=None, grid=None): - """Fit the regression model.""" - # Create the grid for the regression - if grid is None: - if self.truncate: - x_min, x_max = self.x_range - else: - if ax is None: - x_min, x_max = x_range - else: - x_min, x_max = ax.get_xlim() - grid = np.linspace(x_min, x_max, 100) - ci = self.ci - - # Fit the regression - if self.order > 1: - yhat, yhat_boots = self.fit_poly(grid, self.order) - elif self.logistic: - from statsmodels.genmod.generalized_linear_model import GLM - from statsmodels.genmod.families import Binomial - yhat, yhat_boots = self.fit_statsmodels(grid, GLM, - family=Binomial()) - elif self.lowess: - ci = None - grid, yhat = self.fit_lowess() - elif self.robust: - from statsmodels.robust.robust_linear_model import RLM - yhat, yhat_boots = self.fit_statsmodels(grid, RLM) - elif self.logx: - yhat, yhat_boots = self.fit_logx(grid) - else: - yhat, yhat_boots = self.fit_fast(grid) - - # Compute the confidence interval at each grid point - if ci is None: - err_bands = None - else: - err_bands = utils.ci(yhat_boots, ci, axis=0) - - return grid, yhat, err_bands - - def fit_fast(self, grid): - """Low-level regression and prediction using linear algebra.""" - X, y = np.c_[np.ones(len(self.x)), self.x], self.y - grid = np.c_[np.ones(len(grid)), grid] - reg_func = lambda _x, _y: np.linalg.pinv(_x).dot(_y) - yhat = grid.dot(reg_func(X, y)) - if self.ci is None: - return yhat, None - - beta_boots = algo.bootstrap(X, y, func=reg_func, - n_boot=self.n_boot, units=self.units).T - yhat_boots = grid.dot(beta_boots).T - return yhat, yhat_boots - - def fit_poly(self, grid, order): - """Regression using numpy polyfit for higher-order trends.""" - x, y = self.x, self.y - reg_func = lambda _x, _y: np.polyval(np.polyfit(_x, _y, order), grid) - yhat = reg_func(x, y) - if self.ci is None: - return yhat, None - - yhat_boots = algo.bootstrap(x, y, func=reg_func, - n_boot=self.n_boot, units=self.units) - return yhat, yhat_boots - - def fit_statsmodels(self, grid, model, **kwargs): - """More general regression function using statsmodels objects.""" - X, y = np.c_[np.ones(len(self.x)), self.x], self.y - grid = np.c_[np.ones(len(grid)), grid] - import statsmodels.genmod.generalized_linear_model as glm - - def reg_func(_x, _y): - try: - yhat = model(_y, _x, **kwargs).fit().predict(grid) - except glm.PerfectSeparationError: - yhat = np.empty(len(grid)) - yhat.fill(np.nan) - return yhat - - yhat = reg_func(X, y) - if self.ci is None: - return yhat, None - - yhat_boots = algo.bootstrap(X, y, func=reg_func, - n_boot=self.n_boot, units=self.units) - return yhat, yhat_boots - - def fit_lowess(self): - """Fit a locally-weighted regression, which returns its own grid.""" - from statsmodels.nonparametric.smoothers_lowess import lowess - grid, yhat = lowess(self.y, self.x).T - return grid, yhat - - def fit_logx(self, grid): - """Fit the model in log-space.""" - X, y = np.c_[np.ones(len(self.x)), self.x], self.y - grid = np.c_[np.ones(len(grid)), np.log(grid)] - - def reg_func(_x, _y): - _x = np.c_[_x[:, 0], np.log(_x[:, 1])] - return np.linalg.pinv(_x).dot(_y) - - yhat = grid.dot(reg_func(X, y)) - if self.ci is None: - return yhat, None - - beta_boots = algo.bootstrap(X, y, func=reg_func, - n_boot=self.n_boot, units=self.units).T - yhat_boots = grid.dot(beta_boots).T - return yhat, yhat_boots - - def bin_predictor(self, bins): - """Discretize a predictor by assigning value to closest bin.""" - x = self.x - if np.isscalar(bins): - percentiles = np.linspace(0, 100, bins + 2)[1:-1] - bins = np.c_[utils.percentiles(x, percentiles)] - else: - bins = np.c_[np.ravel(bins)] - - dist = distance.cdist(np.c_[x], bins) - x_binned = bins[np.argmin(dist, axis=1)].ravel() - - return x_binned, bins.ravel() - - def regress_out(self, a, b): - """Regress b from a keeping a's original mean.""" - a_mean = a.mean() - a = a - a_mean - b = b - b.mean() - b = np.c_[b] - a_prime = a - b.dot(np.linalg.pinv(b).dot(a)) - return (a_prime + a_mean).reshape(a.shape) - - def plot(self, ax, scatter_kws, line_kws): - """Draw the full plot.""" - # Insert the plot label into the correct set of keyword arguments - if self.scatter: - scatter_kws["label"] = self.label - else: - line_kws["label"] = self.label - - # Use the current color cycle state as a default - if self.color is None: - lines, = plt.plot(self.x.mean(), self.y.mean()) - color = lines.get_color() - lines.remove() - else: - color = self.color - - # Ensure that color is hex to avoid matplotlib weidness - color = mpl.colors.rgb2hex(mpl.colors.colorConverter.to_rgb(color)) - - # Let color in keyword arguments override overall plot color - scatter_kws.setdefault("color", color) - line_kws.setdefault("color", color) - - # Draw the constituent plots - if self.scatter: - self.scatterplot(ax, scatter_kws) - if self.fit_reg: - self.lineplot(ax, line_kws) - - # Label the axes - if hasattr(self.x, "name"): - ax.set_xlabel(self.x.name) - if hasattr(self.y, "name"): - ax.set_ylabel(self.y.name) - - def scatterplot(self, ax, kws): - """Draw the data.""" - # Treat the line-based markers specially, explicitly setting larger - # linewidth than is provided by the seaborn style defaults. - # This would ideally be handled better in matplotlib (i.e., distinguish - # between edgewidth for solid glyphs and linewidth for line glyphs - # but this should do for now. - line_markers = ["1", "2", "3", "4", "+", "x", "|", "_"] - if self.x_estimator is None: - if "marker" in kws and kws["marker"] in line_markers: - lw = mpl.rcParams["lines.linewidth"] - else: - lw = mpl.rcParams["lines.markeredgewidth"] - kws.setdefault("linewidths", lw) - - if not hasattr(kws['color'], 'shape') or kws['color'].shape[1] < 4: - kws.setdefault("alpha", .8) - - x, y = self.scatter_data - ax.scatter(x, y, **kws) - else: - # TODO abstraction - ci_kws = {"color": kws["color"]} - ci_kws["linewidth"] = mpl.rcParams["lines.linewidth"] * 1.75 - kws.setdefault("s", 50) - - xs, ys, cis = self.estimate_data - if [ci for ci in cis if ci is not None]: - for x, ci in zip(xs, cis): - ax.plot([x, x], ci, **ci_kws) - ax.scatter(xs, ys, **kws) - - def lineplot(self, ax, kws): - """Draw the model.""" - xlim = ax.get_xlim() - - # Fit the regression model - grid, yhat, err_bands = self.fit_regression(ax) - - # Get set default aesthetics - fill_color = kws["color"] - lw = kws.pop("lw", mpl.rcParams["lines.linewidth"] * 1.5) - kws.setdefault("linewidth", lw) - - # Draw the regression line and confidence interval - ax.plot(grid, yhat, **kws) - if err_bands is not None: - ax.fill_between(grid, *err_bands, facecolor=fill_color, alpha=.15) - ax.set_xlim(*xlim) - - -_regression_docs = dict( - - model_api=dedent("""\ - There are a number of mutually exclusive options for estimating the - regression model: ``order``, ``logistic``, ``lowess``, ``robust``, and - ``logx``. See the parameter docs for more information on these options.\ - """), - - regplot_vs_lmplot=dedent("""\ - Understanding the difference between :func:`regplot` and :func:`lmplot` can - be a bit tricky. In fact, they are closely related, as :func:`lmplot` uses - :func:`regplot` internally and takes most of its parameters. However, - :func:`regplot` is an axes-level function, so it draws directly onto an - axes (either the currently active axes or the one provided by the ``ax`` - parameter), while :func:`lmplot` is a figure-level function and creates its - own figure, which is managed through a :class:`FacetGrid`. This has a few - consequences, namely that :func:`regplot` can happily coexist in a figure - with other kinds of plots and will follow the global matplotlib color - cycle. In contrast, :func:`lmplot` needs to occupy an entire figure, and - the size and color cycle are controlled through function parameters, - ignoring the global defaults.\ - """), - - x_estimator=dedent("""\ - x_estimator : callable that maps vector -> scalar, optional - Apply this function to each unique value of ``x`` and plot the - resulting estimate. This is useful when ``x`` is a discrete variable. - If ``x_ci`` is given, this estimate will be bootstrapped and a - confidence interval will be drawn.\ - """), - x_bins=dedent("""\ - x_bins : int or vector, optional - Bin the ``x`` variable into discrete bins and then estimate the central - tendency and a confidence interval. This binning only influences how - the scatterplot is drawn; the regression is still fit to the original - data. This parameter is interpreted either as the number of - evenly-sized (not necessary spaced) bins or the positions of the bin - centers. When this parameter is used, it implies that the default of - ``x_estimator`` is ``numpy.mean``.\ - """), - x_ci=dedent("""\ - x_ci : "ci", "sd", int in [0, 100] or None, optional - Size of the confidence interval used when plotting a central tendency - for discrete values of ``x``. If ``"ci"``, defer to the value of the - ``ci`` parameter. If ``"sd"``, skip bootstrappig and show the standard - deviation of the observations in each bin.\ - """), - scatter=dedent("""\ - scatter : bool, optional - If ``True``, draw a scatterplot with the underlying observations (or - the ``x_estimator`` values).\ - """), - fit_reg=dedent("""\ - fit_reg : bool, optional - If ``True``, estimate and plot a regression model relating the ``x`` - and ``y`` variables.\ - """), - ci=dedent("""\ - ci : int in [0, 100] or None, optional - Size of the confidence interval for the regression estimate. This will - be drawn using translucent bands around the regression line. The - confidence interval is estimated using a bootstrap; for large - datasets, it may be advisable to avoid that computation by setting - this parameter to None.\ - """), - n_boot=dedent("""\ - n_boot : int, optional - Number of bootstrap resamples used to estimate the ``ci``. The default - value attempts to balance time and stability; you may want to increase - this value for "final" versions of plots.\ - """), - units=dedent("""\ - units : variable name in ``data``, optional - If the ``x`` and ``y`` observations are nested within sampling units, - those can be specified here. This will be taken into account when - computing the confidence intervals by performing a multilevel bootstrap - that resamples both units and observations (within unit). This does not - otherwise influence how the regression is estimated or drawn.\ - """), - order=dedent("""\ - order : int, optional - If ``order`` is greater than 1, use ``numpy.polyfit`` to estimate a - polynomial regression.\ - """), - logistic=dedent("""\ - logistic : bool, optional - If ``True``, assume that ``y`` is a binary variable and use - ``statsmodels`` to estimate a logistic regression model. Note that this - is substantially more computationally intensive than linear regression, - so you may wish to decrease the number of bootstrap resamples - (``n_boot``) or set ``ci`` to None.\ - """), - lowess=dedent("""\ - lowess : bool, optional - If ``True``, use ``statsmodels`` to estimate a nonparametric lowess - model (locally weighted linear regression). Note that confidence - intervals cannot currently be drawn for this kind of model.\ - """), - robust=dedent("""\ - robust : bool, optional - If ``True``, use ``statsmodels`` to estimate a robust regression. This - will de-weight outliers. Note that this is substantially more - computationally intensive than standard linear regression, so you may - wish to decrease the number of bootstrap resamples (``n_boot``) or set - ``ci`` to None.\ - """), - logx=dedent("""\ - logx : bool, optional - If ``True``, estimate a linear regression of the form y ~ log(x), but - plot the scatterplot and regression model in the input space. Note that - ``x`` must be positive for this to work.\ - """), - xy_partial=dedent("""\ - {x,y}_partial : strings in ``data`` or matrices - Confounding variables to regress out of the ``x`` or ``y`` variables - before plotting.\ - """), - truncate=dedent("""\ - truncate : bool, optional - By default, the regression line is drawn to fill the x axis limits - after the scatterplot is drawn. If ``truncate`` is ``True``, it will - instead by bounded by the data limits.\ - """), - xy_jitter=dedent("""\ - {x,y}_jitter : floats, optional - Add uniform random noise of this size to either the ``x`` or ``y`` - variables. The noise is added to a copy of the data after fitting the - regression, and only influences the look of the scatterplot. This can - be helpful when plotting variables that take discrete values.\ - """), - scatter_line_kws=dedent("""\ - {scatter,line}_kws : dictionaries - Additional keyword arguments to pass to ``plt.scatter`` and - ``plt.plot``.\ - """), - ) -_regression_docs.update(_facet_docs) - - -def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, - col_wrap=None, size=5, aspect=1, markers="o", sharex=True, - sharey=True, hue_order=None, col_order=None, row_order=None, - legend=True, legend_out=True, x_estimator=None, x_bins=None, - x_ci="ci", scatter=True, fit_reg=True, ci=95, n_boot=1000, - units=None, order=1, logistic=False, lowess=False, robust=False, - logx=False, x_partial=None, y_partial=None, truncate=False, - x_jitter=None, y_jitter=None, scatter_kws=None, line_kws=None): - - # Reduce the dataframe to only needed columns - need_cols = [x, y, hue, col, row, units, x_partial, y_partial] - cols = np.unique([a for a in need_cols if a is not None]).tolist() - data = data[cols] - - # Initialize the grid - facets = FacetGrid(data, row, col, hue, palette=palette, - row_order=row_order, col_order=col_order, - hue_order=hue_order, size=size, aspect=aspect, - col_wrap=col_wrap, sharex=sharex, sharey=sharey, - legend_out=legend_out) - - # Add the markers here as FacetGrid has figured out how many levels of the - # hue variable are needed and we don't want to duplicate that process - if facets.hue_names is None: - n_markers = 1 - else: - n_markers = len(facets.hue_names) - if not isinstance(markers, list): - markers = [markers] * n_markers - if len(markers) != n_markers: - raise ValueError(("markers must be a singeton or a list of markers " - "for each level of the hue variable")) - facets.hue_kws = {"marker": markers} - - # Hack to set the x limits properly, which needs to happen here - # because the extent of the regression estimate is determined - # by the limits of the plot - if sharex: - for ax in facets.axes.flat: - ax.scatter(data[x], np.ones(len(data)) * data[y].mean()).remove() - - # Draw the regression plot on each facet - regplot_kws = dict( - x_estimator=x_estimator, x_bins=x_bins, x_ci=x_ci, - scatter=scatter, fit_reg=fit_reg, ci=ci, n_boot=n_boot, units=units, - order=order, logistic=logistic, lowess=lowess, robust=robust, - logx=logx, x_partial=x_partial, y_partial=y_partial, truncate=truncate, - x_jitter=x_jitter, y_jitter=y_jitter, - scatter_kws=scatter_kws, line_kws=line_kws, - ) - facets.map_dataframe(regplot, x, y, **regplot_kws) - - # Add a legend - if legend and (hue is not None) and (hue not in [col, row]): - facets.add_legend() - return facets - - -lmplot.__doc__ = dedent("""\ - Plot data and regression model fits across a FacetGrid. - - This function combines :func:`regplot` and :class:`FacetGrid`. It is - intended as a convenient interface to fit regression models across - conditional subsets of a dataset. - - When thinking about how to assign variables to different facets, a general - rule is that it makes sense to use ``hue`` for the most important - comparison, followed by ``col`` and ``row``. However, always think about - your particular dataset and the goals of the visualization you are - creating. - - {model_api} - - The parameters to this function span most of the options in - :class:`FacetGrid`, although there may be occasional cases where you will - want to use that class and :func:`regplot` directly. - - Parameters - ---------- - x, y : strings, optional - Input variables; these should be column names in ``data``. - {data} - hue, col, row : strings - Variables that define subsets of the data, which will be drawn on - separate facets in the grid. See the ``*_order`` parameters to control - the order of levels of this variable. - {palette} - {col_wrap} - {size} - {aspect} - markers : matplotlib marker code or list of marker codes, optional - Markers for the scatterplot. If a list, each marker in the list will be - used for each level of the ``hue`` variable. - {share_xy} - {{hue,col,row}}_order : lists, optional - Order for the levels of the faceting variables. By default, this will - be the order that the levels appear in ``data`` or, if the variables - are pandas categoricals, the category order. - legend : bool, optional - If ``True`` and there is a ``hue`` variable, add a legend. - {legend_out} - {x_estimator} - {x_bins} - {x_ci} - {scatter} - {fit_reg} - {ci} - {n_boot} - {units} - {order} - {logistic} - {lowess} - {robust} - {logx} - {xy_partial} - {truncate} - {xy_jitter} - {scatter_line_kws} - - See Also - -------- - regplot : Plot data and a conditional model fit. - FacetGrid : Subplot grid for plotting conditional relationships. - pairplot : Combine :func:`regplot` and :class:`PairGrid` (when used with - ``kind="reg"``). - - Notes - ----- - - {regplot_vs_lmplot} - - Examples - -------- - - These examples focus on basic regression model plots to exhibit the - various faceting options; see the :func:`regplot` docs for demonstrations - of the other options for plotting the data and models. There are also - other examples for how to manipulate plot using the returned object on - the :class:`FacetGrid` docs. - - Plot a simple linear relationship between two variables: - - .. plot:: - :context: close-figs - - >>> import seaborn as sns; sns.set(color_codes=True) - >>> tips = sns.load_dataset("tips") - >>> g = sns.lmplot(x="total_bill", y="tip", data=tips) - - Condition on a third variable and plot the levels in different colors: - - .. plot:: - :context: close-figs - - >>> g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips) - - Use different markers as well as colors so the plot will reproduce to - black-and-white more easily: - - .. plot:: - :context: close-figs - - >>> g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips, - ... markers=["o", "x"]) - - Use a different color palette: - - .. plot:: - :context: close-figs - - >>> g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips, - ... palette="Set1") - - Map ``hue`` levels to colors with a dictionary: - - .. plot:: - :context: close-figs - - >>> g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips, - ... palette=dict(Yes="g", No="m")) - - Plot the levels of the third variable across different columns: - - .. plot:: - :context: close-figs - - >>> g = sns.lmplot(x="total_bill", y="tip", col="smoker", data=tips) - - Change the size and aspect ratio of the facets: - - .. plot:: - :context: close-figs - - >>> g = sns.lmplot(x="size", y="total_bill", hue="day", col="day", - ... data=tips, aspect=.4, x_jitter=.1) - - Wrap the levels of the column variable into multiple rows: - - .. plot:: - :context: close-figs - - >>> g = sns.lmplot(x="total_bill", y="tip", col="day", hue="day", - ... data=tips, col_wrap=2, size=3) - - Condition on two variables to make a full grid: - - .. plot:: - :context: close-figs - - >>> g = sns.lmplot(x="total_bill", y="tip", row="sex", col="time", - ... data=tips, size=3) - - Use methods on the returned :class:`FacetGrid` instance to further tweak - the plot: - - .. plot:: - :context: close-figs - - >>> g = sns.lmplot(x="total_bill", y="tip", row="sex", col="time", - ... data=tips, size=3) - >>> g = (g.set_axis_labels("Total bill (US Dollars)", "Tip") - ... .set(xlim=(0, 60), ylim=(0, 12), - ... xticks=[10, 30, 50], yticks=[2, 6, 10]) - ... .fig.subplots_adjust(wspace=.02)) - - - - """).format(**_regression_docs) - - -def regplot(x, y, data=None, x_estimator=None, x_bins=None, x_ci="ci", - scatter=True, fit_reg=True, ci=95, n_boot=1000, units=None, - order=1, logistic=False, lowess=False, robust=False, - logx=False, x_partial=None, y_partial=None, - truncate=False, dropna=True, x_jitter=None, y_jitter=None, - label=None, color=None, marker="o", - scatter_kws=None, line_kws=None, ax=None): - - plotter = _RegressionPlotter(x, y, data, x_estimator, x_bins, x_ci, - scatter, fit_reg, ci, n_boot, units, - order, logistic, lowess, robust, logx, - x_partial, y_partial, truncate, dropna, - x_jitter, y_jitter, color, label) - - if ax is None: - ax = plt.gca() - - scatter_kws = {} if scatter_kws is None else copy.copy(scatter_kws) - scatter_kws["marker"] = marker - line_kws = {} if line_kws is None else copy.copy(line_kws) - plotter.plot(ax, scatter_kws, line_kws) - return ax - -regplot.__doc__ = dedent("""\ - Plot data and a linear regression model fit. - - {model_api} - - Parameters - ---------- - x, y: string, series, or vector array - Input variables. If strings, these should correspond with column names - in ``data``. When pandas objects are used, axes will be labeled with - the series name. - {data} - {x_estimator} - {x_bins} - {x_ci} - {scatter} - {fit_reg} - {ci} - {n_boot} - {units} - {order} - {logistic} - {lowess} - {robust} - {logx} - {xy_partial} - {truncate} - {xy_jitter} - label : string - Label to apply to ether the scatterplot or regression line (if - ``scatter`` is ``False``) for use in a legend. - color : matplotlib color - Color to apply to all plot elements; will be superseded by colors - passed in ``scatter_kws`` or ``line_kws``. - marker : matplotlib marker code - Marker to use for the scatterplot glyphs. - {scatter_line_kws} - ax : matplotlib Axes, optional - Axes object to draw the plot onto, otherwise uses the current Axes. - - Returns - ------- - ax : matplotlib Axes - The Axes object containing the plot. - - See Also - -------- - lmplot : Combine :func:`regplot` and :class:`FacetGrid` to plot multiple - linear relationships in a dataset. - jointplot : Combine :func:`regplot` and :class:`JointGrid` (when used with - ``kind="reg"``). - pairplot : Combine :func:`regplot` and :class:`PairGrid` (when used with - ``kind="reg"``). - residplot : Plot the residuals of a linear regression model. - interactplot : Plot a two-way interaction between continuous variables - - Notes - ----- - - {regplot_vs_lmplot} - - - It's also easy to combine combine :func:`regplot` and :class:`JointGrid` or - :class:`PairGrid` through the :func:`jointplot` and :func:`pairplot` - functions, although these do not directly accept all of :func:`regplot`'s - parameters. - - Examples - -------- - - Plot the relationship between two variables in a DataFrame: - - .. plot:: - :context: close-figs - - >>> import seaborn as sns; sns.set(color_codes=True) - >>> tips = sns.load_dataset("tips") - >>> ax = sns.regplot(x="total_bill", y="tip", data=tips) - - Plot with two variables defined as numpy arrays; use a different color: - - .. plot:: - :context: close-figs - - >>> import numpy as np; np.random.seed(8) - >>> mean, cov = [4, 6], [(1.5, .7), (.7, 1)] - >>> x, y = np.random.multivariate_normal(mean, cov, 80).T - >>> ax = sns.regplot(x=x, y=y, color="g") - - Plot with two variables defined as pandas Series; use a different marker: - - .. plot:: - :context: close-figs - - >>> import pandas as pd - >>> x, y = pd.Series(x, name="x_var"), pd.Series(y, name="y_var") - >>> ax = sns.regplot(x=x, y=y, marker="+") - - Use a 68% confidence interval, which corresponds with the standard error - of the estimate: - - .. plot:: - :context: close-figs - - >>> ax = sns.regplot(x=x, y=y, ci=68) - - Plot with a discrete ``x`` variable and add some jitter: - - .. plot:: - :context: close-figs - - >>> ax = sns.regplot(x="size", y="total_bill", data=tips, x_jitter=.1) - - Plot with a discrete ``x`` variable showing means and confidence intervals - for unique values: - - .. plot:: - :context: close-figs - - >>> ax = sns.regplot(x="size", y="total_bill", data=tips, - ... x_estimator=np.mean) - - Plot with a continuous variable divided into discrete bins: - - .. plot:: - :context: close-figs - - >>> ax = sns.regplot(x=x, y=y, x_bins=4) - - Fit a higher-order polynomial regression and truncate the model prediction: - - .. plot:: - :context: close-figs - - >>> ans = sns.load_dataset("anscombe") - >>> ax = sns.regplot(x="x", y="y", data=ans.loc[ans.dataset == "II"], - ... scatter_kws={{"s": 80}}, - ... order=2, ci=None, truncate=True) - - Fit a robust regression and don't plot a confidence interval: - - .. plot:: - :context: close-figs - - >>> ax = sns.regplot(x="x", y="y", data=ans.loc[ans.dataset == "III"], - ... scatter_kws={{"s": 80}}, - ... robust=True, ci=None) - - Fit a logistic regression; jitter the y variable and use fewer bootstrap - iterations: - - .. plot:: - :context: close-figs - - >>> tips["big_tip"] = (tips.tip / tips.total_bill) > .175 - >>> ax = sns.regplot(x="total_bill", y="big_tip", data=tips, - ... logistic=True, n_boot=500, y_jitter=.03) - - Fit the regression model using log(x) and truncate the model prediction: - - .. plot:: - :context: close-figs - - >>> ax = sns.regplot(x="size", y="total_bill", data=tips, - ... x_estimator=np.mean, logx=True, truncate=True) - - """).format(**_regression_docs) - - -def residplot(x, y, data=None, lowess=False, x_partial=None, y_partial=None, - order=1, robust=False, dropna=True, label=None, color=None, - scatter_kws=None, line_kws=None, ax=None): - """Plot the residuals of a linear regression. - - This function will regress y on x (possibly as a robust or polynomial - regression) and then draw a scatterplot of the residuals. You can - optionally fit a lowess smoother to the residual plot, which can - help in determining if there is structure to the residuals. - - Parameters - ---------- - x : vector or string - Data or column name in `data` for the predictor variable. - y : vector or string - Data or column name in `data` for the response variable. - data : DataFrame, optional - DataFrame to use if `x` and `y` are column names. - lowess : boolean, optional - Fit a lowess smoother to the residual scatterplot. - {x, y}_partial : matrix or string(s) , optional - Matrix with same first dimension as `x`, or column name(s) in `data`. - These variables are treated as confounding and are removed from - the `x` or `y` variables before plotting. - order : int, optional - Order of the polynomial to fit when calculating the residuals. - robust : boolean, optional - Fit a robust linear regression when calculating the residuals. - dropna : boolean, optional - If True, ignore observations with missing data when fitting and - plotting. - label : string, optional - Label that will be used in any plot legends. - color : matplotlib color, optional - Color to use for all elements of the plot. - {scatter, line}_kws : dictionaries, optional - Additional keyword arguments passed to scatter() and plot() for drawing - the components of the plot. - ax : matplotlib axis, optional - Plot into this axis, otherwise grab the current axis or make a new - one if not existing. - - Returns - ------- - ax: matplotlib axes - Axes with the regression plot. - - See Also - -------- - regplot : Plot a simple linear regression model. - jointplot (with kind="resid"): Draw a residplot with univariate - marginal distrbutions. - - """ - plotter = _RegressionPlotter(x, y, data, ci=None, - order=order, robust=robust, - x_partial=x_partial, y_partial=y_partial, - dropna=dropna, color=color, label=label) - - if ax is None: - ax = plt.gca() - - # Calculate the residual from a linear regression - _, yhat, _ = plotter.fit_regression(grid=plotter.x) - plotter.y = plotter.y - yhat - - # Set the regression option on the plotter - if lowess: - plotter.lowess = True - else: - plotter.fit_reg = False - - # Plot a horizontal line at 0 - ax.axhline(0, ls=":", c=".2") - - # Draw the scatterplot - scatter_kws = {} if scatter_kws is None else scatter_kws - line_kws = {} if line_kws is None else line_kws - plotter.plot(ax, scatter_kws, line_kws) - return ax - - -def coefplot(formula, data, groupby=None, intercept=False, ci=95, - palette="husl"): - """Plot the coefficients from a linear model. - - Parameters - ---------- - formula : string - patsy formula for ols model - data : dataframe - data for the plot; formula terms must appear in columns - groupby : grouping object, optional - object to group data with to fit conditional models - intercept : bool, optional - if False, strips the intercept term before plotting - ci : float, optional - size of confidence intervals - palette : seaborn color palette, optional - palette for the horizonal plots - - """ - if not _has_statsmodels: - raise ImportError("The `coefplot` function requires statsmodels") - import statsmodels.formula.api as sf - - alpha = 1 - ci / 100 - if groupby is None: - coefs = sf.ols(formula, data).fit().params - cis = sf.ols(formula, data).fit().conf_int(alpha) - else: - grouped = data.groupby(groupby) - coefs = grouped.apply(lambda d: sf.ols(formula, d).fit().params).T - cis = grouped.apply(lambda d: sf.ols(formula, d).fit().conf_int(alpha)) - - # Possibly ignore the intercept - if not intercept: - coefs = coefs.ix[1:] - - n_terms = len(coefs) - - # Plot seperately depending on groupby - w, h = mpl.rcParams["figure.figsize"] - hsize = lambda n: n * (h / 2) - wsize = lambda n: n * (w / (4 * (n / 5))) - if groupby is None: - colors = itertools.cycle(color_palette(palette, n_terms)) - f, ax = plt.subplots(1, 1, figsize=(wsize(n_terms), hsize(1))) - for i, term in enumerate(coefs.index): - color = next(colors) - low, high = cis.ix[term] - ax.plot([i, i], [low, high], c=color, - solid_capstyle="round", lw=2.5) - ax.plot(i, coefs.ix[term], "o", c=color, ms=8) - ax.set_xlim(-.5, n_terms - .5) - ax.axhline(0, ls="--", c="dimgray") - ax.set_xticks(range(n_terms)) - ax.set_xticklabels(coefs.index) - - else: - n_groups = len(coefs.columns) - f, axes = plt.subplots(n_terms, 1, sharex=True, - figsize=(wsize(n_groups), hsize(n_terms))) - if n_terms == 1: - axes = [axes] - colors = itertools.cycle(color_palette(palette, n_groups)) - for ax, term in zip(axes, coefs.index): - for i, group in enumerate(coefs.columns): - color = next(colors) - low, high = cis.ix[(group, term)] - ax.plot([i, i], [low, high], c=color, - solid_capstyle="round", lw=2.5) - ax.plot(i, coefs.loc[term, group], "o", c=color, ms=8) - ax.set_xlim(-.5, n_groups - .5) - ax.axhline(0, ls="--", c="dimgray") - ax.set_title(term) - ax.set_xlabel(groupby) - ax.set_xticks(range(n_groups)) - ax.set_xticklabels(coefs.columns) - - -def interactplot(x1, x2, y, data=None, filled=False, cmap="RdBu_r", - colorbar=True, levels=30, logistic=False, - contour_kws=None, scatter_kws=None, ax=None, **kwargs): - """Visualize a continuous two-way interaction with a contour plot. - - Parameters - ---------- - x1, x2, y, strings or array-like - Either the two independent variables and the dependent variable, - or keys to extract them from `data` - data : DataFrame - Pandas DataFrame with the data in the columns. - filled : bool - Whether to plot with filled or unfilled contours - cmap : matplotlib colormap - Colormap to represent yhat in the countour plot. - colorbar : bool - Whether to draw the colorbar for interpreting the color values. - levels : int or sequence - Number or position of contour plot levels. - logistic : bool - Fit a logistic regression model instead of linear regression. - contour_kws : dictionary - Keyword arguments for contour[f](). - scatter_kws : dictionary - Keyword arguments for plot(). - ax : matplotlib axis - Axis to draw plot in. - - Returns - ------- - ax : Matplotlib axis - Axis with the contour plot. - - """ - if not _has_statsmodels: - raise ImportError("The `interactplot` function requires statsmodels") - from statsmodels.regression.linear_model import OLS - from statsmodels.genmod.generalized_linear_model import GLM - from statsmodels.genmod.families import Binomial - - # Handle the form of the data - if data is not None: - x1 = data[x1] - x2 = data[x2] - y = data[y] - if hasattr(x1, "name"): - xlabel = x1.name - else: - xlabel = None - if hasattr(x2, "name"): - ylabel = x2.name - else: - ylabel = None - if hasattr(y, "name"): - clabel = y.name - else: - clabel = None - x1 = np.asarray(x1) - x2 = np.asarray(x2) - y = np.asarray(y) - - # Initialize the scatter keyword dictionary - if scatter_kws is None: - scatter_kws = {} - if not ("color" in scatter_kws or "c" in scatter_kws): - scatter_kws["color"] = "#222222" - if "alpha" not in scatter_kws: - scatter_kws["alpha"] = 0.75 - - # Intialize the contour keyword dictionary - if contour_kws is None: - contour_kws = {} - - # Initialize the axis - if ax is None: - ax = plt.gca() - - # Plot once to let matplotlib sort out the axis limits - ax.plot(x1, x2, "o", **scatter_kws) - - # Find the plot limits - x1min, x1max = ax.get_xlim() - x2min, x2max = ax.get_ylim() - - # Make the grid for the contour plot - x1_points = np.linspace(x1min, x1max, 100) - x2_points = np.linspace(x2min, x2max, 100) - xx1, xx2 = np.meshgrid(x1_points, x2_points) - - # Fit the model with an interaction - X = np.c_[np.ones(x1.size), x1, x2, x1 * x2] - if logistic: - lm = GLM(y, X, family=Binomial()).fit() - else: - lm = OLS(y, X).fit() - - # Evaluate the model on the grid - eval = np.vectorize(lambda x1_, x2_: lm.predict([1, x1_, x2_, x1_ * x2_])) - yhat = eval(xx1, xx2) - - # Default color limits put the midpoint at mean(y) - y_bar = y.mean() - c_min = min(np.percentile(y, 2), yhat.min()) - c_max = max(np.percentile(y, 98), yhat.max()) - delta = max(c_max - y_bar, y_bar - c_min) - c_min, cmax = y_bar - delta, y_bar + delta - contour_kws.setdefault("vmin", c_min) - contour_kws.setdefault("vmax", c_max) - - # Draw the contour plot - func_name = "contourf" if filled else "contour" - contour = getattr(ax, func_name) - c = contour(xx1, xx2, yhat, levels, cmap=cmap, **contour_kws) - - # Draw the scatter again so it's visible - ax.plot(x1, x2, "o", **scatter_kws) - - # Draw a colorbar, maybe - if colorbar: - bar = plt.colorbar(c) - - # Label the axes - if xlabel is not None: - ax.set_xlabel(xlabel) - if ylabel is not None: - ax.set_ylabel(ylabel) - if clabel is not None and colorbar: - clabel = "P(%s)" % clabel if logistic else clabel - bar.set_label(clabel, labelpad=15, rotation=270) - - return ax - - -def pairplot(data, hue=None, hue_order=None, palette=None, - vars=None, x_vars=None, y_vars=None, - kind="scatter", diag_kind="hist", markers=None, - size=2.5, aspect=1, dropna=True, - plot_kws=None, diag_kws=None, grid_kws=None): - """Plot pairwise relationships in a dataset. - - By default, this function will create a grid of Axes such that each - variable in ``data`` will by shared in the y-axis across a single row and - in the x-axis across a single column. The diagonal Axes are treated - differently, drawing a plot to show the univariate distribution of the data - for the variable in that column. - - It is also possible to show a subset of variables or plot different - variables on the rows and columns. - - This is a high-level interface for :class:`PairGrid` that is intended to - make it easy to draw a few common styles. You should use :class`PairGrid` - directly if you need more flexibility. - - Parameters - ---------- - data : DataFrame - Tidy (long-form) dataframe where each column is a variable and - each row is an observation. - hue : string (variable name), optional - Variable in ``data`` to map plot aspects to different colors. - hue_order : list of strings - Order for the levels of the hue variable in the palette - palette : dict or seaborn color palette - Set of colors for mapping the ``hue`` variable. If a dict, keys - should be values in the ``hue`` variable. - vars : list of variable names, optional - Variables within ``data`` to use, otherwise use every column with - a numeric datatype. - {x, y}_vars : lists of variable names, optional - Variables within ``data`` to use separately for the rows and - columns of the figure; i.e. to make a non-square plot. - kind : {'scatter', 'reg'}, optional - Kind of plot for the non-identity relationships. - diag_kind : {'hist', 'kde'}, optional - Kind of plot for the diagonal subplots. - markers : single matplotlib marker code or list, optional - Either the marker to use for all datapoints or a list of markers with - a length the same as the number of levels in the hue variable so that - differently colored points will also have different scatterplot - markers. - size : scalar, optional - Height (in inches) of each facet. - aspect : scalar, optional - Aspect * size gives the width (in inches) of each facet. - dropna : boolean, optional - Drop missing values from the data before plotting. - {plot, diag, grid}_kws : dicts, optional - Dictionaries of keyword arguments. - - Returns - ------- - grid : PairGrid - Returns the underlying ``PairGrid`` instance for further tweaking. - - See Also - -------- - PairGrid : Subplot grid for more flexible plotting of pairwise - relationships. - - Examples - -------- - - Draw scatterplots for joint relationships and histograms for univariate - distributions: - - .. plot:: - :context: close-figs - - >>> import seaborn as sns; sns.set(style="ticks", color_codes=True) - >>> iris = sns.load_dataset("iris") - >>> g = sns.pairplot(iris) - - Show different levels of a categorical variable by the color of plot - elements: - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, hue="species") - - Use a different color palette: - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, hue="species", palette="husl") - - Use different markers for each level of the hue variable: - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, hue="species", markers=["o", "s", "D"]) - - Plot a subset of variables: - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, vars=["sepal_width", "sepal_length"]) - - Draw larger plots: - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, size=3, - ... vars=["sepal_width", "sepal_length"]) - - Plot different variables in the rows and columns: - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, - ... x_vars=["sepal_width", "sepal_length"], - ... y_vars=["petal_width", "petal_length"]) - - Use kernel density estimates for univariate plots: - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, diag_kind="kde") - - Fit linear regression models to the scatter plots: - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, kind="reg") - - Pass keyword arguments down to the underlying functions (it may be easier - to use :class:`PairGrid` directly): - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, diag_kind="kde", markers="+", - ... plot_kws=dict(s=50, edgecolor="b", linewidth=1), - ... diag_kws=dict(shade=True)) - - """ - if plot_kws is None: - plot_kws = {} - if diag_kws is None: - diag_kws = {} - if grid_kws is None: - grid_kws = {} - - # Set up the PairGrid - diag_sharey = diag_kind == "hist" - grid = PairGrid(data, vars=vars, x_vars=x_vars, y_vars=y_vars, hue=hue, - hue_order=hue_order, palette=palette, - diag_sharey=diag_sharey, - size=size, aspect=aspect, dropna=dropna, **grid_kws) - - # Add the markers here as PairGrid has figured out how many levels of the - # hue variable are needed and we don't want to duplicate that process - if markers is not None: - if grid.hue_names is None: - n_markers = 1 - else: - n_markers = len(grid.hue_names) - if not isinstance(markers, list): - markers = [markers] * n_markers - if len(markers) != n_markers: - raise ValueError(("markers must be a singeton or a list of markers" - " for each level of the hue variable")) - grid.hue_kws = {"marker": markers} - - # Maybe plot on the diagonal - if grid.square_grid: - if diag_kind == "hist": - grid.map_diag(plt.hist, **diag_kws) - elif diag_kind == "kde": - diag_kws["legend"] = False - grid.map_diag(kdeplot, **diag_kws) - - # Maybe plot on the off-diagonals - if grid.square_grid and diag_kind is not None: - plotter = grid.map_offdiag - else: - plotter = grid.map - - if kind == "scatter": - plot_kws.setdefault("edgecolor", "white") - plotter(plt.scatter, **plot_kws) - elif kind == "reg": - plotter(regplot, **plot_kws) - - # Add a legend - if hue is not None: - grid.add_legend() - - return grid +msg = ( + "The `linearmodels` module has been renamed `regression`." +) +warnings.warn(msg) +from .regression import * diff --git a/seaborn/regression.py b/seaborn/regression.py new file mode 100644 index 0000000000..61ec621ad0 --- /dev/null +++ b/seaborn/regression.py @@ -0,0 +1,1462 @@ +"""Plotting functions for linear models (broadly construed).""" +from __future__ import division +import copy +import itertools +from textwrap import dedent +import numpy as np +import pandas as pd +from scipy.spatial import distance +import matplotlib as mpl +import matplotlib.pyplot as plt + +import warnings + +try: + import statsmodels + assert statsmodels + _has_statsmodels = True +except ImportError: + _has_statsmodels = False + +from .external.six import string_types +from .external.six.moves import range + +from . import utils +from . import algorithms as algo +from .palettes import color_palette +from .axisgrid import FacetGrid, PairGrid, _facet_docs +from .distributions import kdeplot + + +__all__ = ["lmplot", "regplot", "residplot", + "coefplot", "interactplot", + "pairplot"] + + +class _LinearPlotter(object): + """Base class for plotting relational data in tidy format. + + To get anything useful done you'll have to inherit from this, but setup + code that can be abstracted out should be put here. + + """ + def establish_variables(self, data, **kws): + """Extract variables from data or use directly.""" + self.data = data + + # Validate the inputs + any_strings = any([isinstance(v, string_types) for v in kws.values()]) + if any_strings and data is None: + raise ValueError("Must pass `data` if using named variables.") + + # Set the variables + for var, val in kws.items(): + if isinstance(val, string_types): + setattr(self, var, data[val]) + else: + setattr(self, var, val) + + def dropna(self, *vars): + """Remove observations with missing data.""" + vals = [getattr(self, var) for var in vars] + vals = [v for v in vals if v is not None] + not_na = np.all(np.column_stack([pd.notnull(v) for v in vals]), axis=1) + for var in vars: + val = getattr(self, var) + if val is not None: + setattr(self, var, val[not_na]) + + def plot(self, ax): + raise NotImplementedError + + +class _RegressionPlotter(_LinearPlotter): + """Plotter for numeric independent variables with regression model. + + This does the computations and drawing for the `regplot` function, and + is thus also used indirectly by `lmplot`. + """ + def __init__(self, x, y, data=None, x_estimator=None, x_bins=None, + x_ci="ci", scatter=True, fit_reg=True, ci=95, n_boot=1000, + units=None, order=1, logistic=False, lowess=False, + robust=False, logx=False, x_partial=None, y_partial=None, + truncate=False, dropna=True, x_jitter=None, y_jitter=None, + color=None, label=None): + + # Set member attributes + self.x_estimator = x_estimator + self.ci = ci + self.x_ci = ci if x_ci == "ci" else x_ci + self.n_boot = n_boot + self.scatter = scatter + self.fit_reg = fit_reg + self.order = order + self.logistic = logistic + self.lowess = lowess + self.robust = robust + self.logx = logx + self.truncate = truncate + self.x_jitter = x_jitter + self.y_jitter = y_jitter + self.color = color + self.label = label + + # Validate the regression options: + if sum((order > 1, logistic, robust, lowess, logx)) > 1: + raise ValueError("Mutually exclusive regression options.") + + # Extract the data vals from the arguments or passed dataframe + self.establish_variables(data, x=x, y=y, units=units, + x_partial=x_partial, y_partial=y_partial) + + # Drop null observations + if dropna: + self.dropna("x", "y", "units", "x_partial", "y_partial") + + # Regress nuisance variables out of the data + if self.x_partial is not None: + self.x = self.regress_out(self.x, self.x_partial) + if self.y_partial is not None: + self.y = self.regress_out(self.y, self.y_partial) + + # Possibly bin the predictor variable, which implies a point estimate + if x_bins is not None: + self.x_estimator = np.mean if x_estimator is None else x_estimator + x_discrete, x_bins = self.bin_predictor(x_bins) + self.x_discrete = x_discrete + else: + self.x_discrete = self.x + + # Save the range of the x variable for the grid later + self.x_range = self.x.min(), self.x.max() + + @property + def scatter_data(self): + """Data where each observation is a point.""" + x_j = self.x_jitter + if x_j is None: + x = self.x + else: + x = self.x + np.random.uniform(-x_j, x_j, len(self.x)) + + y_j = self.y_jitter + if y_j is None: + y = self.y + else: + y = self.y + np.random.uniform(-y_j, y_j, len(self.y)) + + return x, y + + @property + def estimate_data(self): + """Data with a point estimate and CI for each discrete x value.""" + x, y = self.x_discrete, self.y + vals = sorted(np.unique(x)) + points, cis = [], [] + + for val in vals: + + # Get the point estimate of the y variable + _y = y[x == val] + est = self.x_estimator(_y) + points.append(est) + + # Compute the confidence interval for this estimate + if self.x_ci is None: + cis.append(None) + else: + units = None + if self.x_ci == "sd": + sd = np.std(_y) + _ci = est - sd, est + sd + else: + if self.units is not None: + units = self.units[x == val] + boots = algo.bootstrap(_y, func=self.x_estimator, + n_boot=self.n_boot, units=units) + _ci = utils.ci(boots, self.x_ci) + cis.append(_ci) + + return vals, points, cis + + def fit_regression(self, ax=None, x_range=None, grid=None): + """Fit the regression model.""" + # Create the grid for the regression + if grid is None: + if self.truncate: + x_min, x_max = self.x_range + else: + if ax is None: + x_min, x_max = x_range + else: + x_min, x_max = ax.get_xlim() + grid = np.linspace(x_min, x_max, 100) + ci = self.ci + + # Fit the regression + if self.order > 1: + yhat, yhat_boots = self.fit_poly(grid, self.order) + elif self.logistic: + from statsmodels.genmod.generalized_linear_model import GLM + from statsmodels.genmod.families import Binomial + yhat, yhat_boots = self.fit_statsmodels(grid, GLM, + family=Binomial()) + elif self.lowess: + ci = None + grid, yhat = self.fit_lowess() + elif self.robust: + from statsmodels.robust.robust_linear_model import RLM + yhat, yhat_boots = self.fit_statsmodels(grid, RLM) + elif self.logx: + yhat, yhat_boots = self.fit_logx(grid) + else: + yhat, yhat_boots = self.fit_fast(grid) + + # Compute the confidence interval at each grid point + if ci is None: + err_bands = None + else: + err_bands = utils.ci(yhat_boots, ci, axis=0) + + return grid, yhat, err_bands + + def fit_fast(self, grid): + """Low-level regression and prediction using linear algebra.""" + X, y = np.c_[np.ones(len(self.x)), self.x], self.y + grid = np.c_[np.ones(len(grid)), grid] + reg_func = lambda _x, _y: np.linalg.pinv(_x).dot(_y) + yhat = grid.dot(reg_func(X, y)) + if self.ci is None: + return yhat, None + + beta_boots = algo.bootstrap(X, y, func=reg_func, + n_boot=self.n_boot, units=self.units).T + yhat_boots = grid.dot(beta_boots).T + return yhat, yhat_boots + + def fit_poly(self, grid, order): + """Regression using numpy polyfit for higher-order trends.""" + x, y = self.x, self.y + reg_func = lambda _x, _y: np.polyval(np.polyfit(_x, _y, order), grid) + yhat = reg_func(x, y) + if self.ci is None: + return yhat, None + + yhat_boots = algo.bootstrap(x, y, func=reg_func, + n_boot=self.n_boot, units=self.units) + return yhat, yhat_boots + + def fit_statsmodels(self, grid, model, **kwargs): + """More general regression function using statsmodels objects.""" + X, y = np.c_[np.ones(len(self.x)), self.x], self.y + grid = np.c_[np.ones(len(grid)), grid] + import statsmodels.genmod.generalized_linear_model as glm + + def reg_func(_x, _y): + try: + yhat = model(_y, _x, **kwargs).fit().predict(grid) + except glm.PerfectSeparationError: + yhat = np.empty(len(grid)) + yhat.fill(np.nan) + return yhat + + yhat = reg_func(X, y) + if self.ci is None: + return yhat, None + + yhat_boots = algo.bootstrap(X, y, func=reg_func, + n_boot=self.n_boot, units=self.units) + return yhat, yhat_boots + + def fit_lowess(self): + """Fit a locally-weighted regression, which returns its own grid.""" + from statsmodels.nonparametric.smoothers_lowess import lowess + grid, yhat = lowess(self.y, self.x).T + return grid, yhat + + def fit_logx(self, grid): + """Fit the model in log-space.""" + X, y = np.c_[np.ones(len(self.x)), self.x], self.y + grid = np.c_[np.ones(len(grid)), np.log(grid)] + + def reg_func(_x, _y): + _x = np.c_[_x[:, 0], np.log(_x[:, 1])] + return np.linalg.pinv(_x).dot(_y) + + yhat = grid.dot(reg_func(X, y)) + if self.ci is None: + return yhat, None + + beta_boots = algo.bootstrap(X, y, func=reg_func, + n_boot=self.n_boot, units=self.units).T + yhat_boots = grid.dot(beta_boots).T + return yhat, yhat_boots + + def bin_predictor(self, bins): + """Discretize a predictor by assigning value to closest bin.""" + x = self.x + if np.isscalar(bins): + percentiles = np.linspace(0, 100, bins + 2)[1:-1] + bins = np.c_[utils.percentiles(x, percentiles)] + else: + bins = np.c_[np.ravel(bins)] + + dist = distance.cdist(np.c_[x], bins) + x_binned = bins[np.argmin(dist, axis=1)].ravel() + + return x_binned, bins.ravel() + + def regress_out(self, a, b): + """Regress b from a keeping a's original mean.""" + a_mean = a.mean() + a = a - a_mean + b = b - b.mean() + b = np.c_[b] + a_prime = a - b.dot(np.linalg.pinv(b).dot(a)) + return (a_prime + a_mean).reshape(a.shape) + + def plot(self, ax, scatter_kws, line_kws): + """Draw the full plot.""" + # Insert the plot label into the correct set of keyword arguments + if self.scatter: + scatter_kws["label"] = self.label + else: + line_kws["label"] = self.label + + # Use the current color cycle state as a default + if self.color is None: + lines, = plt.plot(self.x.mean(), self.y.mean()) + color = lines.get_color() + lines.remove() + else: + color = self.color + + # Ensure that color is hex to avoid matplotlib weidness + color = mpl.colors.rgb2hex(mpl.colors.colorConverter.to_rgb(color)) + + # Let color in keyword arguments override overall plot color + scatter_kws.setdefault("color", color) + line_kws.setdefault("color", color) + + # Draw the constituent plots + if self.scatter: + self.scatterplot(ax, scatter_kws) + if self.fit_reg: + self.lineplot(ax, line_kws) + + # Label the axes + if hasattr(self.x, "name"): + ax.set_xlabel(self.x.name) + if hasattr(self.y, "name"): + ax.set_ylabel(self.y.name) + + def scatterplot(self, ax, kws): + """Draw the data.""" + # Treat the line-based markers specially, explicitly setting larger + # linewidth than is provided by the seaborn style defaults. + # This would ideally be handled better in matplotlib (i.e., distinguish + # between edgewidth for solid glyphs and linewidth for line glyphs + # but this should do for now. + line_markers = ["1", "2", "3", "4", "+", "x", "|", "_"] + if self.x_estimator is None: + if "marker" in kws and kws["marker"] in line_markers: + lw = mpl.rcParams["lines.linewidth"] + else: + lw = mpl.rcParams["lines.markeredgewidth"] + kws.setdefault("linewidths", lw) + + if not hasattr(kws['color'], 'shape') or kws['color'].shape[1] < 4: + kws.setdefault("alpha", .8) + + x, y = self.scatter_data + ax.scatter(x, y, **kws) + else: + # TODO abstraction + ci_kws = {"color": kws["color"]} + ci_kws["linewidth"] = mpl.rcParams["lines.linewidth"] * 1.75 + kws.setdefault("s", 50) + + xs, ys, cis = self.estimate_data + if [ci for ci in cis if ci is not None]: + for x, ci in zip(xs, cis): + ax.plot([x, x], ci, **ci_kws) + ax.scatter(xs, ys, **kws) + + def lineplot(self, ax, kws): + """Draw the model.""" + xlim = ax.get_xlim() + + # Fit the regression model + grid, yhat, err_bands = self.fit_regression(ax) + + # Get set default aesthetics + fill_color = kws["color"] + lw = kws.pop("lw", mpl.rcParams["lines.linewidth"] * 1.5) + kws.setdefault("linewidth", lw) + + # Draw the regression line and confidence interval + ax.plot(grid, yhat, **kws) + if err_bands is not None: + ax.fill_between(grid, *err_bands, facecolor=fill_color, alpha=.15) + ax.set_xlim(*xlim) + + +_regression_docs = dict( + + model_api=dedent("""\ + There are a number of mutually exclusive options for estimating the + regression model: ``order``, ``logistic``, ``lowess``, ``robust``, and + ``logx``. See the parameter docs for more information on these options.\ + """), + + regplot_vs_lmplot=dedent("""\ + Understanding the difference between :func:`regplot` and :func:`lmplot` can + be a bit tricky. In fact, they are closely related, as :func:`lmplot` uses + :func:`regplot` internally and takes most of its parameters. However, + :func:`regplot` is an axes-level function, so it draws directly onto an + axes (either the currently active axes or the one provided by the ``ax`` + parameter), while :func:`lmplot` is a figure-level function and creates its + own figure, which is managed through a :class:`FacetGrid`. This has a few + consequences, namely that :func:`regplot` can happily coexist in a figure + with other kinds of plots and will follow the global matplotlib color + cycle. In contrast, :func:`lmplot` needs to occupy an entire figure, and + the size and color cycle are controlled through function parameters, + ignoring the global defaults.\ + """), + + x_estimator=dedent("""\ + x_estimator : callable that maps vector -> scalar, optional + Apply this function to each unique value of ``x`` and plot the + resulting estimate. This is useful when ``x`` is a discrete variable. + If ``x_ci`` is given, this estimate will be bootstrapped and a + confidence interval will be drawn.\ + """), + x_bins=dedent("""\ + x_bins : int or vector, optional + Bin the ``x`` variable into discrete bins and then estimate the central + tendency and a confidence interval. This binning only influences how + the scatterplot is drawn; the regression is still fit to the original + data. This parameter is interpreted either as the number of + evenly-sized (not necessary spaced) bins or the positions of the bin + centers. When this parameter is used, it implies that the default of + ``x_estimator`` is ``numpy.mean``.\ + """), + x_ci=dedent("""\ + x_ci : "ci", "sd", int in [0, 100] or None, optional + Size of the confidence interval used when plotting a central tendency + for discrete values of ``x``. If ``"ci"``, defer to the value of the + ``ci`` parameter. If ``"sd"``, skip bootstrappig and show the standard + deviation of the observations in each bin.\ + """), + scatter=dedent("""\ + scatter : bool, optional + If ``True``, draw a scatterplot with the underlying observations (or + the ``x_estimator`` values).\ + """), + fit_reg=dedent("""\ + fit_reg : bool, optional + If ``True``, estimate and plot a regression model relating the ``x`` + and ``y`` variables.\ + """), + ci=dedent("""\ + ci : int in [0, 100] or None, optional + Size of the confidence interval for the regression estimate. This will + be drawn using translucent bands around the regression line. The + confidence interval is estimated using a bootstrap; for large + datasets, it may be advisable to avoid that computation by setting + this parameter to None.\ + """), + n_boot=dedent("""\ + n_boot : int, optional + Number of bootstrap resamples used to estimate the ``ci``. The default + value attempts to balance time and stability; you may want to increase + this value for "final" versions of plots.\ + """), + units=dedent("""\ + units : variable name in ``data``, optional + If the ``x`` and ``y`` observations are nested within sampling units, + those can be specified here. This will be taken into account when + computing the confidence intervals by performing a multilevel bootstrap + that resamples both units and observations (within unit). This does not + otherwise influence how the regression is estimated or drawn.\ + """), + order=dedent("""\ + order : int, optional + If ``order`` is greater than 1, use ``numpy.polyfit`` to estimate a + polynomial regression.\ + """), + logistic=dedent("""\ + logistic : bool, optional + If ``True``, assume that ``y`` is a binary variable and use + ``statsmodels`` to estimate a logistic regression model. Note that this + is substantially more computationally intensive than linear regression, + so you may wish to decrease the number of bootstrap resamples + (``n_boot``) or set ``ci`` to None.\ + """), + lowess=dedent("""\ + lowess : bool, optional + If ``True``, use ``statsmodels`` to estimate a nonparametric lowess + model (locally weighted linear regression). Note that confidence + intervals cannot currently be drawn for this kind of model.\ + """), + robust=dedent("""\ + robust : bool, optional + If ``True``, use ``statsmodels`` to estimate a robust regression. This + will de-weight outliers. Note that this is substantially more + computationally intensive than standard linear regression, so you may + wish to decrease the number of bootstrap resamples (``n_boot``) or set + ``ci`` to None.\ + """), + logx=dedent("""\ + logx : bool, optional + If ``True``, estimate a linear regression of the form y ~ log(x), but + plot the scatterplot and regression model in the input space. Note that + ``x`` must be positive for this to work.\ + """), + xy_partial=dedent("""\ + {x,y}_partial : strings in ``data`` or matrices + Confounding variables to regress out of the ``x`` or ``y`` variables + before plotting.\ + """), + truncate=dedent("""\ + truncate : bool, optional + By default, the regression line is drawn to fill the x axis limits + after the scatterplot is drawn. If ``truncate`` is ``True``, it will + instead by bounded by the data limits.\ + """), + xy_jitter=dedent("""\ + {x,y}_jitter : floats, optional + Add uniform random noise of this size to either the ``x`` or ``y`` + variables. The noise is added to a copy of the data after fitting the + regression, and only influences the look of the scatterplot. This can + be helpful when plotting variables that take discrete values.\ + """), + scatter_line_kws=dedent("""\ + {scatter,line}_kws : dictionaries + Additional keyword arguments to pass to ``plt.scatter`` and + ``plt.plot``.\ + """), + ) +_regression_docs.update(_facet_docs) + + +def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, + col_wrap=None, size=5, aspect=1, markers="o", sharex=True, + sharey=True, hue_order=None, col_order=None, row_order=None, + legend=True, legend_out=True, x_estimator=None, x_bins=None, + x_ci="ci", scatter=True, fit_reg=True, ci=95, n_boot=1000, + units=None, order=1, logistic=False, lowess=False, robust=False, + logx=False, x_partial=None, y_partial=None, truncate=False, + x_jitter=None, y_jitter=None, scatter_kws=None, line_kws=None): + + # Reduce the dataframe to only needed columns + need_cols = [x, y, hue, col, row, units, x_partial, y_partial] + cols = np.unique([a for a in need_cols if a is not None]).tolist() + data = data[cols] + + # Initialize the grid + facets = FacetGrid(data, row, col, hue, palette=palette, + row_order=row_order, col_order=col_order, + hue_order=hue_order, size=size, aspect=aspect, + col_wrap=col_wrap, sharex=sharex, sharey=sharey, + legend_out=legend_out) + + # Add the markers here as FacetGrid has figured out how many levels of the + # hue variable are needed and we don't want to duplicate that process + if facets.hue_names is None: + n_markers = 1 + else: + n_markers = len(facets.hue_names) + if not isinstance(markers, list): + markers = [markers] * n_markers + if len(markers) != n_markers: + raise ValueError(("markers must be a singeton or a list of markers " + "for each level of the hue variable")) + facets.hue_kws = {"marker": markers} + + # Hack to set the x limits properly, which needs to happen here + # because the extent of the regression estimate is determined + # by the limits of the plot + if sharex: + for ax in facets.axes.flat: + ax.scatter(data[x], np.ones(len(data)) * data[y].mean()).remove() + + # Draw the regression plot on each facet + regplot_kws = dict( + x_estimator=x_estimator, x_bins=x_bins, x_ci=x_ci, + scatter=scatter, fit_reg=fit_reg, ci=ci, n_boot=n_boot, units=units, + order=order, logistic=logistic, lowess=lowess, robust=robust, + logx=logx, x_partial=x_partial, y_partial=y_partial, truncate=truncate, + x_jitter=x_jitter, y_jitter=y_jitter, + scatter_kws=scatter_kws, line_kws=line_kws, + ) + facets.map_dataframe(regplot, x, y, **regplot_kws) + + # Add a legend + if legend and (hue is not None) and (hue not in [col, row]): + facets.add_legend() + return facets + + +lmplot.__doc__ = dedent("""\ + Plot data and regression model fits across a FacetGrid. + + This function combines :func:`regplot` and :class:`FacetGrid`. It is + intended as a convenient interface to fit regression models across + conditional subsets of a dataset. + + When thinking about how to assign variables to different facets, a general + rule is that it makes sense to use ``hue`` for the most important + comparison, followed by ``col`` and ``row``. However, always think about + your particular dataset and the goals of the visualization you are + creating. + + {model_api} + + The parameters to this function span most of the options in + :class:`FacetGrid`, although there may be occasional cases where you will + want to use that class and :func:`regplot` directly. + + Parameters + ---------- + x, y : strings, optional + Input variables; these should be column names in ``data``. + {data} + hue, col, row : strings + Variables that define subsets of the data, which will be drawn on + separate facets in the grid. See the ``*_order`` parameters to control + the order of levels of this variable. + {palette} + {col_wrap} + {size} + {aspect} + markers : matplotlib marker code or list of marker codes, optional + Markers for the scatterplot. If a list, each marker in the list will be + used for each level of the ``hue`` variable. + {share_xy} + {{hue,col,row}}_order : lists, optional + Order for the levels of the faceting variables. By default, this will + be the order that the levels appear in ``data`` or, if the variables + are pandas categoricals, the category order. + legend : bool, optional + If ``True`` and there is a ``hue`` variable, add a legend. + {legend_out} + {x_estimator} + {x_bins} + {x_ci} + {scatter} + {fit_reg} + {ci} + {n_boot} + {units} + {order} + {logistic} + {lowess} + {robust} + {logx} + {xy_partial} + {truncate} + {xy_jitter} + {scatter_line_kws} + + See Also + -------- + regplot : Plot data and a conditional model fit. + FacetGrid : Subplot grid for plotting conditional relationships. + pairplot : Combine :func:`regplot` and :class:`PairGrid` (when used with + ``kind="reg"``). + + Notes + ----- + + {regplot_vs_lmplot} + + Examples + -------- + + These examples focus on basic regression model plots to exhibit the + various faceting options; see the :func:`regplot` docs for demonstrations + of the other options for plotting the data and models. There are also + other examples for how to manipulate plot using the returned object on + the :class:`FacetGrid` docs. + + Plot a simple linear relationship between two variables: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set(color_codes=True) + >>> tips = sns.load_dataset("tips") + >>> g = sns.lmplot(x="total_bill", y="tip", data=tips) + + Condition on a third variable and plot the levels in different colors: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips) + + Use different markers as well as colors so the plot will reproduce to + black-and-white more easily: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips, + ... markers=["o", "x"]) + + Use a different color palette: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips, + ... palette="Set1") + + Map ``hue`` levels to colors with a dictionary: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips, + ... palette=dict(Yes="g", No="m")) + + Plot the levels of the third variable across different columns: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="total_bill", y="tip", col="smoker", data=tips) + + Change the size and aspect ratio of the facets: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="size", y="total_bill", hue="day", col="day", + ... data=tips, aspect=.4, x_jitter=.1) + + Wrap the levels of the column variable into multiple rows: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="total_bill", y="tip", col="day", hue="day", + ... data=tips, col_wrap=2, size=3) + + Condition on two variables to make a full grid: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="total_bill", y="tip", row="sex", col="time", + ... data=tips, size=3) + + Use methods on the returned :class:`FacetGrid` instance to further tweak + the plot: + + .. plot:: + :context: close-figs + + >>> g = sns.lmplot(x="total_bill", y="tip", row="sex", col="time", + ... data=tips, size=3) + >>> g = (g.set_axis_labels("Total bill (US Dollars)", "Tip") + ... .set(xlim=(0, 60), ylim=(0, 12), + ... xticks=[10, 30, 50], yticks=[2, 6, 10]) + ... .fig.subplots_adjust(wspace=.02)) + + + + """).format(**_regression_docs) + + +def regplot(x, y, data=None, x_estimator=None, x_bins=None, x_ci="ci", + scatter=True, fit_reg=True, ci=95, n_boot=1000, units=None, + order=1, logistic=False, lowess=False, robust=False, + logx=False, x_partial=None, y_partial=None, + truncate=False, dropna=True, x_jitter=None, y_jitter=None, + label=None, color=None, marker="o", + scatter_kws=None, line_kws=None, ax=None): + + plotter = _RegressionPlotter(x, y, data, x_estimator, x_bins, x_ci, + scatter, fit_reg, ci, n_boot, units, + order, logistic, lowess, robust, logx, + x_partial, y_partial, truncate, dropna, + x_jitter, y_jitter, color, label) + + if ax is None: + ax = plt.gca() + + scatter_kws = {} if scatter_kws is None else copy.copy(scatter_kws) + scatter_kws["marker"] = marker + line_kws = {} if line_kws is None else copy.copy(line_kws) + plotter.plot(ax, scatter_kws, line_kws) + return ax + +regplot.__doc__ = dedent("""\ + Plot data and a linear regression model fit. + + {model_api} + + Parameters + ---------- + x, y: string, series, or vector array + Input variables. If strings, these should correspond with column names + in ``data``. When pandas objects are used, axes will be labeled with + the series name. + {data} + {x_estimator} + {x_bins} + {x_ci} + {scatter} + {fit_reg} + {ci} + {n_boot} + {units} + {order} + {logistic} + {lowess} + {robust} + {logx} + {xy_partial} + {truncate} + {xy_jitter} + label : string + Label to apply to ether the scatterplot or regression line (if + ``scatter`` is ``False``) for use in a legend. + color : matplotlib color + Color to apply to all plot elements; will be superseded by colors + passed in ``scatter_kws`` or ``line_kws``. + marker : matplotlib marker code + Marker to use for the scatterplot glyphs. + {scatter_line_kws} + ax : matplotlib Axes, optional + Axes object to draw the plot onto, otherwise uses the current Axes. + + Returns + ------- + ax : matplotlib Axes + The Axes object containing the plot. + + See Also + -------- + lmplot : Combine :func:`regplot` and :class:`FacetGrid` to plot multiple + linear relationships in a dataset. + jointplot : Combine :func:`regplot` and :class:`JointGrid` (when used with + ``kind="reg"``). + pairplot : Combine :func:`regplot` and :class:`PairGrid` (when used with + ``kind="reg"``). + residplot : Plot the residuals of a linear regression model. + interactplot : Plot a two-way interaction between continuous variables + + Notes + ----- + + {regplot_vs_lmplot} + + + It's also easy to combine combine :func:`regplot` and :class:`JointGrid` or + :class:`PairGrid` through the :func:`jointplot` and :func:`pairplot` + functions, although these do not directly accept all of :func:`regplot`'s + parameters. + + Examples + -------- + + Plot the relationship between two variables in a DataFrame: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set(color_codes=True) + >>> tips = sns.load_dataset("tips") + >>> ax = sns.regplot(x="total_bill", y="tip", data=tips) + + Plot with two variables defined as numpy arrays; use a different color: + + .. plot:: + :context: close-figs + + >>> import numpy as np; np.random.seed(8) + >>> mean, cov = [4, 6], [(1.5, .7), (.7, 1)] + >>> x, y = np.random.multivariate_normal(mean, cov, 80).T + >>> ax = sns.regplot(x=x, y=y, color="g") + + Plot with two variables defined as pandas Series; use a different marker: + + .. plot:: + :context: close-figs + + >>> import pandas as pd + >>> x, y = pd.Series(x, name="x_var"), pd.Series(y, name="y_var") + >>> ax = sns.regplot(x=x, y=y, marker="+") + + Use a 68% confidence interval, which corresponds with the standard error + of the estimate: + + .. plot:: + :context: close-figs + + >>> ax = sns.regplot(x=x, y=y, ci=68) + + Plot with a discrete ``x`` variable and add some jitter: + + .. plot:: + :context: close-figs + + >>> ax = sns.regplot(x="size", y="total_bill", data=tips, x_jitter=.1) + + Plot with a discrete ``x`` variable showing means and confidence intervals + for unique values: + + .. plot:: + :context: close-figs + + >>> ax = sns.regplot(x="size", y="total_bill", data=tips, + ... x_estimator=np.mean) + + Plot with a continuous variable divided into discrete bins: + + .. plot:: + :context: close-figs + + >>> ax = sns.regplot(x=x, y=y, x_bins=4) + + Fit a higher-order polynomial regression and truncate the model prediction: + + .. plot:: + :context: close-figs + + >>> ans = sns.load_dataset("anscombe") + >>> ax = sns.regplot(x="x", y="y", data=ans.loc[ans.dataset == "II"], + ... scatter_kws={{"s": 80}}, + ... order=2, ci=None, truncate=True) + + Fit a robust regression and don't plot a confidence interval: + + .. plot:: + :context: close-figs + + >>> ax = sns.regplot(x="x", y="y", data=ans.loc[ans.dataset == "III"], + ... scatter_kws={{"s": 80}}, + ... robust=True, ci=None) + + Fit a logistic regression; jitter the y variable and use fewer bootstrap + iterations: + + .. plot:: + :context: close-figs + + >>> tips["big_tip"] = (tips.tip / tips.total_bill) > .175 + >>> ax = sns.regplot(x="total_bill", y="big_tip", data=tips, + ... logistic=True, n_boot=500, y_jitter=.03) + + Fit the regression model using log(x) and truncate the model prediction: + + .. plot:: + :context: close-figs + + >>> ax = sns.regplot(x="size", y="total_bill", data=tips, + ... x_estimator=np.mean, logx=True, truncate=True) + + """).format(**_regression_docs) + + +def residplot(x, y, data=None, lowess=False, x_partial=None, y_partial=None, + order=1, robust=False, dropna=True, label=None, color=None, + scatter_kws=None, line_kws=None, ax=None): + """Plot the residuals of a linear regression. + + This function will regress y on x (possibly as a robust or polynomial + regression) and then draw a scatterplot of the residuals. You can + optionally fit a lowess smoother to the residual plot, which can + help in determining if there is structure to the residuals. + + Parameters + ---------- + x : vector or string + Data or column name in `data` for the predictor variable. + y : vector or string + Data or column name in `data` for the response variable. + data : DataFrame, optional + DataFrame to use if `x` and `y` are column names. + lowess : boolean, optional + Fit a lowess smoother to the residual scatterplot. + {x, y}_partial : matrix or string(s) , optional + Matrix with same first dimension as `x`, or column name(s) in `data`. + These variables are treated as confounding and are removed from + the `x` or `y` variables before plotting. + order : int, optional + Order of the polynomial to fit when calculating the residuals. + robust : boolean, optional + Fit a robust linear regression when calculating the residuals. + dropna : boolean, optional + If True, ignore observations with missing data when fitting and + plotting. + label : string, optional + Label that will be used in any plot legends. + color : matplotlib color, optional + Color to use for all elements of the plot. + {scatter, line}_kws : dictionaries, optional + Additional keyword arguments passed to scatter() and plot() for drawing + the components of the plot. + ax : matplotlib axis, optional + Plot into this axis, otherwise grab the current axis or make a new + one if not existing. + + Returns + ------- + ax: matplotlib axes + Axes with the regression plot. + + See Also + -------- + regplot : Plot a simple linear regression model. + jointplot (with kind="resid"): Draw a residplot with univariate + marginal distrbutions. + + """ + plotter = _RegressionPlotter(x, y, data, ci=None, + order=order, robust=robust, + x_partial=x_partial, y_partial=y_partial, + dropna=dropna, color=color, label=label) + + if ax is None: + ax = plt.gca() + + # Calculate the residual from a linear regression + _, yhat, _ = plotter.fit_regression(grid=plotter.x) + plotter.y = plotter.y - yhat + + # Set the regression option on the plotter + if lowess: + plotter.lowess = True + else: + plotter.fit_reg = False + + # Plot a horizontal line at 0 + ax.axhline(0, ls=":", c=".2") + + # Draw the scatterplot + scatter_kws = {} if scatter_kws is None else scatter_kws + line_kws = {} if line_kws is None else line_kws + plotter.plot(ax, scatter_kws, line_kws) + return ax + + +def coefplot(formula, data, groupby=None, intercept=False, ci=95, + palette="husl"): + """Plot the coefficients from a linear model. + + Parameters + ---------- + formula : string + patsy formula for ols model + data : dataframe + data for the plot; formula terms must appear in columns + groupby : grouping object, optional + object to group data with to fit conditional models + intercept : bool, optional + if False, strips the intercept term before plotting + ci : float, optional + size of confidence intervals + palette : seaborn color palette, optional + palette for the horizonal plots + + """ + if not _has_statsmodels: + raise ImportError("The `coefplot` function requires statsmodels") + import statsmodels.formula.api as sf + + alpha = 1 - ci / 100 + if groupby is None: + coefs = sf.ols(formula, data).fit().params + cis = sf.ols(formula, data).fit().conf_int(alpha) + else: + grouped = data.groupby(groupby) + coefs = grouped.apply(lambda d: sf.ols(formula, d).fit().params).T + cis = grouped.apply(lambda d: sf.ols(formula, d).fit().conf_int(alpha)) + + # Possibly ignore the intercept + if not intercept: + coefs = coefs.ix[1:] + + n_terms = len(coefs) + + # Plot seperately depending on groupby + w, h = mpl.rcParams["figure.figsize"] + hsize = lambda n: n * (h / 2) + wsize = lambda n: n * (w / (4 * (n / 5))) + if groupby is None: + colors = itertools.cycle(color_palette(palette, n_terms)) + f, ax = plt.subplots(1, 1, figsize=(wsize(n_terms), hsize(1))) + for i, term in enumerate(coefs.index): + color = next(colors) + low, high = cis.ix[term] + ax.plot([i, i], [low, high], c=color, + solid_capstyle="round", lw=2.5) + ax.plot(i, coefs.ix[term], "o", c=color, ms=8) + ax.set_xlim(-.5, n_terms - .5) + ax.axhline(0, ls="--", c="dimgray") + ax.set_xticks(range(n_terms)) + ax.set_xticklabels(coefs.index) + + else: + n_groups = len(coefs.columns) + f, axes = plt.subplots(n_terms, 1, sharex=True, + figsize=(wsize(n_groups), hsize(n_terms))) + if n_terms == 1: + axes = [axes] + colors = itertools.cycle(color_palette(palette, n_groups)) + for ax, term in zip(axes, coefs.index): + for i, group in enumerate(coefs.columns): + color = next(colors) + low, high = cis.ix[(group, term)] + ax.plot([i, i], [low, high], c=color, + solid_capstyle="round", lw=2.5) + ax.plot(i, coefs.loc[term, group], "o", c=color, ms=8) + ax.set_xlim(-.5, n_groups - .5) + ax.axhline(0, ls="--", c="dimgray") + ax.set_title(term) + ax.set_xlabel(groupby) + ax.set_xticks(range(n_groups)) + ax.set_xticklabels(coefs.columns) + + +def interactplot(x1, x2, y, data=None, filled=False, cmap="RdBu_r", + colorbar=True, levels=30, logistic=False, + contour_kws=None, scatter_kws=None, ax=None, **kwargs): + """Visualize a continuous two-way interaction with a contour plot. + + Parameters + ---------- + x1, x2, y, strings or array-like + Either the two independent variables and the dependent variable, + or keys to extract them from `data` + data : DataFrame + Pandas DataFrame with the data in the columns. + filled : bool + Whether to plot with filled or unfilled contours + cmap : matplotlib colormap + Colormap to represent yhat in the countour plot. + colorbar : bool + Whether to draw the colorbar for interpreting the color values. + levels : int or sequence + Number or position of contour plot levels. + logistic : bool + Fit a logistic regression model instead of linear regression. + contour_kws : dictionary + Keyword arguments for contour[f](). + scatter_kws : dictionary + Keyword arguments for plot(). + ax : matplotlib axis + Axis to draw plot in. + + Returns + ------- + ax : Matplotlib axis + Axis with the contour plot. + + """ + if not _has_statsmodels: + raise ImportError("The `interactplot` function requires statsmodels") + from statsmodels.regression.linear_model import OLS + from statsmodels.genmod.generalized_linear_model import GLM + from statsmodels.genmod.families import Binomial + + # Handle the form of the data + if data is not None: + x1 = data[x1] + x2 = data[x2] + y = data[y] + if hasattr(x1, "name"): + xlabel = x1.name + else: + xlabel = None + if hasattr(x2, "name"): + ylabel = x2.name + else: + ylabel = None + if hasattr(y, "name"): + clabel = y.name + else: + clabel = None + x1 = np.asarray(x1) + x2 = np.asarray(x2) + y = np.asarray(y) + + # Initialize the scatter keyword dictionary + if scatter_kws is None: + scatter_kws = {} + if not ("color" in scatter_kws or "c" in scatter_kws): + scatter_kws["color"] = "#222222" + if "alpha" not in scatter_kws: + scatter_kws["alpha"] = 0.75 + + # Intialize the contour keyword dictionary + if contour_kws is None: + contour_kws = {} + + # Initialize the axis + if ax is None: + ax = plt.gca() + + # Plot once to let matplotlib sort out the axis limits + ax.plot(x1, x2, "o", **scatter_kws) + + # Find the plot limits + x1min, x1max = ax.get_xlim() + x2min, x2max = ax.get_ylim() + + # Make the grid for the contour plot + x1_points = np.linspace(x1min, x1max, 100) + x2_points = np.linspace(x2min, x2max, 100) + xx1, xx2 = np.meshgrid(x1_points, x2_points) + + # Fit the model with an interaction + X = np.c_[np.ones(x1.size), x1, x2, x1 * x2] + if logistic: + lm = GLM(y, X, family=Binomial()).fit() + else: + lm = OLS(y, X).fit() + + # Evaluate the model on the grid + eval = np.vectorize(lambda x1_, x2_: lm.predict([1, x1_, x2_, x1_ * x2_])) + yhat = eval(xx1, xx2) + + # Default color limits put the midpoint at mean(y) + y_bar = y.mean() + c_min = min(np.percentile(y, 2), yhat.min()) + c_max = max(np.percentile(y, 98), yhat.max()) + delta = max(c_max - y_bar, y_bar - c_min) + c_min, cmax = y_bar - delta, y_bar + delta + contour_kws.setdefault("vmin", c_min) + contour_kws.setdefault("vmax", c_max) + + # Draw the contour plot + func_name = "contourf" if filled else "contour" + contour = getattr(ax, func_name) + c = contour(xx1, xx2, yhat, levels, cmap=cmap, **contour_kws) + + # Draw the scatter again so it's visible + ax.plot(x1, x2, "o", **scatter_kws) + + # Draw a colorbar, maybe + if colorbar: + bar = plt.colorbar(c) + + # Label the axes + if xlabel is not None: + ax.set_xlabel(xlabel) + if ylabel is not None: + ax.set_ylabel(ylabel) + if clabel is not None and colorbar: + clabel = "P(%s)" % clabel if logistic else clabel + bar.set_label(clabel, labelpad=15, rotation=270) + + return ax + + +def pairplot(data, hue=None, hue_order=None, palette=None, + vars=None, x_vars=None, y_vars=None, + kind="scatter", diag_kind="hist", markers=None, + size=2.5, aspect=1, dropna=True, + plot_kws=None, diag_kws=None, grid_kws=None): + """Plot pairwise relationships in a dataset. + + By default, this function will create a grid of Axes such that each + variable in ``data`` will by shared in the y-axis across a single row and + in the x-axis across a single column. The diagonal Axes are treated + differently, drawing a plot to show the univariate distribution of the data + for the variable in that column. + + It is also possible to show a subset of variables or plot different + variables on the rows and columns. + + This is a high-level interface for :class:`PairGrid` that is intended to + make it easy to draw a few common styles. You should use :class`PairGrid` + directly if you need more flexibility. + + Parameters + ---------- + data : DataFrame + Tidy (long-form) dataframe where each column is a variable and + each row is an observation. + hue : string (variable name), optional + Variable in ``data`` to map plot aspects to different colors. + hue_order : list of strings + Order for the levels of the hue variable in the palette + palette : dict or seaborn color palette + Set of colors for mapping the ``hue`` variable. If a dict, keys + should be values in the ``hue`` variable. + vars : list of variable names, optional + Variables within ``data`` to use, otherwise use every column with + a numeric datatype. + {x, y}_vars : lists of variable names, optional + Variables within ``data`` to use separately for the rows and + columns of the figure; i.e. to make a non-square plot. + kind : {'scatter', 'reg'}, optional + Kind of plot for the non-identity relationships. + diag_kind : {'hist', 'kde'}, optional + Kind of plot for the diagonal subplots. + markers : single matplotlib marker code or list, optional + Either the marker to use for all datapoints or a list of markers with + a length the same as the number of levels in the hue variable so that + differently colored points will also have different scatterplot + markers. + size : scalar, optional + Height (in inches) of each facet. + aspect : scalar, optional + Aspect * size gives the width (in inches) of each facet. + dropna : boolean, optional + Drop missing values from the data before plotting. + {plot, diag, grid}_kws : dicts, optional + Dictionaries of keyword arguments. + + Returns + ------- + grid : PairGrid + Returns the underlying ``PairGrid`` instance for further tweaking. + + See Also + -------- + PairGrid : Subplot grid for more flexible plotting of pairwise + relationships. + + Examples + -------- + + Draw scatterplots for joint relationships and histograms for univariate + distributions: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set(style="ticks", color_codes=True) + >>> iris = sns.load_dataset("iris") + >>> g = sns.pairplot(iris) + + Show different levels of a categorical variable by the color of plot + elements: + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, hue="species") + + Use a different color palette: + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, hue="species", palette="husl") + + Use different markers for each level of the hue variable: + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, hue="species", markers=["o", "s", "D"]) + + Plot a subset of variables: + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, vars=["sepal_width", "sepal_length"]) + + Draw larger plots: + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, size=3, + ... vars=["sepal_width", "sepal_length"]) + + Plot different variables in the rows and columns: + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, + ... x_vars=["sepal_width", "sepal_length"], + ... y_vars=["petal_width", "petal_length"]) + + Use kernel density estimates for univariate plots: + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, diag_kind="kde") + + Fit linear regression models to the scatter plots: + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, kind="reg") + + Pass keyword arguments down to the underlying functions (it may be easier + to use :class:`PairGrid` directly): + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, diag_kind="kde", markers="+", + ... plot_kws=dict(s=50, edgecolor="b", linewidth=1), + ... diag_kws=dict(shade=True)) + + """ + if plot_kws is None: + plot_kws = {} + if diag_kws is None: + diag_kws = {} + if grid_kws is None: + grid_kws = {} + + # Set up the PairGrid + diag_sharey = diag_kind == "hist" + grid = PairGrid(data, vars=vars, x_vars=x_vars, y_vars=y_vars, hue=hue, + hue_order=hue_order, palette=palette, + diag_sharey=diag_sharey, + size=size, aspect=aspect, dropna=dropna, **grid_kws) + + # Add the markers here as PairGrid has figured out how many levels of the + # hue variable are needed and we don't want to duplicate that process + if markers is not None: + if grid.hue_names is None: + n_markers = 1 + else: + n_markers = len(grid.hue_names) + if not isinstance(markers, list): + markers = [markers] * n_markers + if len(markers) != n_markers: + raise ValueError(("markers must be a singeton or a list of markers" + " for each level of the hue variable")) + grid.hue_kws = {"marker": markers} + + # Maybe plot on the diagonal + if grid.square_grid: + if diag_kind == "hist": + grid.map_diag(plt.hist, **diag_kws) + elif diag_kind == "kde": + diag_kws["legend"] = False + grid.map_diag(kdeplot, **diag_kws) + + # Maybe plot on the off-diagonals + if grid.square_grid and diag_kind is not None: + plotter = grid.map_offdiag + else: + plotter = grid.map + + if kind == "scatter": + plot_kws.setdefault("edgecolor", "white") + plotter(plt.scatter, **plot_kws) + elif kind == "reg": + plotter(regplot, **plot_kws) + + # Add a legend + if hue is not None: + grid.add_legend() + + return grid diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index ec392d92ce..737bf4fa25 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -21,7 +21,7 @@ from ..palettes import color_palette from ..distributions import kdeplot from ..categorical import pointplot -from ..linearmodels import pairplot +from ..regression import pairplot from ..utils import categorical_order rs = np.random.RandomState(0) diff --git a/seaborn/tests/test_linearmodels.py b/seaborn/tests/test_regression.py similarity index 99% rename from seaborn/tests/test_linearmodels.py rename to seaborn/tests/test_regression.py index 19559fc0d7..3a43a0dccd 100644 --- a/seaborn/tests/test_linearmodels.py +++ b/seaborn/tests/test_regression.py @@ -19,9 +19,7 @@ _no_statsmodels = True from . import PlotTestCase -from .. import linearmodels as lm -from .. import algorithms as algo -from .. import utils +from .. import regression as lm from ..palettes import color_palette rs = np.random.RandomState(0) From 9ba1b7d4e23b624e307de5ad481add4ff703afa9 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 15:16:32 -0400 Subject: [PATCH 0426/1738] Deprecate interactplot and coefplot for removal --- doc/api.rst | 2 -- doc/releases/v0.8.0.txt | 2 ++ examples/interactplot.py | 21 --------------------- seaborn/regression.py | 11 ++++++++++- 4 files changed, 12 insertions(+), 24 deletions(-) delete mode 100644 examples/interactplot.py diff --git a/doc/api.rst b/doc/api.rst index ad6f562083..c8a20d8763 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -30,8 +30,6 @@ Regression plots lmplot regplot residplot - interactplot - coefplot .. _categorical_api: diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index 53a885b876..30dd4d271c 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -27,3 +27,5 @@ v0.8.0 (Unreleased) - Turn off dendrogram axes in :func:`clustermap` rather than setting the background color to white. - New matplotlib qualitative palettes (e.g. "tab10") are now handled correctly. + +- The ``interactplot`` and ``coefplot`` functions are officially deprecated and will be removed in a future version. diff --git a/examples/interactplot.py b/examples/interactplot.py deleted file mode 100644 index 28ae928fd6..0000000000 --- a/examples/interactplot.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -Continuous interactions -======================= - -""" -import numpy as np -import pandas as pd -import seaborn as sns -sns.set(style="darkgrid") - -# Generate a random dataset with strong simple effects and an interaction -n = 80 -rs = np.random.RandomState(11) -x1 = rs.randn(n) -x2 = x1 / 5 + rs.randn(n) -b0, b1, b2, b3 = .5, .25, -1, 2 -y = b0 + b1 * x1 + b2 * x2 + b3 * x1 * x2 + rs.randn(n) -df = pd.DataFrame(np.c_[x1, x2, y], columns=["x1", "x2", "y"]) - -# Show a scatterplot of the predictors with the estimated model surface -sns.interactplot("x1", "x2", "y", df) diff --git a/seaborn/regression.py b/seaborn/regression.py index 61ec621ad0..2b0abbc5ea 100644 --- a/seaborn/regression.py +++ b/seaborn/regression.py @@ -847,7 +847,6 @@ def regplot(x, y, data=None, x_estimator=None, x_bins=None, x_ci="ci", pairplot : Combine :func:`regplot` and :class:`PairGrid` (when used with ``kind="reg"``). residplot : Plot the residuals of a linear regression model. - interactplot : Plot a two-way interaction between continuous variables Notes ----- @@ -1064,6 +1063,11 @@ def coefplot(formula, data, groupby=None, intercept=False, ci=95, palette for the horizonal plots """ + msg = ( + "The `coefplot` function has been deprecated and will be removed " + "in a future version." + ) + warnings.warn(msg, UserWarning) if not _has_statsmodels: raise ImportError("The `coefplot` function requires statsmodels") import statsmodels.formula.api as sf @@ -1158,6 +1162,11 @@ def interactplot(x1, x2, y, data=None, filled=False, cmap="RdBu_r", Axis with the contour plot. """ + msg = ( + "The `interactplot` function has been deprecated and will be removed " + "in a future version." + ) + warnings.warn(msg, UserWarning) if not _has_statsmodels: raise ImportError("The `interactplot` function requires statsmodels") from statsmodels.regression.linear_model import OLS From a8a8727fcfafe08c5dc05be9353a152ebcce636c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 15:31:54 -0400 Subject: [PATCH 0427/1738] Add deprecation warning message about tsplot --- doc/releases/v0.8.0.txt | 4 +++- seaborn/timeseries.py | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index 30dd4d271c..f5f76c8888 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -28,4 +28,6 @@ v0.8.0 (Unreleased) - New matplotlib qualitative palettes (e.g. "tab10") are now handled correctly. -- The ``interactplot`` and ``coefplot`` functions are officially deprecated and will be removed in a future version. +- Added a deprecation warning to func:`tsplot` function to indicate that it will be removed or replaced with a substantially altered version in a future release. + +- The ``interactplot`` and ``coefplot`` functions are officially deprecated and will be removed in a future release. diff --git a/seaborn/timeseries.py b/seaborn/timeseries.py index 1b1aab9389..3bd0c58abb 100644 --- a/seaborn/timeseries.py +++ b/seaborn/timeseries.py @@ -6,6 +6,8 @@ import matplotlib as mpl import matplotlib.pyplot as plt +import warnings + from .external.six import string_types from . import utils @@ -174,6 +176,12 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, >>> ax = sns.tsplot(data=data, err_style="unit_traces") """ + msg = ( + "The tsplot function is deprecated and will be removed or replaced " + "(in a substantially altered version) in a future release." + ) + warnings.warn(msg, UserWarning) + # Sort out default values for the parameters if ax is None: ax = plt.gca() From 9eed36100eda8199fedd74572c5b1f17059fc9c6 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 15:32:10 -0400 Subject: [PATCH 0428/1738] Reorganize API homepage --- doc/api.rst | 66 ++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index c8a20d8763..315ecc584f 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -2,22 +2,41 @@ .. currentmodule:: seaborn +.. _grid_api: + +Axis grids +---------- + +.. autosummary:: + :toctree: generated/ + + FacetGrid + factorplot + lmplot + PairGrid + pairplot + JointGrid + jointplot + API reference ============= -.. _distribution_api: +.. _categorical_api: -Distribution plots ------------------- +Categorical plots +----------------- .. autosummary:: :toctree: generated/ - jointplot - pairplot - distplot - kdeplot - rugplot + boxplot + violinplot + lvplot + stripplot + swarmplot + pointplot + barplot + countplot .. _regression_api: @@ -27,27 +46,20 @@ Regression plots .. autosummary:: :toctree: generated/ - lmplot regplot residplot -.. _categorical_api: +.. _distribution_api: -Categorical plots ------------------ +Distribution plots +------------------ .. autosummary:: :toctree: generated/ - factorplot - boxplot - violinplot - lvplot - stripplot - swarmplot - pointplot - barplot - countplot + distplot + kdeplot + rugplot .. _matrix_api: @@ -76,18 +88,6 @@ Miscellaneous plots palplot -.. _grid_api: - -Axis grids ----------- - -.. autosummary:: - :toctree: generated/ - - FacetGrid - PairGrid - JointGrid - .. _style_api: Style frontend From 10168fef9de312fcecfceed342579e1e4007d366 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 16:22:38 -0400 Subject: [PATCH 0429/1738] Add 0.8 release notes to docs --- doc/whatsnew.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/whatsnew.rst b/doc/whatsnew.rst index 902d861e90..4dfb93ce6b 100644 --- a/doc/whatsnew.rst +++ b/doc/whatsnew.rst @@ -5,7 +5,7 @@ What's new in the package ========================= -A catalog of new features, improvements, and bug-fixes in each release. +.. include:: releases/v0.8.0.txt .. include:: releases/v0.7.1.txt From f56095722ed793b92d37d82da15992ee58a640e6 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 16:23:17 -0400 Subject: [PATCH 0430/1738] Update tutorial buildchain --- doc/Makefile | 2 ++ doc/tutorial/axis_grids.ipynb | 15 ++++++++++++--- doc/tutorial/categorical.ipynb | 2 +- doc/tutorial/color_palettes.ipynb | 2 +- doc/tutorial/distributions.ipynb | 2 +- doc/tutorial/regression.ipynb | 2 +- doc/tutorial/tools/nb_to_doc.py | 10 +++++----- 7 files changed, 23 insertions(+), 12 deletions(-) diff --git a/doc/Makefile b/doc/Makefile index 9d1e410d9d..e10872b0df 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -18,7 +18,9 @@ I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . help: @echo "Please use \`make ' where is one of" + @echo " clean to remove genrated output" @echo " html to make standalone HTML files" + @echo " notebooks to make the Jupyter notebook-based tutorials" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" diff --git a/doc/tutorial/axis_grids.ipynb b/doc/tutorial/axis_grids.ipynb index c955e615ee..55a906bb49 100644 --- a/doc/tutorial/axis_grids.ipynb +++ b/doc/tutorial/axis_grids.ipynb @@ -201,7 +201,7 @@ "outputs": [], "source": [ "titanic = sns.load_dataset(\"titanic\")\n", - "titanic = titanic.assign(deck=titanic.deck.astype(object)).sort(\"deck\")\n", + "titanic = titanic.assign(deck=titanic.deck.astype(object)).sort_values(\"deck\")\n", "g = sns.FacetGrid(titanic, col=\"class\", sharex=False,\n", " gridspec_kws={\"width_ratios\": [5, 3, 3]})\n", "g.map(sns.boxplot, \"deck\", \"age\");" @@ -642,6 +642,15 @@ "source": [ "g = sns.pairplot(iris, hue=\"species\", palette=\"Set2\", diag_kind=\"kde\", size=2.5)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] } ], "metadata": { @@ -660,9 +669,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", - "version": "2.7.10" + "version": "2.7.12" } }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file diff --git a/doc/tutorial/categorical.ipynb b/doc/tutorial/categorical.ipynb index d540ab2c1a..0f8661cd0b 100644 --- a/doc/tutorial/categorical.ipynb +++ b/doc/tutorial/categorical.ipynb @@ -637,4 +637,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index e2430ad084..2ab10d1058 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -778,4 +778,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file diff --git a/doc/tutorial/distributions.ipynb b/doc/tutorial/distributions.ipynb index 22aa12772d..ae0eb94e8b 100644 --- a/doc/tutorial/distributions.ipynb +++ b/doc/tutorial/distributions.ipynb @@ -507,4 +507,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file diff --git a/doc/tutorial/regression.ipynb b/doc/tutorial/regression.ipynb index 24c5ac3a2e..8763255fea 100644 --- a/doc/tutorial/regression.ipynb +++ b/doc/tutorial/regression.ipynb @@ -590,4 +590,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file diff --git a/doc/tutorial/tools/nb_to_doc.py b/doc/tutorial/tools/nb_to_doc.py index 47c3a7365a..c0a2cbae63 100755 --- a/doc/tutorial/tools/nb_to_doc.py +++ b/doc/tutorial/tools/nb_to_doc.py @@ -10,15 +10,15 @@ def convert_nb(nbname): # Execute the notebook - sh(["ipython", "nbconvert", "--to", "notebook", - "--execute", "--inplace", nbname + ".ipynb"]) + sh(["jupyter", "nbconvert", "--to", "notebook", + "--execute", "--inplace", nbname]) # Convert to .rst for Sphinx - sh(["ipython", "nbconvert", "--to", "rst", nbname + ".ipynb"]) + sh(["jupyter", "nbconvert", "--to", "rst", nbname]) # Clear notebook output - sh(["ipython", "nbconvert", "--to", "notebook", "--inplace", - "--ClearOutputPreprocessor.enabled=True", nbname + ".ipynb"]) + sh(["jupyter", "nbconvert", "--to", "notebook", "--inplace", + "--ClearOutputPreprocessor.enabled=True", nbname]) if __name__ == "__main__": From 93494c6b5b5cc74cdc645ffb51fa20156989bb42 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 16:23:30 -0400 Subject: [PATCH 0431/1738] Fix API headers --- doc/api.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 315ecc584f..a564ee8a3e 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -2,6 +2,9 @@ .. currentmodule:: seaborn +API reference +============= + .. _grid_api: Axis grids @@ -18,9 +21,6 @@ Axis grids JointGrid jointplot -API reference -============= - .. _categorical_api: Categorical plots From 852ef3e9463f5d878ab1e4007e908378b0070e21 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 16:26:45 -0400 Subject: [PATCH 0432/1738] Update doc style --- doc/_static/style.css | 6 ++++++ doc/conf.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/_static/style.css b/doc/_static/style.css index 2cb8474df5..3a5a90b127 100644 --- a/doc/_static/style.css +++ b/doc/_static/style.css @@ -1,3 +1,9 @@ +h1 { font-size: 40px !important; } +h2 { font-size: 32px !important; } +h3 { font-size: 24px !important; } +h4 { font-size: 18px !important; } +h5 { font-size: 14px !important; } +h6 { font-size: 10px !important; } blockquote p { font-size: 14px !important; diff --git a/doc/conf.py b/doc/conf.py index 6cb2b7043c..0a89486c2e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -125,7 +125,7 @@ # documentation. html_theme_options = { 'source_link_position': "footer", - 'bootswatch_theme': "flatly", + 'bootswatch_theme': "paper", 'navbar_sidebarrel': False, 'bootstrap_version': "3", 'navbar_links': [("API", "api"), From 31f2840087ba5e05d6d69e2de01bcbe2e5926827 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 16:32:45 -0400 Subject: [PATCH 0433/1738] Move pairplot and jointplot --- doc/_static/favicon.ico | Bin 4286 -> 21662 bytes doc/releases/v0.8.0.txt | 2 + seaborn/axisgrid.py | 438 +++++++++++++++++++++++++++- seaborn/distributions.py | 230 +-------------- seaborn/regression.py | 212 +------------- seaborn/tests/test_axisgrid.py | 118 +++++++- seaborn/tests/test_distributions.py | 105 ------- 7 files changed, 553 insertions(+), 552 deletions(-) diff --git a/doc/_static/favicon.ico b/doc/_static/favicon.ico index e928e78b3a6c2d78dfad594a8d798fe93e18c207..7be543d48e7ff823dc321740c6e523772e2b2278 100644 GIT binary patch literal 21662 zcmeI4TWl3Y7{|8-1@Rg*>XWVdpz%RT)Wl0-8xyrA_+Z2rACMO;D54?3ASSeG1fmfz z@e-{{f|n5SQX+<^QBEXj6cxNyt%#?fplIUp5^c2h`1|jfIh}4!dv<$nyK5o;GvCg9 z`_1?LcV^Da?DTkE6aPCpJU&~!wevjhWY6=?24r~~fb#u+*LvRUqYEfxcwRoA_tCp- z?gFq2^n;&3-rxh$Hb?I!u44_y*JgaZ5^M(_f^WegU}Ljsj=XQcN1zYfXl;y_qH`)( z3}kZz+zXmOthteYiiz}m@F@@n!$7=z4tl^Up!U!PP618h@lY#z%yMy7QjUGKn>9cjZ3S`WXaoL<8}*fwDv3MFz0@U+HUagu`#~Hz5;y;V zJ>bMB@l`{YI*$Q|!H+;=Q=B+@2!F)Qeo%LAEWgx!4NyN@2;#zzxKW?l4Q5+k8W1k1UTo2TS)ZZJzk9?u-bHFd)3(&AWWqGkFzRUocLu$^| z5Pqn4CeVE52heJLi%W`6c062y+z5_VQ?J_aRdM0O^#i{yH~u|JT5o>tpw7R*GpM^A3^`{c?Hn3 zT?VSgky{UTwb%Z-o0k+Bzb^&ir>bi)8$WiB1KQW^vUy2L@mu>Hn)AN_s@{IEY1hIX zN#e!zl^EUu`)b6GofkHA@6?Sqsthlyf!@PaZ9mBRfX!YoV6T&y;{Vk^W6*=3Dt$~m zU`uP5Lm;+$Jz<^~{+|FehQ11_$b5J%M+)Ao-EU6`Y(aMAK{cm;YaJ; zU%{BMEU6p?II#2S6^bWN7M7!(+_y8Sd<6Y=IWU}PFI49kj&jQ)$Z9)}1nWuawHvqL z=Q8p%zG@r^+h(ku-E-?qDjz|=T@DOCT3hZ0yWFynvfXdKI9N|ouidz94CJi@YC}=i z9Iu%Bj|x>{Uc08hJp@M@ z%QIo@rbUN%*8z>u>%l~A$i53q^!uEs&kauW{zv?1K0OgX+S8EUny7UoU7wi00$Mjr z#E;&eY3`Hw_gzt+7cuMmU4Qd95kH?tx~}e(iCn4+ZBz8nczZd@^>^au4y(F+Auu8QeD1 zWAZOZORSTL0}h(OZy>mqf0sOsJ<}#*H%<4zK_AcM|G_#5a&Q7*)?+j}xe)RYEz;^&OO{1zv!whVJYkgj literal 4286 zcmb`Le@s?Y7{~7`Zxa<0GqKVX%`M%mW~S?pN@HuLA>_*o3X1VdK?ev|O^q=?&`eEb zfrg?f!Lit^fg!?@W7yHN~vy9qy59qF)OZGp4u7(9O%Hho#4Lb?kB;Cia%DNb>Kq(ERZ#LwkKma z7{6xpmWZsYUq)}do0GV*EpuJm(IVZqh`dM8?E{r?O6uso2nyb%e*m;W$bab{(o+UL z$_IN;QG7~UZp;Riy*#xPZB?u%-!0ZxcNFRKKb9DDUj_%vlQ!DbkSlex%OLMjxlJ1y zUd8b#4QAUcu`dL*F??sEzi&kWIdkA-k-osYSYILIXrh0D_@kWzwP3|38ty=Uv{%D* zc;q=Geg?E$hnx0 z&oGepkw3nZLH2-ioDXSCBKB^6?E1qtpGvwOeE+bGhuh4_F6=erlxOz=S=SNdDuq0J zo`MT-0{Y^Y2j@WcK^YX_C;PA&V$J+g;y(;z>__dsKNb>GW7r2`I}ayR#G5N`N8$BO@U{9ZErWvo%q0h2!{ofUH3PzA@f z8sidswA8U3f}g=cjy$LmJ2|Rhr>Sd}#L1kNuxD;W_?Mg}?mJ>4p(Q5Q?nVq}AS@f7 zHrQ-vY{l;?$ozU?k2ma-u*tK_bN2^1OEQOpK)&m9P2J8V@v?WSd0#a}1sqlZA%!!E zYlomBJ>qz=p437AK5!wooSAzNN54E{t)Xqf_5+**vCjorNBO4sg3RO4&bpoJG6v@0 z5Gc-cW5&9r7jqL<-NerOVhLjp9AN0ky4QhxGo}D$=e+x4lV^WJ+SrdkiPX`}0OvW8 z_Txayk{Ow?YFlK6%8yTN$V$^4xe2l1#@MHmTlSXh`CvmM6OHq+bN9$MrU9}I`$+5# zkUcDS+>5a8ak>xN$&UxO`iTxSs|gdm`)wta;@%>?0(O+~IH^hAo+s z>(GOCj5X{7vE76uX`}lgG=lRCi|zk_&QL#FSv94ssS33pREY~V&6HSj%f0D0$T2iz zZwDHEsn})Sh8TT(cf7xldRIZ$5|$oc5|+O7&jQ^??xhKmgWRDYdwaaJGuQgo&{s@b z_M^;&z)OD&H>WUG}|; zp`SvGd_$k;j zjKt4vw^A}^|B8mYp&@7Mlu=(py$PN++B$Xl-VN`GZs%BXg@Eie=ekqA54bqj`bp|? zZ~h+S{?G!co#T62C!ZBg!!O_s_3-ZYYO@BeW2Uwa9+T8Q*qY>Uu_mcLu1ck`sj5;?s6go- D`%ydS diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index f5f76c8888..b318edc4f9 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -28,6 +28,8 @@ v0.8.0 (Unreleased) - New matplotlib qualitative palettes (e.g. "tab10") are now handled correctly. +- Some modules and functions have been internally reorganized; there should be no effect on code that uses the ``seaborn`` namespace. + - Added a deprecation warning to func:`tsplot` function to indicate that it will be removed or replaced with a substantially altered version in a future release. - The ``interactplot`` and ``coefplot`` functions are officially deprecated and will be removed in a future release. diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index a0599c821f..c47ac3757a 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -6,15 +6,17 @@ import numpy as np import pandas as pd +from scipy import stats import matplotlib as mpl import matplotlib.pyplot as plt from . import utils -from .palettes import color_palette +from .palettes import color_palette, blend_palette from .external.six import string_types +from .distributions import distplot, kdeplot, _freedman_diaconis_bins -__all__ = ["FacetGrid", "PairGrid", "JointGrid"] +__all__ = ["FacetGrid", "PairGrid", "JointGrid", "pairplot", "jointplot"] class Grid(object): @@ -1845,3 +1847,435 @@ def savefig(self, *args, **kwargs): """Wrap figure.savefig defaulting to tight bounding box.""" kwargs.setdefault("bbox_inches", "tight") self.fig.savefig(*args, **kwargs) + + +def pairplot(data, hue=None, hue_order=None, palette=None, + vars=None, x_vars=None, y_vars=None, + kind="scatter", diag_kind="hist", markers=None, + size=2.5, aspect=1, dropna=True, + plot_kws=None, diag_kws=None, grid_kws=None): + """Plot pairwise relationships in a dataset. + + By default, this function will create a grid of Axes such that each + variable in ``data`` will by shared in the y-axis across a single row and + in the x-axis across a single column. The diagonal Axes are treated + differently, drawing a plot to show the univariate distribution of the data + for the variable in that column. + + It is also possible to show a subset of variables or plot different + variables on the rows and columns. + + This is a high-level interface for :class:`PairGrid` that is intended to + make it easy to draw a few common styles. You should use :class`PairGrid` + directly if you need more flexibility. + + Parameters + ---------- + data : DataFrame + Tidy (long-form) dataframe where each column is a variable and + each row is an observation. + hue : string (variable name), optional + Variable in ``data`` to map plot aspects to different colors. + hue_order : list of strings + Order for the levels of the hue variable in the palette + palette : dict or seaborn color palette + Set of colors for mapping the ``hue`` variable. If a dict, keys + should be values in the ``hue`` variable. + vars : list of variable names, optional + Variables within ``data`` to use, otherwise use every column with + a numeric datatype. + {x, y}_vars : lists of variable names, optional + Variables within ``data`` to use separately for the rows and + columns of the figure; i.e. to make a non-square plot. + kind : {'scatter', 'reg'}, optional + Kind of plot for the non-identity relationships. + diag_kind : {'hist', 'kde'}, optional + Kind of plot for the diagonal subplots. + markers : single matplotlib marker code or list, optional + Either the marker to use for all datapoints or a list of markers with + a length the same as the number of levels in the hue variable so that + differently colored points will also have different scatterplot + markers. + size : scalar, optional + Height (in inches) of each facet. + aspect : scalar, optional + Aspect * size gives the width (in inches) of each facet. + dropna : boolean, optional + Drop missing values from the data before plotting. + {plot, diag, grid}_kws : dicts, optional + Dictionaries of keyword arguments. + + Returns + ------- + grid : PairGrid + Returns the underlying ``PairGrid`` instance for further tweaking. + + See Also + -------- + PairGrid : Subplot grid for more flexible plotting of pairwise + relationships. + + Examples + -------- + + Draw scatterplots for joint relationships and histograms for univariate + distributions: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set(style="ticks", color_codes=True) + >>> iris = sns.load_dataset("iris") + >>> g = sns.pairplot(iris) + + Show different levels of a categorical variable by the color of plot + elements: + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, hue="species") + + Use a different color palette: + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, hue="species", palette="husl") + + Use different markers for each level of the hue variable: + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, hue="species", markers=["o", "s", "D"]) + + Plot a subset of variables: + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, vars=["sepal_width", "sepal_length"]) + + Draw larger plots: + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, size=3, + ... vars=["sepal_width", "sepal_length"]) + + Plot different variables in the rows and columns: + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, + ... x_vars=["sepal_width", "sepal_length"], + ... y_vars=["petal_width", "petal_length"]) + + Use kernel density estimates for univariate plots: + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, diag_kind="kde") + + Fit linear regression models to the scatter plots: + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, kind="reg") + + Pass keyword arguments down to the underlying functions (it may be easier + to use :class:`PairGrid` directly): + + .. plot:: + :context: close-figs + + >>> g = sns.pairplot(iris, diag_kind="kde", markers="+", + ... plot_kws=dict(s=50, edgecolor="b", linewidth=1), + ... diag_kws=dict(shade=True)) + + """ + if plot_kws is None: + plot_kws = {} + if diag_kws is None: + diag_kws = {} + if grid_kws is None: + grid_kws = {} + + # Set up the PairGrid + diag_sharey = diag_kind == "hist" + grid = PairGrid(data, vars=vars, x_vars=x_vars, y_vars=y_vars, hue=hue, + hue_order=hue_order, palette=palette, + diag_sharey=diag_sharey, + size=size, aspect=aspect, dropna=dropna, **grid_kws) + + # Add the markers here as PairGrid has figured out how many levels of the + # hue variable are needed and we don't want to duplicate that process + if markers is not None: + if grid.hue_names is None: + n_markers = 1 + else: + n_markers = len(grid.hue_names) + if not isinstance(markers, list): + markers = [markers] * n_markers + if len(markers) != n_markers: + raise ValueError(("markers must be a singeton or a list of markers" + " for each level of the hue variable")) + grid.hue_kws = {"marker": markers} + + # Maybe plot on the diagonal + if grid.square_grid: + if diag_kind == "hist": + grid.map_diag(plt.hist, **diag_kws) + elif diag_kind == "kde": + diag_kws["legend"] = False + grid.map_diag(kdeplot, **diag_kws) + + # Maybe plot on the off-diagonals + if grid.square_grid and diag_kind is not None: + plotter = grid.map_offdiag + else: + plotter = grid.map + + if kind == "scatter": + plot_kws.setdefault("edgecolor", "white") + plotter(plt.scatter, **plot_kws) + elif kind == "reg": + from .regression import regplot # Avoid circular import + plotter(regplot, **plot_kws) + + # Add a legend + if hue is not None: + grid.add_legend() + + return grid + + +def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, + color=None, size=6, ratio=5, space=.2, + dropna=True, xlim=None, ylim=None, + joint_kws=None, marginal_kws=None, annot_kws=None, **kwargs): + """Draw a plot of two variables with bivariate and univariate graphs. + + This function provides a convenient interface to the :class:`JointGrid` + class, with several canned plot kinds. This is intended to be a fairly + lightweight wrapper; if you need more flexibility, you should use + :class:`JointGrid` directly. + + Parameters + ---------- + x, y : strings or vectors + Data or names of variables in ``data``. + data : DataFrame, optional + DataFrame when ``x`` and ``y`` are variable names. + kind : { "scatter" | "reg" | "resid" | "kde" | "hex" }, optional + Kind of plot to draw. + stat_func : callable or None, optional + Function used to calculate a statistic about the relationship and + annotate the plot. Should map `x` and `y` either to a single value + or to a (value, p) tuple. Set to ``None`` if you don't want to + annotate the plot. + color : matplotlib color, optional + Color used for the plot elements. + size : numeric, optional + Size of the figure (it will be square). + ratio : numeric, optional + Ratio of joint axes size to marginal axes height. + space : numeric, optional + Space between the joint and marginal axes + dropna : bool, optional + If True, remove observations that are missing from ``x`` and ``y``. + {x, y}lim : two-tuples, optional + Axis limits to set before plotting. + {joint, marginal, annot}_kws : dicts, optional + Additional keyword arguments for the plot components. + kwargs : key, value pairings + Additional keyword arguments are passed to the function used to + draw the plot on the joint Axes, superseding items in the + ``joint_kws`` dictionary. + + Returns + ------- + grid : :class:`JointGrid` + :class:`JointGrid` object with the plot on it. + + See Also + -------- + JointGrid : The Grid class used for drawing this plot. Use it directly if + you need more flexibility. + + Examples + -------- + + Draw a scatterplot with marginal histograms: + + .. plot:: + :context: close-figs + + >>> import numpy as np, pandas as pd; np.random.seed(0) + >>> import seaborn as sns; sns.set(style="white", color_codes=True) + >>> tips = sns.load_dataset("tips") + >>> g = sns.jointplot(x="total_bill", y="tip", data=tips) + + Add regression and kernel density fits: + + .. plot:: + :context: close-figs + + >>> g = sns.jointplot("total_bill", "tip", data=tips, kind="reg") + + Replace the scatterplot with a joint histogram using hexagonal bins: + + .. plot:: + :context: close-figs + + >>> g = sns.jointplot("total_bill", "tip", data=tips, kind="hex") + + Replace the scatterplots and histograms with density estimates and align + the marginal Axes tightly with the joint Axes: + + .. plot:: + :context: close-figs + + >>> iris = sns.load_dataset("iris") + >>> g = sns.jointplot("sepal_width", "petal_length", data=iris, + ... kind="kde", space=0, color="g") + + Use a different statistic for the annotation: + + .. plot:: + :context: close-figs + + >>> from scipy.stats import spearmanr + >>> g = sns.jointplot("size", "total_bill", data=tips, + ... stat_func=spearmanr, color="m") + + Draw a scatterplot, then add a joint density estimate: + + .. plot:: + :context: close-figs + + >>> g = (sns.jointplot("sepal_length", "sepal_width", + ... data=iris, color="k") + ... .plot_joint(sns.kdeplot, zorder=0, n_levels=6)) + + Pass vectors in directly without using Pandas, then name the axes: + + .. plot:: + :context: close-figs + + >>> x, y = np.random.randn(2, 300) + >>> g = (sns.jointplot(x, y, kind="hex", stat_func=None) + ... .set_axis_labels("x", "y")) + + Draw a smaller figure with more space devoted to the marginal plots: + + .. plot:: + :context: close-figs + + >>> g = sns.jointplot("total_bill", "tip", data=tips, + ... size=5, ratio=3, color="g") + + Pass keyword arguments down to the underlying plots: + + .. plot:: + :context: close-figs + + >>> g = sns.jointplot("petal_length", "sepal_length", data=iris, + ... marginal_kws=dict(bins=15, rug=True), + ... annot_kws=dict(stat="r"), + ... s=40, edgecolor="w", linewidth=1) + + """ + # Set up empty default kwarg dicts + if joint_kws is None: + joint_kws = {} + joint_kws.update(kwargs) + if marginal_kws is None: + marginal_kws = {} + if annot_kws is None: + annot_kws = {} + + # Make a colormap based off the plot color + if color is None: + color = color_palette()[0] + color_rgb = mpl.colors.colorConverter.to_rgb(color) + colors = [utils.set_hls_values(color_rgb, l=l) + for l in np.linspace(1, 0, 12)] + cmap = blend_palette(colors, as_cmap=True) + + # Initialize the JointGrid object + grid = JointGrid(x, y, data, dropna=dropna, + size=size, ratio=ratio, space=space, + xlim=xlim, ylim=ylim) + + # Plot the data using the grid + if kind == "scatter": + + joint_kws.setdefault("color", color) + grid.plot_joint(plt.scatter, **joint_kws) + + marginal_kws.setdefault("kde", False) + marginal_kws.setdefault("color", color) + grid.plot_marginals(distplot, **marginal_kws) + + elif kind.startswith("hex"): + + x_bins = min(_freedman_diaconis_bins(grid.x), 50) + y_bins = min(_freedman_diaconis_bins(grid.y), 50) + gridsize = int(np.mean([x_bins, y_bins])) + + joint_kws.setdefault("gridsize", gridsize) + joint_kws.setdefault("cmap", cmap) + grid.plot_joint(plt.hexbin, **joint_kws) + + marginal_kws.setdefault("kde", False) + marginal_kws.setdefault("color", color) + grid.plot_marginals(distplot, **marginal_kws) + + elif kind.startswith("kde"): + + joint_kws.setdefault("shade", True) + joint_kws.setdefault("cmap", cmap) + grid.plot_joint(kdeplot, **joint_kws) + + marginal_kws.setdefault("shade", True) + marginal_kws.setdefault("color", color) + grid.plot_marginals(kdeplot, **marginal_kws) + + elif kind.startswith("reg"): + + from .regression import regplot + + marginal_kws.setdefault("color", color) + grid.plot_marginals(distplot, **marginal_kws) + + joint_kws.setdefault("color", color) + grid.plot_joint(regplot, **joint_kws) + + elif kind.startswith("resid"): + + from .regression import residplot + + joint_kws.setdefault("color", color) + grid.plot_joint(residplot, **joint_kws) + + x, y = grid.ax_joint.collections[0].get_offsets().T + marginal_kws.setdefault("color", color) + marginal_kws.setdefault("kde", False) + distplot(x, ax=grid.ax_marg_x, **marginal_kws) + distplot(y, vertical=True, fit=stats.norm, ax=grid.ax_marg_y, + **marginal_kws) + stat_func = None + else: + msg = "kind must be either 'scatter', 'reg', 'resid', 'kde', or 'hex'" + raise ValueError(msg) + + if stat_func is not None: + grid.annotate(stat_func, **annot_kws) + + return grid diff --git a/seaborn/distributions.py b/seaborn/distributions.py index eb5996bb58..43a93d74f5 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -15,12 +15,11 @@ except ImportError: _has_statsmodels = False -from .utils import set_hls_values, iqr, _kde_support +from .utils import iqr, _kde_support from .palettes import color_palette, blend_palette -from .axisgrid import JointGrid -__all__ = ["distplot", "kdeplot", "rugplot", "jointplot"] +__all__ = ["distplot", "kdeplot", "rugplot"] def _freedman_diaconis_bins(a): @@ -668,228 +667,3 @@ def rugplot(a, height=.05, axis="x", ax=None, **kwargs): func(pt, 0, height, **kwargs) return ax - - -def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, - color=None, size=6, ratio=5, space=.2, - dropna=True, xlim=None, ylim=None, - joint_kws=None, marginal_kws=None, annot_kws=None, **kwargs): - """Draw a plot of two variables with bivariate and univariate graphs. - - This function provides a convenient interface to the :class:`JointGrid` - class, with several canned plot kinds. This is intended to be a fairly - lightweight wrapper; if you need more flexibility, you should use - :class:`JointGrid` directly. - - Parameters - ---------- - x, y : strings or vectors - Data or names of variables in ``data``. - data : DataFrame, optional - DataFrame when ``x`` and ``y`` are variable names. - kind : { "scatter" | "reg" | "resid" | "kde" | "hex" }, optional - Kind of plot to draw. - stat_func : callable or None, optional - Function used to calculate a statistic about the relationship and - annotate the plot. Should map `x` and `y` either to a single value - or to a (value, p) tuple. Set to ``None`` if you don't want to - annotate the plot. - color : matplotlib color, optional - Color used for the plot elements. - size : numeric, optional - Size of the figure (it will be square). - ratio : numeric, optional - Ratio of joint axes size to marginal axes height. - space : numeric, optional - Space between the joint and marginal axes - dropna : bool, optional - If True, remove observations that are missing from ``x`` and ``y``. - {x, y}lim : two-tuples, optional - Axis limits to set before plotting. - {joint, marginal, annot}_kws : dicts, optional - Additional keyword arguments for the plot components. - kwargs : key, value pairings - Additional keyword arguments are passed to the function used to - draw the plot on the joint Axes, superseding items in the - ``joint_kws`` dictionary. - - Returns - ------- - grid : :class:`JointGrid` - :class:`JointGrid` object with the plot on it. - - See Also - -------- - JointGrid : The Grid class used for drawing this plot. Use it directly if - you need more flexibility. - - Examples - -------- - - Draw a scatterplot with marginal histograms: - - .. plot:: - :context: close-figs - - >>> import numpy as np, pandas as pd; np.random.seed(0) - >>> import seaborn as sns; sns.set(style="white", color_codes=True) - >>> tips = sns.load_dataset("tips") - >>> g = sns.jointplot(x="total_bill", y="tip", data=tips) - - Add regression and kernel density fits: - - .. plot:: - :context: close-figs - - >>> g = sns.jointplot("total_bill", "tip", data=tips, kind="reg") - - Replace the scatterplot with a joint histogram using hexagonal bins: - - .. plot:: - :context: close-figs - - >>> g = sns.jointplot("total_bill", "tip", data=tips, kind="hex") - - Replace the scatterplots and histograms with density estimates and align - the marginal Axes tightly with the joint Axes: - - .. plot:: - :context: close-figs - - >>> iris = sns.load_dataset("iris") - >>> g = sns.jointplot("sepal_width", "petal_length", data=iris, - ... kind="kde", space=0, color="g") - - Use a different statistic for the annotation: - - .. plot:: - :context: close-figs - - >>> from scipy.stats import spearmanr - >>> g = sns.jointplot("size", "total_bill", data=tips, - ... stat_func=spearmanr, color="m") - - Draw a scatterplot, then add a joint density estimate: - - .. plot:: - :context: close-figs - - >>> g = (sns.jointplot("sepal_length", "sepal_width", - ... data=iris, color="k") - ... .plot_joint(sns.kdeplot, zorder=0, n_levels=6)) - - Pass vectors in directly without using Pandas, then name the axes: - - .. plot:: - :context: close-figs - - >>> x, y = np.random.randn(2, 300) - >>> g = (sns.jointplot(x, y, kind="hex", stat_func=None) - ... .set_axis_labels("x", "y")) - - Draw a smaller figure with more space devoted to the marginal plots: - - .. plot:: - :context: close-figs - - >>> g = sns.jointplot("total_bill", "tip", data=tips, - ... size=5, ratio=3, color="g") - - Pass keyword arguments down to the underlying plots: - - .. plot:: - :context: close-figs - - >>> g = sns.jointplot("petal_length", "sepal_length", data=iris, - ... marginal_kws=dict(bins=15, rug=True), - ... annot_kws=dict(stat="r"), - ... s=40, edgecolor="w", linewidth=1) - - """ - # Set up empty default kwarg dicts - if joint_kws is None: - joint_kws = {} - joint_kws.update(kwargs) - if marginal_kws is None: - marginal_kws = {} - if annot_kws is None: - annot_kws = {} - - # Make a colormap based off the plot color - if color is None: - color = color_palette()[0] - color_rgb = mpl.colors.colorConverter.to_rgb(color) - colors = [set_hls_values(color_rgb, l=l) for l in np.linspace(1, 0, 12)] - cmap = blend_palette(colors, as_cmap=True) - - # Initialize the JointGrid object - grid = JointGrid(x, y, data, dropna=dropna, - size=size, ratio=ratio, space=space, - xlim=xlim, ylim=ylim) - - # Plot the data using the grid - if kind == "scatter": - - joint_kws.setdefault("color", color) - grid.plot_joint(plt.scatter, **joint_kws) - - marginal_kws.setdefault("kde", False) - marginal_kws.setdefault("color", color) - grid.plot_marginals(distplot, **marginal_kws) - - elif kind.startswith("hex"): - - x_bins = min(_freedman_diaconis_bins(grid.x), 50) - y_bins = min(_freedman_diaconis_bins(grid.y), 50) - gridsize = int(np.mean([x_bins, y_bins])) - - joint_kws.setdefault("gridsize", gridsize) - joint_kws.setdefault("cmap", cmap) - grid.plot_joint(plt.hexbin, **joint_kws) - - marginal_kws.setdefault("kde", False) - marginal_kws.setdefault("color", color) - grid.plot_marginals(distplot, **marginal_kws) - - elif kind.startswith("kde"): - - joint_kws.setdefault("shade", True) - joint_kws.setdefault("cmap", cmap) - grid.plot_joint(kdeplot, **joint_kws) - - marginal_kws.setdefault("shade", True) - marginal_kws.setdefault("color", color) - grid.plot_marginals(kdeplot, **marginal_kws) - - elif kind.startswith("reg"): - - from .regression import regplot - - marginal_kws.setdefault("color", color) - grid.plot_marginals(distplot, **marginal_kws) - - joint_kws.setdefault("color", color) - grid.plot_joint(regplot, **joint_kws) - - elif kind.startswith("resid"): - - from .regression import residplot - - joint_kws.setdefault("color", color) - grid.plot_joint(residplot, **joint_kws) - - x, y = grid.ax_joint.collections[0].get_offsets().T - marginal_kws.setdefault("color", color) - marginal_kws.setdefault("kde", False) - distplot(x, ax=grid.ax_marg_x, **marginal_kws) - distplot(y, vertical=True, fit=stats.norm, ax=grid.ax_marg_y, - **marginal_kws) - stat_func = None - else: - msg = "kind must be either 'scatter', 'reg', 'resid', 'kde', or 'hex'" - raise ValueError(msg) - - if stat_func is not None: - grid.annotate(stat_func, **annot_kws) - - return grid diff --git a/seaborn/regression.py b/seaborn/regression.py index 2b0abbc5ea..55530c1679 100644 --- a/seaborn/regression.py +++ b/seaborn/regression.py @@ -24,13 +24,10 @@ from . import utils from . import algorithms as algo from .palettes import color_palette -from .axisgrid import FacetGrid, PairGrid, _facet_docs -from .distributions import kdeplot +from .axisgrid import FacetGrid, _facet_docs -__all__ = ["lmplot", "regplot", "residplot", - "coefplot", "interactplot", - "pairplot"] +__all__ = ["lmplot", "regplot", "residplot"] class _LinearPlotter(object): @@ -1264,208 +1261,3 @@ def interactplot(x1, x2, y, data=None, filled=False, cmap="RdBu_r", bar.set_label(clabel, labelpad=15, rotation=270) return ax - - -def pairplot(data, hue=None, hue_order=None, palette=None, - vars=None, x_vars=None, y_vars=None, - kind="scatter", diag_kind="hist", markers=None, - size=2.5, aspect=1, dropna=True, - plot_kws=None, diag_kws=None, grid_kws=None): - """Plot pairwise relationships in a dataset. - - By default, this function will create a grid of Axes such that each - variable in ``data`` will by shared in the y-axis across a single row and - in the x-axis across a single column. The diagonal Axes are treated - differently, drawing a plot to show the univariate distribution of the data - for the variable in that column. - - It is also possible to show a subset of variables or plot different - variables on the rows and columns. - - This is a high-level interface for :class:`PairGrid` that is intended to - make it easy to draw a few common styles. You should use :class`PairGrid` - directly if you need more flexibility. - - Parameters - ---------- - data : DataFrame - Tidy (long-form) dataframe where each column is a variable and - each row is an observation. - hue : string (variable name), optional - Variable in ``data`` to map plot aspects to different colors. - hue_order : list of strings - Order for the levels of the hue variable in the palette - palette : dict or seaborn color palette - Set of colors for mapping the ``hue`` variable. If a dict, keys - should be values in the ``hue`` variable. - vars : list of variable names, optional - Variables within ``data`` to use, otherwise use every column with - a numeric datatype. - {x, y}_vars : lists of variable names, optional - Variables within ``data`` to use separately for the rows and - columns of the figure; i.e. to make a non-square plot. - kind : {'scatter', 'reg'}, optional - Kind of plot for the non-identity relationships. - diag_kind : {'hist', 'kde'}, optional - Kind of plot for the diagonal subplots. - markers : single matplotlib marker code or list, optional - Either the marker to use for all datapoints or a list of markers with - a length the same as the number of levels in the hue variable so that - differently colored points will also have different scatterplot - markers. - size : scalar, optional - Height (in inches) of each facet. - aspect : scalar, optional - Aspect * size gives the width (in inches) of each facet. - dropna : boolean, optional - Drop missing values from the data before plotting. - {plot, diag, grid}_kws : dicts, optional - Dictionaries of keyword arguments. - - Returns - ------- - grid : PairGrid - Returns the underlying ``PairGrid`` instance for further tweaking. - - See Also - -------- - PairGrid : Subplot grid for more flexible plotting of pairwise - relationships. - - Examples - -------- - - Draw scatterplots for joint relationships and histograms for univariate - distributions: - - .. plot:: - :context: close-figs - - >>> import seaborn as sns; sns.set(style="ticks", color_codes=True) - >>> iris = sns.load_dataset("iris") - >>> g = sns.pairplot(iris) - - Show different levels of a categorical variable by the color of plot - elements: - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, hue="species") - - Use a different color palette: - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, hue="species", palette="husl") - - Use different markers for each level of the hue variable: - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, hue="species", markers=["o", "s", "D"]) - - Plot a subset of variables: - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, vars=["sepal_width", "sepal_length"]) - - Draw larger plots: - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, size=3, - ... vars=["sepal_width", "sepal_length"]) - - Plot different variables in the rows and columns: - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, - ... x_vars=["sepal_width", "sepal_length"], - ... y_vars=["petal_width", "petal_length"]) - - Use kernel density estimates for univariate plots: - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, diag_kind="kde") - - Fit linear regression models to the scatter plots: - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, kind="reg") - - Pass keyword arguments down to the underlying functions (it may be easier - to use :class:`PairGrid` directly): - - .. plot:: - :context: close-figs - - >>> g = sns.pairplot(iris, diag_kind="kde", markers="+", - ... plot_kws=dict(s=50, edgecolor="b", linewidth=1), - ... diag_kws=dict(shade=True)) - - """ - if plot_kws is None: - plot_kws = {} - if diag_kws is None: - diag_kws = {} - if grid_kws is None: - grid_kws = {} - - # Set up the PairGrid - diag_sharey = diag_kind == "hist" - grid = PairGrid(data, vars=vars, x_vars=x_vars, y_vars=y_vars, hue=hue, - hue_order=hue_order, palette=palette, - diag_sharey=diag_sharey, - size=size, aspect=aspect, dropna=dropna, **grid_kws) - - # Add the markers here as PairGrid has figured out how many levels of the - # hue variable are needed and we don't want to duplicate that process - if markers is not None: - if grid.hue_names is None: - n_markers = 1 - else: - n_markers = len(grid.hue_names) - if not isinstance(markers, list): - markers = [markers] * n_markers - if len(markers) != n_markers: - raise ValueError(("markers must be a singeton or a list of markers" - " for each level of the hue variable")) - grid.hue_kws = {"marker": markers} - - # Maybe plot on the diagonal - if grid.square_grid: - if diag_kind == "hist": - grid.map_diag(plt.hist, **diag_kws) - elif diag_kind == "kde": - diag_kws["legend"] = False - grid.map_diag(kdeplot, **diag_kws) - - # Maybe plot on the off-diagonals - if grid.square_grid and diag_kind is not None: - plotter = grid.map_offdiag - else: - plotter = grid.map - - if kind == "scatter": - plot_kws.setdefault("edgecolor", "white") - plotter(plt.scatter, **plot_kws) - elif kind == "reg": - plotter(regplot, **plot_kws) - - # Add a legend - if hue is not None: - grid.add_legend() - - return grid diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 737bf4fa25..6d081cb3ad 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -19,9 +19,8 @@ from .. import axisgrid as ag from .. import rcmod from ..palettes import color_palette -from ..distributions import kdeplot +from ..distributions import kdeplot, _freedman_diaconis_bins from ..categorical import pointplot -from ..regression import pairplot from ..utils import categorical_order rs = np.random.RandomState(0) @@ -1127,7 +1126,7 @@ def test_nondefault_index(self): def test_pairplot(self): vars = ["x", "y", "z"] - g = pairplot(self.df) + g = ag.pairplot(self.df) for ax in g.diag_axes: nt.assert_equal(len(ax.patches), 10) @@ -1156,7 +1155,7 @@ def test_pairplot(self): def test_pairplot_reg(self): vars = ["x", "y", "z"] - g = pairplot(self.df, kind="reg") + g = ag.pairplot(self.df, kind="reg") for ax in g.diag_axes: nt.assert_equal(len(ax.patches), 10) @@ -1191,7 +1190,7 @@ def test_pairplot_reg(self): def test_pairplot_kde(self): vars = ["x", "y", "z"] - g = pairplot(self.df, diag_kind="kde") + g = ag.pairplot(self.df, diag_kind="kde") for ax in g.diag_axes: nt.assert_equal(len(ax.lines), 1) @@ -1221,12 +1220,12 @@ def test_pairplot_markers(self): vars = ["x", "y", "z"] markers = ["o", "x", "s", "d"] - g = pairplot(self.df, hue="a", vars=vars, markers=markers) + g = ag.pairplot(self.df, hue="a", vars=vars, markers=markers) nt.assert_equal(g.hue_kws["marker"], markers) plt.close("all") with nt.assert_raises(ValueError): - g = pairplot(self.df, hue="a", vars=vars, markers=markers[:-2]) + g = ag.pairplot(self.df, hue="a", vars=vars, markers=markers[:-2]) class TestJointGrid(PlotTestCase): @@ -1368,3 +1367,108 @@ def test_space(self): nt.assert_equal(joint_bounds[2], marg_x_bounds[2]) nt.assert_equal(joint_bounds[3], marg_y_bounds[3]) + + +class TestJointPlot(PlotTestCase): + + rs = np.random.RandomState(sum(map(ord, "jointplot"))) + x = rs.randn(100) + y = rs.randn(100) + data = pd.DataFrame(dict(x=x, y=y)) + + def test_scatter(self): + + g = ag.jointplot("x", "y", self.data) + nt.assert_equal(len(g.ax_joint.collections), 1) + + x, y = g.ax_joint.collections[0].get_offsets().T + npt.assert_array_equal(self.x, x) + npt.assert_array_equal(self.y, y) + + x_bins = _freedman_diaconis_bins(self.x) + nt.assert_equal(len(g.ax_marg_x.patches), x_bins) + + y_bins = _freedman_diaconis_bins(self.y) + nt.assert_equal(len(g.ax_marg_y.patches), y_bins) + + def test_reg(self): + + g = ag.jointplot("x", "y", self.data, kind="reg") + nt.assert_equal(len(g.ax_joint.collections), 2) + + x, y = g.ax_joint.collections[0].get_offsets().T + npt.assert_array_equal(self.x, x) + npt.assert_array_equal(self.y, y) + + x_bins = _freedman_diaconis_bins(self.x) + nt.assert_equal(len(g.ax_marg_x.patches), x_bins) + + y_bins = _freedman_diaconis_bins(self.y) + nt.assert_equal(len(g.ax_marg_y.patches), y_bins) + + nt.assert_equal(len(g.ax_joint.lines), 1) + nt.assert_equal(len(g.ax_marg_x.lines), 1) + nt.assert_equal(len(g.ax_marg_y.lines), 1) + + def test_resid(self): + + g = ag.jointplot("x", "y", self.data, kind="resid") + nt.assert_equal(len(g.ax_joint.collections), 1) + nt.assert_equal(len(g.ax_joint.lines), 1) + nt.assert_equal(len(g.ax_marg_x.lines), 0) + nt.assert_equal(len(g.ax_marg_y.lines), 1) + + def test_hex(self): + + g = ag.jointplot("x", "y", self.data, kind="hex") + nt.assert_equal(len(g.ax_joint.collections), 1) + + x_bins = _freedman_diaconis_bins(self.x) + nt.assert_equal(len(g.ax_marg_x.patches), x_bins) + + y_bins = _freedman_diaconis_bins(self.y) + nt.assert_equal(len(g.ax_marg_y.patches), y_bins) + + def test_kde(self): + + g = ag.jointplot("x", "y", self.data, kind="kde") + + nt.assert_true(len(g.ax_joint.collections) > 0) + nt.assert_equal(len(g.ax_marg_x.collections), 1) + nt.assert_equal(len(g.ax_marg_y.collections), 1) + + nt.assert_equal(len(g.ax_marg_x.lines), 1) + nt.assert_equal(len(g.ax_marg_y.lines), 1) + + def test_color(self): + + g = ag.jointplot("x", "y", self.data, color="purple") + + purple = mpl.colors.colorConverter.to_rgb("purple") + scatter_color = g.ax_joint.collections[0].get_facecolor()[0, :3] + nt.assert_equal(tuple(scatter_color), purple) + + hist_color = g.ax_marg_x.patches[0].get_facecolor()[:3] + nt.assert_equal(hist_color, purple) + + def test_annotation(self): + + g = ag.jointplot("x", "y", self.data) + nt.assert_equal(len(g.ax_joint.legend_.get_texts()), 1) + + g = ag.jointplot("x", "y", self.data, stat_func=None) + nt.assert_is(g.ax_joint.legend_, None) + + def test_hex_customise(self): + + # test that default gridsize can be overridden + g = ag.jointplot("x", "y", self.data, kind="hex", + joint_kws=dict(gridsize=5)) + nt.assert_equal(len(g.ax_joint.collections), 1) + a = g.ax_joint.collections[0].get_array() + nt.assert_equal(28, a.shape[0]) # 28 hexagons expected for gridsize 5 + + def test_bad_kind(self): + + with nt.assert_raises(ValueError): + ag.jointplot("x", "y", self.data, kind="not_a_kind") diff --git a/seaborn/tests/test_distributions.py b/seaborn/tests/test_distributions.py index 49066e17a0..a146d6d39c 100644 --- a/seaborn/tests/test_distributions.py +++ b/seaborn/tests/test_distributions.py @@ -123,108 +123,3 @@ def test_bivariate_kde_colorbar(self): ax=ax) nt.assert_equal(len(f.axes), 2) nt.assert_equal(f.axes[1].get_ylabel(), "density") - - -class TestJointPlot(PlotTestCase): - - rs = np.random.RandomState(sum(map(ord, "jointplot"))) - x = rs.randn(100) - y = rs.randn(100) - data = pd.DataFrame(dict(x=x, y=y)) - - def test_scatter(self): - - g = dist.jointplot("x", "y", self.data) - nt.assert_equal(len(g.ax_joint.collections), 1) - - x, y = g.ax_joint.collections[0].get_offsets().T - npt.assert_array_equal(self.x, x) - npt.assert_array_equal(self.y, y) - - x_bins = dist._freedman_diaconis_bins(self.x) - nt.assert_equal(len(g.ax_marg_x.patches), x_bins) - - y_bins = dist._freedman_diaconis_bins(self.y) - nt.assert_equal(len(g.ax_marg_y.patches), y_bins) - - def test_reg(self): - - g = dist.jointplot("x", "y", self.data, kind="reg") - nt.assert_equal(len(g.ax_joint.collections), 2) - - x, y = g.ax_joint.collections[0].get_offsets().T - npt.assert_array_equal(self.x, x) - npt.assert_array_equal(self.y, y) - - x_bins = dist._freedman_diaconis_bins(self.x) - nt.assert_equal(len(g.ax_marg_x.patches), x_bins) - - y_bins = dist._freedman_diaconis_bins(self.y) - nt.assert_equal(len(g.ax_marg_y.patches), y_bins) - - nt.assert_equal(len(g.ax_joint.lines), 1) - nt.assert_equal(len(g.ax_marg_x.lines), 1) - nt.assert_equal(len(g.ax_marg_y.lines), 1) - - def test_resid(self): - - g = dist.jointplot("x", "y", self.data, kind="resid") - nt.assert_equal(len(g.ax_joint.collections), 1) - nt.assert_equal(len(g.ax_joint.lines), 1) - nt.assert_equal(len(g.ax_marg_x.lines), 0) - nt.assert_equal(len(g.ax_marg_y.lines), 1) - - def test_hex(self): - - g = dist.jointplot("x", "y", self.data, kind="hex") - nt.assert_equal(len(g.ax_joint.collections), 1) - - x_bins = dist._freedman_diaconis_bins(self.x) - nt.assert_equal(len(g.ax_marg_x.patches), x_bins) - - y_bins = dist._freedman_diaconis_bins(self.y) - nt.assert_equal(len(g.ax_marg_y.patches), y_bins) - - def test_kde(self): - - g = dist.jointplot("x", "y", self.data, kind="kde") - - nt.assert_true(len(g.ax_joint.collections) > 0) - nt.assert_equal(len(g.ax_marg_x.collections), 1) - nt.assert_equal(len(g.ax_marg_y.collections), 1) - - nt.assert_equal(len(g.ax_marg_x.lines), 1) - nt.assert_equal(len(g.ax_marg_y.lines), 1) - - def test_color(self): - - g = dist.jointplot("x", "y", self.data, color="purple") - - purple = mpl.colors.colorConverter.to_rgb("purple") - scatter_color = g.ax_joint.collections[0].get_facecolor()[0, :3] - nt.assert_equal(tuple(scatter_color), purple) - - hist_color = g.ax_marg_x.patches[0].get_facecolor()[:3] - nt.assert_equal(hist_color, purple) - - def test_annotation(self): - - g = dist.jointplot("x", "y", self.data) - nt.assert_equal(len(g.ax_joint.legend_.get_texts()), 1) - - g = dist.jointplot("x", "y", self.data, stat_func=None) - nt.assert_is(g.ax_joint.legend_, None) - - def test_hex_customise(self): - - # test that default gridsize can be overridden - g = dist.jointplot("x", "y", self.data, kind="hex", - joint_kws=dict(gridsize=5)) - nt.assert_equal(len(g.ax_joint.collections), 1) - a = g.ax_joint.collections[0].get_array() - nt.assert_equal(28, a.shape[0]) # 28 hexagons expected for gridsize 5 - - def test_bad_kind(self): - - with nt.assert_raises(ValueError): - dist.jointplot("x", "y", self.data, kind="not_a_kind") From 20f409e078d32226f184d359c6beed2d8324e151 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 1 Jul 2017 19:26:31 -0400 Subject: [PATCH 0434/1738] Some updates to the docs --- doc/_static/favicon.ico | Bin 21662 -> 270398 bytes doc/introduction.rst | 31 +++++++++++++++---------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/doc/_static/favicon.ico b/doc/_static/favicon.ico index 7be543d48e7ff823dc321740c6e523772e2b2278..f914098d366cd550d40133cb91fd9fe6438d65aa 100644 GIT binary patch literal 270398 zcmeHQ3$!Igb-lxYBSslNs3al}6b&)UrO_*edUiQB4)_wKrR-Nwq?!Dc8yZ4)Q zru+7(s;)X`?^9L%scyBF;2-~%w6<;SuXG+2>UaE*>bL>r?2-4pVLId;s((~o?d|y<`1lOSG~`am z?;#dfeh2*w$oh>umaV00wtbEQ zKG@H%hd7)Su_gXFwzwJcZOHc^KY=^|8RhvoR=FKQ9$o|Cc;ynvry;8$dqW7Ij+!c?E>?s4QDv!1OL}))r|)-|KgCZE_^=a6=A^Wnr*tdd`nO4`KgJuIJ`Q z7JrJ=qu$FQ{{zXg??30Q7s?XfWrKr&gSC)h&h4`>T7>>a@IQ`e(6&cI zIES%pyH}pJp$zfAnKt+^e9voY7KTzfj;Gf`j)1%f@?^-S4sY1!e3kvwwve46Z-N{R z;oRbG2=k}PUd(Y`EBp`0PLQ!2+dJFk-J+e(hdcyH*^BXESyp*SBmURi20jMhZ_Xi4 zg)C;Qe>?K=xt_=QW%j#|{F_1k3bH%o{g5?~n;?1bGw}Yvl@R8A4x|yB5ABS0p9x9v zpU2-E<_Uizjrd=48#tfC*Z&G(|7&3;rsF-pmqIpiWsPEs@|Qw*eeNZY*F$){j&l@Z z^wp3TK>h;KNWO|e@{9lVw*j9sIU5q=wsnm6!Cwp!-!h)(4|!G8 zZ}{ef48K0xstfXp|Mj-Paq#gEGcXdy0H$YuE8bU>_m0!>5ywN;^^}jhw6QJnivRVr z0msglLHe2J^Z7c?sW*>dMaS%S`pU8o{BtdPz=Xqw zzx@o-KRJaH9j_+$=@ ze?4r#>%i8%ARiNVA?>a)Jm^?;Id1s|{`_l*@8fX)6|wxtBmR%S4cL$8UGwHbqJ_P^edEe$fq}#>$T-)*O=PlpC$2&qo-uu6I4D%zs_&=UD z;C22y*ZS^8x~GPI*YEnp0Qi~r0eEf4=e)mvSm<}875~T22E4zY=N{jkNOxf9bN#L# z_nlAS@3SC2=l%UJx^!-fwBrAG*?`aWebex<^Y;&shH>KU&ez)3m-o)M@b{huHu!w! zf_H+656M{B;Bv#ij{EHQ_ji7E*wD5S{C`pgK3?GCz+pt&v4{cI*v@hP9{BP=-=Ds} zwcm&sa4Y=p@DOgNd<^(F(SEgJK;Wn6!9Q8o`hSHq+xz*nUw^)bJ_ZI(gM_?yzpwGr zBb~uwE=w% z|2zu9Z(xLe41eo7pSa)t<|weTWd>gU&czX=6aTAg1Fna4X@)Nu2TpSS)V6u!{g}^z z3F{ib0WJ^HiT~BL0oU#c`}y$q6-d+bT&MG|w#|$Oz{GzW*jeLnfi&WOHEob*y}s|k zf6JXOwQVNXV}1uN_A&T>hsz`WS2F(beZGeb|8We!XZrPAujc&iIltqx+X5TU&A?Cl zn@C8b{=Zr__#k|eb)S#d`geA|)>i#~hymb(-&ptqB%Ck0-!Eq#=*9o)*x(5GgX=Xq zAEmZ$4gIa(iUA=8fTOP%SUOt#uT<=N4}A4Y!(T2IoDlk1zs3C+27o253FUATZm$&o zE5m zvp776zk!#hWZ>%0p%?$lvq4_&-vU1k^v%sdaHPEB8FPRct_NbR5yNjfivQ)iT}mh;N7r$9`5rV|1-P)H2qT6{k;eBt-|N7EIMKQ48*6|kj`LHPJICQo+mXe9FB|rEej)c)`92UQ>&wX^eG5!o zVBl}9k2&oRJq9qoI*zBd4oT~1IrMsuSL6PXj{E%>0=%7XVDc*H)c+612G<&P=6K+4$dA}|2#?T~@Z?)RdRPO+eW8=RM6Uq1W)$Rd2Lb10^S7y$M*gILd^ zd=Pr^esMP7H9x+u>&E!8?LUP+(QomzNDKgj5MF zzBzGgePixKU{?Kq4$Q(%XF;sK|6lU>Luu6S7l{F2_A3T%*FY!!XYe03+77b8u;aI& z+oVXp>RgH=c`yK6t~PLcHFV;CHvdmIY{)Ue?s+U z{sc*l{b@t>{e}&T#=OoqA7_Eh-3{#Wo513~fnC_=6vKXu30l*~u%Ea%&&4uq0en8w zz%TD*?*nOc3`qF}c6khh>vX63ey*jeYyRSYrLOt=cm_T~XeXFWxhGs_-?BFWI8n zwg6tO{*2GHHu^n4AHT506CwOgS8Bd=sh?i^)yBU3Gur}|mm7E{&Kvz6ppOyQ;_wVx z?dqr3zW6^57yzd4HE?}Abd8+@IRC*0n?UX~?8Iv&OI&(w#r<)?0C4+t1K0d!z4(9d zZZ9-!#BtKw#>L;Nr(%JMuIql@GZN#1d)0F;MgzZF8~7&f`Aw^)#(=DECB92IQeQvL7y!3< zZ)-gy)yEyvVE(i1DZ6m(pT~`}?^SojE@A*UzQw>f-w|qJ|IcS1t@ooZ5r4#=I$!`e z|GdHfiyO-S;|!Z{Oz^5Y_+D*L3Vjsal%3Ex7wok19sci|!G$`$ z2Yi*$<~yO&`u>{x*Lv>HnAYJx=l0fnz$ba>>T(_67id%Jn&-)NjrVGo#xgzzJYHM( z!O!#3)q(%ke1|dNg}#lnFV55z10Juxiv3i7$oFIFz(`L-df*1xuLGHUhUF2Cd7cp>(vJT`8}L*=DT0_{m_;TMq5{hHqvkL zrmh(9cx`?6rcU1j{+Pji&VSxrS6{1Lien)LJpLb=!Hc?l5BPtLcK(?52yLX_;!RyK z;PId9Uq5JYW0jZIA5V|l{?CmzUd}ew)z@m5;#i1*fYbLF-1yHR?YQxt^?jewK92oA z!ZwCB(r@vmt{5QRJ^2pyQ}+OQ&&*Fdj{D~u?c*9K?e87c)z@m5;#i1*fYWOYetbDd zJC6Ln$!OoVT>C;B>9=@OR}47JcebB0_;GWPcHH>SzTH~qjAMg#o9gOowM%g<#DK$m zXL}m9sqsIrn~fXi{rYllpK2SAAMV;0+DN~}o4R7aVZO6H8ru{{_{gvtj5aYQ@R`7F8|vz7wM%g<#DI_c&i)UvP4$y!N78qBQ(>L8&L`(Jd?%pW zj?hN>E#A}>13vCM``2Te;{O*S>ASqCuha(hqdjaV?|Cd}LtTBXb}5d9 z7znYC-`2Xgzm23{z?}-}FF~DB@oZhJzM+luTfC_&1|s-hY4C+ST5zo_lD}&4;5MT@ z*Tw2zS6{1Lien)LBKUs?j`7=9sXlWmlBa5%clF`(3|3oCkF_PVk$#Ieb;Upo|2Xzb zNQ(cbB=R`URcXt*rrN>pn(+IdJ@u`tuhlNau{;<6C#-K{92k}n<4{G8S?!of)iG?J zev3Cv!T@;jUyxM4+4F6T6pt!!{4S#%?X`Ylb!?Ka)mFu}5Chm9__anGZnf&xBwwqoifthVz>O^p?l3NF<>#@uRHE))qYZo}C^={G z+opZ-qiGlbH6?Bg&1zF7AFkMh!0h5uZeDAo7#UBCs;ExY<_ zE6fW6y%+#rjx+eQ#@P&Ms=|Nk`=#f*`fA(jYuQ9L2{8a(zs=zP^)7vd`2TaGzVC4L z)mB&+2C^{#F70gaiPzcM<7sTG(Eq>Rs4M3L&ydfveJ&fxMh*ku%#$)W^?0U5nkw+$ zdgh<+0&4EB=l%7(e;*hiXCS|aq^_y8-vflc3jF`7QO}EAOw(5Vn=p_c1K^bPJk6VE z%Zl*ddf&fi%~D|?KL%v0=GqF}{Rh>vCDIX;pSpkzF)lW!5yxZksAMB zSP}l8Y1EPL0&Xe{EFJ^0U2|;*?!3j|){Pb6|20M(FBg8Bi(mPEFyDh$&o{Wm?`rcJ zVcFOJ?lJ1PQWzKv1F~Op?FU|MWpIo00_w};f7Uwx{~-J}7r*lTV7>>p*}vZpNnHcr z_@GSwf840sdgzu31A}2e_G`BNz@cv%9OLzYGWq{Sqi)l}ZnLo~{}1ke@arOjW4u35 zCjWn6)a@Age{lcHhRwDi_;rlIF+Lwq7XP_M;UA52`d!A9R9}2h4#;yE69RGu3PcV4K ze)dpE7FS21|B6ws3*`Ud_+NHxwjIGS-UGDO)jXjj{@-oz{~+PE*|?Sehv$FrjB977 zo&i`>0{;)dIX4=0TJVihVPSY!kS&{MOYrP$gKKM{8^!qEDwAW5R6KeRy3K{t=HXO6 zAC1q!r}r9syA?W%tE13aYZKof9E=7BvTHN#3Vy%R;6JYojDq7aX9DW7GH<0v%c~B2H{{dIFMbNX;*N2Q;7AfU6IcLuS8u^bAKLxx^UV| zoXYQ`^E-HDeGiP+2Z!Cqr^-9s;Qz0n(^|iMtn(_)jE*yPvMqRJeP8Fw0{MTH!T&4j zWOKDe+^h-)z`3Ih-WB;Cz#T?Cj;e~iR1a~tP8a~^UT^U37tp1~>%%<$PMnAL_fzx# zm(|JUYKvk>RWJa~J=@?NpT*l3GEA<=%Cg=?W=z1<3ToWmd(MxtoHzp z8zKL{ZSenUVX;|Ql)uaIH#m5c!Nc=M$bWusFm>=S(K42Jg{jiKP4E}Rm@OI&$96ZSG&9XbV_Hu)Z4?wp! z`8Z^`+Bhe#3yA-^_OEAu3cvukwza{<_E?;J9J1VLoRjwj#Qy?(ufD!neSOT|;2OUN z@JoY_{3iD>`M(d!v)=jRa{=PN_^*T&ZLMz)@qIw? zzo2nn3=2Beychw_WjzOch~woDwp?2%HU3}Y$`=lsheP?i9G`=8OCkLBcZ!$oYjTd8 z18fgA&cSnYJs|b}>i>1^uV4&-ch?!*yx8$_!0ktkbMT%3*8>#)3--PG-E#WfD%l;p zJI&zc4UU(2wyAa@ueIL5b*X$hE_@0Dqs0KY`Bq43{C{sA-n%p@UqOF`agGyH`E-1= zzL$MvU-0r-1~(ZOa$f_C;R*TqoDsk0lZr8~i{;ZXVN)0wJqEx_t_8s7{Zst>7c>9Q{0WgYf?iM%lb4sPFvgx}UE5mxuvy z@OXoxxz_`Y^(QFzRHN+6VtI5-{gN^RsL%TgNZ} zer{)Qlyh{h+mjgs!m^QP3&=x|)U}0w4D;x>Fe(fbivjSH*XVz1@RaNJEQbFtHp<)p z-6Q4eVtp-p%ib{zfQx+JFV#;^L$?_IThIO65UYod37^71@fZNVt#$eDS|tCkGGfZf z@^kTimd$1J7zV)8j~HC#xZog&IR@~{MOr>ra6cq9rr5(zuYKWC7$^?|;OV{)&dF1J zJq5ac`2QTEES_U)`MW%S%l5LpkAV)iZ#Ov0wZi)0|NcfH}AZeMP2 zmTUL+!~f43Wv!9F%k{VHFZ=r#=y2P5PoMYk_p@SvD-UV7t_ar;OU)&;&Tom&dpbX9 zs~F%g(Bbs~*lsjzyC<|9a=ALKyIPZG98q9xq*&_wJa-|0{7UHTFN-rPH?jxGx{b2gHEmFt#Te z+$E=1dTAHLfKE9NgWg(C;ElqZFeeOD3j-df`JJvmLQ?%VzZ<@Q_wILx|GOA@S*Gs$ zS7_WP9#)HoW%zvbje&sEYYYyrhmK=} zF7Lg4zDGC%N%8+<@Hlh=ITKgD4l=bAn-{%5Tv`ogl>r}K(i!ay1W z5k6br>$*OYp7G#FNGfjdd7#bYgR~E18`-8T3`F>BJqyh1gRy4;zhUtI{Ic4n^NLr( zfQ^9&|E;w<*vFp{~Gs&_K|(c#6X9~uHSdt_u)8so)UK*@Ejuz z^S-Riw&~pBmM~yr033Ck54X1(+~&K2t04CoY4|+AQrX|Ozw9IXl!*awG~~VeZ9UJI z8W;G-SC-i>om;#T2C^|=@SkzOdZy3kdbs}}@@^&jXWL&kl8ws5KnDL$gFfVTp5LdI z*(RM^oDv4|VIYJ5dqdyv7=UX9JX-e8XMfpAb}ADC8T{uQ;0TCyk1re#>|SPjbZ+rU z7|4%-9Qx2_%CeN729f8Hnzq8Y#RwjvQb?zApQ&6b;Y*YC2R`=!hkR!34g^oNm{p=fBCeeq&7kFeGN>_}5wy0*+{$0{Y{1Xt$+D|LH^J@2oYI&|Lo*e z@He$UADWLL$o~t_gR-W5b!#FhJ=Cwr&`%CsKeM2Iz2y&8KhaZtL}2f4MTUN|SI>^5 z`Z{a*eo z{q%%y7q>rO|F|Awuul7?qWL}j3RmWW?R@?WeboIN19r|opq}H&*q&B$NAelOGhv#qv1(YpZ?Q$x`wdbe)* zhJIVte^7m*_rv+8+vSg1KY=sF|EAfz)f5o8Jbc5()Cja zKUV#wTFa+br16hSc$xa>r4d&o@|*hU9QsLDSY*rm8T!QBd9E;yX$E~WPBe%f+1KH90 ARsaA1 literal 21662 zcmeI4TWl3Y7{|8-1@Rg*>XWVdpz%RT)Wl0-8xyrA_+Z2rACMO;D54?3ASSeG1fmfz z@e-{{f|n5SQX+<^QBEXj6cxNyt%#?fplIUp5^c2h`1|jfIh}4!dv<$nyK5o;GvCg9 z`_1?LcV^Da?DTkE6aPCpJU&~!wevjhWY6=?24r~~fb#u+*LvRUqYEfxcwRoA_tCp- z?gFq2^n;&3-rxh$Hb?I!u44_y*JgaZ5^M(_f^WegU}Ljsj=XQcN1zYfXl;y_qH`)( z3}kZz+zXmOthteYiiz}m@F@@n!$7=z4tl^Up!U!PP618h@lY#z%yMy7QjUGKn>9cjZ3S`WXaoL<8}*fwDv3MFz0@U+HUagu`#~Hz5;y;V zJ>bMB@l`{YI*$Q|!H+;=Q=B+@2!F)Qeo%LAEWgx!4NyN@2;#zzxKW?l4Q5+k8W1k1UTo2TS)ZZJzk9?u-bHFd)3(&AWWqGkFzRUocLu$^| z5Pqn4CeVE52heJLi%W`6c062y+z5_VQ?J_aRdM0O^#i{yH~u|JT5o>tpw7R*GpM^A3^`{c?Hn3 zT?VSgky{UTwb%Z-o0k+Bzb^&ir>bi)8$WiB1KQW^vUy2L@mu>Hn)AN_s@{IEY1hIX zN#e!zl^EUu`)b6GofkHA@6?Sqsthlyf!@PaZ9mBRfX!YoV6T&y;{Vk^W6*=3Dt$~m zU`uP5Lm;+$Jz<^~{+|FehQ11_$b5J%M+)Ao-EU6`Y(aMAK{cm;YaJ; zU%{BMEU6p?II#2S6^bWN7M7!(+_y8Sd<6Y=IWU}PFI49kj&jQ)$Z9)}1nWuawHvqL z=Q8p%zG@r^+h(ku-E-?qDjz|=T@DOCT3hZ0yWFynvfXdKI9N|ouidz94CJi@YC}=i z9Iu%Bj|x>{Uc08hJp@M@ z%QIo@rbUN%*8z>u>%l~A$i53q^!uEs&kauW{zv?1K0OgX+S8EUny7UoU7wi00$Mjr z#E;&eY3`Hw_gzt+7cuMmU4Qd95kH?tx~}e(iCn4+ZBz8nczZd@^>^au4y(F+Auu8QeD1 zWAZOZORSTL0}h(OZy>mqf0sOsJ<}#*H%<4zK_AcM|G_#5a&Q7*)?+j}xe)RYEz;^&OO{1zv!whVJYkgj diff --git a/doc/introduction.rst b/doc/introduction.rst index 761c5d22b7..4afff6667b 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -33,24 +33,23 @@ set of arguments, and they expose a number of customizable options through additional parameters. Some of the functions plot directly into a matplotlib axes object, while others operate on an entire figure and produce plots with several panels. In the latter case, the plot is drawn using a Grid object that -links the structure of the figure to the structure of the dataset in an -abstract way. - -Because seaborn uses matplotlib, the graphics can be further tweaked using -matplotlib tools and rendered with any of the matplotlib backends to generate -publication-quality figures. Seaborn can also be used to target web-based -graphics through the `mpld3 `_ and `Bokeh -`_ libraries. +links the structure of the figure to the structure of the dataset. Seaborn should be thought of as a complement to matplotlib, not a replacement for it. When using seaborn, it is likely that you will often invoke matplotlib functions directly to draw simpler plots already available through the -``pyplot`` namespace. Further, while the seaborn functions aim to make plots -that are reasonably "production ready" (including extracting semantic -information from Pandas objects to add informative labels), full customization -of the figures will require a sophisticated understanding of matplotlib objects. - -For more detailed information and copious examples of the syntax and resulting -plots, you can check out the :ref:`example gallery `, -:ref:`tutorial ` or :ref:`API reference `. +``pyplot`` namespace. Further, the seaborn functions aim to make plots that are +reasonably "production ready" (including extracting semantic information from +Pandas objects to add informative labels), but full customization will reqiure +changing attributes on the matplotlib objects directly. The combination of +seaborn's high-level interface and matplotlib's customizability and wide range +of backends makes it easy to generate publication-quality figures. + +The documentation is divided into three separate domains. The breadth of +functionality offered by the package is demonstrated in a set of short scripts +and rendered in the :ref:`example gallery `. Some motivation +behind the kind of plots that seaborn can help to make is explained in the +:ref:`tutorial `. Authoritative documentation on the options afforded +by each function and class can be found by consulting the :ref:`API reference +`. From 4191176b2a54d08ba1950481af7cae3e5d730cc2 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 2 Jul 2017 13:36:43 -0400 Subject: [PATCH 0435/1738] Remove figure size from plotting_context --- doc/releases/v0.8.0.txt | 2 ++ doc/tutorial/aesthetics.ipynb | 3 --- seaborn/rcmod.py | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index b318edc4f9..662a46e697 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -22,6 +22,8 @@ v0.8.0 (Unreleased) - Allow side-specific offsets in :func:`despine`. +- Figure size is no longer part of the seaborn plotting context parameters. + - Put a cap on the number of bins used in :func:`jointplot` for ``type=="hex"`` to avoid hanging when the reference rule prescribes too many. - Turn off dendrogram axes in :func:`clustermap` rather than setting the background color to white. diff --git a/doc/tutorial/aesthetics.ipynb b/doc/tutorial/aesthetics.ipynb index d158844aa8..d283673de1 100644 --- a/doc/tutorial/aesthetics.ipynb +++ b/doc/tutorial/aesthetics.ipynb @@ -382,7 +382,6 @@ "outputs": [], "source": [ "sns.set_context(\"paper\")\n", - "plt.figure(figsize=(8, 6))\n", "sinplot()" ] }, @@ -395,7 +394,6 @@ "outputs": [], "source": [ "sns.set_context(\"talk\")\n", - "plt.figure(figsize=(8, 6))\n", "sinplot()" ] }, @@ -408,7 +406,6 @@ "outputs": [], "source": [ "sns.set_context(\"poster\")\n", - "plt.figure(figsize=(8, 6))\n", "sinplot()" ] }, diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index f1d77c0dd6..960c4f0f91 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -53,7 +53,6 @@ ) _context_keys = ( - "figure.figsize", "font.size", "axes.labelsize", @@ -348,7 +347,6 @@ def plotting_context(context=None, font_scale=1, rc=None): # Set up dictionary of default parameters base_context = { - "figure.figsize": np.array([6.4, 4.8]), "font.size": 12, "axes.labelsize": 11, "axes.titlesize": 12, @@ -369,6 +367,7 @@ def plotting_context(context=None, font_scale=1, rc=None): "xtick.major.pad": 7, "ytick.major.pad": 7, + } # Scale all the parameters by the same factor depending on the context From f8d6de98c888e07eb0961df901896dc7b39cc1d5 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 2 Jul 2017 13:42:58 -0400 Subject: [PATCH 0436/1738] Remove object links from headlines --- doc/tutorial/aesthetics.ipynb | 12 ++++++------ doc/tutorial/color_palettes.ipynb | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/doc/tutorial/aesthetics.ipynb b/doc/tutorial/aesthetics.ipynb index d283673de1..0e412176aa 100644 --- a/doc/tutorial/aesthetics.ipynb +++ b/doc/tutorial/aesthetics.ipynb @@ -121,8 +121,8 @@ "\n", ".. _axes_style:\n", "\n", - "Styling figures with :func:`axes_style` and :func:`set_style`\n", - "-------------------------------------------------------------\n", + "Seaborn figure styles\n", + "---------------------\n", "\n", "There are five preset seaborn themes: ``darkgrid``, ``whitegrid``, ``dark``, ``white``, and ``ticks``. They are each suited to different applications and personal preferences. The default theme is ``darkgrid``. As mentioned above, the grid helps the plot serve as a lookup table for quantitative information, and the white-on grey helps to keep the grid from competing with lines that represent data. The ``whitegrid`` theme is similar, but it is better suited to plots with heavy data elements:" ] @@ -216,8 +216,8 @@ "source": [ ".. _remove_spines:\n", "\n", - "Removing spines with :func:`despine`\n", - "------------------------------------\n", + "Removing axes spines\n", + "--------------------\n", "\n", "Both the ``white`` and ``ticks`` styles can benefit from removing the top and right axes spines, which are not needed. It's impossible to do this through the matplotlib parameters, but you can call the seaborn function :func:`despine` to remove them:" ] @@ -347,8 +347,8 @@ "source": [ ".. _plotting_context:\n", "\n", - "Scaling plot elements with :func:`plotting_context` and :func:`set_context`\n", - "---------------------------------------------------------------------------\n", + "Scaling plot elements\n", + "---------------------\n", "\n", "A separate set of parameters control the scale of plot elements, which should let you use the same code to make plots that are suited for use in settings where larger or smaller plots are appropriate.\n", "\n", diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index 2ab10d1058..e2d94db241 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -71,8 +71,8 @@ "raw_mimetype": "text/restructuredtext" }, "source": [ - "Building color palettes with :func:`color_palette`\n", - "--------------------------------------------------\n", + "Building color palettes\n", + "-----------------------\n", "\n", "The most important function for working with discrete color palettes is :func:`color_palette`. This function provides an interface to many (though not all) of the possible ways you can generate colors in seaborn, and it's used internally by any function that has a ``palette`` argument (and in some cases for a ``color`` argument when multiple colors are needed).\n", "\n", @@ -357,8 +357,8 @@ "source": [ ".. _cubehelix_palettes:\n", "\n", - "Sequential palettes with :func:`cubehelix_palette`\n", - "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", + "Sequential \"cubehelix\" palettes\n", + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", "\n", "The `cubehelix `_ color palette system makes sequential palettes with a linear increase or decrease in brightness and some variation in hue. This means that the information in your colormap will be preserved when converted to black and white (for printing) or when viewed by a colorblind individual.\n", "\n", @@ -463,8 +463,8 @@ "cell_type": "raw", "metadata": {}, "source": [ - "Custom sequential palettes with :func:`light_palette` and :func:`dark_palette`\n", - "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", + "Custom sequential palettes\n", + "~~~~~~~~~~~~~~~~~~~~~~~~~~\n", "\n", "For a simpler interface to custom sequential palettes, you can use :func:`light_palette` or :func:`dark_palette`, which are both seeded with a single color and produce a palette that ramps either from light or dark desaturated values to that color. These functions are also accompanied by the :func:`choose_light_palette` and :func:`choose_dark_palette` functions that launch interactive widgets to create these palettes." ] @@ -626,8 +626,8 @@ "cell_type": "raw", "metadata": {}, "source": [ - "Custom diverging palettes with :func:`diverging_palette`\n", - "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", + "Custom diverging palettes\n", + "~~~~~~~~~~~~~~~~~~~~~~~~~\n", "\n", "You can also use the seaborn function :func:`diverging_palette` to create a custom colormap for diverging data. (Naturally there is also a companion interactive widget, :func:`choose_diverging_palette`). This function makes diverging palettes using the ``husl`` color system. You pass it two hues (in degrees) and, optionally, the lightness and saturation values for the extremes. Using ``husl`` means that the extreme values, and the resulting ramps to the midpoint, will be well-balanced" ] @@ -696,8 +696,8 @@ "source": [ ".. _palette_contexts:\n", "\n", - "Changing default palettes with :func:`set_palette`\n", - "--------------------------------------------------\n", + "Setting the default color palette\n", + "---------------------------------\n", "\n", "The :func:`color_palette` function has a companion called :func:`set_palette`. The relationship between them is similar to the pairs covered in the :ref:`aesthetics tutorial `. :func:`set_palette` accepts the same arguments as :func:`color_palette`, but it changes the default matplotlib parameters so that the palette is used for all plots." ] @@ -773,9 +773,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", - "version": "2.7.9" + "version": "2.7.12" } }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} From 96b674c9277531a7c6f5f67acb99aeacc1f034e7 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 2 Jul 2017 13:43:09 -0400 Subject: [PATCH 0437/1738] White background on fixed width font --- doc/_static/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/_static/style.css b/doc/_static/style.css index 3a5a90b127..c647be179f 100644 --- a/doc/_static/style.css +++ b/doc/_static/style.css @@ -15,7 +15,7 @@ blockquote { code { color: #49759c !important; - background-color: #f3f5f9 !important; + background-color: #ffffff !important; } .alert-info { From 8b34ba815e1e66b20e97735f26ed30fea90bf559 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 2 Jul 2017 13:43:20 -0400 Subject: [PATCH 0438/1738] Decapitalization on homepage --- doc/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/index.rst b/doc/index.rst index 3756e9bf91..12464b4f29 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -19,7 +19,7 @@ -Seaborn: statistical data visualization +seaborn: statistical data visualization ======================================= .. raw:: html From 87e30536dcbb79df0ee2907ad96ba5bccecebe73 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 2 Jul 2017 14:28:58 -0400 Subject: [PATCH 0439/1738] Finish removing object names from headlines --- doc/tutorial/aesthetics.ipynb | 2 +- doc/tutorial/axis_grids.ipynb | 8 ++++---- doc/tutorial/color_palettes.ipynb | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/tutorial/aesthetics.ipynb b/doc/tutorial/aesthetics.ipynb index 0e412176aa..dd65a764ec 100644 --- a/doc/tutorial/aesthetics.ipynb +++ b/doc/tutorial/aesthetics.ipynb @@ -463,4 +463,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file diff --git a/doc/tutorial/axis_grids.ipynb b/doc/tutorial/axis_grids.ipynb index 55a906bb49..c98e03ef88 100644 --- a/doc/tutorial/axis_grids.ipynb +++ b/doc/tutorial/axis_grids.ipynb @@ -72,8 +72,8 @@ "source": [ ".. _facet_grid:\n", "\n", - "Subsetting data with :class:`FacetGrid`\n", - "---------------------------------------\n", + "Plotting small multiples of data subsets\n", + "----------------------------------------\n", "\n", "The :class:`FacetGrid` class is useful when you want to visualize the distribution of a variable or the relationship between multiple variables separately within subsets of your dataset. A :class:`FacetGrid` can be drawn with up to three dimensions: ``row``, ``col``, and ``hue``. The first two have obvious correspondence with the resulting array of axes; think of the hue variable as a third dimension along a depth axis, where different levels are plotted with different colors.\n", "\n", @@ -463,8 +463,8 @@ "source": [ ".. _pair_grid:\n", "\n", - "Plotting pairwise relationships with :class:`PairGrid` and :func:`pairplot`\n", - "---------------------------------------------------------------------------\n", + "Plotting pairwise relationships in a dataset\n", + "--------------------------------------------\n", "\n", ":class:`PairGrid` also allows you to quickly draw a grid of small subplots using the same plot type to visualize data in each. In a :class:`PairGrid`, each row and column is assigned to a different variable, so the resulting plot shows each pairwise relationship in the dataset. This style of plot is sometimes called a \"scatterplot matrix\", as this is the most common way to show each relationship, but :class:`PairGrid` is not limited to scatterplots.\n", "\n", diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index e2d94db241..7e80bb88c0 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -778,4 +778,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file From 934d545fe44cfa431402811baccc36ab7cab8a1d Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 2 Jul 2017 14:48:41 -0400 Subject: [PATCH 0440/1738] Update favicon --- doc/_static/favicon.ico | Bin 270398 -> 270398 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/_static/favicon.ico b/doc/_static/favicon.ico index f914098d366cd550d40133cb91fd9fe6438d65aa..1145b96d4e0d1bb44ef97a5faabaa0b58b3733e8 100644 GIT binary patch literal 270398 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%0A7ZoLoC3YhNg4 z)RWh#FrdWk`;}agoRMUVjI!ND9JSojOg_I+s@T#F^F#;(1|G@0KALlY+YNOV@z?au zEZQUQ>uUNUW!@R`G4x=AiJGs{c3;_Fu$JBt%GB@@~0o&1S()t?hcn3s#?*CzJ zy)4K4Fb)_n@NfmnYRAi5ZKyXlH)wr{c39r`d!@EsmLvbi0Ru)JYR3Vozl~AUUX3qd zYh8%UO{6*g%N!w#H1)C!xj!x#0ETmqrxmx$J1-jU$bGYYD%Me{eHwqF{?{toFMU2n zQzy%i`{RTGBL^+xfQ~ZO-Io_MK1Kb9K{WYG%KV+CPL?76#|Z;Q4$8NEwfvDW#C;sL ztZRIV`tDG+|0$GPUsET`kpJU^0So7PWo6zAO%5=9Bgb!dtWQz?50&kfzMdajANMEk z#}NZQj}+_IDtRRH2=1k*FZY}w*0-p4Z)LlM|EBSVv2Er4%pG-MAOZhmP7lrTU&{Pv zg8IApAl5&q_cBAfw}`Ee`;)`th=BzBvy7d;AVK}n{70S7hdd3@pRS(aRyWDI~;+ZniZ$S{0^_Pxi@#(jp-M%KYR(L@Y@S8`un?vHEE z0d5&apV78+mF<&0{n0Sm$U4aXreXjb(axnK^8svJXTM>8J&1gRSd*WAP-oj{o1C8n z41inzuH=_+^6ho{0QTQ(Xxrg+wvD#Q|4G0A__eixW5?F%1GMW4hPJ(~&bHAu`9CQb z0LSi8^6aj7w3~f5IjyBOI zc|VC50MB+caBa^ze1i6T$k3*D)zK!}B>yK71K^o_Pe<}?Fue&6fHlx?9ca=r~10N)OQXpa9-5ZJ{mlzYQ1w-*z-`?zogbLOV_~v}L=LZJ{mlzYQ1w-_`sbCG22XhY1|N9MLH2Ee&fmAn(KzA9@=`x|yFWB!t!hiLo2a^zJLF#zsauDd^y zqR(i<2@uV(Kly!#6#bzM=7OeR0K9tvM0?-;>(+jv?!^%8`-sV(Ls5p-;fW9SmHIz6L;Da^L@9L!Fxwx5Ou+%GKm%vx9H4t20Lyn8?Yx^f zY%dNO`_aT6oZSo}bL1= zu^*4vGY*V{U;v!lcS!tyKQTwl5d-6e0dQaD?6rI=tjc-7y{KauV?SQ8XFM1W!2r1V zGKghNP$&nyfmkEfh=K9N0QkQ*B*bHl{vgJEeB;j8FgAh#a9`f#P5nGT?Df4fOuP|q z#K3rB09;)catcIqocGIIEcO`d@r*U&!novN0GyR~gg$V$k1#2&E}S0<*JAN#X0t8T=ny-a`h;(ylXyRzZCklYUno@ zw7;a&S=`gF&eNmBTJOxy7wI=#C=X*ff6IINxlX=*I-u{V1t58UJ@0k_l&5#yD=P20 zSEQeE^$SvR{+0*y(*eCx3qW)J7P?oUU+7+e-bDx14}61q@NIdoWosAx31AAVA^mh& zc{Ha_?InPz)t-9>_5JRVBUI*Z3J27F`<{N*bDv*+)^ne)p9$)x`s?pc08_sEyacgj zSU+869|to}-Cuu>ekS9-&St**y#y1|Z{)k*E5DKNeoz1Gfp4K3v)T6f=*C`=`l$uO zy&qorkbb#eepP?_rj2^P_`GL7pwFiMwY&Bwppf2U4@~rzx9a+igKlBg%<9UGi$edu1A09fqI}r5GxcUbdQaL9NXl;+*LH(M+ zON(w_U3sg1E?|Fd@Y1EPeye`9e`VQ=r0btmzp69Uv)I3I_O1Hafc;tT#(`^vtG`H( z{kj*mA^lAMeHmw7mfi9OeYfb~mGwduR{boRj{6h~_WU2x^&%;f zmr*(A_F^1=0_`IDRMoHUhiwmX=@C zjd78E!A1!^y4ad2pr6HO)DGz;7>#=>Nvy(|LPjT{(E^8|d>(?tS zzpArd34G@f+-INYx?oGhKR%N!#l)^(YU|IMjK*iZdswvApqF+<$KI~B5JuxwsfBz3v7=R4` literal 270398 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 ARsaA1 From 1408c56b8c1606896cbfd24c3181f16f5314d2e6 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 2 Jul 2017 14:49:17 -0400 Subject: [PATCH 0441/1738] Update some details and commentary on installing --- doc/installing.rst | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/doc/installing.rst b/doc/installing.rst index 79872db4a9..ea5c01dee2 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -9,22 +9,16 @@ install seaborn``). It's also possible to install the released version using version available from PyPI. Alternatively, you can use ``pip`` to install the development version, with the -command ``pip install git+https://github.com/mwaskom/seaborn.git#egg=seaborn``. +command ``pip install git+https://github.com/mwaskom/seaborn.git``. Another option would be to to clone the `github repository `_ and install with ``pip install .`` from the source directory. Seaborn itself is pure Python, so installation should be reasonably straightforward. -When using the development version, you may want to refer to the `development -docs `_. Note that these -are not built automatically and may at times fall out of sync with the actual -master branch on github. - - Dependencies ~~~~~~~~~~~~ -- Python 2.7 or 3.3+ +- Python 2.7 or 3.4+ Mandatory dependencies ^^^^^^^^^^^^^^^^^^^^^^ @@ -42,23 +36,37 @@ Recommended dependencies - `statsmodels `__ -The `pip` installation script will attempt to download the mandatory -dependencies if they do not exist at install-time. +The ``pip`` installation script will attempt to download the mandatory +dependencies only if they do not exist at install-time. -I recommend using seaborn with the `Anaconda distribution -`_, as this makes it easy to manage -the main dependencies, which otherwise can be difficult to install. - -I attempt to keep seaborn importable and generally functional on the versions -available through the stable Debian channels. There may be cases where some -more advanced features only work with newer versions of these dependencies, -although these should be relatively rare. +Unit tests aim to keep seaborn importable and generally functional on the +versions available through the stable Debian channels. There may be cases +where some more advanced features only work with newer versions of these +libraries, although these should be relatively rare. There are also some known bugs on older versions of matplotlib, so you should in general try to use a modern version. For many use cases, though, older matplotlibs will work fine. -Seaborn is tested on the most recent versions offered through ``conda``. +Seaborn is also tested on the most recent versions offered through ``conda``. + +Importing seaborn +~~~~~~~~~~~~~~~~~ + +Seaborn will apply its default style parameters to the global matplotlib style +dictionary when you import it. This will change the look of all plots, +including those created by using matplotlib functions directly. To avoid this +behavior and use the default matplotlib aesthetics (along with any +customization in your ``matplotlibrc``), you can import the ``seaborn.apionly`` +namespace. + +Seaborn has several other pre-packaged styles along with high-level :ref:`tools +` for managing them, so you should not limit yourself to the +default aesthetics. + +By convention, ``seaborn`` is abbreviated to ``sns`` on import. + +>>>>>>> Update some details and commentary on installing Testing ~~~~~~~ From de8ef73f822f185ee935fcef0f9233e291afee93 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 3 Jul 2017 15:19:38 -0400 Subject: [PATCH 0442/1738] Further reorganization of API index --- doc/api.rst | 26 +++++++++++++------------- seaborn/rcmod.py | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index a564ee8a3e..7745bd63b8 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -29,26 +29,15 @@ Categorical plots .. autosummary:: :toctree: generated/ + stripplot + swarmplot boxplot violinplot lvplot - stripplot - swarmplot pointplot barplot countplot -.. _regression_api: - -Regression plots ----------------- - -.. autosummary:: - :toctree: generated/ - - regplot - residplot - .. _distribution_api: Distribution plots @@ -61,6 +50,17 @@ Distribution plots kdeplot rugplot +.. _regression_api: + +Regression plots +---------------- + +.. autosummary:: + :toctree: generated/ + + regplot + residplot + .. _matrix_api: Matrix plots diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index 960c4f0f91..3eca091eb0 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -187,7 +187,7 @@ def axes_style(style=None, rc=None): "axes.axisbelow": True, "image.cmap": "rocket", "font.family": ["sans-serif"], - "font.sans-serif": ["DejaVu Sans", "Arial", "Liberation Sans", + "font.sans-serif": ["Arial", "DejaVu Sans", "Liberation Sans", "Bitstream Vera Sans", "sans-serif"], "grid.linestyle": "-", "lines.solid_capstyle": "round", From ce9970721a76e3b205d80de9519d5a30d1c8acea Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 3 Jul 2017 16:47:56 -0400 Subject: [PATCH 0443/1738] Add path to favicon in Sphinx conf --- doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index 0a89486c2e..2f2675ab6e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -151,7 +151,7 @@ # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -html_favicon = "favicon.ico" +html_favicon = "_static/favicon.ico" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, From 1c2b321f44e6d704d5bad4bfb5d23b5ea2d71a00 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 3 Jul 2017 16:48:12 -0400 Subject: [PATCH 0444/1738] Don't change default matplotlib figure size --- seaborn/distributions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 43a93d74f5..a8cc254dc1 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -102,7 +102,7 @@ def distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, :context: close-figs >>> import seaborn as sns, numpy as np - >>> sns.set(rc={"figure.figsize": (8, 4)}); np.random.seed(0) + >>> sns.set(); np.random.seed(0) >>> x = np.random.randn(100) >>> ax = sns.distplot(x) From 86c439a1535962a1392ed7b761b8f6f9b3323294 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 3 Jul 2017 21:07:21 -0400 Subject: [PATCH 0445/1738] Tweaks and other changes to example gallery scripts --- examples/heatmap_annotation.py | 4 +++- examples/horizontal_boxplot.py | 20 +++++++++++++------- examples/large_distributions.py | 13 +++++++++++++ examples/many_facets.py | 3 +++ examples/many_pairwise_correlations.py | 5 ++--- examples/marginal_ticks.py | 2 +- examples/multiple_joint_kde.py | 2 +- examples/multiple_regression.py | 14 ++++++-------- examples/network_correlations.py | 26 -------------------------- examples/network_heatap.py | 17 +++++++++++++++++ examples/scatterplot_matrix.py | 2 +- examples/structured_heatmap.py | 12 +++++------- 12 files changed, 65 insertions(+), 55 deletions(-) create mode 100644 examples/large_distributions.py delete mode 100644 examples/network_correlations.py create mode 100644 examples/network_heatap.py diff --git a/examples/heatmap_annotation.py b/examples/heatmap_annotation.py index 33de576008..7512dd3cce 100644 --- a/examples/heatmap_annotation.py +++ b/examples/heatmap_annotation.py @@ -3,6 +3,7 @@ ================== """ +import matplotlib.pyplot as plt import seaborn as sns sns.set() @@ -11,4 +12,5 @@ flights = flights_long.pivot("month", "year", "passengers") # Draw a heatmap with the numeric values in each cell -sns.heatmap(flights, annot=True, fmt="d", linewidths=.5) +f, ax = plt.subplots(figsize=(9, 6)) +sns.heatmap(flights, annot=True, fmt="d", linewidths=.5, ax=ax) diff --git a/examples/horizontal_boxplot.py b/examples/horizontal_boxplot.py index 44995ddbf9..be6c6c2012 100644 --- a/examples/horizontal_boxplot.py +++ b/examples/horizontal_boxplot.py @@ -2,24 +2,30 @@ Horizontal boxplot with observations ==================================== -_thumb: .7, .45 +_thumb: .7, .37 """ import numpy as np import seaborn as sns +import matplotlib.pyplot as plt sns.set(style="ticks", palette="muted", color_codes=True) +# Initialize the figure +f, ax = plt.subplots(figsize=(7, 6)) +ax.set_xscale("log") + # Load the example planets dataset planets = sns.load_dataset("planets") # Plot the orbital period with horizontal boxes -ax = sns.boxplot(x="distance", y="method", data=planets, - whis=np.inf, color="c") +sns.boxplot(x="distance", y="method", data=planets, + whis=np.inf, palette="vlag") # Add in points to show each observation -sns.stripplot(x="distance", y="method", data=planets, - jitter=True, size=3, color=".3", linewidth=0) +sns.swarmplot(x="distance", y="method", data=planets, + size=2, color=".3", linewidth=0) # Make the quantitative axis logarithmic -ax.set_xscale("log") -sns.despine(trim=True) +ax.xaxis.grid(True) +ax.set(ylabel="") +sns.despine(trim=True, left=True) diff --git a/examples/large_distributions.py b/examples/large_distributions.py new file mode 100644 index 0000000000..f8e4b14336 --- /dev/null +++ b/examples/large_distributions.py @@ -0,0 +1,13 @@ +""" +Plotting large distributions +============================ + +""" +import seaborn as sns +sns.set(style="whitegrid") + +networks = sns.load_dataset("brain_networks", index_col=0, header=[0, 1, 2]) +networks = networks.T.groupby(level="network").mean().T +order = networks.std().sort_values().index + +sns.lvplot(data=networks, order=order, scale="linear", palette="mako") diff --git a/examples/many_facets.py b/examples/many_facets.py index 05cca4d2a7..c4f23ea21f 100644 --- a/examples/many_facets.py +++ b/examples/many_facets.py @@ -2,11 +2,14 @@ Plotting on a large number of facets ==================================== +_thumb: .35, .35 + """ import numpy as np import pandas as pd import seaborn as sns import matplotlib.pyplot as plt + sns.set(style="ticks") # Create a dataset with many short random walks diff --git a/examples/many_pairwise_correlations.py b/examples/many_pairwise_correlations.py index 8f9caa700a..e414e76d75 100644 --- a/examples/many_pairwise_correlations.py +++ b/examples/many_pairwise_correlations.py @@ -31,6 +31,5 @@ cmap = sns.diverging_palette(220, 10, as_cmap=True) # Draw the heatmap with the mask and correct aspect ratio -sns.heatmap(corr, mask=mask, cmap=cmap, vmax=.3, - square=True, xticklabels=5, yticklabels=5, - linewidths=.5, cbar_kws={"shrink": .5}, ax=ax) +sns.heatmap(corr, mask=mask, cmap=cmap, vmax=.3, center=0, + square=True, linewidths=.5, cbar_kws={"shrink": .5}) diff --git a/examples/marginal_ticks.py b/examples/marginal_ticks.py index 17938e1480..aa3aad7d8e 100644 --- a/examples/marginal_ticks.py +++ b/examples/marginal_ticks.py @@ -2,7 +2,7 @@ Scatterplot with marginal ticks =============================== -_thumb: .65, .35 +_thumb: .68, .32 """ import numpy as np import seaborn as sns diff --git a/examples/multiple_joint_kde.py b/examples/multiple_joint_kde.py index 7877d6b281..521144d5ec 100644 --- a/examples/multiple_joint_kde.py +++ b/examples/multiple_joint_kde.py @@ -2,7 +2,7 @@ Multiple bivariate KDE plots ============================ -_thumb: .6, .4 +_thumb: .6, .45 """ import seaborn as sns import matplotlib.pyplot as plt diff --git a/examples/multiple_regression.py b/examples/multiple_regression.py index e5e65ab832..5af8667240 100644 --- a/examples/multiple_regression.py +++ b/examples/multiple_regression.py @@ -2,19 +2,17 @@ Multiple linear regression ========================== +_thumb: .45, .45 """ import seaborn as sns -sns.set(style="ticks", context="talk") +sns.set() # Load the example tips dataset -tips = sns.load_dataset("tips") - -# Make a custom sequential palette using the cubehelix system -pal = sns.cubehelix_palette(4, 1.5, .75, light=.6, dark=.2) +iris = sns.load_dataset("iris") # Plot tip as a function of toal bill across days -g = sns.lmplot(x="total_bill", y="tip", hue="day", data=tips, - palette=pal, size=7) +g = sns.lmplot(x="sepal_length", y="sepal_width", hue="species", + truncate=True, size=5, data=iris) # Use more informative axis labels than are provided by default -g.set_axis_labels("Total bill ($)", "Tip ($)") +g.set_axis_labels("Sepal length (mm)", "Sepal width (mm)") diff --git a/examples/network_correlations.py b/examples/network_correlations.py deleted file mode 100644 index dd394ab6e8..0000000000 --- a/examples/network_correlations.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -Correlation matrix heatmap -========================== - -""" -import seaborn as sns -import matplotlib.pyplot as plt -sns.set(context="paper", font="monospace") - -# Load the datset of correlations between cortical brain networks -df = sns.load_dataset("brain_networks", header=[0, 1, 2], index_col=0) -corrmat = df.corr() - -# Set up the matplotlib figure -f, ax = plt.subplots(figsize=(12, 9)) - -# Draw the heatmap using seaborn -sns.heatmap(corrmat, center=0, vmax=.8, square=True) - -# Use matplotlib directly to emphasize known networks -networks = corrmat.columns.get_level_values("network") -for i, network in enumerate(networks): - if i and network != networks[i - 1]: - ax.axhline(len(networks) - i, c="w") - ax.axvline(i, c="w") -f.tight_layout() diff --git a/examples/network_heatap.py b/examples/network_heatap.py new file mode 100644 index 0000000000..3d48f18849 --- /dev/null +++ b/examples/network_heatap.py @@ -0,0 +1,17 @@ +""" +Large heatmap with divergent colormap +===================================== + +""" +import seaborn as sns +import matplotlib.pyplot as plt +sns.set(context="paper", font="monospace") + +# Load the datset of correlations between cortical brain networks +df = sns.load_dataset("brain_networks", header=[0, 1, 2], index_col=0) + +# Set up the matplotlib figure +f, ax = plt.subplots(figsize=(11, 6)) + +# Draw the heatmap using seaborn +sns.heatmap(df.T, center=0, robust=True) diff --git a/examples/scatterplot_matrix.py b/examples/scatterplot_matrix.py index ebafe56274..46fd7e6265 100644 --- a/examples/scatterplot_matrix.py +++ b/examples/scatterplot_matrix.py @@ -5,7 +5,7 @@ _thumb: .5, .4 """ import seaborn as sns -sns.set() +sns.set(style="ticks") df = sns.load_dataset("iris") sns.pairplot(df, hue="species") diff --git a/examples/structured_heatmap.py b/examples/structured_heatmap.py index 5fe55b42bb..bfae2f56e9 100644 --- a/examples/structured_heatmap.py +++ b/examples/structured_heatmap.py @@ -2,7 +2,7 @@ Discovering structure in heatmap data ===================================== -_thumb: .4, .2 +_thumb: .4, .25 """ import pandas as pd import seaborn as sns @@ -12,16 +12,14 @@ df = sns.load_dataset("brain_networks", header=[0, 1, 2], index_col=0) # Select a subset of the networks -used_networks = [1, 5, 6, 7, 8, 11, 12, 13, 16, 17] +used_networks = [1, 5, 6, 7, 8, 12, 13, 17] used_columns = (df.columns.get_level_values("network") .astype(int) .isin(used_networks)) df = df.loc[:, used_columns] -# Create a custom palette to identify the networks -network_pal = sns.cubehelix_palette(len(used_networks), - light=.9, dark=.1, reverse=True, - start=1, rot=-2) +# Create a categorical palette to identify the networks +network_pal = sns.husl_palette(8, s=.45) network_lut = dict(zip(map(str, used_networks), network_pal)) # Convert the palette to vectors that will be drawn on the side of the matrix @@ -31,4 +29,4 @@ # Draw the full plot sns.clustermap(df.corr(), center=0, cmap="vlag", row_colors=network_colors, col_colors=network_colors, - figsize=(13, 13)) + linewidths=.75, figsize=(13, 13)) From 7cca2ecc8c750dd331fb9a6215d6badfc4abd914 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 3 Jul 2017 21:07:37 -0400 Subject: [PATCH 0446/1738] Standardize major doc component order --- doc/conf.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 2f2675ab6e..8a6785edf7 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -128,9 +128,11 @@ 'bootswatch_theme': "paper", 'navbar_sidebarrel': False, 'bootstrap_version': "3", - 'navbar_links': [("API", "api"), + 'navbar_links': [ + ("Gallery", "examples/index"), ("Tutorial", "tutorial"), - ("Gallery", "examples/index")], + ("API", "api"), + ], } @@ -183,7 +185,7 @@ #html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +html_show_sourcelink = False # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True From a9073f0bed47a3b4cf380a7c11a3ae02c24bac5b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 3 Jul 2017 21:07:58 -0400 Subject: [PATCH 0447/1738] Feature new examples on the homepage --- doc/index.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index 12464b4f29..7048f63d40 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -33,9 +33,9 @@ seaborn: statistical data visualization
- +
- +
@@ -43,19 +43,19 @@ seaborn: statistical data visualization
- +
- +
- +
- +
- +
- +
@@ -92,8 +92,8 @@ To see the code or report a bug, please visit the `github repository whatsnew installing examples/index - api tutorial + api .. raw:: html From 2388e0113177169cd261831cde39be38da6a9bbd Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 3 Jul 2017 21:08:26 -0400 Subject: [PATCH 0448/1738] Pick up on registered but not built-in matplotlib cmaps --- seaborn/palettes.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/seaborn/palettes.py b/seaborn/palettes.py index b43d39e50b..7d151e499a 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -172,12 +172,11 @@ def color_palette(palette=None, n_colors=None, desat=None): raise ValueError("No.") elif palette in SEABORN_PALETTES: palette = SEABORN_PALETTES[palette] - elif palette in dir(mpl.cm): - palette = mpl_palette(palette, n_colors) - elif palette[:-2] in dir(mpl.cm): - palette = mpl_palette(palette, n_colors) else: - raise ValueError("%s is not a valid palette name" % palette) + try: + palette = mpl_palette(palette, n_colors) + except ValueError: + raise ValueError("%s is not a valid palette name" % palette) if desat is not None: palette = [desaturate(c, desat) for c in palette] @@ -404,7 +403,7 @@ def mpl_palette(name, n_colors=6): pal.extend(color_palette(name.replace("_d", "_r"), 2)) cmap = blend_palette(pal, n_colors, as_cmap=True) else: - cmap = getattr(mpl.cm, name) + cmap = mpl.cm.get_cmap(name) if name in mpl_qual_pals: bins = np.linspace(0, 1, mpl_qual_pals[name])[:n_colors] else: From bcb5fe4adad5574bddf707b087adb3c0f2571ea7 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 3 Jul 2017 21:08:37 -0400 Subject: [PATCH 0449/1738] Change link color --- doc/_static/style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/_static/style.css b/doc/_static/style.css index c647be179f..0cef710f66 100644 --- a/doc/_static/style.css +++ b/doc/_static/style.css @@ -5,6 +5,10 @@ h4 { font-size: 18px !important; } h5 { font-size: 14px !important; } h6 { font-size: 10px !important; } +a.reference { + color: #4c72b0 !important; +} + blockquote p { font-size: 14px !important; } From 3572da3f2c6cda0c890f1955a38cf1d49f29789c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 3 Jul 2017 21:09:27 -0400 Subject: [PATCH 0450/1738] Remove leftover text from rebase conflict --- doc/installing.rst | 18 ------------------ doc/sphinxext/plot_generator.py | 2 +- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/doc/installing.rst b/doc/installing.rst index ea5c01dee2..74819f3259 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -50,24 +50,6 @@ matplotlibs will work fine. Seaborn is also tested on the most recent versions offered through ``conda``. -Importing seaborn -~~~~~~~~~~~~~~~~~ - -Seaborn will apply its default style parameters to the global matplotlib style -dictionary when you import it. This will change the look of all plots, -including those created by using matplotlib functions directly. To avoid this -behavior and use the default matplotlib aesthetics (along with any -customization in your ``matplotlibrc``), you can import the ``seaborn.apionly`` -namespace. - -Seaborn has several other pre-packaged styles along with high-level :ref:`tools -` for managing them, so you should not limit yourself to the -default aesthetics. - -By convention, ``seaborn`` is abbreviated to ``sns`` on import. - ->>>>>>> Update some details and commentary on installing - Testing ~~~~~~~ diff --git a/doc/sphinxext/plot_generator.py b/doc/sphinxext/plot_generator.py index 78abddb46c..b5c056c93d 100644 --- a/doc/sphinxext/plot_generator.py +++ b/doc/sphinxext/plot_generator.py @@ -124,7 +124,7 @@ def execfile(filename, globals=None, locals=None): def create_thumbnail(infile, thumbfile, - width=300, height=300, + width=275, height=275, cx=0.5, cy=0.5, border=4): baseout, extout = op.splitext(thumbfile) From fa0862bb24a18de0bc2e1e8f1b423f2f988f12d2 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 3 Jul 2017 21:29:11 -0400 Subject: [PATCH 0451/1738] Fix example script name --- examples/{network_heatap.py => network_heatmap.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{network_heatap.py => network_heatmap.py} (100%) diff --git a/examples/network_heatap.py b/examples/network_heatmap.py similarity index 100% rename from examples/network_heatap.py rename to examples/network_heatmap.py From 53c1978cdf37ca699c5cd8ae07ff7da2e0d55552 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 3 Jul 2017 21:30:08 -0400 Subject: [PATCH 0452/1738] Handle non-exeception when getting colormap --- seaborn/palettes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/seaborn/palettes.py b/seaborn/palettes.py index 7d151e499a..c0965b1000 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -404,6 +404,8 @@ def mpl_palette(name, n_colors=6): cmap = blend_palette(pal, n_colors, as_cmap=True) else: cmap = mpl.cm.get_cmap(name) + if cmap is None: + raise ValueError("{} is not a valid colormap".format(name)) if name in mpl_qual_pals: bins = np.linspace(0, 1, mpl_qual_pals[name])[:n_colors] else: From cfd894de77d22be3a3340ebf3eff2bd81567f5a7 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 3 Jul 2017 21:40:57 -0400 Subject: [PATCH 0453/1738] Fix formatting in release notes --- doc/releases/v0.8.0.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index 662a46e697..c1470728a1 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -1,6 +1,6 @@ -v0.8.0 (Unreleased) -------------------- +v0.8.0 (July 2017) +------------------ - The default style is no longer applied when seaborn is imported. It is now necessary to explicitly call :func:`set` or one or more of :func:`set_style`, :func:`set_context`, and :func:`set_palette`. Correspondingly, the ``seaborn.apionly`` module has been deprecated. @@ -8,17 +8,17 @@ v0.8.0 (Unreleased) - Removed automatic detection of diverging data in :func:`heatmap` (and by extension :func:`clustermap`). If you want the colormap to be treated as diverging (see above), it is now necessary to specify the ``center`` value. When no colormap is specified, specifying ``center`` will still change the default to be one that is more appropriate for displaying diverging data. -- Added four new colormaps, created using `viscm `_ for perceptual uniformity. The new colormaps include two sequential colormaps ("rocket" and "mako") and two diverging colormaps ("vlag", and "icefire"). These colormaps are registered with matplotlib on seaborn input and the colormap objects can be accessed in the `seaborn.cm` namespace. +- Added four new colormaps, created using `viscm `_ for perceptual uniformity. The new colormaps include two sequential colormaps ("rocket" and "mako") and two diverging colormaps ("icefire" and "vlag"). These colormaps are registered with matplotlib on seaborn input and the colormap objects can be accessed in the ``seaborn.cm`` namespace. - Changed the default :func:`heatmap` colormaps to be "rocket" (in the case of sequential data) or "icefire" (in the case of diverging data). Note that this change reverses the direction of the luminance ramp from the previous defaults. While potentially confusing and disruptive, this change better aligns the seaborn defaults with the new matplotlib default colormap ("viridis") and arguably better aligns the semantics of a "heat" map with the appearance of the colormap. -- Added `"auto"` as a (default) option for tick labels in :func:`heatmap` and :func:`clustermap`. This will try to estimate how many ticks can be labeled without the text objects overlapping, which should improve performance for larger matrices. +- Added ``"auto"`` as a (default) option for tick labels in :func:`heatmap` and :func:`clustermap`. This will try to estimate how many ticks can be labeled without the text objects overlapping, which should improve performance for larger matrices. - Added the ``dodge`` argument to :func:`boxplot`, :func:`violinplot`, and :func:`barplot` to allow use of ``hue`` without changing the position or width of the plot elements, as when the ``hue`` varible is not nested within the main categorical variable. - Added the ability to draw a colorbar for a bivariate :func:`kdeplot` with the ``cbar`` parameter (and related ``cbar_ax`` and ``cbar_kws`` parameters). -- Added the ability to use error bars to show standard deviations rather than bootstrap confidence intervals in most statistical functions by putting `ci="sd"`. +- Added the ability to use error bars to show standard deviations rather than bootstrap confidence intervals in most statistical functions by putting ``ci="sd"``. - Allow side-specific offsets in :func:`despine`. @@ -32,6 +32,6 @@ v0.8.0 (Unreleased) - Some modules and functions have been internally reorganized; there should be no effect on code that uses the ``seaborn`` namespace. -- Added a deprecation warning to func:`tsplot` function to indicate that it will be removed or replaced with a substantially altered version in a future release. +- Added a deprecation warning to :func:`tsplot` function to indicate that it will be removed or replaced with a substantially altered version in a future release. - The ``interactplot`` and ``coefplot`` functions are officially deprecated and will be removed in a future release. From 3640fc0f4c607c6f6891884c8ac5349af3658c4b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 4 Jul 2017 11:14:34 -0400 Subject: [PATCH 0454/1738] Remove dead links --- doc/tutorial/axis_grids.ipynb | 4 ++-- doc/tutorial/color_palettes.ipynb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/tutorial/axis_grids.ipynb b/doc/tutorial/axis_grids.ipynb index c98e03ef88..7c51db953f 100644 --- a/doc/tutorial/axis_grids.ipynb +++ b/doc/tutorial/axis_grids.ipynb @@ -20,7 +20,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "When exploring medium-dimensional data, a useful approach is to draw multiple instances of the same plot on different subsets of your dataset. This technique is sometimes called either \"lattice\", or `\"trellis\" `_ plotting, and it is related to the idea of `\"small multiples\" `_. It allows a viewer to quickly extract a large amount of information about complex data. Matplotlib offers good support for making figures with multiple axes; seaborn builds on top of this to directly link the structure of the plot to the structure of your dataset.\n", + "When exploring medium-dimensional data, a useful approach is to draw multiple instances of the same plot on different subsets of your dataset. This technique is sometimes called either \"lattice\", or \"trellis\" plotting, and it is related to the idea of `\"small multiples\" `_. It allows a viewer to quickly extract a large amount of information about complex data. Matplotlib offers good support for making figures with multiple axes; seaborn builds on top of this to directly link the structure of the plot to the structure of your dataset.\n", "\n", "To use these features, your data has to be in a Pandas DataFrame and it must take the form of what Hadley Whickam calls `\"tidy\" data `_. In brief, that means your dataframe should be structured such that each column is a variable and each row is an observation.\n", "\n", @@ -674,4 +674,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index 7e80bb88c0..a39d3c4fe4 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -262,7 +262,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "If you want to spend some time picking colors, this `interactive visualization `_ may be useful. In addition to pulling out single colors from the ``xkcd_rgb`` dictionary, you can also pass a list of names to the :func:`xkcd_palette` function." + "In addition to pulling out single colors from the ``xkcd_rgb`` dictionary, you can also pass a list of names to the :func:`xkcd_palette` function." ] }, { @@ -778,4 +778,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} From 0fb6d1a5a0a495fc6259eca67c572302b0a35aea Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 4 Jul 2017 12:28:17 -0400 Subject: [PATCH 0455/1738] A few more doc tweaks --- doc/_static/style.css | 4 ++++ doc/introduction.rst | 2 +- examples/horizontal_boxplot.py | 2 +- examples/network_heatmap.py | 2 +- examples/scatterplot_matrix.py | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/_static/style.css b/doc/_static/style.css index 0cef710f66..6777c040fc 100644 --- a/doc/_static/style.css +++ b/doc/_static/style.css @@ -5,6 +5,10 @@ h4 { font-size: 18px !important; } h5 { font-size: 14px !important; } h6 { font-size: 10px !important; } +footer a{ + + color: #4c72b0 !important; +} a.reference { color: #4c72b0 !important; } diff --git a/doc/introduction.rst b/doc/introduction.rst index 4afff6667b..2fec20470f 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -13,7 +13,7 @@ support for `numpy `_ and `pandas Some of the features that seaborn offers are -- Several :ref:`built-in themes ` that improve on the default matplotlib aesthetics +- Several :ref:`built-in themes ` for styling matplotlib graphics - Tools for choosing :ref:`color palettes ` to make beautiful plots that reveal patterns in your data - Functions for visualizing :ref:`univariate ` and :ref:`bivariate ` distributions or for :ref:`comparing ` them between subsets of data - Tools that fit and visualize :ref:`linear regression ` models for different kinds of :ref:`independent ` and :ref:`dependent ` variables diff --git a/examples/horizontal_boxplot.py b/examples/horizontal_boxplot.py index be6c6c2012..84cf4bf3c5 100644 --- a/examples/horizontal_boxplot.py +++ b/examples/horizontal_boxplot.py @@ -7,7 +7,7 @@ import numpy as np import seaborn as sns import matplotlib.pyplot as plt -sns.set(style="ticks", palette="muted", color_codes=True) +sns.set(style="ticks") # Initialize the figure f, ax = plt.subplots(figsize=(7, 6)) diff --git a/examples/network_heatmap.py b/examples/network_heatmap.py index 3d48f18849..c117dd20f4 100644 --- a/examples/network_heatmap.py +++ b/examples/network_heatmap.py @@ -5,7 +5,7 @@ """ import seaborn as sns import matplotlib.pyplot as plt -sns.set(context="paper", font="monospace") +sns.set() # Load the datset of correlations between cortical brain networks df = sns.load_dataset("brain_networks", header=[0, 1, 2], index_col=0) diff --git a/examples/scatterplot_matrix.py b/examples/scatterplot_matrix.py index 46fd7e6265..399072f701 100644 --- a/examples/scatterplot_matrix.py +++ b/examples/scatterplot_matrix.py @@ -2,7 +2,7 @@ Scatterplot Matrix ================== -_thumb: .5, .4 +_thumb: .5, .43 """ import seaborn as sns sns.set(style="ticks") From 7c6ab63988935db67a8c604bb538df2a21458018 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 4 Jul 2017 18:17:10 -0400 Subject: [PATCH 0456/1738] Don't include estimator name in quantitative axis label Closes #1196 --- seaborn/categorical.py | 5 ----- seaborn/tests/test_categorical.py | 28 ++++++++-------------------- 2 files changed, 8 insertions(+), 25 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 64fad354b4..7e961a1b11 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1546,11 +1546,6 @@ def estimate_statistic(self, estimator, ci, n_boot): self.statistic = np.array(statistic) self.confint = np.array(confint) - # Rename the value label to reflect the estimation - if self.value_label is not None: - self.value_label = u"{}({})".format(estimator.__name__, - self.value_label) - def draw_confints(self, ax, at_group, confint, colors, errwidth=None, capsize=None, **kws): diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 59cb6f3b04..f47aab1f08 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -648,18 +648,6 @@ def test_nested_sd_error_bars(self): ci_want = mean - half_ci, mean + half_ci npt.assert_array_almost_equal(ci_want, ci, 2) - def test_estimator_value_label(self): - - p = cat._CategoricalStatPlotter() - p.establish_variables("g", "y", data=self.df) - p.estimate_statistic(np.mean, None, 100) - nt.assert_equal(p.value_label, "mean(y)") - - p = cat._CategoricalStatPlotter() - p.establish_variables("g", "y", data=self.df) - p.estimate_statistic(np.median, None, 100) - nt.assert_equal(p.value_label, "median(y)") - def test_draw_cis(self): p = cat._CategoricalStatPlotter() @@ -2050,12 +2038,12 @@ def test_simple_barplots(self): ax = cat.barplot("g", "y", data=self.df) nt.assert_equal(len(ax.patches), len(self.g.unique())) nt.assert_equal(ax.get_xlabel(), "g") - nt.assert_equal(ax.get_ylabel(), "mean(y)") + nt.assert_equal(ax.get_ylabel(), "y") plt.close("all") ax = cat.barplot("y", "g", orient="h", data=self.df) nt.assert_equal(len(ax.patches), len(self.g.unique())) - nt.assert_equal(ax.get_xlabel(), "mean(y)") + nt.assert_equal(ax.get_xlabel(), "y") nt.assert_equal(ax.get_ylabel(), "g") plt.close("all") @@ -2063,13 +2051,13 @@ def test_simple_barplots(self): nt.assert_equal(len(ax.patches), len(self.g.unique()) * len(self.h.unique())) nt.assert_equal(ax.get_xlabel(), "g") - nt.assert_equal(ax.get_ylabel(), "mean(y)") + nt.assert_equal(ax.get_ylabel(), "y") plt.close("all") ax = cat.barplot("y", "g", "h", orient="h", data=self.df) nt.assert_equal(len(ax.patches), len(self.g.unique()) * len(self.h.unique())) - nt.assert_equal(ax.get_xlabel(), "mean(y)") + nt.assert_equal(ax.get_xlabel(), "y") nt.assert_equal(ax.get_ylabel(), "g") plt.close("all") @@ -2273,13 +2261,13 @@ def test_simple_pointplots(self): nt.assert_equal(len(ax.collections), 1) nt.assert_equal(len(ax.lines), len(self.g.unique()) + 1) nt.assert_equal(ax.get_xlabel(), "g") - nt.assert_equal(ax.get_ylabel(), "mean(y)") + nt.assert_equal(ax.get_ylabel(), "y") plt.close("all") ax = cat.pointplot("y", "g", orient="h", data=self.df) nt.assert_equal(len(ax.collections), 1) nt.assert_equal(len(ax.lines), len(self.g.unique()) + 1) - nt.assert_equal(ax.get_xlabel(), "mean(y)") + nt.assert_equal(ax.get_xlabel(), "y") nt.assert_equal(ax.get_ylabel(), "g") plt.close("all") @@ -2290,7 +2278,7 @@ def test_simple_pointplots(self): len(self.h.unique()) + len(self.h.unique()))) nt.assert_equal(ax.get_xlabel(), "g") - nt.assert_equal(ax.get_ylabel(), "mean(y)") + nt.assert_equal(ax.get_ylabel(), "y") plt.close("all") ax = cat.pointplot("y", "g", "h", orient="h", data=self.df) @@ -2299,7 +2287,7 @@ def test_simple_pointplots(self): (len(self.g.unique()) * len(self.h.unique()) + len(self.h.unique()))) - nt.assert_equal(ax.get_xlabel(), "mean(y)") + nt.assert_equal(ax.get_xlabel(), "y") nt.assert_equal(ax.get_ylabel(), "g") plt.close("all") From 9bf59b3c27a535cbeae35a2519f6df229dad1e6b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 4 Jul 2017 18:54:54 -0400 Subject: [PATCH 0457/1738] Fix tests that assume 'deep' is the defaul palette --- seaborn/tests/test_categorical.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index f47aab1f08..0288709a9d 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -367,12 +367,12 @@ def test_default_palettes(self): # Test palette mapping the x position p.establish_variables("g", "y", data=self.df) p.establish_colors(None, None, 1) - nt.assert_equal(p.colors, palettes.color_palette("deep", 3)) + nt.assert_equal(p.colors, palettes.color_palette(n_colors=3)) # Test palette mapping the hue position p.establish_variables("g", "y", "h", data=self.df) p.establish_colors(None, None, 1) - nt.assert_equal(p.colors, palettes.color_palette("deep", 2)) + nt.assert_equal(p.colors, palettes.color_palette(n_colors=2)) def test_default_palette_with_many_levels(self): @@ -787,14 +787,14 @@ def test_axes_data(self): def test_box_colors(self): ax = cat.boxplot("g", "y", data=self.df, saturation=1) - pal = palettes.color_palette("deep", 3) + pal = palettes.color_palette(n_colors=3) for patch, color in zip(ax.artists, pal): nt.assert_equal(patch.get_facecolor()[:3], color) plt.close("all") ax = cat.boxplot("g", "y", "h", data=self.df, saturation=1) - pal = palettes.color_palette("deep", 2) + pal = palettes.color_palette(n_colors=2) for patch, color in zip(ax.artists, pal * 2): nt.assert_equal(patch.get_facecolor()[:3], color) @@ -2560,14 +2560,14 @@ def test_axes_data(self): def test_box_colors(self): ax = cat.lvplot("g", "y", data=self.df, saturation=1) - pal = palettes.color_palette("deep", 3) + pal = palettes.color_palette(n_colors=3) for patch, color in zip(ax.artists, pal): nt.assert_equal(patch.get_facecolor()[:3], color) plt.close("all") ax = cat.lvplot("g", "y", "h", data=self.df, saturation=1) - pal = palettes.color_palette("deep", 2) + pal = palettes.color_palette(n_colors=2) for patch, color in zip(ax.artists, pal * 2): nt.assert_equal(patch.get_facecolor()[:3], color) From 6d041fbf3e25a6085f5c3cb367e52f113bebdf0e Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 4 Jul 2017 19:58:29 -0400 Subject: [PATCH 0458/1738] Fix test for empty tick labels (closes #1195) --- seaborn/matrix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 041d6cf7f5..56a805fc0f 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -137,7 +137,7 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, # Get the positions and used label for the ticks nx, ny = data.T.shape - if xticklabels == []: + if not xticklabels: self.xticks = [] self.xticklabels = [] elif isinstance(xticklabels, string_types) and xticklabels == "auto": @@ -147,7 +147,7 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, self.xticks, self.xticklabels = self._skip_ticks(xticklabels, xtickevery) - if yticklabels == []: + if not yticklabels: self.yticks = [] self.yticklabels = [] elif isinstance(yticklabels, string_types) and yticklabels == "auto": From 4d2f67fe60ca38b6e757e3fc8c931815d878ae8a Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 4 Jul 2017 20:18:45 -0400 Subject: [PATCH 0459/1738] Fix ticklabel check --- seaborn/matrix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 56a805fc0f..e56b3795cb 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -137,7 +137,7 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, # Get the positions and used label for the ticks nx, ny = data.T.shape - if not xticklabels: + if not len(xticklabels): self.xticks = [] self.xticklabels = [] elif isinstance(xticklabels, string_types) and xticklabels == "auto": @@ -147,7 +147,7 @@ def __init__(self, data, vmin, vmax, cmap, center, robust, annot, fmt, self.xticks, self.xticklabels = self._skip_ticks(xticklabels, xtickevery) - if not yticklabels: + if not len(yticklabels): self.yticks = [] self.yticklabels = [] elif isinstance(yticklabels, string_types) and yticklabels == "auto": From ea0ea45f2091bfb352c48c6ee5e83f4fc22f3d5d Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 5 Jul 2017 11:21:57 -0400 Subject: [PATCH 0460/1738] Add violinplot API example demonstrating clipping --- seaborn/categorical.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 2e1f4bbeea..409bdd5be3 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2515,24 +2515,33 @@ def violinplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, ... scale="count", inner="stick", ... scale_hue=False, bw=.2) - Use ``hue`` without changing violin position or width: + Draw horizontal violins: .. plot:: :context: close-figs - >>> tips["weekend"] = tips["day"].isin(["Sat", "Sun"]) - >>> ax = sns.violinplot(x="day", y="total_bill", hue="weekend", - ... data=tips, dodge=False) + >>> planets = sns.load_dataset("planets") + >>> ax = sns.violinplot(x="orbital_period", y="method", + ... data=planets[planets.orbital_period < 1000], + ... scale="width", palette="Set3") - Draw horizontal violins: + Don't let density extend past extreme values in the data: .. plot:: :context: close-figs - >>> planets = sns.load_dataset("planets") >>> ax = sns.violinplot(x="orbital_period", y="method", ... data=planets[planets.orbital_period < 1000], - ... scale="width", palette="Set3") + ... cut=0, scale="width", palette="Set3") + + Use ``hue`` without changing violin position or width: + + .. plot:: + :context: close-figs + + >>> tips["weekend"] = tips["day"].isin(["Sat", "Sun"]) + >>> ax = sns.violinplot(x="day", y="total_bill", hue="weekend", + ... data=tips, dodge=False) Use :func:`factorplot` to combine a :func:`violinplot` and a :class:`FacetGrid`. This allows grouping within additional categorical From 375042fce709d9721ae201a43ed9e9733f1b5681 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 6 Jul 2017 09:46:41 -0400 Subject: [PATCH 0461/1738] Switch clustermap examples to iris dataset and update other matrix examples --- examples/network_heatmap.py | 3 ++- examples/structured_heatmap.py | 2 +- seaborn/matrix.py | 45 ++++++++++++++-------------------- 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/examples/network_heatmap.py b/examples/network_heatmap.py index c117dd20f4..4a465295aa 100644 --- a/examples/network_heatmap.py +++ b/examples/network_heatmap.py @@ -14,4 +14,5 @@ f, ax = plt.subplots(figsize=(11, 6)) # Draw the heatmap using seaborn -sns.heatmap(df.T, center=0, robust=True) +cbar_ticks = [-50, 0, 50] +sns.heatmap(df.T, center=0, robust=True, cbar_kws=dict(ticks=cbar_ticks)) diff --git a/examples/structured_heatmap.py b/examples/structured_heatmap.py index bfae2f56e9..7986eae77d 100644 --- a/examples/structured_heatmap.py +++ b/examples/structured_heatmap.py @@ -6,7 +6,7 @@ """ import pandas as pd import seaborn as sns -sns.set(font="monospace") +sns.set() # Load the brain networks example dataset df = sns.load_dataset("brain_networks", header=[0, 1, 2], index_col=0) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index e56b3795cb..daa841cf19 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -1227,78 +1227,69 @@ def clustermap(data, pivot_kws=None, method='average', metric='euclidean', .. plot:: :context: close-figs - >>> import seaborn as sns; sns.set() - >>> networks = sns.load_dataset("brain_networks", - ... index_col=0, header=[0, 1, 2]) - >>> networks = networks.loc[:200].T - >>> g = sns.clustermap(networks) + >>> import seaborn as sns; sns.set(color_codes=True) + >>> iris = sns.load_dataset("iris") + >>> species = iris.pop("species") + >>> g = sns.clustermap(iris) Use a different similarity metric: .. plot:: :context: close-figs - >>> g = sns.clustermap(networks, metric="correlation") + >>> g = sns.clustermap(iris, metric="correlation") Use a different clustering method: .. plot:: :context: close-figs - >>> g = sns.clustermap(networks, method="single") + >>> g = sns.clustermap(iris, method="single") Use a different colormap and ignore outliers in colormap limits: .. plot:: :context: close-figs - >>> g = sns.clustermap(networks, cmap="mako", robust=True) + >>> g = sns.clustermap(iris, cmap="mako", robust=True) Change the size of the figure: .. plot:: :context: close-figs - >>> g = sns.clustermap(networks, figsize=(9, 5)) + >>> g = sns.clustermap(iris, figsize=(6, 7)) - Standardize the data across the columns: + Plot one of the axes in its original organization: .. plot:: :context: close-figs - >>> g = sns.clustermap(networks, standard_scale=1) + >>> g = sns.clustermap(iris, col_cluster=False) - Normalize the data across the rows: + Add colored labels: .. plot:: :context: close-figs - >>> g = sns.clustermap(networks, z_score=0) + >>> lut = dict(zip(species.unique(), "rbg")) + >>> row_colors = species.map(lut) + >>> g = sns.clustermap(iris, row_colors=row_colors) - Plot one of the axes in its original organization: + Standardize the data within the columns: .. plot:: :context: close-figs - >>> g = sns.clustermap(networks, col_cluster=False) + >>> g = sns.clustermap(iris, standard_scale=1) - Add colored labels: + Normalize the data within the rows: .. plot:: :context: close-figs - >>> from matplotlib.colors import Normalize - >>> labels = networks.index.get_level_values("network").astype(int) - >>> colors = sns.cm.vlag(Normalize(1, 17)(labels)) - >>> g = sns.clustermap(networks, row_colors=colors) - - Show data with a divergent colormap: - - .. plot:: - :context: close-figs + >>> g = sns.clustermap(iris, z_score=0) - >>> corrmat = networks.T.corr() - >>> g = sns.clustermap(corrmat, center=0) """ plotter = ClusterGrid(data, pivot_kws=pivot_kws, figsize=figsize, From c43b125bd17d532bf89443397ab62d8d777420d6 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 6 Jul 2017 10:57:58 -0400 Subject: [PATCH 0462/1738] Swap out an example script --- examples/jitter_stripplot.py | 32 ++++++++++++++++++++++++++++++++ examples/network_heatmap.py | 18 ------------------ examples/paired_pointplots.py | 4 ++-- 3 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 examples/jitter_stripplot.py delete mode 100644 examples/network_heatmap.py diff --git a/examples/jitter_stripplot.py b/examples/jitter_stripplot.py new file mode 100644 index 0000000000..5f046d1942 --- /dev/null +++ b/examples/jitter_stripplot.py @@ -0,0 +1,32 @@ +""" +Conditional means with observations +=================================== + +""" +import pandas as pd +import seaborn as sns +import matplotlib.pyplot as plt + +sns.set(style="whitegrid") +iris = sns.load_dataset("iris") + +# "Melt" the dataset to "long-form" or "tidy" representation +iris = pd.melt(iris, "species", var_name="measurement") + +# Initialize the figure +f, ax = plt.subplots() +sns.despine(bottom=True, left=True) + +# Show each observation with a scatterplot +sns.stripplot(x="value", y="measurement", hue="species", data=iris, + split=True, jitter=True, alpha=.2, label=None) + +# Show the conditional means +sns.pointplot(x="value", y="measurement", hue="species", data=iris, + join=False, dodge=.5, markers="d", scale=.75, ci=None) + +# Improve the legend +handles, labels = ax.get_legend_handles_labels() +ax.legend(handles[3:], labels[3:], title="species", + handletextpad=0, columnspacing=1, + loc="lower right", ncol=3, frameon=True) diff --git a/examples/network_heatmap.py b/examples/network_heatmap.py deleted file mode 100644 index 4a465295aa..0000000000 --- a/examples/network_heatmap.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -Large heatmap with divergent colormap -===================================== - -""" -import seaborn as sns -import matplotlib.pyplot as plt -sns.set() - -# Load the datset of correlations between cortical brain networks -df = sns.load_dataset("brain_networks", header=[0, 1, 2], index_col=0) - -# Set up the matplotlib figure -f, ax = plt.subplots(figsize=(11, 6)) - -# Draw the heatmap using seaborn -cbar_ticks = [-50, 0, 50] -sns.heatmap(df.T, center=0, robust=True, cbar_kws=dict(ticks=cbar_ticks)) diff --git a/examples/paired_pointplots.py b/examples/paired_pointplots.py index 9b558fbeb0..39b764369f 100644 --- a/examples/paired_pointplots.py +++ b/examples/paired_pointplots.py @@ -1,6 +1,6 @@ """ -Paired discrete plots -===================== +Paired categorical plots +======================== """ import seaborn as sns From f6e43ffd25a97062fd85477cb79e811ab89b3df0 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 7 Jul 2017 12:48:55 -0400 Subject: [PATCH 0463/1738] Remove tips datset from examples directory --- examples/tips.csv | 245 ---------------------------------------------- 1 file changed, 245 deletions(-) delete mode 100644 examples/tips.csv diff --git a/examples/tips.csv b/examples/tips.csv deleted file mode 100644 index 1280a10886..0000000000 --- a/examples/tips.csv +++ /dev/null @@ -1,245 +0,0 @@ -"total_bill","tip","sex","smoker","day","time","size" -16.99,1.01,"Female","No","Sun","Dinner",2 -10.34,1.66,"Male","No","Sun","Dinner",3 -21.01,3.5,"Male","No","Sun","Dinner",3 -23.68,3.31,"Male","No","Sun","Dinner",2 -24.59,3.61,"Female","No","Sun","Dinner",4 -25.29,4.71,"Male","No","Sun","Dinner",4 -8.77,2,"Male","No","Sun","Dinner",2 -26.88,3.12,"Male","No","Sun","Dinner",4 -15.04,1.96,"Male","No","Sun","Dinner",2 -14.78,3.23,"Male","No","Sun","Dinner",2 -10.27,1.71,"Male","No","Sun","Dinner",2 -35.26,5,"Female","No","Sun","Dinner",4 -15.42,1.57,"Male","No","Sun","Dinner",2 -18.43,3,"Male","No","Sun","Dinner",4 -14.83,3.02,"Female","No","Sun","Dinner",2 -21.58,3.92,"Male","No","Sun","Dinner",2 -10.33,1.67,"Female","No","Sun","Dinner",3 -16.29,3.71,"Male","No","Sun","Dinner",3 -16.97,3.5,"Female","No","Sun","Dinner",3 -20.65,3.35,"Male","No","Sat","Dinner",3 -17.92,4.08,"Male","No","Sat","Dinner",2 -20.29,2.75,"Female","No","Sat","Dinner",2 -15.77,2.23,"Female","No","Sat","Dinner",2 -39.42,7.58,"Male","No","Sat","Dinner",4 -19.82,3.18,"Male","No","Sat","Dinner",2 -17.81,2.34,"Male","No","Sat","Dinner",4 -13.37,2,"Male","No","Sat","Dinner",2 -12.69,2,"Male","No","Sat","Dinner",2 -21.7,4.3,"Male","No","Sat","Dinner",2 -19.65,3,"Female","No","Sat","Dinner",2 -9.55,1.45,"Male","No","Sat","Dinner",2 -18.35,2.5,"Male","No","Sat","Dinner",4 -15.06,3,"Female","No","Sat","Dinner",2 -20.69,2.45,"Female","No","Sat","Dinner",4 -17.78,3.27,"Male","No","Sat","Dinner",2 -24.06,3.6,"Male","No","Sat","Dinner",3 -16.31,2,"Male","No","Sat","Dinner",3 -16.93,3.07,"Female","No","Sat","Dinner",3 -18.69,2.31,"Male","No","Sat","Dinner",3 -31.27,5,"Male","No","Sat","Dinner",3 -16.04,2.24,"Male","No","Sat","Dinner",3 -17.46,2.54,"Male","No","Sun","Dinner",2 -13.94,3.06,"Male","No","Sun","Dinner",2 -9.68,1.32,"Male","No","Sun","Dinner",2 -30.4,5.6,"Male","No","Sun","Dinner",4 -18.29,3,"Male","No","Sun","Dinner",2 -22.23,5,"Male","No","Sun","Dinner",2 -32.4,6,"Male","No","Sun","Dinner",4 -28.55,2.05,"Male","No","Sun","Dinner",3 -18.04,3,"Male","No","Sun","Dinner",2 -12.54,2.5,"Male","No","Sun","Dinner",2 -10.29,2.6,"Female","No","Sun","Dinner",2 -34.81,5.2,"Female","No","Sun","Dinner",4 -9.94,1.56,"Male","No","Sun","Dinner",2 -25.56,4.34,"Male","No","Sun","Dinner",4 -19.49,3.51,"Male","No","Sun","Dinner",2 -38.01,3,"Male","Yes","Sat","Dinner",4 -26.41,1.5,"Female","No","Sat","Dinner",2 -11.24,1.76,"Male","Yes","Sat","Dinner",2 -48.27,6.73,"Male","No","Sat","Dinner",4 -20.29,3.21,"Male","Yes","Sat","Dinner",2 -13.81,2,"Male","Yes","Sat","Dinner",2 -11.02,1.98,"Male","Yes","Sat","Dinner",2 -18.29,3.76,"Male","Yes","Sat","Dinner",4 -17.59,2.64,"Male","No","Sat","Dinner",3 -20.08,3.15,"Male","No","Sat","Dinner",3 -16.45,2.47,"Female","No","Sat","Dinner",2 -3.07,1,"Female","Yes","Sat","Dinner",1 -20.23,2.01,"Male","No","Sat","Dinner",2 -15.01,2.09,"Male","Yes","Sat","Dinner",2 -12.02,1.97,"Male","No","Sat","Dinner",2 -17.07,3,"Female","No","Sat","Dinner",3 -26.86,3.14,"Female","Yes","Sat","Dinner",2 -25.28,5,"Female","Yes","Sat","Dinner",2 -14.73,2.2,"Female","No","Sat","Dinner",2 -10.51,1.25,"Male","No","Sat","Dinner",2 -17.92,3.08,"Male","Yes","Sat","Dinner",2 -27.2,4,"Male","No","Thur","Lunch",4 -22.76,3,"Male","No","Thur","Lunch",2 -17.29,2.71,"Male","No","Thur","Lunch",2 -19.44,3,"Male","Yes","Thur","Lunch",2 -16.66,3.4,"Male","No","Thur","Lunch",2 -10.07,1.83,"Female","No","Thur","Lunch",1 -32.68,5,"Male","Yes","Thur","Lunch",2 -15.98,2.03,"Male","No","Thur","Lunch",2 -34.83,5.17,"Female","No","Thur","Lunch",4 -13.03,2,"Male","No","Thur","Lunch",2 -18.28,4,"Male","No","Thur","Lunch",2 -24.71,5.85,"Male","No","Thur","Lunch",2 -21.16,3,"Male","No","Thur","Lunch",2 -28.97,3,"Male","Yes","Fri","Dinner",2 -22.49,3.5,"Male","No","Fri","Dinner",2 -5.75,1,"Female","Yes","Fri","Dinner",2 -16.32,4.3,"Female","Yes","Fri","Dinner",2 -22.75,3.25,"Female","No","Fri","Dinner",2 -40.17,4.73,"Male","Yes","Fri","Dinner",4 -27.28,4,"Male","Yes","Fri","Dinner",2 -12.03,1.5,"Male","Yes","Fri","Dinner",2 -21.01,3,"Male","Yes","Fri","Dinner",2 -12.46,1.5,"Male","No","Fri","Dinner",2 -11.35,2.5,"Female","Yes","Fri","Dinner",2 -15.38,3,"Female","Yes","Fri","Dinner",2 -44.3,2.5,"Female","Yes","Sat","Dinner",3 -22.42,3.48,"Female","Yes","Sat","Dinner",2 -20.92,4.08,"Female","No","Sat","Dinner",2 -15.36,1.64,"Male","Yes","Sat","Dinner",2 -20.49,4.06,"Male","Yes","Sat","Dinner",2 -25.21,4.29,"Male","Yes","Sat","Dinner",2 -18.24,3.76,"Male","No","Sat","Dinner",2 -14.31,4,"Female","Yes","Sat","Dinner",2 -14,3,"Male","No","Sat","Dinner",2 -7.25,1,"Female","No","Sat","Dinner",1 -38.07,4,"Male","No","Sun","Dinner",3 -23.95,2.55,"Male","No","Sun","Dinner",2 -25.71,4,"Female","No","Sun","Dinner",3 -17.31,3.5,"Female","No","Sun","Dinner",2 -29.93,5.07,"Male","No","Sun","Dinner",4 -10.65,1.5,"Female","No","Thur","Lunch",2 -12.43,1.8,"Female","No","Thur","Lunch",2 -24.08,2.92,"Female","No","Thur","Lunch",4 -11.69,2.31,"Male","No","Thur","Lunch",2 -13.42,1.68,"Female","No","Thur","Lunch",2 -14.26,2.5,"Male","No","Thur","Lunch",2 -15.95,2,"Male","No","Thur","Lunch",2 -12.48,2.52,"Female","No","Thur","Lunch",2 -29.8,4.2,"Female","No","Thur","Lunch",6 -8.52,1.48,"Male","No","Thur","Lunch",2 -14.52,2,"Female","No","Thur","Lunch",2 -11.38,2,"Female","No","Thur","Lunch",2 -22.82,2.18,"Male","No","Thur","Lunch",3 -19.08,1.5,"Male","No","Thur","Lunch",2 -20.27,2.83,"Female","No","Thur","Lunch",2 -11.17,1.5,"Female","No","Thur","Lunch",2 -12.26,2,"Female","No","Thur","Lunch",2 -18.26,3.25,"Female","No","Thur","Lunch",2 -8.51,1.25,"Female","No","Thur","Lunch",2 -10.33,2,"Female","No","Thur","Lunch",2 -14.15,2,"Female","No","Thur","Lunch",2 -16,2,"Male","Yes","Thur","Lunch",2 -13.16,2.75,"Female","No","Thur","Lunch",2 -17.47,3.5,"Female","No","Thur","Lunch",2 -34.3,6.7,"Male","No","Thur","Lunch",6 -41.19,5,"Male","No","Thur","Lunch",5 -27.05,5,"Female","No","Thur","Lunch",6 -16.43,2.3,"Female","No","Thur","Lunch",2 -8.35,1.5,"Female","No","Thur","Lunch",2 -18.64,1.36,"Female","No","Thur","Lunch",3 -11.87,1.63,"Female","No","Thur","Lunch",2 -9.78,1.73,"Male","No","Thur","Lunch",2 -7.51,2,"Male","No","Thur","Lunch",2 -14.07,2.5,"Male","No","Sun","Dinner",2 -13.13,2,"Male","No","Sun","Dinner",2 -17.26,2.74,"Male","No","Sun","Dinner",3 -24.55,2,"Male","No","Sun","Dinner",4 -19.77,2,"Male","No","Sun","Dinner",4 -29.85,5.14,"Female","No","Sun","Dinner",5 -48.17,5,"Male","No","Sun","Dinner",6 -25,3.75,"Female","No","Sun","Dinner",4 -13.39,2.61,"Female","No","Sun","Dinner",2 -16.49,2,"Male","No","Sun","Dinner",4 -21.5,3.5,"Male","No","Sun","Dinner",4 -12.66,2.5,"Male","No","Sun","Dinner",2 -16.21,2,"Female","No","Sun","Dinner",3 -13.81,2,"Male","No","Sun","Dinner",2 -17.51,3,"Female","Yes","Sun","Dinner",2 -24.52,3.48,"Male","No","Sun","Dinner",3 -20.76,2.24,"Male","No","Sun","Dinner",2 -31.71,4.5,"Male","No","Sun","Dinner",4 -10.59,1.61,"Female","Yes","Sat","Dinner",2 -10.63,2,"Female","Yes","Sat","Dinner",2 -50.81,10,"Male","Yes","Sat","Dinner",3 -15.81,3.16,"Male","Yes","Sat","Dinner",2 -7.25,5.15,"Male","Yes","Sun","Dinner",2 -31.85,3.18,"Male","Yes","Sun","Dinner",2 -16.82,4,"Male","Yes","Sun","Dinner",2 -32.9,3.11,"Male","Yes","Sun","Dinner",2 -17.89,2,"Male","Yes","Sun","Dinner",2 -14.48,2,"Male","Yes","Sun","Dinner",2 -9.6,4,"Female","Yes","Sun","Dinner",2 -34.63,3.55,"Male","Yes","Sun","Dinner",2 -34.65,3.68,"Male","Yes","Sun","Dinner",4 -23.33,5.65,"Male","Yes","Sun","Dinner",2 -45.35,3.5,"Male","Yes","Sun","Dinner",3 -23.17,6.5,"Male","Yes","Sun","Dinner",4 -40.55,3,"Male","Yes","Sun","Dinner",2 -20.69,5,"Male","No","Sun","Dinner",5 -20.9,3.5,"Female","Yes","Sun","Dinner",3 -30.46,2,"Male","Yes","Sun","Dinner",5 -18.15,3.5,"Female","Yes","Sun","Dinner",3 -23.1,4,"Male","Yes","Sun","Dinner",3 -15.69,1.5,"Male","Yes","Sun","Dinner",2 -19.81,4.19,"Female","Yes","Thur","Lunch",2 -28.44,2.56,"Male","Yes","Thur","Lunch",2 -15.48,2.02,"Male","Yes","Thur","Lunch",2 -16.58,4,"Male","Yes","Thur","Lunch",2 -7.56,1.44,"Male","No","Thur","Lunch",2 -10.34,2,"Male","Yes","Thur","Lunch",2 -43.11,5,"Female","Yes","Thur","Lunch",4 -13,2,"Female","Yes","Thur","Lunch",2 -13.51,2,"Male","Yes","Thur","Lunch",2 -18.71,4,"Male","Yes","Thur","Lunch",3 -12.74,2.01,"Female","Yes","Thur","Lunch",2 -13,2,"Female","Yes","Thur","Lunch",2 -16.4,2.5,"Female","Yes","Thur","Lunch",2 -20.53,4,"Male","Yes","Thur","Lunch",4 -16.47,3.23,"Female","Yes","Thur","Lunch",3 -26.59,3.41,"Male","Yes","Sat","Dinner",3 -38.73,3,"Male","Yes","Sat","Dinner",4 -24.27,2.03,"Male","Yes","Sat","Dinner",2 -12.76,2.23,"Female","Yes","Sat","Dinner",2 -30.06,2,"Male","Yes","Sat","Dinner",3 -25.89,5.16,"Male","Yes","Sat","Dinner",4 -48.33,9,"Male","No","Sat","Dinner",4 -13.27,2.5,"Female","Yes","Sat","Dinner",2 -28.17,6.5,"Female","Yes","Sat","Dinner",3 -12.9,1.1,"Female","Yes","Sat","Dinner",2 -28.15,3,"Male","Yes","Sat","Dinner",5 -11.59,1.5,"Male","Yes","Sat","Dinner",2 -7.74,1.44,"Male","Yes","Sat","Dinner",2 -30.14,3.09,"Female","Yes","Sat","Dinner",4 -12.16,2.2,"Male","Yes","Fri","Lunch",2 -13.42,3.48,"Female","Yes","Fri","Lunch",2 -8.58,1.92,"Male","Yes","Fri","Lunch",1 -15.98,3,"Female","No","Fri","Lunch",3 -13.42,1.58,"Male","Yes","Fri","Lunch",2 -16.27,2.5,"Female","Yes","Fri","Lunch",2 -10.09,2,"Female","Yes","Fri","Lunch",2 -20.45,3,"Male","No","Sat","Dinner",4 -13.28,2.72,"Male","No","Sat","Dinner",2 -22.12,2.88,"Female","Yes","Sat","Dinner",2 -24.01,2,"Male","Yes","Sat","Dinner",4 -15.69,3,"Male","Yes","Sat","Dinner",3 -11.61,3.39,"Male","No","Sat","Dinner",2 -10.77,1.47,"Male","No","Sat","Dinner",2 -15.53,3,"Male","Yes","Sat","Dinner",2 -10.07,1.25,"Male","No","Sat","Dinner",2 -12.6,1,"Male","Yes","Sat","Dinner",2 -32.83,1.17,"Male","Yes","Sat","Dinner",2 -35.83,4.67,"Female","No","Sat","Dinner",3 -29.03,5.92,"Male","No","Sat","Dinner",3 -27.18,2,"Female","Yes","Sat","Dinner",2 -22.67,2,"Male","Yes","Sat","Dinner",2 -17.82,1.75,"Male","No","Sat","Dinner",2 -18.78,3,"Female","No","Thur","Dinner",2 From 9b085921054aec0dddf86b15b4c944b4d6714770 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 7 Jul 2017 14:41:49 -0400 Subject: [PATCH 0464/1738] Rephrase warning message --- seaborn/distributions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 585057c6d0..9d26ef5796 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -625,9 +625,9 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", y = data2 if warn: - warn_msg = ("This 'matrix' style of input has been deprecated" - "in favor of kdeplot(x,y) and will be removed in " - "a forthcoming release. Please update your code.") + warn_msg = ("Passing a 2D dataset for a bivariate plot is deprecated " + "in favor of kdeplot(x, y), and it will cause an error in " + "future versions. Please update your code.") warnings.warn(warn_msg, UserWarning) if bivariate and cumulative: From faab1199ddfbe8c175870f6bc1633a0a18606bca Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 7 Jul 2017 14:33:03 -0400 Subject: [PATCH 0465/1738] Rename split to dodge in stripplot/swarmplot --- doc/releases/v0.8.0.txt | 4 +++- examples/jitter_stripplot.py | 10 ++++---- seaborn/categorical.py | 40 ++++++++++++++++++------------- seaborn/tests/test_categorical.py | 30 +++++++++++------------ 4 files changed, 48 insertions(+), 36 deletions(-) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index c1470728a1..b99989b435 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -14,7 +14,9 @@ v0.8.0 (July 2017) - Added ``"auto"`` as a (default) option for tick labels in :func:`heatmap` and :func:`clustermap`. This will try to estimate how many ticks can be labeled without the text objects overlapping, which should improve performance for larger matrices. -- Added the ``dodge`` argument to :func:`boxplot`, :func:`violinplot`, and :func:`barplot` to allow use of ``hue`` without changing the position or width of the plot elements, as when the ``hue`` varible is not nested within the main categorical variable. +- Added the ``dodge`` parameter to :func:`boxplot`, :func:`violinplot`, and :func:`barplot` to allow use of ``hue`` without changing the position or width of the plot elements, as when the ``hue`` varible is not nested within the main categorical variable. + +- Correspondingly, the ``split`` parameter for :func:`stripplot` and :func:`swarmplot` has been renamed to ``dodge`` for consistency with the other categorical functions (and for differentiation from the meaning of ``split`` in :func:`violinplot`). - Added the ability to draw a colorbar for a bivariate :func:`kdeplot` with the ``cbar`` parameter (and related ``cbar_ax`` and ``cbar_kws`` parameters). diff --git a/examples/jitter_stripplot.py b/examples/jitter_stripplot.py index 5f046d1942..f990ff08fc 100644 --- a/examples/jitter_stripplot.py +++ b/examples/jitter_stripplot.py @@ -18,12 +18,14 @@ sns.despine(bottom=True, left=True) # Show each observation with a scatterplot -sns.stripplot(x="value", y="measurement", hue="species", data=iris, - split=True, jitter=True, alpha=.2, label=None) +sns.stripplot(x="value", y="measurement", hue="species", + data=iris, dodge=True, jitter=True, + alpha=.25, zorder=1) # Show the conditional means -sns.pointplot(x="value", y="measurement", hue="species", data=iris, - join=False, dodge=.5, markers="d", scale=.75, ci=None) +sns.pointplot(x="value", y="measurement", hue="species", + data=iris, dodge=.532, join=False, palette="dark", + markers="d", scale=.75, ci=None) # Improve the legend handles, labels = ax.get_legend_handles_labels() diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 409bdd5be3..16bd6a11bb 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1077,8 +1077,6 @@ def plot(self, ax): class _CategoricalScatterPlotter(_CategoricalPlotter): - dodge = True - @property def point_colors(self): """Return a color for each scatter point based on group and hue.""" @@ -1119,20 +1117,20 @@ def add_legend_data(self, ax): class _StripPlotter(_CategoricalScatterPlotter): """1-d scatterplot with categorical organization.""" def __init__(self, x, y, hue, data, order, hue_order, - jitter, split, orient, color, palette): + jitter, dodge, orient, color, palette): """Initialize the plotter.""" self.establish_variables(x, y, hue, data, orient, order, hue_order) self.establish_colors(color, palette, 1) # Set object attributes - self.split = split + self.dodge = dodge self.width = .8 if jitter == 1: # Use a good default for `jitter = True` jlim = 0.1 else: jlim = float(jitter) - if self.hue_names is not None and split: + if self.hue_names is not None and dodge: jlim /= len(self.hue_names) self.jitterer = stats.uniform(-jlim, jlim * 2).rvs @@ -1141,7 +1139,7 @@ def draw_stripplot(self, ax, kws): # Set the default zorder to 2.1, so that the points # will be drawn on top of line elements (like in a boxplot) for i, group_data in enumerate(self.plot_data): - if self.plot_hues is None or not self.split: + if self.plot_hues is None or not self.dodge: if self.hue_names is None: hue_mask = np.ones(group_data.size, np.bool) @@ -1190,13 +1188,13 @@ def plot(self, ax, kws): class _SwarmPlotter(_CategoricalScatterPlotter): def __init__(self, x, y, hue, data, order, hue_order, - split, orient, color, palette): + dodge, orient, color, palette): """Initialize the plotter.""" self.establish_variables(x, y, hue, data, orient, order, hue_order) self.establish_colors(color, palette, 1) # Set object attributes - self.split = split + self.dodge = dodge self.width = .8 def could_overlap(self, xy_i, swarm, d): @@ -1361,7 +1359,7 @@ def draw_swarmplot(self, ax, kws): # Plot each swarm for i, group_data in enumerate(self.plot_data): - if self.plot_hues is None or not self.split: + if self.plot_hues is None or not self.dodge: width = self.width @@ -2560,11 +2558,16 @@ def violinplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, - jitter=False, split=False, orient=None, color=None, palette=None, + jitter=False, dodge=False, orient=None, color=None, palette=None, size=5, edgecolor="gray", linewidth=0, ax=None, **kwargs): + if "split" in kwargs: + dodge = kwargs.pop("split") + msg = "The `split` parameter has been renamed to `dodge`." + warnings.warn(msg, UserWarning) + plotter = _StripPlotter(x, y, hue, data, order, hue_order, - jitter, split, orient, color, palette) + jitter, dodge, orient, color, palette) if ax is None: ax = plt.gca() @@ -2697,7 +2700,7 @@ def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.stripplot(x="day", y="total_bill", hue="smoker", ... data=tips, jitter=True, - ... palette="Set2", split=True) + ... palette="Set2", dodge=True) Control strip order by passing an explicit order: @@ -2752,11 +2755,16 @@ def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, - split=False, orient=None, color=None, palette=None, + dodge=False, orient=None, color=None, palette=None, size=5, edgecolor="gray", linewidth=0, ax=None, **kwargs): + if "split" in kwargs: + dodge = kwargs.pop("split") + msg = "The `split` parameter has been renamed to `dodge`." + warnings.warn(msg, UserWarning) + plotter = _SwarmPlotter(x, y, hue, data, order, hue_order, - split, orient, color, palette) + dodge, orient, color, palette) if ax is None: ax = plt.gca() @@ -2870,7 +2878,7 @@ def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, :context: close-figs >>> ax = sns.swarmplot(x="day", y="total_bill", hue="smoker", - ... data=tips, palette="Set2", split=True) + ... data=tips, palette="Set2", dodge=True) Control swarm order by passing an explicit order: @@ -3639,7 +3647,7 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, ... hue="sex", row="class", ... data=titanic[titanic.embark_town.notnull()], ... orient="h", size=2, aspect=3.5, palette="Set3", - ... kind="violin", split=True, cut=0, bw=.2) + ... kind="violin", dodge=True, cut=0, bw=.2) Use methods on the returned :class:`FacetGrid` to tweak the presentation: diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 0288709a9d..5eb470e3db 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -1600,11 +1600,11 @@ def test_stripplot_jitter(self): npt.assert_equal(ax.collections[i].get_facecolors()[0, :3], pal[i]) - def test_split_nested_stripplot_vertical(self): + def test_dodge_nested_stripplot_vertical(self): pal = palettes.color_palette() - ax = cat.stripplot("g", "y", "h", data=self.df, split=True) + ax = cat.stripplot("g", "y", "h", data=self.df, dodge=True) for i, (_, group_vals) in enumerate(self.y.groupby(self.g)): for j, (_, vals) in enumerate(group_vals.groupby(self.h)): @@ -1617,12 +1617,12 @@ def test_split_nested_stripplot_vertical(self): npt.assert_equal(fc, pal[j]) @skipif(not pandas_has_categoricals) - def test_split_nested_stripplot_horizontal(self): + def test_dodge_nested_stripplot_horizontal(self): df = self.df.copy() df.g = df.g.astype("category") - ax = cat.stripplot("y", "g", "h", data=df, split=True) + ax = cat.stripplot("y", "g", "h", data=df, dodge=True) for i, (_, group_vals) in enumerate(self.y.groupby(self.g)): for j, (_, vals) in enumerate(group_vals.groupby(self.h)): @@ -1631,10 +1631,10 @@ def test_split_nested_stripplot_horizontal(self): npt.assert_array_equal(x, vals) npt.assert_array_equal(y, np.ones(len(x)) * i + [-.2, .2][j]) - def test_unsplit_nested_stripplot_vertical(self): + def test_nested_stripplot_vertical(self): # Test a simple vertical strip plot - ax = cat.stripplot("g", "y", "h", data=self.df, split=False) + ax = cat.stripplot("g", "y", "h", data=self.df, dodge=False) for i, (_, group_vals) in enumerate(self.y.groupby(self.g)): x, y = ax.collections[i].get_offsets().T @@ -1643,12 +1643,12 @@ def test_unsplit_nested_stripplot_vertical(self): npt.assert_array_equal(y, group_vals) @skipif(not pandas_has_categoricals) - def test_unsplit_nested_stripplot_horizontal(self): + def test_nested_stripplot_horizontal(self): df = self.df.copy() df.g = df.g.astype("category") - ax = cat.stripplot("y", "g", "h", data=df, split=False) + ax = cat.stripplot("y", "g", "h", data=df, dodge=False) for i, (_, group_vals) in enumerate(self.y.groupby(self.g)): x, y = ax.collections[i].get_offsets().T @@ -1668,7 +1668,7 @@ def test_three_strip_points(self): class TestSwarmPlotter(CategoricalFixture): default_kws = dict(x=None, y=None, hue=None, data=None, - order=None, hue_order=None, split=False, + order=None, hue_order=None, dodge=False, orient=None, color=None, palette=None) def test_could_overlap(self): @@ -1745,11 +1745,11 @@ def test_swarmplot_horizontal(self): fc = ax.collections[i].get_facecolors()[0, :3] npt.assert_equal(fc, pal[i]) - def test_split_nested_swarmplot_vetical(self): + def test_dodge_nested_swarmplot_vetical(self): pal = palettes.color_palette() - ax = cat.swarmplot("g", "y", "h", data=self.df, split=True) + ax = cat.swarmplot("g", "y", "h", data=self.df, dodge=True) for i, (_, group_vals) in enumerate(self.y.groupby(self.g)): for j, (_, vals) in enumerate(group_vals.groupby(self.h)): @@ -1759,11 +1759,11 @@ def test_split_nested_swarmplot_vetical(self): fc = ax.collections[i * 2 + j].get_facecolors()[0, :3] npt.assert_equal(fc, pal[j]) - def test_split_nested_swarmplot_horizontal(self): + def test_dodge_nested_swarmplot_horizontal(self): pal = palettes.color_palette() - ax = cat.swarmplot("y", "g", "h", data=self.df, orient="h", split=True) + ax = cat.swarmplot("y", "g", "h", data=self.df, orient="h", dodge=True) for i, (_, group_vals) in enumerate(self.y.groupby(self.g)): for j, (_, vals) in enumerate(group_vals.groupby(self.h)): @@ -1773,7 +1773,7 @@ def test_split_nested_swarmplot_horizontal(self): fc = ax.collections[i * 2 + j].get_facecolors()[0, :3] npt.assert_equal(fc, pal[j]) - def test_unsplit_nested_swarmplot_vertical(self): + def test_nested_swarmplot_vertical(self): ax = cat.swarmplot("g", "y", "h", data=self.df) @@ -1794,7 +1794,7 @@ def test_unsplit_nested_swarmplot_vertical(self): npt.assert_equal(fc[:3], pal[hue_names.index(hue)]) - def test_unsplit_nested_swarmplot_horizontal(self): + def test_nested_swarmplot_horizontal(self): ax = cat.swarmplot("y", "g", "h", data=self.df, orient="h") From 48b127b41e88b74991606a091535a361d565135d Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 7 Jul 2017 15:06:20 -0400 Subject: [PATCH 0466/1738] Raise on invalid input in tsplot --- seaborn/timeseries.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/seaborn/timeseries.py b/seaborn/timeseries.py index 3bd0c58abb..f8226031cb 100644 --- a/seaborn/timeseries.py +++ b/seaborn/timeseries.py @@ -349,6 +349,9 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, label = cond if legend else "_nolegend_" ax.plot(x, central_data, color=color, label=label, **kwargs) + else: + raise RuntimeError("Invalid input data for tsplot.") + # Pad the sides of the plot only when not interpolating ax.set_xlim(x.min(), x.max()) x_diff = x[1] - x[0] From 36b36afb56c2170b74a8d3776a0a7271f7b17e68 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 7 Jul 2017 15:19:24 -0400 Subject: [PATCH 0467/1738] Fix check for invalid tsplot input --- seaborn/timeseries.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seaborn/timeseries.py b/seaborn/timeseries.py index f8226031cb..6278838107 100644 --- a/seaborn/timeseries.py +++ b/seaborn/timeseries.py @@ -290,6 +290,7 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, colors = [color] * n_cond # Do a groupby with condition and plot each trace + c = None for c, (cond, df_c) in enumerate(data.groupby(condition, sort=False)): df_c = df_c.pivot(unit, time, value) @@ -349,7 +350,7 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, label = cond if legend else "_nolegend_" ax.plot(x, central_data, color=color, label=label, **kwargs) - else: + if c is None: raise RuntimeError("Invalid input data for tsplot.") # Pad the sides of the plot only when not interpolating From 8793b3834ee80ee8d307cf95a717c3cd854ef7eb Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 7 Jul 2017 15:34:56 -0400 Subject: [PATCH 0468/1738] Fix PairGrid diagonal color tests --- seaborn/tests/test_axisgrid.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 45ddd60e03..9b4491133c 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -937,26 +937,23 @@ def test_map_diag(self): @skipif(old_matplotlib) def test_map_diag_color(self): - color_set = {mpl.colors.cnames[x].lower() - for x in ['red', 'white', 'black']} + + color = "red" + rgb_color = mpl.colors.colorConverter.to_rgba(color) g1 = ag.PairGrid(self.df) - g1.map_diag(plt.hist, color='red') + g1.map_diag(plt.hist, color=color) for ax in g1.diag_axes: - colors = [mpl.colors.rgb2hex(patch.get_facecolor()).lower() - for patch in ax.patches] - for color in colors: - nt.assert_true(color in color_set, color) + for patch in ax.patches: + nt.assert_equals(patch.get_facecolor(), rgb_color) g2 = ag.PairGrid(self.df) g2.map_diag(kdeplot, color='red') for ax in g2.diag_axes: - colors = [mpl.colors.rgb2hex(patch.get_facecolor()).lower() - for patch in ax.patches] - for color in colors: - nt.assert_true(color in color_set, color) + for line in ax.lines: + nt.assert_equals(line.get_color(), color) @skipif(old_matplotlib) def test_map_diag_and_offdiag(self): From e924732d49358987730f65f17c651dcbd472c1d8 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 8 Jul 2017 17:30:40 -0400 Subject: [PATCH 0469/1738] Swap order of regression and categorical tutorials --- doc/index.rst | 2 +- doc/tutorial.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index 7048f63d40..a8437ac23e 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -104,8 +104,8 @@ To see the code or report a bug, please visit the `github repository * Style functions: :ref:`API ` | :ref:`Tutorial ` * Color palettes: :ref:`API ` | :ref:`Tutorial ` * Distribution plots: :ref:`API ` | :ref:`Tutorial ` -* Regression plots: :ref:`API ` | :ref:`Tutorial ` * Categorical plots: :ref:`API ` | :ref:`Tutorial ` +* Regression plots: :ref:`API ` | :ref:`Tutorial ` * Axis grid objects: :ref:`API ` | :ref:`Tutorial ` .. raw:: html diff --git a/doc/tutorial.rst b/doc/tutorial.rst index 1967b874c7..03c2a39567 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -19,8 +19,8 @@ Plotting functions :maxdepth: 2 tutorial/distributions - tutorial/regression tutorial/categorical + tutorial/regression Structured grids ---------------- From 9a7d8367d54b3f1197320aaa0654a5b38df465aa Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 8 Jul 2017 18:40:57 -0400 Subject: [PATCH 0470/1738] Increment version for 0.8.0 release --- seaborn/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/__init__.py b/seaborn/__init__.py index 27ac4fcc09..22275147ff 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -18,4 +18,4 @@ from .crayons import crayons from . import cm -__version__ = "0.8.dev" +__version__ = "0.8.0" diff --git a/setup.py b/setup.py index aa61f8a87c..639c478cc9 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ URL = 'http://seaborn.stanford.edu' LICENSE = 'BSD (3-clause)' DOWNLOAD_URL = 'https://github.com/mwaskom/seaborn/' -VERSION = '0.8.dev' +VERSION = '0.8.0' try: from setuptools import setup From f0aeb4e37f35ad1aeb15671ba22005032b025cd1 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 8 Jul 2017 18:55:46 -0400 Subject: [PATCH 0471/1738] Add v0.8.0 DOI badge --- README.md | 2 +- doc/releases/v0.8.0.txt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e6c7272701..9bf921270d 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ The documentation has an [example gallery](http://seaborn.pydata.org/examples/in Citing ------ -Seaborn can be cited using a DOI provided through Zenodo: [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.54844.svg)](https://doi.org/10.5281/zenodo.54844) +Seaborn can be cited using a DOI provided through Zenodo: ![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.54844.svg)](https://doi.org/10.5281/zenodo.54844) Dependencies ------------ diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index b99989b435..944c239492 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -2,6 +2,9 @@ v0.8.0 (July 2017) ------------------ +.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.824567.svg + :target: https://doi.org/10.5281/zenodo.824567 + - The default style is no longer applied when seaborn is imported. It is now necessary to explicitly call :func:`set` or one or more of :func:`set_style`, :func:`set_context`, and :func:`set_palette`. Correspondingly, the ``seaborn.apionly`` module has been deprecated. - Changed the behavior of :func:`heatmap` (and by extension :func:`clustermap`) when plotting divergent dataesets (i.e. when the ``center`` parameter is used). Instead of extending the lower and upper limits of the colormap to be symettrical around the ``center`` value, the colormap is modified so that its middle color corresponds to ``center``. This means that the full range of the colormap will not be used (unless the data or specified ``vmin`` and ``vmax`` are symettric), but the upper and lower limits of the colorbar will correspond to the range of the data. See the Github pull request `(#1184) `_ for examples of the behavior. From 7a5eaf84baa706222bfed449a1e2368932183f15 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 8 Jul 2017 19:33:51 -0400 Subject: [PATCH 0472/1738] Increment version post release --- seaborn/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/__init__.py b/seaborn/__init__.py index 22275147ff..cf349b7e93 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -18,4 +18,4 @@ from .crayons import crayons from . import cm -__version__ = "0.8.0" +__version__ = "0.9.dev" diff --git a/setup.py b/setup.py index 639c478cc9..a4f8e3434c 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ URL = 'http://seaborn.stanford.edu' LICENSE = 'BSD (3-clause)' DOWNLOAD_URL = 'https://github.com/mwaskom/seaborn/' -VERSION = '0.8.0' +VERSION = '0.9.dev' try: from setuptools import setup From 1f562a1a8d45f99c4ec607269f26e202618f8e43 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 8 Jul 2017 19:34:09 -0400 Subject: [PATCH 0473/1738] Fix python version tags in setup file --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a4f8e3434c..6cdc43ad89 100644 --- a/setup.py +++ b/setup.py @@ -81,8 +81,9 @@ def check_dependencies(): classifiers=[ 'Intended Audience :: Science/Research', 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'License :: OSI Approved :: BSD License', 'Topic :: Scientific/Engineering :: Visualization', 'Topic :: Multimedia :: Graphics', From 07c6fed76992a215d07e5644483f65ddf3c54fb7 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 8 Jul 2017 19:34:59 -0400 Subject: [PATCH 0474/1738] Fix other setup.py metadata --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 6cdc43ad89..b980d42f30 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ #! /usr/bin/env python # -# Copyright (C) 2012-2016 Michael Waskom +# Copyright (C) 2012-2017 Michael Waskom import os # temporarily redirect config directory to prevent matplotlib importing # testing that for writeable directory which results in sandbox error in @@ -24,8 +24,8 @@ DISTNAME = 'seaborn' MAINTAINER = 'Michael Waskom' -MAINTAINER_EMAIL = 'mwaskom@stanford.edu' -URL = 'http://seaborn.stanford.edu' +MAINTAINER_EMAIL = 'mwaskom@nyu.edu' +URL = 'http://seaborn.pydata.org' LICENSE = 'BSD (3-clause)' DOWNLOAD_URL = 'https://github.com/mwaskom/seaborn/' VERSION = '0.9.dev' From bcd598a3ac899fbe6e5e1c8b0f3da265c4d825b9 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 8 Jul 2017 20:09:56 -0400 Subject: [PATCH 0475/1738] Run travis tests on 3.6 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 0b4a8b1203..1d431835d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ env: - PYTHON=2.7 DEPS=minimal - PYTHON=3.4 DEPS=modern - PYTHON=3.5 DEPS=modern + - PYTHON=3.6 DEPS=modern install: - conda update conda --yes From 245efcec11af80673b472cceb621f2073248b14c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 8 Jul 2017 20:23:41 -0400 Subject: [PATCH 0476/1738] Add dependencies for 3.6 tests --- testing/deps_modern_3.6.txt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 testing/deps_modern_3.6.txt diff --git a/testing/deps_modern_3.6.txt b/testing/deps_modern_3.6.txt new file mode 100644 index 0000000000..500bb9fbd1 --- /dev/null +++ b/testing/deps_modern_3.6.txt @@ -0,0 +1,8 @@ +ipython-notebook +nose +numpy +scipy +matplotlib +pandas +statsmodels +patsy From a3f3ddee1fe87a397c69c10505f852d81991bef7 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 8 Jul 2017 20:36:26 -0400 Subject: [PATCH 0477/1738] Revert py3.6 tests to fix in another branch --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1d431835d7..0b4a8b1203 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ env: - PYTHON=2.7 DEPS=minimal - PYTHON=3.4 DEPS=modern - PYTHON=3.5 DEPS=modern - - PYTHON=3.6 DEPS=modern install: - conda update conda --yes From 8c42d0724c753d50725066faecbce34904b8802f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 8 Jul 2017 20:39:00 -0400 Subject: [PATCH 0478/1738] Don't require ipython-notebook on travis --- .travis.yml | 1 + testing/deps_minimal_2.7.txt | 2 -- testing/deps_modern_2.7.txt | 2 -- testing/deps_modern_3.4.txt | 1 - testing/deps_modern_3.5.txt | 2 -- testing/deps_modern_3.6.txt | 2 -- 6 files changed, 1 insertion(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0b4a8b1203..1d431835d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ env: - PYTHON=2.7 DEPS=minimal - PYTHON=3.4 DEPS=modern - PYTHON=3.5 DEPS=modern + - PYTHON=3.6 DEPS=modern install: - conda update conda --yes diff --git a/testing/deps_minimal_2.7.txt b/testing/deps_minimal_2.7.txt index 3b6c70cc3e..aa3a74c148 100644 --- a/testing/deps_minimal_2.7.txt +++ b/testing/deps_minimal_2.7.txt @@ -1,8 +1,6 @@ -ipython-notebook nose numpy=1.6.2 scipy=0.11.0 matplotlib=1.1.1 pandas=0.12.0 statsmodels=0.5.0 -patsy=0.2.1 diff --git a/testing/deps_modern_2.7.txt b/testing/deps_modern_2.7.txt index 500bb9fbd1..fc511cd76f 100644 --- a/testing/deps_modern_2.7.txt +++ b/testing/deps_modern_2.7.txt @@ -1,8 +1,6 @@ -ipython-notebook nose numpy scipy matplotlib pandas statsmodels -patsy diff --git a/testing/deps_modern_3.4.txt b/testing/deps_modern_3.4.txt index a63b15f21e..fc511cd76f 100644 --- a/testing/deps_modern_3.4.txt +++ b/testing/deps_modern_3.4.txt @@ -1,4 +1,3 @@ -ipython-notebook nose numpy scipy diff --git a/testing/deps_modern_3.5.txt b/testing/deps_modern_3.5.txt index 500bb9fbd1..fc511cd76f 100644 --- a/testing/deps_modern_3.5.txt +++ b/testing/deps_modern_3.5.txt @@ -1,8 +1,6 @@ -ipython-notebook nose numpy scipy matplotlib pandas statsmodels -patsy diff --git a/testing/deps_modern_3.6.txt b/testing/deps_modern_3.6.txt index 500bb9fbd1..fc511cd76f 100644 --- a/testing/deps_modern_3.6.txt +++ b/testing/deps_modern_3.6.txt @@ -1,8 +1,6 @@ -ipython-notebook nose numpy scipy matplotlib pandas statsmodels -patsy From 8c2f7280e356a96066944930231c4cb1a28ea6bb Mon Sep 17 00:00:00 2001 From: Cameron Pye Date: Sat, 8 Jul 2017 20:49:24 -0700 Subject: [PATCH 0479/1738] type check for pairplot --- seaborn/axisgrid.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index a1c948313e..7e816f9841 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -2004,6 +2004,11 @@ def pairplot(data, hue=None, hue_order=None, palette=None, ... diag_kws=dict(shade=True)) """ + if not isinstance(data,pd.DataFrame): + raise TypeError( + "'data' must be pandas DataFrame object, not: {typefound}".format( + typefound=type(data))) + if plot_kws is None: plot_kws = {} if diag_kws is None: From ceea764db4ce35bdfcf7a22c293064da4ba72b98 Mon Sep 17 00:00:00 2001 From: Cameron Pye Date: Sat, 8 Jul 2017 21:36:56 -0700 Subject: [PATCH 0480/1738] fixed pep8 errors --- seaborn/axisgrid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 7e816f9841..47c4167621 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -2004,10 +2004,10 @@ def pairplot(data, hue=None, hue_order=None, palette=None, ... diag_kws=dict(shade=True)) """ - if not isinstance(data,pd.DataFrame): + if not isinstance(data, pd.DataFrame): raise TypeError( "'data' must be pandas DataFrame object, not: {typefound}".format( - typefound=type(data))) + typefound=type(data))) if plot_kws is None: plot_kws = {} From 7d52ee6030b2e59a6b6cb6dce78686e8d551281b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 9 Jul 2017 17:51:21 -0400 Subject: [PATCH 0481/1738] Fix comments in horizontal boxplot example --- examples/horizontal_boxplot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/horizontal_boxplot.py b/examples/horizontal_boxplot.py index 84cf4bf3c5..a28ef3da4c 100644 --- a/examples/horizontal_boxplot.py +++ b/examples/horizontal_boxplot.py @@ -7,9 +7,10 @@ import numpy as np import seaborn as sns import matplotlib.pyplot as plt + sns.set(style="ticks") -# Initialize the figure +# Initialize the figure with a logarithmic x axis f, ax = plt.subplots(figsize=(7, 6)) ax.set_xscale("log") @@ -24,8 +25,7 @@ sns.swarmplot(x="distance", y="method", data=planets, size=2, color=".3", linewidth=0) - -# Make the quantitative axis logarithmic +# Tweak the visual presentation ax.xaxis.grid(True) ax.set(ylabel="") sns.despine(trim=True, left=True) From 1b77d537d625aa7fbee87ca14c35a2a1ce6ff3b2 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 10 Jul 2017 14:56:45 -0400 Subject: [PATCH 0482/1738] Fix error in clustermap with no yticklabels Closes #1231 --- doc/whatsnew.rst | 2 ++ seaborn/matrix.py | 10 ++++------ seaborn/tests/test_matrix.py | 13 +++++++++++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/doc/whatsnew.rst b/doc/whatsnew.rst index 4dfb93ce6b..49956eb77a 100644 --- a/doc/whatsnew.rst +++ b/doc/whatsnew.rst @@ -5,6 +5,8 @@ What's new in the package ========================= +.. include:: releases/v0.8.1.txt + .. include:: releases/v0.8.0.txt .. include:: releases/v0.7.1.txt diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 45af3b3ed7..223f5a003c 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -1115,14 +1115,12 @@ def plot_matrix(self, colorbar_kws, xind, yind, **kws): cbar_kws=colorbar_kws, mask=self.mask, xticklabels=xtl, yticklabels=ytl, **kws) - xtl_rot = self.ax_heatmap.get_xticklabels()[0].get_rotation() - ytl_rot = self.ax_heatmap.get_yticklabels()[0].get_rotation() - + ytl = self.ax_heatmap.get_yticklabels() + ytl_rot = None if not ytl else ytl[0].get_rotation() self.ax_heatmap.yaxis.set_ticks_position('right') self.ax_heatmap.yaxis.set_label_position('right') - - plt.setp(self.ax_heatmap.get_xticklabels(), rotation=xtl_rot) - plt.setp(self.ax_heatmap.get_yticklabels(), rotation=ytl_rot) + if ytl_rot is not None: + plt.setp(ytl, rotation=ytl_rot) def plot(self, metric, method, colorbar_kws, row_cluster, col_cluster, row_linkage, col_linkage, **kws): diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index 9da9c7f4e7..ecae4b7903 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -1084,3 +1084,16 @@ def test_ticklabel_reorganization(self): npt.assert_array_equal(xtl_actual, xtl_want) npt.assert_array_equal(ytl_actual, ytl_want) + + def test_noticklabels(self): + + kws = self.default_kws.copy() + kws["xticklabels"] = False + kws["yticklabels"] = False + + g = mat.clustermap(self.df_norm, **kws) + + xtl_actual = [t.get_text() for t in g.ax_heatmap.get_xticklabels()] + ytl_actual = [t.get_text() for t in g.ax_heatmap.get_yticklabels()] + nt.assert_equal(xtl_actual, []) + nt.assert_equal(ytl_actual, []) From 58bfd9eb68c7ae0ebddb2fe08be8f2416f78831e Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Mon, 10 Jul 2017 23:19:30 -0700 Subject: [PATCH 0483/1738] Fix legend_out with GUI backend There was a bug that FacetGrid.add_legend() with legend_out=True did not work when the backend was "GUI"-based ones such as Qt5Agg and NbAgg (checked with matplotlib 2.0.2). Note that it was working correctly with Agg backend. This is likely because pyplot.draw calls canvas.draw_idle() which may not force re-draw [1]. [1] https://github.com/matplotlib/matplotlib/blob/v2.0.2/lib/matplotlib/pyplot.py#L691 --- seaborn/axisgrid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index a1c948313e..8c68f17cf6 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -93,7 +93,7 @@ def add_legend(self, legend_data=None, title=None, label_order=None, figlegend._legend_title_box._text.set_font_properties(prop) # Draw the plot to set the bounding boxes correctly - plt.draw() + self.fig.draw(self.fig.canvas.get_renderer()) # Calculate and set the new width of the figure so the legend fits legend_width = figlegend.get_window_extent().width / self.fig.dpi @@ -101,7 +101,7 @@ def add_legend(self, legend_data=None, title=None, label_order=None, self.fig.set_figwidth(figure_width + legend_width) # Draw the plot again to get the new transformations - plt.draw() + self.fig.draw(self.fig.canvas.get_renderer()) # Now calculate how much space we need on the right side legend_width = figlegend.get_window_extent().width / self.fig.dpi From d62c7d6722ad4653b54ed83b0b62105385bb8744 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Tue, 11 Jul 2017 07:18:58 -0700 Subject: [PATCH 0484/1738] Fix Zenodo badge in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9bf921270d..e6c7272701 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ The documentation has an [example gallery](http://seaborn.pydata.org/examples/in Citing ------ -Seaborn can be cited using a DOI provided through Zenodo: ![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.54844.svg)](https://doi.org/10.5281/zenodo.54844) +Seaborn can be cited using a DOI provided through Zenodo: [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.54844.svg)](https://doi.org/10.5281/zenodo.54844) Dependencies ------------ From 475d0f282ee524fcd996f27cd8c8590bd3eddd73 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 11 Jul 2017 11:21:57 -0400 Subject: [PATCH 0485/1738] Add v0.8.1 release notes to git --- doc/releases/v0.8.0.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index 944c239492..732505545b 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -11,7 +11,7 @@ v0.8.0 (July 2017) - Removed automatic detection of diverging data in :func:`heatmap` (and by extension :func:`clustermap`). If you want the colormap to be treated as diverging (see above), it is now necessary to specify the ``center`` value. When no colormap is specified, specifying ``center`` will still change the default to be one that is more appropriate for displaying diverging data. -- Added four new colormaps, created using `viscm `_ for perceptual uniformity. The new colormaps include two sequential colormaps ("rocket" and "mako") and two diverging colormaps ("icefire" and "vlag"). These colormaps are registered with matplotlib on seaborn input and the colormap objects can be accessed in the ``seaborn.cm`` namespace. +- Added four new colormaps, created using `viscm `_ for perceptual uniformity. The new colormaps include two sequential colormaps ("rocket" and "mako") and two diverging colormaps ("icefire" and "vlag"). These colormaps are registered with matplotlib on seaborn import and the colormap objects can be accessed in the ``seaborn.cm`` namespace. - Changed the default :func:`heatmap` colormaps to be "rocket" (in the case of sequential data) or "icefire" (in the case of diverging data). Note that this change reverses the direction of the luminance ramp from the previous defaults. While potentially confusing and disruptive, this change better aligns the seaborn defaults with the new matplotlib default colormap ("viridis") and arguably better aligns the semantics of a "heat" map with the appearance of the colormap. From c59a7062d7dfc3510d7db72ebaa2411f85d37850 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 12 Jul 2017 18:47:33 -0400 Subject: [PATCH 0486/1738] Add v0.8.1 release notes to git (actually) --- doc/releases/v0.8.1.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 doc/releases/v0.8.1.txt diff --git a/doc/releases/v0.8.1.txt b/doc/releases/v0.8.1.txt new file mode 100644 index 0000000000..607e60e2c7 --- /dev/null +++ b/doc/releases/v0.8.1.txt @@ -0,0 +1,5 @@ + +v0.8.1 (Unreleased) +------------------- + +- Fixed an error in :func:`clustermap` when using ``yticklabels=False``. From 7b445822bc4c1edf22c8e8e11f67e632ffb90f86 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 13 Jul 2017 18:41:23 -0400 Subject: [PATCH 0487/1738] Change how a draw is forced in matrix plots for interactive backend compatability --- seaborn/matrix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 223f5a003c..7459dc9d33 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -312,7 +312,7 @@ def plot(self, ax, cax, kws): ytl = ax.set_yticklabels(yticklabels, rotation="vertical") # Possibly rotate them if they overlap - plt.draw() + ax.figure.draw(ax.figure.canvas.get_renderer()) if axis_ticklabels_overlap(xtl): plt.setp(xtl, rotation="vertical") if axis_ticklabels_overlap(ytl): @@ -697,7 +697,7 @@ def plot(self, ax): ytl = ax.set_yticklabels(self.yticklabels, rotation='vertical') # Force a draw of the plot to avoid matplotlib window error - plt.draw() + ax.figure.draw(ax.figure.canvas.get_renderer()) if len(ytl) > 0 and axis_ticklabels_overlap(ytl): plt.setp(ytl, rotation="horizontal") if len(xtl) > 0 and axis_ticklabels_overlap(xtl): From 4a5a73c02217693a2c8092598e89122771b85fdc Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 13 Jul 2017 18:42:46 -0400 Subject: [PATCH 0488/1738] Update release notes --- doc/releases/v0.8.1.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/releases/v0.8.1.txt b/doc/releases/v0.8.1.txt index 607e60e2c7..892ec77c7e 100644 --- a/doc/releases/v0.8.1.txt +++ b/doc/releases/v0.8.1.txt @@ -2,4 +2,6 @@ v0.8.1 (Unreleased) ------------------- -- Fixed an error in :func:`clustermap` when using ``yticklabels=False``. +- Fixed a problem where the :class:`FacetGrid` or :class:`PairGrid` legend remained inside the figure when using ``legend_out=True`` on an interactive backend. + +- Fixed an bug in :func:`clustermap` when using ``yticklabels=False``. From fe4ca741b979d5672ad9c1d27f7d5b432e33827d Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 13 Jul 2017 18:49:33 -0400 Subject: [PATCH 0489/1738] Improve scope of CI tests --- .travis.yml | 48 ++++++++++++------- .../{deps_modern_2.7.txt => deps_latest.txt} | 0 .../{deps_modern_3.4.txt => deps_minimal.txt} | 1 - testing/deps_modern_3.5.txt | 6 --- testing/deps_modern_3.6.txt | 6 --- .../{deps_minimal_2.7.txt => deps_pinned.txt} | 0 testing/matplotlibrc | 1 - testing/matplotlibrc_agg | 1 + testing/matplotlibrc_qtagg | 1 + 9 files changed, 33 insertions(+), 31 deletions(-) rename testing/{deps_modern_2.7.txt => deps_latest.txt} (100%) rename testing/{deps_modern_3.4.txt => deps_minimal.txt} (74%) delete mode 100644 testing/deps_modern_3.5.txt delete mode 100644 testing/deps_modern_3.6.txt rename testing/{deps_minimal_2.7.txt => deps_pinned.txt} (100%) delete mode 100644 testing/matplotlibrc create mode 100644 testing/matplotlibrc_agg create mode 100644 testing/matplotlibrc_qtagg diff --git a/.travis.yml b/.travis.yml index 1d431835d7..dfb4af4aa9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,22 +1,16 @@ language: python + env: - - PYTHON=2.7 DEPS=modern - - PYTHON=2.7 DEPS=minimal - - PYTHON=3.4 DEPS=modern - - PYTHON=3.5 DEPS=modern - - PYTHON=3.6 DEPS=modern + - PYTHON=2.7 DEPS=latest BACKEND=agg DOCTESTS=true + - PYTHON=2.7 DEPS=pinned BACKEND=agg DOCTESTS=false + - PYTHON=2.7 DEPS=latest BACKEND=qtagg DOCTESTS=true + - PYTHON=3.4 DEPS=latest BACKEND=agg DOCTESTS=true + - PYTHON=3.5 DEPS=latest BACKEND=agg DOCTESTS=true + - PYTHON=3.6 DEPS=latest BACKEND=agg DOCTESTS=true + - PYTHON=3.6 DEPS=latest BACKEND=qtagg DOCTESTS=true + - PYTHON=3.6 DEPS=minimal BACKEND=agg DOCTESTS=false -install: - - conda update conda --yes - - conda create -n testenv --yes pip python=$PYTHON - - conda update conda --yes - - source activate testenv - - conda install --yes --file testing/deps_${DEPS}_${PYTHON}.txt - - pip install pep8==1.5 # Later versions get stricter... - - pip install https://github.com/dcramer/pyflakes/tarball/master - - pip install . - - cp testing/matplotlibrc . before_install: - sudo apt-get update -yq @@ -32,12 +26,32 @@ before_install: - conda update -q conda - conda info -a + +install: + - conda update conda --yes + - conda create -n testenv --yes pip python=$PYTHON + - conda update conda --yes + - source activate testenv + - conda install --yes --file testing/deps_${DEPS}.txt + - pip install pep8==1.5 # Later versions get stricter... + - pip install https://github.com/dcramer/pyflakes/tarball/master + - pip install . + + before_script: + - cp testing/matplotlibrc_${BACKEND} matplotlibrc + - if [ $BACKEND == "qtagg" ]; then + export DISPLAY=:99.0; + sh -e /etc/init.d/xvfb start; + sleep 3; + fi + + +script: - if [ ${PYTHON:0:1} == "2" ]; then make lint; fi -script: - - if [ $DEPS == 'modern' ]; + - if [ $DOCTESTS == 'true' ]; then nosetests --with-doctest; else nosetests; fi diff --git a/testing/deps_modern_2.7.txt b/testing/deps_latest.txt similarity index 100% rename from testing/deps_modern_2.7.txt rename to testing/deps_latest.txt diff --git a/testing/deps_modern_3.4.txt b/testing/deps_minimal.txt similarity index 74% rename from testing/deps_modern_3.4.txt rename to testing/deps_minimal.txt index fc511cd76f..e9f007f498 100644 --- a/testing/deps_modern_3.4.txt +++ b/testing/deps_minimal.txt @@ -3,4 +3,3 @@ numpy scipy matplotlib pandas -statsmodels diff --git a/testing/deps_modern_3.5.txt b/testing/deps_modern_3.5.txt deleted file mode 100644 index fc511cd76f..0000000000 --- a/testing/deps_modern_3.5.txt +++ /dev/null @@ -1,6 +0,0 @@ -nose -numpy -scipy -matplotlib -pandas -statsmodels diff --git a/testing/deps_modern_3.6.txt b/testing/deps_modern_3.6.txt deleted file mode 100644 index fc511cd76f..0000000000 --- a/testing/deps_modern_3.6.txt +++ /dev/null @@ -1,6 +0,0 @@ -nose -numpy -scipy -matplotlib -pandas -statsmodels diff --git a/testing/deps_minimal_2.7.txt b/testing/deps_pinned.txt similarity index 100% rename from testing/deps_minimal_2.7.txt rename to testing/deps_pinned.txt diff --git a/testing/matplotlibrc b/testing/matplotlibrc deleted file mode 100644 index 13468274c2..0000000000 --- a/testing/matplotlibrc +++ /dev/null @@ -1 +0,0 @@ -backend : Agg diff --git a/testing/matplotlibrc_agg b/testing/matplotlibrc_agg new file mode 100644 index 0000000000..88a8365733 --- /dev/null +++ b/testing/matplotlibrc_agg @@ -0,0 +1 @@ +backend: Agg diff --git a/testing/matplotlibrc_qtagg b/testing/matplotlibrc_qtagg new file mode 100644 index 0000000000..957624030e --- /dev/null +++ b/testing/matplotlibrc_qtagg @@ -0,0 +1 @@ +backend: Qt5Agg From 2550fea1120a0dbc3f19a1e65730b22ba6839df9 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 14 Jul 2017 14:49:30 -0400 Subject: [PATCH 0490/1738] Remove matplotlibrc reference form makefile --- Makefile | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index f9d5aa7fda..1a6b0ba368 100644 --- a/Makefile +++ b/Makefile @@ -2,22 +2,17 @@ export SHELL := /bin/bash test: - cp testing/matplotlibrc . nosetests --with-doctest - rm matplotlibrc +test-nodoctest: + + nosetests coverage: - cp testing/matplotlibrc . nosetests --cover-erase --with-coverage --cover-html --cover-package seaborn - rm matplotlibrc lint: pyflakes -x W -X seaborn/external/six.py seaborn pep8 --exclude external,cm.py seaborn - -hexstrip: - - make -C examples hexstrip From 3050ffe15655c90154e60e262f297b8adbb3b915 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 14 Jul 2017 15:33:38 -0400 Subject: [PATCH 0491/1738] Ignore widgets file when estimating test coverage --- .coveragerc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.coveragerc b/.coveragerc index 266986a2bf..55415f6b58 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,3 +1,3 @@ [run] include = seaborn/* -omit = *external* +omit = *external* *widgets.py From cc331fdd65e8a2d25c5f6073e656f46330889393 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 14 Jul 2017 15:34:07 -0400 Subject: [PATCH 0492/1738] Pass kde clipping kwarg to shade glyph --- seaborn/distributions.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index c3ae5b96cd..12fa21ae31 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -305,12 +305,16 @@ def _univariate_kdeplot(data, shade, vertical, kernel, bw, gridsize, cut, # Draw the KDE plot and, optionally, shade ax.plot(x, y, color=color, label=label, **kwargs) - alpha = kwargs.get("alpha", 0.25) + shade_kws = dict( + facecolor=color, + alpha=kwargs.get("alpha", 0.25), + clip_on=kwargs.get("clip_on", True), + ) if shade: if vertical: - ax.fill_betweenx(y, 0, x, facecolor=color, alpha=alpha) + ax.fill_betweenx(y, 0, x, **shade_kws) else: - ax.fill_between(x, 0, y, facecolor=color, alpha=alpha) + ax.fill_between(x, 0, y, **shade_kws) # Set the density axis minimum to 0 if vertical: From 3ef0dc2ab1e55e1fffb116ed0777ff5b5a406c4b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 14 Jul 2017 19:07:10 -0400 Subject: [PATCH 0493/1738] Use facecolor for different kdeplot fill --- seaborn/distributions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 12fa21ae31..290bba5988 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -298,17 +298,20 @@ def _univariate_kdeplot(data, shade, vertical, kernel, bw, gridsize, cut, label = "_nolegend_" if label is None else label # Use the active color cycle to find the plot color + facecolor = kwargs.pop("facecolor", None) line, = ax.plot(x, y, **kwargs) color = line.get_color() line.remove() kwargs.pop("color", None) + facecolor = color if facecolor is None else facecolor # Draw the KDE plot and, optionally, shade ax.plot(x, y, color=color, label=label, **kwargs) shade_kws = dict( - facecolor=color, + facecolor=facecolor, alpha=kwargs.get("alpha", 0.25), clip_on=kwargs.get("clip_on", True), + zorder=kwargs.get("zorder", 1), ) if shade: if vertical: From 2c17cb829516d0a3856d870b12f25271efb296ce Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 14 Jul 2017 19:07:28 -0400 Subject: [PATCH 0494/1738] Add an example script to make a Joy Division plot --- examples/kde_joyplot.py | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 examples/kde_joyplot.py diff --git a/examples/kde_joyplot.py b/examples/kde_joyplot.py new file mode 100644 index 0000000000..68d4f717e3 --- /dev/null +++ b/examples/kde_joyplot.py @@ -0,0 +1,43 @@ +""" +Overlapping KDEs ('Joy Division plot') +====================================== + + +""" +import numpy as np +import pandas as pd +import seaborn as sns +import matplotlib.pyplot as plt +sns.set(style="white", rc={"axes.facecolor": (0, 0, 0, 0)}) + +# Create the data +rs = np.random.RandomState(1979) +x = rs.randn(500) +g = np.tile(list("ABCDEFGHIJ"), 50) +df = pd.DataFrame(dict(x=x, g=g)) +m = df.g.map(ord) +df["x"] += m + +# Initialize the FacetGrid object +g = sns.FacetGrid(df, row="g", hue="g", aspect=12, size=.5, palette="tab10") + +# Draw the densities in a few steps +g.map(sns.kdeplot, "x", clip_on=False, shade=True, alpha=1, lw=1.5, bw=.2) +g.map(sns.kdeplot, "x", clip_on=False, color="w", lw=2, bw=.2) +g.map(plt.axhline, y=0, lw=2, clip_on=False) + +# Define and use a simple function to label the plot in axes coordinates +def label(x, color, label): + ax = plt.gca() + ax.text(0, .2, label, fontweight="bold", color=color, + ha="left", va="center", transform=ax.transAxes) + +g.map(label, "x") + +# Set the subplots to overlap +g.fig.subplots_adjust(hspace=-.25) + +# Remove axes details that don't play will with overlap +g.set_titles("") +g.set(yticks=[]) +g.despine(bottom=True, left=True) From d7340199072e8f962af0992a148aa0581cc86d7f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 14 Jul 2017 19:07:58 -0400 Subject: [PATCH 0495/1738] Remove tsplot example with functionality that will likely disappear --- examples/timeseries_bootstrapped.py | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 examples/timeseries_bootstrapped.py diff --git a/examples/timeseries_bootstrapped.py b/examples/timeseries_bootstrapped.py deleted file mode 100644 index 1b18804782..0000000000 --- a/examples/timeseries_bootstrapped.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Plotting timeseries data with bootstrap resampling -================================================== - -""" -import numpy as np -import seaborn as sns -sns.set(style="darkgrid", palette="Set2") - -# Create a noisy periodic dataset -sines = [] -rs = np.random.RandomState(8) -for _ in range(15): - x = np.linspace(0, 30 / 2, 30) - y = np.sin(x) + rs.normal(0, 1.5) + rs.normal(0, .3, 30) - sines.append(y) - -# Plot the average over replicates with bootstrap resamples -sns.tsplot(sines, err_style="boot_traces", n_boot=500) From 5256e854a9f8f10f60fcd7e8f841273f2a4d40ab Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 14 Jul 2017 21:06:36 -0400 Subject: [PATCH 0496/1738] Change joy division plot palette --- examples/kde_joyplot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/kde_joyplot.py b/examples/kde_joyplot.py index 68d4f717e3..f408505da0 100644 --- a/examples/kde_joyplot.py +++ b/examples/kde_joyplot.py @@ -19,7 +19,8 @@ df["x"] += m # Initialize the FacetGrid object -g = sns.FacetGrid(df, row="g", hue="g", aspect=12, size=.5, palette="tab10") +pal = sns.cubehelix_palette(10, rot=-.25, light=.7) +g = sns.FacetGrid(df, row="g", hue="g", aspect=12, size=.5, palette=pal) # Draw the densities in a few steps g.map(sns.kdeplot, "x", clip_on=False, shade=True, alpha=1, lw=1.5, bw=.2) From 343644eac2a12f3cc0f303ab818f96ba992fd562 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 20 Jul 2017 11:04:29 -0400 Subject: [PATCH 0497/1738] Fix typo --- seaborn/axisgrid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 8c68f17cf6..27e06572b7 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1871,7 +1871,7 @@ def pairplot(data, hue=None, hue_order=None, palette=None, variables on the rows and columns. This is a high-level interface for :class:`PairGrid` that is intended to - make it easy to draw a few common styles. You should use :class`PairGrid` + make it easy to draw a few common styles. You should use :class:`PairGrid` directly if you need more flexibility. Parameters From f2e7599c4740fe5054a60bbbf571319402140f7f Mon Sep 17 00:00:00 2001 From: gfyoung Date: Mon, 17 Jul 2017 00:37:04 -0700 Subject: [PATCH 0498/1738] Remove usage of pandas remove_na function Closes gh-1240. --- seaborn/categorical.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 16bd6a11bb..639d2aa66e 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -4,7 +4,6 @@ import numpy as np from scipy import stats import pandas as pd -from pandas.core.series import remove_na import matplotlib as mpl from matplotlib.collections import PatchCollection import matplotlib.patches as Patches @@ -25,6 +24,24 @@ "pointplot", "barplot", "countplot", "factorplot"] +def remove_na(arr): + """ + Helper method for removing NA values from array-like. + + Parameters + ---------- + arr : array-like + The array-like from which to remove NA values. + + Returns + ------- + clean_arr : array-like + The original array, just with NA values removed. + """ + + return arr[pd.notnull(arr)] + + class _CategoricalPlotter(object): width = .8 From 74e7aa9a2e57f9c28725db8e14e0a758760cdadd Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 27 Aug 2017 13:32:18 -0400 Subject: [PATCH 0499/1738] Fix PairGrid map_diag with hue (closes #1265) --- seaborn/axisgrid.py | 27 ++++++++++++++++----------- seaborn/tests/test_axisgrid.py | 11 +++++++++++ 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 27e06572b7..0da28427a4 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1328,7 +1328,7 @@ def map_diag(self, func, **kwargs): self.diag_axes = np.array(diag_axes, np.object) # Plot on each of the diagonal axes - color = kwargs.pop('color', None) + fixed_color = kwargs.pop("color", None) for i, var in enumerate(self.x_vars): ax = self.diag_axes[i] hue_grouped = self.data[var].groupby(self.hue_vals) @@ -1336,6 +1336,7 @@ def map_diag(self, func, **kwargs): # Special-case plt.hist with stacked bars if func is plt.hist: plt.sca(ax) + vals = [] for label in self.hue_names: # Attempt to get data for this level, allowing for empty @@ -1344,26 +1345,30 @@ def map_diag(self, func, **kwargs): except KeyError: vals.append(np.array([])) - if color is None: - color = self.palette - # check and see if histtype override was provided in kwargs - if 'histtype' in kwargs: + color = self.palette if fixed_color is None else fixed_color + + if "histtype" in kwargs: func(vals, color=color, **kwargs) else: - func(vals, color=color, histtype="barstacked", - **kwargs) + func(vals, color=color, histtype="barstacked", **kwargs) + else: + plt.sca(ax) + for k, label_k in enumerate(self.hue_names): + # Attempt to get data for this level, allowing for empty try: data_k = hue_grouped.get_group(label_k) except KeyError: data_k = np.array([]) - plt.sca(ax) - if color is None: + + if fixed_color is None: color = self.palette[k] - func(data_k, label=label_k, - color=color, **kwargs) + else: + color = fixed_color + + func(data_k, label=label_k, color=color, **kwargs) self._clean_axis(ax) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 1e39a646ae..0be1130924 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -954,6 +954,17 @@ def test_map_diag_color(self): for line in ax.lines: nt.assert_equals(line.get_color(), color) + @skipif(old_matplotlib) + def test_map_diag_palette(self): + + pal = color_palette(n_colors=len(self.df.a.unique())) + g = ag.PairGrid(self.df, hue="a") + g.map_diag(kdeplot) + + for ax in g.diag_axes: + for line, color in zip(ax.lines, pal): + nt.assert_equals(line.get_color(), color) + @skipif(old_matplotlib) def test_map_diag_and_offdiag(self): From 7535d8c5e9cf73f1e404b178929c19a2f4cb58fe Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 27 Aug 2017 13:38:50 -0400 Subject: [PATCH 0500/1738] Ensure that scatter gets hex colors in pointplot (closes #1263) --- seaborn/categorical.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 16bd6a11bb..ec8c0e0a46 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1763,17 +1763,19 @@ def draw_points(self, ax): zorder=z) # Draw the estimate points + n_points = len(offpos) marker = self.markers[j] + point_colors = [mpl.colors.rgb2hex(self.colors[j])] * n_points if self.orient == "h": ax.scatter(statistic, offpos, label=hue_level, - c=[self.colors[j]] * len(offpos), + c=point_colors, edgecolor=point_colors, linewidth=mew, marker=marker, s=markersize, - edgecolor=self.colors[j], zorder=z) + zorder=z) else: ax.scatter(offpos, statistic, label=hue_level, - c=[self.colors[j]] * len(offpos), + c=point_colors, edgecolor=point_colors, linewidth=mew, marker=marker, s=markersize, - edgecolor=self.colors[j], zorder=z) + zorder=z) def plot(self, ax): """Make the plot.""" From 2df495447c09a5a37c2d0a19cc72455df38ecda6 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 27 Aug 2017 14:25:24 -0400 Subject: [PATCH 0501/1738] Use dark_palette as default for categorical plots with small elements --- seaborn/categorical.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index ec8c0e0a46..d15dbf73b5 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -17,7 +17,7 @@ from . import utils from .utils import iqr, categorical_order from .algorithms import bootstrap -from .palettes import color_palette, husl_palette, light_palette +from .palettes import color_palette, husl_palette, light_palette, dark_palette from .axisgrid import FacetGrid, _facet_docs @@ -28,6 +28,7 @@ class _CategoricalPlotter(object): width = .8 + default_palette = "light" def establish_variables(self, x=None, y=None, hue=None, data=None, orient=None, order=None, hue_order=None, @@ -281,7 +282,12 @@ def establish_colors(self, color, palette, saturation): if self.hue_names is None: colors = [color] * n_colors else: - colors = light_palette(color, n_colors) + if self.default_palette == "light": + colors = light_palette(color, n_colors) + elif self.default_palette == "dark": + colors = dark_palette(color, n_colors) + else: + raise RuntimeError("No default palette specified") else: # Let `palette` be a dict mapping level to color @@ -1077,6 +1083,8 @@ def plot(self, ax): class _CategoricalScatterPlotter(_CategoricalPlotter): + default_palette = "dark" + @property def point_colors(self): """Return a color for each scatter point based on group and hue.""" @@ -1644,6 +1652,9 @@ def plot(self, ax, bar_kws): class _PointPlotter(_CategoricalStatPlotter): + + default_palette = "dark" + """Show point estimates and confidence intervals with (joined) points.""" def __init__(self, x, y, hue, data, order, hue_order, estimator, ci, n_boot, units, @@ -2100,8 +2111,7 @@ def plot(self, ax, boxplot_kws): """), color=dedent("""\ color : matplotlib color, optional - Color for all of the elements, or seed for :func:`light_palette` when - using hue nesting.\ + Color for all of the elements, or seed for when using hue nesting.\ """), palette=dedent("""\ palette : palette name, list, or dict, optional From 5dde73eade3f062ea99ba460b0ea0189d3281482 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 27 Aug 2017 14:26:05 -0400 Subject: [PATCH 0502/1738] Fix typo (closes #1261) --- doc/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/introduction.rst b/doc/introduction.rst index 2fec20470f..5ea8920c09 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -40,7 +40,7 @@ for it. When using seaborn, it is likely that you will often invoke matplotlib functions directly to draw simpler plots already available through the ``pyplot`` namespace. Further, the seaborn functions aim to make plots that are reasonably "production ready" (including extracting semantic information from -Pandas objects to add informative labels), but full customization will reqiure +Pandas objects to add informative labels), but full customization will require changing attributes on the matplotlib objects directly. The combination of seaborn's high-level interface and matplotlib's customizability and wide range of backends makes it easy to generate publication-quality figures. From 33e04f75e8ea82726889c8a417df6a12a1c0173c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 27 Aug 2017 14:34:02 -0400 Subject: [PATCH 0503/1738] Don't fail in distplot with single observation (closes #1256) --- seaborn/distributions.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 290bba5988..45a359ce63 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -26,6 +26,8 @@ def _freedman_diaconis_bins(a): """Calculate number of hist bins using Freedman-Diaconis rule.""" # From http://stats.stackexchange.com/questions/798/ a = np.asarray(a) + if len(a) < 2: + return 1 h = 2 * iqr(a) / (len(a) ** (1 / 3)) # fall back to sqrt(a) bins if iqr is 0 if h == 0: @@ -168,7 +170,9 @@ def distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, label_ax = True # Make a a 1-d array - a = np.asarray(a).squeeze() + a = np.asarray(a) + if a.ndim > 1: + a = a.squeeze() # Decide if the hist is normed norm_hist = norm_hist or kde or (fit is not None) From 12f92eed4685dd5566e743b87ca9e2953f239cb0 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 27 Aug 2017 14:35:28 -0400 Subject: [PATCH 0504/1738] Add note to 0.8.0 release notes about heatmap (closes #1251) --- doc/releases/v0.8.0.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index 732505545b..274d92a79e 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -31,6 +31,8 @@ v0.8.0 (July 2017) - Put a cap on the number of bins used in :func:`jointplot` for ``type=="hex"`` to avoid hanging when the reference rule prescribes too many. +- Changed the y axis in :func:`heatmap`. Instead of reversing the rows of the data internally, the y axis is now inverted. This may affect code that draws on top of the heatmap in data coordinates. + - Turn off dendrogram axes in :func:`clustermap` rather than setting the background color to white. - New matplotlib qualitative palettes (e.g. "tab10") are now handled correctly. From 9f770ada562f0254f2106ef8ff829693be4bb123 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 27 Aug 2017 14:42:17 -0400 Subject: [PATCH 0505/1738] Move remove_na function and add a test --- seaborn/categorical.py | 20 +------------------- seaborn/tests/test_utils.py | 11 +++++++++++ seaborn/utils.py | 18 +++++++++++++++++- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 2d57add4fb..d9b0d62f2b 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -14,7 +14,7 @@ from .external.six.moves import range from . import utils -from .utils import iqr, categorical_order +from .utils import iqr, categorical_order, remove_na from .algorithms import bootstrap from .palettes import color_palette, husl_palette, light_palette, dark_palette from .axisgrid import FacetGrid, _facet_docs @@ -24,24 +24,6 @@ "pointplot", "barplot", "countplot", "factorplot"] -def remove_na(arr): - """ - Helper method for removing NA values from array-like. - - Parameters - ---------- - arr : array-like - The array-like from which to remove NA values. - - Returns - ------- - clean_arr : array-like - The original array, just with NA values removed. - """ - - return arr[pd.notnull(arr)] - - class _CategoricalPlotter(object): width = .8 diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index 351b55d4f3..c44835ba74 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -374,3 +374,14 @@ def test_relative_luminance(): for lum1, lum2 in zip(lums1, lums2): nose.tools.assert_almost_equal(lum1, lum2) + + +def test_remove_na(): + + a_array = np.array([1, 2, np.nan, 3]) + a_array_rm = utils.remove_na(a_array) + npt.assert_array_equal(a_array_rm, np.array([1, 2, 3])) + + a_series = pd.Series([1, 2, np.nan, 3]) + a_series_rm = utils.remove_na(a_series) + pdt.assert_series_equal(a_series_rm, pd.Series([1., 2, 3], [0, 1, 3])) diff --git a/seaborn/utils.py b/seaborn/utils.py index 1e3089550d..5a379dde2f 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -1,7 +1,6 @@ """Small plotting-related utility functions.""" from __future__ import print_function, division import colorsys -import warnings import os import numpy as np @@ -22,6 +21,23 @@ "despine", "get_dataset_names", "load_dataset"] +def remove_na(arr): + """Helper method for removing NA values from array-like. + + Parameters + ---------- + arr : array-like + The array-like from which to remove NA values. + + Returns + ------- + clean_arr : array-like + The original array with NA values removed. + + """ + return arr[pd.notnull(arr)] + + def ci_to_errsize(cis, heights): """Convert intervals to error arguments relative to plot heights. From 528964122beb43cd0a1f6ae4773d8a03c9a73019 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 27 Aug 2017 14:43:45 -0400 Subject: [PATCH 0506/1738] Fix typo (closes 1255) --- seaborn/axisgrid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 0da28427a4..95aae7ce04 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -2033,8 +2033,8 @@ def pairplot(data, hue=None, hue_order=None, palette=None, if not isinstance(markers, list): markers = [markers] * n_markers if len(markers) != n_markers: - raise ValueError(("markers must be a singeton or a list of markers" - " for each level of the hue variable")) + raise ValueError(("markers must be a singleton or a list of " + "markers for each level of the hue variable")) grid.hue_kws = {"marker": markers} # Maybe plot on the diagonal From 50638976a762c3ad9ab3246a344d02d8fe642fc6 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 27 Aug 2017 14:48:54 -0400 Subject: [PATCH 0507/1738] Don't fail in kdeplot with empty data (closes #1235) --- seaborn/distributions.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 45a359ce63..a851de5f31 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -612,6 +612,9 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", if isinstance(data, list): data = np.asarray(data) + if len(data) == 0: + return ax + data = data.astype(np.float64) if data2 is not None: if isinstance(data2, list): From d488c3e939988eb85466c96e01ca38d365d4721b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 27 Aug 2017 15:43:27 -0400 Subject: [PATCH 0508/1738] Further improve pointplot color robustness --- seaborn/categorical.py | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index d9b0d62f2b..f305ca6484 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -1730,16 +1730,17 @@ def draw_points(self, ax): # Draw the confidence intervals self.draw_confints(ax, pointpos, self.confint, self.colors, self.errwidth, self.capsize) + # Draw the estimate points marker = self.markers[0] + hex_colors = [mpl.colors.rgb2hex(c) for c in self.colors] if self.orient == "h": - ax.scatter(self.statistic, pointpos, - linewidth=mew, marker=marker, s=markersize, - c=self.colors, edgecolor=self.colors) + x, y = self.statistic, pointpos else: - ax.scatter(pointpos, self.statistic, - linewidth=mew, marker=marker, s=markersize, - c=self.colors, edgecolor=self.colors) + x, y = pointpos, self.statistic + ax.scatter(x, y, + linewidth=mew, marker=marker, s=markersize, + c=hex_colors, edgecolor=hex_colors) else: @@ -1773,19 +1774,23 @@ def draw_points(self, ax): zorder=z) # Draw the estimate points - n_points = len(offpos) + n_points = len(remove_na(offpos)) marker = self.markers[j] - point_colors = [mpl.colors.rgb2hex(self.colors[j])] * n_points + hex_color = mpl.colors.rgb2hex(self.colors[j]) + if n_points: + point_colors = [hex_color for _ in range(n_points)] + else: + point_colors = hex_color if self.orient == "h": - ax.scatter(statistic, offpos, label=hue_level, - c=point_colors, edgecolor=point_colors, - linewidth=mew, marker=marker, s=markersize, - zorder=z) + x, y = statistic, offpos else: - ax.scatter(offpos, statistic, label=hue_level, - c=point_colors, edgecolor=point_colors, - linewidth=mew, marker=marker, s=markersize, - zorder=z) + x, y = offpos, statistic + if not len(remove_na(statistic)): + x, y = [], [] + ax.scatter(x, y, label=hue_level, + c=point_colors, edgecolor=point_colors, + linewidth=mew, marker=marker, s=markersize, + zorder=z) def plot(self, ax): """Make the plot.""" From ec4d8d7d7b7c9f9c5c0e7cc91646357b09d437a9 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 27 Aug 2017 15:46:04 -0400 Subject: [PATCH 0509/1738] Update release notes --- doc/releases/v0.8.1.txt | 10 ++++++++++ seaborn/tests/test_categorical.py | 5 +++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/doc/releases/v0.8.1.txt b/doc/releases/v0.8.1.txt index 892ec77c7e..618669afff 100644 --- a/doc/releases/v0.8.1.txt +++ b/doc/releases/v0.8.1.txt @@ -5,3 +5,13 @@ v0.8.1 (Unreleased) - Fixed a problem where the :class:`FacetGrid` or :class:`PairGrid` legend remained inside the figure when using ``legend_out=True`` on an interactive backend. - Fixed an bug in :func:`clustermap` when using ``yticklabels=False``. + +- Improved robustness of :func:`kdeplot` and :func:`distplot` to data with fewer than two observations. + +- In categorical plot functions with small plot elements, use :func:`dark_palette` instead of :func:`light_palette` to generate colors when both ``hue`` and ``color`` are specified. + +- Fixed an issue in :func:`pointplot` where colors were wrong if exactly three points were being drawn. + +- Fixed an issue in :func:`pointplot` where legend entries for missing data appeared with empty markers. + +- Improved compatibility with future versions of pandas. diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 5eb470e3db..7450bc9107 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -4,6 +4,7 @@ from scipy import stats, spatial import matplotlib as mpl import matplotlib.pyplot as plt +from matplotlib.colors import rgb2hex from distutils.version import LooseVersion @@ -2213,7 +2214,7 @@ def test_pointplot_colors(self): nt.assert_equal(line.get_color(), color[:-1]) for got_color in ax.collections[0].get_facecolors(): - npt.assert_array_equal(got_color, color) + npt.assert_array_equal(rgb2hex(got_color), rgb2hex(color)) plt.close("all") @@ -2232,7 +2233,7 @@ def test_pointplot_colors(self): for point_color, pal_color in zip(ax.collections[0].get_facecolors(), palette): - npt.assert_array_equal(point_color[:-1], pal_color) + npt.assert_array_equal(rgb2hex(point_color), rgb2hex(pal_color)) plt.close("all") From 556e116ab0223699a774a2c5d9d8c27c50be635c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 27 Aug 2017 16:30:24 -0400 Subject: [PATCH 0510/1738] Add warning in FacetGrid when drawing categorical plot without {hue}_order --- doc/releases/v0.8.1.txt | 2 ++ seaborn/axisgrid.py | 13 +++++++++++++ seaborn/categorical.py | 5 ++++- seaborn/tests/test_axisgrid.py | 10 +++++++++- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/doc/releases/v0.8.1.txt b/doc/releases/v0.8.1.txt index 618669afff..50bd54e960 100644 --- a/doc/releases/v0.8.1.txt +++ b/doc/releases/v0.8.1.txt @@ -2,6 +2,8 @@ v0.8.1 (Unreleased) ------------------- +- Added a warning in :class:`FacetGrid` when passing a categorical plot function without specifying ``order`` (or ``hue_order`` when ``hue`` is used), which is likely to produce a plot that is incorrect. + - Fixed a problem where the :class:`FacetGrid` or :class:`PairGrid` legend remained inside the figure when using ``legend_out=True`` on an interactive backend. - Fixed an bug in :func:`clustermap` when using ``yticklabels=False``. diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 3354912d5b..0427c040b1 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -695,6 +695,19 @@ def map(self, func, *args, **kwargs): # If color was a keyword argument, grab it here kw_color = kwargs.pop("color", None) + # Check for categorical plots without order information + if func.__module__ == "seaborn.categorical": + if "order" not in kwargs: + warning = ("Using the {} function without specifying " + "`order` is likely to produce an incorrect " + "plot.".format(func.__name__)) + warnings.warn(warning) + if len(args) == 3 and "hue_order" not in kwargs: + warning = ("Using the {} function without specifying " + "`hue_order` is likely to produce an incorrect " + "plot.".format(func.__name__)) + warnings.warn(warning) + # Iterate over the data subsets for (row_i, col_j, hue_k), data_ijk in self.facet_data(): diff --git a/seaborn/categorical.py b/seaborn/categorical.py index f305ca6484..62adb9ff08 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -322,7 +322,10 @@ def infer_orient(self, x, y, orient=None): def is_categorical(s): try: # Correct way, but doesnt exist in older Pandas - return pd.core.common.is_categorical_dtype(s) + try: + return pd.api.types.is_categorical_dtype(s) + except AttributeError: + return pd.core.common.is_categorical_dtype(s) except AttributeError: # Also works, but feels hackier return str(s.dtype) == "categorical" diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 0be1130924..793b4e7f36 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -475,7 +475,7 @@ def test_set_ticklabels(self): x, y = np.arange(10), np.arange(10) df = pd.DataFrame(np.c_[x, y], columns=["x", "y"]) - g = ag.FacetGrid(df).map(pointplot, "x", "y") + g = ag.FacetGrid(df).map(pointplot, "x", "y", order=x) g.set_xticklabels(step=2) got_x = [int(l.get_text()) for l in g.axes[0, 0].get_xticklabels()] npt.assert_array_equal(x[::2], got_x) @@ -727,6 +727,14 @@ def test_categorical_column_missing_categories(self): nt.assert_equal(g.axes.shape, (len(df['a'].cat.categories),)) + def test_categorical_warning(self): + + g = ag.FacetGrid(self.df, col="b") + with warnings.catch_warnings(): + warnings.resetwarnings() + warnings.simplefilter("always") + npt.assert_warns(UserWarning, g.map, pointplot, "b", "x") + class TestPairGrid(PlotTestCase): From 99c279394955a2137f9ab5ecf1dd157d18eace82 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 27 Aug 2017 17:06:29 -0400 Subject: [PATCH 0511/1738] Remove doctest examples of FacetGrid with categorical plots Even though these cases were fine because the data were balanced or order information was communicated with categorical variables, this is not obvious just from reading the code. --- seaborn/axisgrid.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 0427c040b1..087d219e6e 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -500,7 +500,7 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, :context: close-figs >>> g = sns.FacetGrid(tips, col="day", size=4, aspect=.5) - >>> g = g.map(sns.boxplot, "time", "total_bill") + >>> g = g.map(plt.hist, "total_bill", bins=bins) Specify the order for plot elements: @@ -549,9 +549,8 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, :context: close-figs >>> attend = sns.load_dataset("attention") - >>> g = sns.FacetGrid(attend, col="subject", col_wrap=5, - ... size=1.5, ylim=(0, 10)) - >>> g = g.map(sns.pointplot, "solutions", "score", scale=.7) + >>> g = sns.FacetGrid(attend, col="subject", col_wrap=5, size=1.5) + >>> g = g.map(plt.plot, "solutions", "score", marker=".") Define a custom bivariate function to map onto the grid: From 65d99ed05e662513a5d0f3964fc370077fc24f5b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 29 Aug 2017 20:25:03 -0400 Subject: [PATCH 0512/1738] Darken body text in docs (closes #1259) --- doc/_static/style.css | 1 + doc/tutorial/axis_grids.ipynb | 2 +- doc/tutorial/color_palettes.ipynb | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/_static/style.css b/doc/_static/style.css index 6777c040fc..4b95f64bd7 100644 --- a/doc/_static/style.css +++ b/doc/_static/style.css @@ -1,3 +1,4 @@ +body { color: #444444 !important; } h1 { font-size: 40px !important; } h2 { font-size: 32px !important; } h3 { font-size: 24px !important; } diff --git a/doc/tutorial/axis_grids.ipynb b/doc/tutorial/axis_grids.ipynb index 7c51db953f..779e0c1623 100644 --- a/doc/tutorial/axis_grids.ipynb +++ b/doc/tutorial/axis_grids.ipynb @@ -674,4 +674,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index a39d3c4fe4..a07efea16f 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -778,4 +778,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file From e8c5ab2045bbd30d9c5f93c503d63968edba5b43 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 29 Aug 2017 20:30:02 -0400 Subject: [PATCH 0513/1738] Remove annot from side color kwargs (closes #1150) --- seaborn/matrix.py | 1 + 1 file changed, 1 insertion(+) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 7459dc9d33..a0f6a5bc74 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -1048,6 +1048,7 @@ def plot_colors(self, xind, yind, **kws): kws = kws.copy() kws.pop('cmap', None) kws.pop('center', None) + kws.pop('annot', None) kws.pop('vmin', None) kws.pop('vmax', None) kws.pop('robust', None) From f285ec00e7ad541d99ac0c5fcc31ce4b606e5ad8 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 29 Aug 2017 20:40:33 -0400 Subject: [PATCH 0514/1738] Fix clustermap yticklabel rotation --- seaborn/matrix.py | 1 + 1 file changed, 1 insertion(+) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index a0f6a5bc74..9b05d71fe6 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -1121,6 +1121,7 @@ def plot_matrix(self, colorbar_kws, xind, yind, **kws): self.ax_heatmap.yaxis.set_ticks_position('right') self.ax_heatmap.yaxis.set_label_position('right') if ytl_rot is not None: + ytl = self.ax_heatmap.get_yticklabels() plt.setp(ytl, rotation=ytl_rot) def plot(self, metric, method, colorbar_kws, row_cluster, col_cluster, From 086a6e7c17e36adea1fd69d70b2c59e7f46b5745 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Tue, 29 Aug 2017 21:11:34 -0400 Subject: [PATCH 0515/1738] Update release notes --- doc/releases/v0.8.1.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/releases/v0.8.1.txt b/doc/releases/v0.8.1.txt index 50bd54e960..9f9400e1c6 100644 --- a/doc/releases/v0.8.1.txt +++ b/doc/releases/v0.8.1.txt @@ -16,4 +16,8 @@ v0.8.1 (Unreleased) - Fixed an issue in :func:`pointplot` where legend entries for missing data appeared with empty markers. +- Fixed a bug in :func:`clustermap` where an error was raised when annotating the main heatmap and showing category colors. + +- Fixed an issue in :func:`clustermap` where row labels were not being properly rotated when they overlapped. + - Improved compatibility with future versions of pandas. From 2c8cfb3baee393b7817e6936f39f2d19cae1d03f Mon Sep 17 00:00:00 2001 From: Alvaro Ulloa Date: Wed, 30 Aug 2017 17:17:33 -0400 Subject: [PATCH 0516/1738] typo observerations should be observations --- seaborn/categorical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 62adb9ff08..7dde331bff 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2100,7 +2100,7 @@ def plot(self, ax, boxplot_kws): ci : float or "sd" or None, optional Size of confidence intervals to draw around estimated values. If "sd", skip bootstrapping and draw the standard deviation of the - observerations. If ``None``, no bootstrapping will be performed, and + observations. If ``None``, no bootstrapping will be performed, and error bars will not be drawn. n_boot : int, optional Number of bootstrap iterations to use when computing confidence From c3f2bf48b8f1657c3a7c4be30f97a386ae16ed9e Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 2 Sep 2017 18:27:52 -0400 Subject: [PATCH 0517/1738] Small doc improvements --- doc/_static/style.css | 5 ++++- examples/kde_joyplot.py | 6 +++--- seaborn/axisgrid.py | 2 +- seaborn/categorical.py | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/doc/_static/style.css b/doc/_static/style.css index 4b95f64bd7..f64910e5c6 100644 --- a/doc/_static/style.css +++ b/doc/_static/style.css @@ -1,4 +1,5 @@ body { color: #444444 !important; } + h1 { font-size: 40px !important; } h2 { font-size: 32px !important; } h3 { font-size: 24px !important; } @@ -19,7 +20,9 @@ blockquote p { } blockquote { - margin: 0 0 4px !important; + padding-top: 4px !important; + padding-bottom: 4px !important; + margin: 0 0 0px !important; } code { diff --git a/examples/kde_joyplot.py b/examples/kde_joyplot.py index f408505da0..630cafeeda 100644 --- a/examples/kde_joyplot.py +++ b/examples/kde_joyplot.py @@ -1,6 +1,6 @@ """ -Overlapping KDEs ('Joy Division plot') -====================================== +Overlapping densities ('joy plot') +================================== """ @@ -20,7 +20,7 @@ # Initialize the FacetGrid object pal = sns.cubehelix_palette(10, rot=-.25, light=.7) -g = sns.FacetGrid(df, row="g", hue="g", aspect=12, size=.5, palette=pal) +g = sns.FacetGrid(df, row="g", hue="g", aspect=15, size=.5, palette=pal) # Draw the densities in a few steps g.map(sns.kdeplot, "x", clip_on=False, shade=True, alpha=1, lw=1.5, bw=.2) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 087d219e6e..0097456629 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -197,7 +197,7 @@ def _get_palette(self, data, hue, hue_order, palette): of each facet in inches.\ """), palette=dedent("""\ - palette : seaborn color palette or dict, optional + palette : palette name, list, or dict, optional Colors to use for the different levels of the ``hue`` variable. Should be something that can be interpreted by :func:`color_palette`, or a dictionary mapping hue levels to matplotlib colors.\ diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 62adb9ff08..ee080c622b 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2118,7 +2118,7 @@ def plot(self, ax, boxplot_kws): """), color=dedent("""\ color : matplotlib color, optional - Color for all of the elements, or seed for when using hue nesting.\ + Color for all of the elements, or seed for a gradient palette. """), palette=dedent("""\ palette : palette name, list, or dict, optional @@ -2999,10 +2999,10 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, {saturation} errcolor : matplotlib color Color for the lines that represent the confidence interval. - {ax_in} {errwidth} {capsize} {dodge} + {ax_in} kwargs : key, value mappings Other keyword arguments are passed through to ``plt.bar`` at draw time. From 6eb0b6eb43e2c45c063a90613b68b519c33655f9 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 1 Sep 2017 23:34:22 -0400 Subject: [PATCH 0518/1738] Fix kdeplot density axis limits on matplotlib 2.0 --- doc/releases/v0.8.1.txt | 2 ++ seaborn/distributions.py | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/releases/v0.8.1.txt b/doc/releases/v0.8.1.txt index 9f9400e1c6..2935acb6bc 100644 --- a/doc/releases/v0.8.1.txt +++ b/doc/releases/v0.8.1.txt @@ -20,4 +20,6 @@ v0.8.1 (Unreleased) - Fixed an issue in :func:`clustermap` where row labels were not being properly rotated when they overlapped. +- Fixed an issue in :func:`kdeplot` where the maximum limit on the density axes was not being updated when multiple densities were drawn. + - Improved compatibility with future versions of pandas. diff --git a/seaborn/distributions.py b/seaborn/distributions.py index a851de5f31..ac94ca0ce1 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -324,10 +324,11 @@ def _univariate_kdeplot(data, shade, vertical, kernel, bw, gridsize, cut, ax.fill_between(x, 0, y, **shade_kws) # Set the density axis minimum to 0 + xmargin, ymargin = ax.margins() if vertical: - ax.set_xlim(0, None) + ax.set_xlim(0, max(ax.get_xlim()[1], (1 + xmargin) * x.max())) else: - ax.set_ylim(0, None) + ax.set_ylim(0, max(ax.get_ylim()[1], (1 + ymargin) * y.max())) # Draw the legend here if legend: From 7bff3a5c810756077507c96859722aeabc7f0d75 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 3 Sep 2017 11:59:54 -0400 Subject: [PATCH 0519/1738] Update release notes --- doc/releases/v0.8.1.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/releases/v0.8.1.txt b/doc/releases/v0.8.1.txt index 2935acb6bc..5c8a1b75ce 100644 --- a/doc/releases/v0.8.1.txt +++ b/doc/releases/v0.8.1.txt @@ -1,25 +1,25 @@ -v0.8.1 (Unreleased) -------------------- +v0.8.1 (September 2017) +----------------------- - Added a warning in :class:`FacetGrid` when passing a categorical plot function without specifying ``order`` (or ``hue_order`` when ``hue`` is used), which is likely to produce a plot that is incorrect. -- Fixed a problem where the :class:`FacetGrid` or :class:`PairGrid` legend remained inside the figure when using ``legend_out=True`` on an interactive backend. +- Improved compatibility between :class:`FacetGrid` or :class:`PairGrid` and interactive matplotlib backends so that the legend no longer remains inside the figure when using ``legend_out=True``. -- Fixed an bug in :func:`clustermap` when using ``yticklabels=False``. +- Changed categorical plot functions with small plot elements to use :func:`dark_palette` instead of :func:`light_palette` when generating a sequential palette from a specified color. - Improved robustness of :func:`kdeplot` and :func:`distplot` to data with fewer than two observations. -- In categorical plot functions with small plot elements, use :func:`dark_palette` instead of :func:`light_palette` to generate colors when both ``hue`` and ``color`` are specified. +- Fixed a bug in :func:`clustermap` when using ``yticklabels=False``. -- Fixed an issue in :func:`pointplot` where colors were wrong if exactly three points were being drawn. +- Fixed a bug in :func:`pointplot` where colors were wrong if exactly three points were being drawn. -- Fixed an issue in :func:`pointplot` where legend entries for missing data appeared with empty markers. +- Fixed a bug in :func:`pointplot` where legend entries for missing data appeared with empty markers. - Fixed a bug in :func:`clustermap` where an error was raised when annotating the main heatmap and showing category colors. -- Fixed an issue in :func:`clustermap` where row labels were not being properly rotated when they overlapped. +- Fixed a bug in :func:`clustermap` where row labels were not being properly rotated when they overlapped. -- Fixed an issue in :func:`kdeplot` where the maximum limit on the density axes was not being updated when multiple densities were drawn. +- Fixed a bug in :func:`kdeplot` where the maximum limit on the density axes was not being updated when multiple densities were drawn. - Improved compatibility with future versions of pandas. From 9ee1eee6078b4fb502d142e132e4a4b3ba0a5e4a Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 3 Sep 2017 12:17:55 -0400 Subject: [PATCH 0520/1738] Update install info --- doc/installing.rst | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/doc/installing.rst b/doc/installing.rst index 74819f3259..2e04709996 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -3,17 +3,23 @@ Installing and getting started ------------------------------ -To install the released version of seaborn, you can use ``pip`` (i.e. ``pip -install seaborn``). It's also possible to install the released version using -``conda`` (i.e. ``conda install seaborn``), although this may lag behind the -version available from PyPI. +To install the latest release of seaborn, you can use ``pip``:: + + pip install seaborn + +It's also possible to install the released version using ``conda``:: + + conda install seaborn + +Alternatively, you can use ``pip`` to install the development version directly from github:: + + pip install git+https://github.com/mwaskom/seaborn.git -Alternatively, you can use ``pip`` to install the development version, with the -command ``pip install git+https://github.com/mwaskom/seaborn.git``. Another option would be to to clone the `github repository -`_ and install with ``pip install .`` from -the source directory. Seaborn itself is pure Python, so installation should be -reasonably straightforward. +`_ install from your local copy:: + + pip install . + Dependencies ~~~~~~~~~~~~ @@ -39,27 +45,27 @@ Recommended dependencies The ``pip`` installation script will attempt to download the mandatory dependencies only if they do not exist at install-time. -Unit tests aim to keep seaborn importable and generally functional on the -versions available through the stable Debian channels. There may be cases -where some more advanced features only work with newer versions of these -libraries, although these should be relatively rare. - -There are also some known bugs on older versions of matplotlib, so you should -in general try to use a modern version. For many use cases, though, older -matplotlibs will work fine. +There are not hard minimum version requirements for the dependencies. Unit +tests aim to keep seaborn importable and generally functional on the versions +available through the stable Debian channels, which are relatively old. There +are some known bugs when using older versions of matplotlib (generally meaning +1.3 or earlier), so you should in general try to use a modern version. For +most use cases, though, older versions of matplotlib will work fine. Seaborn is also tested on the most recent versions offered through ``conda``. + Testing ~~~~~~~ To test seaborn, run ``make test`` in the root directory of the source distribution. This runs the unit test suite (which can also be exercised -separately by running ``nosetests``). It also runs the code in the example -notebooks to smoke-test a broader and more realistic range of example usage. +separately by running ``nosetests``). It also runs the example code in function +docstrings to smoke-test a broader and more realistic range of example usage. The full set of tests requires an internet connection to download the example -datasets, but the unit tests should be able to run offline. +datasets (if they haven't been previously cached), but the unit tests should +be able to run offline. Bugs From 9aba35ced1a9ad9c9c66006d623d9f93d76f7796 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 3 Sep 2017 12:21:28 -0400 Subject: [PATCH 0521/1738] Update versions for 0.8.1 release --- seaborn/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/__init__.py b/seaborn/__init__.py index cf349b7e93..eeb98d7d14 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -18,4 +18,4 @@ from .crayons import crayons from . import cm -__version__ = "0.9.dev" +__version__ = "0.8.1" diff --git a/setup.py b/setup.py index b980d42f30..45669dab01 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ URL = 'http://seaborn.pydata.org' LICENSE = 'BSD (3-clause)' DOWNLOAD_URL = 'https://github.com/mwaskom/seaborn/' -VERSION = '0.9.dev' +VERSION = '0.8.1' try: from setuptools import setup From e9c520296df89b8228ef2c6d4d03f84b59062099 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 3 Sep 2017 12:27:35 -0400 Subject: [PATCH 0522/1738] Add v0.8.1 DOI --- README.md | 2 +- doc/releases/v0.8.1.txt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e6c7272701..1fe0fefc71 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ The documentation has an [example gallery](http://seaborn.pydata.org/examples/in Citing ------ -Seaborn can be cited using a DOI provided through Zenodo: [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.54844.svg)](https://doi.org/10.5281/zenodo.54844) +Seaborn can be cited using a DOI provided through Zenodo: [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.883859.svg)](https://doi.org/10.5281/zenodo.883859) Dependencies ------------ diff --git a/doc/releases/v0.8.1.txt b/doc/releases/v0.8.1.txt index 5c8a1b75ce..ade4c857bf 100644 --- a/doc/releases/v0.8.1.txt +++ b/doc/releases/v0.8.1.txt @@ -2,6 +2,9 @@ v0.8.1 (September 2017) ----------------------- +.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.883859.svg + :target: https://doi.org/10.5281/zenodo.883859 + - Added a warning in :class:`FacetGrid` when passing a categorical plot function without specifying ``order`` (or ``hue_order`` when ``hue`` is used), which is likely to produce a plot that is incorrect. - Improved compatibility between :class:`FacetGrid` or :class:`PairGrid` and interactive matplotlib backends so that the legend no longer remains inside the figure when using ``legend_out=True``. From 71b96caf7dc884f731f76302cc98a13c1221d722 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 3 Sep 2017 12:41:42 -0400 Subject: [PATCH 0523/1738] Set version back to development --- seaborn/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/__init__.py b/seaborn/__init__.py index eeb98d7d14..cf349b7e93 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -18,4 +18,4 @@ from .crayons import crayons from . import cm -__version__ = "0.8.1" +__version__ = "0.9.dev" diff --git a/setup.py b/setup.py index 45669dab01..b980d42f30 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ URL = 'http://seaborn.pydata.org' LICENSE = 'BSD (3-clause)' DOWNLOAD_URL = 'https://github.com/mwaskom/seaborn/' -VERSION = '0.8.1' +VERSION = '0.9.dev' try: from setuptools import setup From 485c915168e6a7a6cf85f2e54092bd6d5e2027d8 Mon Sep 17 00:00:00 2001 From: Mitch Negus Date: Mon, 4 Sep 2017 13:49:36 -0700 Subject: [PATCH 0524/1738] fixed typo --- examples/kde_joyplot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/kde_joyplot.py b/examples/kde_joyplot.py index 630cafeeda..0b2c30ccc9 100644 --- a/examples/kde_joyplot.py +++ b/examples/kde_joyplot.py @@ -38,7 +38,7 @@ def label(x, color, label): # Set the subplots to overlap g.fig.subplots_adjust(hspace=-.25) -# Remove axes details that don't play will with overlap +# Remove axes details that don't play well with overlap g.set_titles("") g.set(yticks=[]) g.despine(bottom=True, left=True) From 6946af8ccabd56a37bc275f024debb28055f8ecc Mon Sep 17 00:00:00 2001 From: Paul Hobson Date: Thu, 14 Sep 2017 13:29:59 -0700 Subject: [PATCH 0525/1738] update comments in regression example comments were about the tips dataset, but the examples users iris. --- examples/multiple_regression.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/multiple_regression.py b/examples/multiple_regression.py index 5af8667240..b4541651b6 100644 --- a/examples/multiple_regression.py +++ b/examples/multiple_regression.py @@ -7,10 +7,10 @@ import seaborn as sns sns.set() -# Load the example tips dataset +# Load the iris dataset iris = sns.load_dataset("iris") -# Plot tip as a function of toal bill across days +# Plot sepal with as a function of sepal_length across days g = sns.lmplot(x="sepal_length", y="sepal_width", hue="species", truncate=True, size=5, data=iris) From 4faff205565be1df99b4088b8d7c8fa0bde876ea Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 29 Sep 2017 19:43:42 -0400 Subject: [PATCH 0526/1738] Changes to get pytest execution passing --- seaborn/conftest.py | 14 +++++++++ seaborn/tests/test_categorical.py | 50 +++++++++++++++++-------------- 2 files changed, 42 insertions(+), 22 deletions(-) create mode 100644 seaborn/conftest.py diff --git a/seaborn/conftest.py b/seaborn/conftest.py new file mode 100644 index 0000000000..8a0425d678 --- /dev/null +++ b/seaborn/conftest.py @@ -0,0 +1,14 @@ +import numpy as np +import matplotlib.pyplot as plt +import pytest + + +@pytest.fixture(autouse=True) +def close_figs(): + yield + plt.close("all") + + +@pytest.fixture(autouse=True) +def random_seed(): + np.random.seed(47) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 7450bc9107..9b4aed127e 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -2458,30 +2458,36 @@ def test_plot_colors(self): plt.close("all") +def edge_calc(n, data): + + q = np.asanyarray([0.5 ** n, 1 - 0.5 ** n]) * 100 + q = list(np.unique(q)) + return np.percentile(data, q) + + class TestLVPlotter(CategoricalFixture): - def setUp(self): - def edge_calc(n, data): - q = np.asanyarray([0.5**n, 1 - 0.5**n]) * 100 - q = list(np.unique(q)) - return np.percentile(data, q) - - self.ispatch = lambda c: isinstance(c, mpl.collections.PatchCollection) - - self.default_kws = dict(x=None, y=None, hue=None, data=None, - order=None, hue_order=None, - orient=None, color=None, palette=None, - saturation=.75, width=.8, dodge=True, - k_depth='proportion', linewidth=None, - scale='exponential', outlier_prop=None) - self.linear_data = np.arange(101) - self.n = len(self.linear_data) - self.expected_k = int(np.log2(self.n)) - int(np.log2(self.n*0.007)) + 1 - self.expected_edges_l = map(lambda i: edge_calc(i, self.linear_data), - range(self.expected_k + 2, 1, -1)) - self.outlier_data = np.concatenate((np.arange(100), [200])) - self.expected_edges_o = map(lambda i: edge_calc(i, self.outlier_data), - range(self.expected_k + 2, 1, -1)) + default_kws = dict(x=None, y=None, hue=None, data=None, + order=None, hue_order=None, + orient=None, color=None, palette=None, + saturation=.75, width=.8, dodge=True, + k_depth='proportion', linewidth=None, + scale='exponential', outlier_prop=None) + + linear_data = np.arange(101) + n = len(linear_data) + expected_k = int(np.log2(n)) - int(np.log2(n*0.007)) + 1 + + expected_edges_l = [edge_calc(i, linear_data) + for i in range(expected_k + 2, 1, -1)] + + outlier_data = np.concatenate((np.arange(100), [200])) + expected_edges_o = [edge_calc(i, outlier_data) + for i in range(expected_k + 2, 1, -1)] + + def ispatch(self, c): + + return isinstance(c, mpl.collections.PatchCollection) def test_box_ends_finite(self): p = cat._LVPlotter(**self.default_kws) From 00535f21f3af9cf7b7310e961623cd48067a5c10 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 29 Sep 2017 19:55:01 -0400 Subject: [PATCH 0527/1738] Update Makefile test and lint commands --- Makefile | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 1a6b0ba368..36d426e67b 100644 --- a/Makefile +++ b/Makefile @@ -2,17 +2,16 @@ export SHELL := /bin/bash test: - nosetests --with-doctest + pytest --doctest-modules seaborn test-nodoctest: - nosetests + pytest seaborn coverage: - nosetests --cover-erase --with-coverage --cover-html --cover-package seaborn + nosetests --cov=seaborn seaborn lint: - pyflakes -x W -X seaborn/external/six.py seaborn - pep8 --exclude external,cm.py seaborn + flake8 --exclude seaborn/__init__.py,seaborn/cm.py,seaborn/tests,seaborn/external seaborn From 908e2be6306fe8f32f8ed7f36874971cff36941d Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 29 Sep 2017 19:57:45 -0400 Subject: [PATCH 0528/1738] Satisfy flake8 --- seaborn/algorithms.py | 96 ---------------------------------------- seaborn/apionly.py | 6 +-- seaborn/axisgrid.py | 2 +- seaborn/categorical.py | 44 +++++++++--------- seaborn/distributions.py | 5 ++- seaborn/linearmodels.py | 3 +- seaborn/matrix.py | 3 +- seaborn/rcmod.py | 3 -- seaborn/regression.py | 21 ++++++--- seaborn/utils.py | 5 ++- 10 files changed, 53 insertions(+), 135 deletions(-) diff --git a/seaborn/algorithms.py b/seaborn/algorithms.py index b00b67b214..9f850a607e 100644 --- a/seaborn/algorithms.py +++ b/seaborn/algorithms.py @@ -106,99 +106,3 @@ def _smooth_bootstrap(args, n_boot, func, func_kwargs): sample = [a.resample(n).T for a in kde] boot_dist.append(func(*sample, **func_kwargs)) return np.array(boot_dist) - - -def randomize_corrmat(a, tail="both", corrected=True, n_iter=1000, - random_seed=None, return_dist=False): - """Test the significance of set of correlations with permutations. - - By default this corrects for multiple comparisons across one side - of the matrix. - - Parameters - ---------- - a : n_vars x n_obs array - array with variables as rows - tail : both | upper | lower - whether test should be two-tailed, or which tail to integrate over - corrected : boolean - if True reports p values with respect to the max stat distribution - n_iter : int - number of permutation iterations - random_seed : int or None - seed for RNG - return_dist : bool - if True, return n_vars x n_vars x n_iter - - Returns - ------- - p_mat : float - array of probabilites for actual correlation from null CDF - - """ - if tail not in ["upper", "lower", "both"]: - raise ValueError("'tail' must be 'upper', 'lower', or 'both'") - - rs = np.random.RandomState(random_seed) - - a = np.asarray(a, np.float) - flat_a = a.ravel() - n_vars, n_obs = a.shape - - # Do the permutations to establish a null distribution - null_dist = np.empty((n_vars, n_vars, n_iter)) - for i_i in range(n_iter): - perm_i = np.concatenate([rs.permutation(n_obs) + (v * n_obs) - for v in range(n_vars)]) - a_i = flat_a[perm_i].reshape(n_vars, n_obs) - null_dist[..., i_i] = np.corrcoef(a_i) - - # Get the observed correlation values - real_corr = np.corrcoef(a) - - # Figure out p values based on the permutation distribution - p_mat = np.zeros((n_vars, n_vars)) - upper_tri = np.triu_indices(n_vars, 1) - - if corrected: - if tail == "both": - max_dist = np.abs(null_dist[upper_tri]).max(axis=0) - elif tail == "lower": - max_dist = null_dist[upper_tri].min(axis=0) - elif tail == "upper": - max_dist = null_dist[upper_tri].max(axis=0) - - cdf = lambda x: stats.percentileofscore(max_dist, x) / 100. - - for i, j in zip(*upper_tri): - observed = real_corr[i, j] - if tail == "both": - p_ij = 1 - cdf(abs(observed)) - elif tail == "lower": - p_ij = cdf(observed) - elif tail == "upper": - p_ij = 1 - cdf(observed) - p_mat[i, j] = p_ij - - else: - for i, j in zip(*upper_tri): - - null_corrs = null_dist[i, j] - cdf = lambda x: stats.percentileofscore(null_corrs, x) / 100. - - observed = real_corr[i, j] - if tail == "both": - p_ij = 2 * (1 - cdf(abs(observed))) - elif tail == "lower": - p_ij = cdf(observed) - elif tail == "upper": - p_ij = 1 - cdf(observed) - p_mat[i, j] = p_ij - - # Make p matrix symettrical with nans on the diagonal - p_mat += p_mat.T - p_mat[np.diag_indices(n_vars)] = np.nan - - if return_dist: - return p_mat, null_dist - return p_mat diff --git a/seaborn/apionly.py b/seaborn/apionly.py index 686e4776ba..1a27045b1c 100644 --- a/seaborn/apionly.py +++ b/seaborn/apionly.py @@ -1,9 +1,9 @@ import warnings +from seaborn import * # noqa +reset_orig() # noqa + msg = ( "As seaborn no longer sets a default style on import, the seaborn.apionly " "module is deprecated. It will be removed in a future version." ) warnings.warn(msg, UserWarning) - -from seaborn import * -reset_orig() diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 0097456629..1d84ee271f 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -302,7 +302,7 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, gridspec_kw=gridspec_kws) if OLD_MPL: - _ = kwargs.pop('gridspec_kw', None) + kwargs.pop('gridspec_kw', None) if gridspec_kws: msg = "gridspec module only available in mpl >= {}" warnings.warn(msg.format(MPL_GRIDSPEC_VERSION)) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 950b1c2b1a..040b6b9ff5 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2000,16 +2000,16 @@ def draw_letter_value_plot(self, ax, kws): color = self.colors[i] - artist_dict = self._lvplot(box_data, - positions=[i], - color=color, - vert=vert, - widths=self.width, - k_depth=self.k_depth, - ax=ax, - scale=self.scale, - outlier_prop=self.outlier_prop, - **kws) + self._lvplot(box_data, + positions=[i], + color=color, + vert=vert, + widths=self.width, + k_depth=self.k_depth, + ax=ax, + scale=self.scale, + outlier_prop=self.outlier_prop, + **kws) else: # Draw nested groups of boxes @@ -2033,16 +2033,16 @@ def draw_letter_value_plot(self, ax, kws): color = self.colors[j] center = i + offsets[j] - artist_dict = self._lvplot(box_data, - positions=[center], - color=color, - vert=vert, - widths=self.nested_width, - k_depth=self.k_depth, - ax=ax, - scale=self.scale, - outlier_prop=self.outlier_prop, - **kws) + self._lvplot(box_data, + positions=[center], + color=color, + vert=vert, + widths=self.nested_width, + k_depth=self.k_depth, + ax=ax, + scale=self.scale, + outlier_prop=self.outlier_prop, + **kws) def plot(self, ax, boxplot_kws): """Make the plot.""" @@ -2051,6 +2051,7 @@ def plot(self, ax, boxplot_kws): if self.orient == "h": ax.invert_yaxis() + _categorical_docs = dict( # Shared narrative docs @@ -2217,6 +2218,7 @@ def boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, plotter.plot(ax, kwargs) return ax + boxplot.__doc__ = dedent("""\ Draw a box plot to show distributions with respect to categories. @@ -2369,6 +2371,7 @@ def violinplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, plotter.plot(ax) return ax + violinplot.__doc__ = dedent("""\ Draw a combination of boxplot and kernel density estimate. @@ -3701,6 +3704,7 @@ def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, plotter.plot(ax, kwargs) return ax + lvplot.__doc__ = dedent("""\ Draw a letter value plot to show distributions of large datasets. diff --git a/seaborn/distributions.py b/seaborn/distributions.py index ac94ca0ce1..ce50ae9124 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -233,6 +233,10 @@ def distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, rug_kws["color"] = rug_color if fit is not None: + + def pdf(x): + return fit.pdf(x, *params) + fit_color = fit_kws.pop("color", "#282828") gridsize = fit_kws.pop("gridsize", 200) cut = fit_kws.pop("cut", 3) @@ -240,7 +244,6 @@ def distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, bw = stats.gaussian_kde(a).scotts_factor() * a.std(ddof=1) x = _kde_support(a, bw, gridsize, cut, clip) params = fit.fit(a) - pdf = lambda x: fit.pdf(x, *params) y = pdf(x) if vertical: x, y = y, x diff --git a/seaborn/linearmodels.py b/seaborn/linearmodels.py index 4b0493575e..ad5e039f57 100644 --- a/seaborn/linearmodels.py +++ b/seaborn/linearmodels.py @@ -1,6 +1,7 @@ import warnings +from .regression import * # noqa + msg = ( "The `linearmodels` module has been renamed `regression`." ) warnings.warn(msg) -from .regression import * diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 9b05d71fe6..f64170bd08 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -8,7 +8,6 @@ from matplotlib import gridspec import numpy as np import pandas as pd -from scipy.spatial import distance from scipy.cluster import hierarchy from . import cm @@ -607,7 +606,7 @@ def _calculate_linkage_fastcluster(self): import fastcluster # Fastcluster has a memory-saving vectorized version, but only # with certain linkage methods, and mostly with euclidean metric - vector_methods = ('single', 'centroid', 'median', 'ward') + # vector_methods = ('single', 'centroid', 'median', 'ward') euclidean_methods = ('centroid', 'median', 'ward') euclidean = self.metric == 'euclidean' and self.method in \ euclidean_methods diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index 3eca091eb0..80ccd3d299 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -1,10 +1,7 @@ """Functions that alter the matplotlib rc dictionary on the fly.""" from distutils.version import LooseVersion import functools - -import numpy as np import matplotlib as mpl - from . import palettes, _orig_rc_params diff --git a/seaborn/regression.py b/seaborn/regression.py index 55530c1679..365f489668 100644 --- a/seaborn/regression.py +++ b/seaborn/regression.py @@ -219,9 +219,11 @@ def fit_regression(self, ax=None, x_range=None, grid=None): def fit_fast(self, grid): """Low-level regression and prediction using linear algebra.""" + def reg_func(_x, _y): + return np.linalg.pinv(_x).dot(_y) + X, y = np.c_[np.ones(len(self.x)), self.x], self.y grid = np.c_[np.ones(len(grid)), grid] - reg_func = lambda _x, _y: np.linalg.pinv(_x).dot(_y) yhat = grid.dot(reg_func(X, y)) if self.ci is None: return yhat, None @@ -233,8 +235,10 @@ def fit_fast(self, grid): def fit_poly(self, grid, order): """Regression using numpy polyfit for higher-order trends.""" + def reg_func(_x, _y): + return np.polyval(np.polyfit(_x, _y, order), grid) + x, y = self.x, self.y - reg_func = lambda _x, _y: np.polyval(np.polyfit(_x, _y, order), grid) yhat = reg_func(x, y) if self.ci is None: return yhat, None @@ -245,9 +249,9 @@ def fit_poly(self, grid, order): def fit_statsmodels(self, grid, model, **kwargs): """More general regression function using statsmodels objects.""" + import statsmodels.genmod.generalized_linear_model as glm X, y = np.c_[np.ones(len(self.x)), self.x], self.y grid = np.c_[np.ones(len(grid)), grid] - import statsmodels.genmod.generalized_linear_model as glm def reg_func(_x, _y): try: @@ -790,6 +794,7 @@ def regplot(x, y, data=None, x_estimator=None, x_bins=None, x_ci="ci", plotter.plot(ax, scatter_kws, line_kws) return ax + regplot.__doc__ = dedent("""\ Plot data and a linear regression model fit. @@ -1085,9 +1090,13 @@ def coefplot(formula, data, groupby=None, intercept=False, ci=95, n_terms = len(coefs) # Plot seperately depending on groupby + def hsize(n): + return n * (h / 2) + + def wsize(n): + return n * (w / (4 * (n / 5))) + w, h = mpl.rcParams["figure.figsize"] - hsize = lambda n: n * (h / 2) - wsize = lambda n: n * (w / (4 * (n / 5))) if groupby is None: colors = itertools.cycle(color_palette(palette, n_terms)) f, ax = plt.subplots(1, 1, figsize=(wsize(n_terms), hsize(1))) @@ -1235,7 +1244,7 @@ def interactplot(x1, x2, y, data=None, filled=False, cmap="RdBu_r", c_min = min(np.percentile(y, 2), yhat.min()) c_max = max(np.percentile(y, 98), yhat.max()) delta = max(c_max - y_bar, y_bar - c_min) - c_min, cmax = y_bar - delta, y_bar + delta + c_min, c_max = y_bar - delta, y_bar + delta contour_kws.setdefault("vmin", c_min) contour_kws.setdefault("vmax", c_max) diff --git a/seaborn/utils.py b/seaborn/utils.py index 5a379dde2f..db329d6707 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -10,11 +10,12 @@ import matplotlib.colors as mplcol import matplotlib.pyplot as plt +from .external.six.moves.urllib.request import urlopen, urlretrieve +from .external.six.moves.http_client import HTTPException + from distutils.version import LooseVersion pandas_has_categoricals = LooseVersion(pd.__version__) >= "0.15" mpl_ge_150 = LooseVersion(mpl.__version__) >= "1.5.0" -from .external.six.moves.urllib.request import urlopen, urlretrieve -from .external.six.moves.http_client import HTTPException __all__ = ["desaturate", "saturate", "set_hls_values", From 62f55fac2b15ed1929a3e247501e46360e520e0b Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 29 Sep 2017 19:58:36 -0400 Subject: [PATCH 0529/1738] Update testing requirements Replace nose in testing deps --- testing/deps_latest.txt | 4 +++- testing/deps_minimal.txt | 4 +++- testing/deps_pinned.txt | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/testing/deps_latest.txt b/testing/deps_latest.txt index fc511cd76f..a8d65bbf35 100644 --- a/testing/deps_latest.txt +++ b/testing/deps_latest.txt @@ -1,6 +1,8 @@ -nose numpy scipy matplotlib pandas statsmodels +pytest +flake8 +nose diff --git a/testing/deps_minimal.txt b/testing/deps_minimal.txt index e9f007f498..99298f11e1 100644 --- a/testing/deps_minimal.txt +++ b/testing/deps_minimal.txt @@ -1,5 +1,7 @@ -nose numpy scipy matplotlib pandas +pytest +flake8 +nose diff --git a/testing/deps_pinned.txt b/testing/deps_pinned.txt index aa3a74c148..77b750b5f8 100644 --- a/testing/deps_pinned.txt +++ b/testing/deps_pinned.txt @@ -1,6 +1,8 @@ -nose numpy=1.6.2 scipy=0.11.0 matplotlib=1.1.1 pandas=0.12.0 statsmodels=0.5.0 +pytest +flake8 +nose From e339d55691db56044e75c093a5c6548bb40b66dd Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 29 Sep 2017 20:22:36 -0400 Subject: [PATCH 0530/1738] Remove randomize_corrmat tests --- seaborn/tests/test_algorithms.py | 74 -------------------------------- 1 file changed, 74 deletions(-) diff --git a/seaborn/tests/test_algorithms.py b/seaborn/tests/test_algorithms.py index e2bf01ac13..5296b0d508 100644 --- a/seaborn/tests/test_algorithms.py +++ b/seaborn/tests/test_algorithms.py @@ -128,77 +128,3 @@ def test_bootstrap_noncallable(): """Test that we get a TypeError with noncallable algo.unc.""" non_func = "mean" algo.bootstrap(a_norm, 100, non_func) - - -def test_randomize_corrmat(): - """Test the correctness of the correlation matrix p values.""" - a = rs.randn(30) - b = a + rs.rand(30) * 3 - c = rs.randn(30) - d = [a, b, c] - - p_mat, dist = algo.randomize_corrmat(d, tail="upper", corrected=False, - return_dist=True) - nose.tools.assert_greater(p_mat[2, 0], p_mat[1, 0]) - - corrmat = np.corrcoef(d) - pctile = 100 - stats.percentileofscore(dist[2, 1], corrmat[2, 1]) - nose.tools.assert_almost_equal(p_mat[2, 1] * 100, pctile) - - d[1] = -a + rs.rand(30) - p_mat = algo.randomize_corrmat(d) - nose.tools.assert_greater(0.05, p_mat[1, 0]) - - -def test_randomize_corrmat_dist(): - """Test that the distribution looks right.""" - a = rs.randn(3, 20) - for n_i in [5, 10]: - p_mat, dist = algo.randomize_corrmat(a, n_iter=n_i, return_dist=True) - assert_equal(n_i, dist.shape[-1]) - - p_mat, dist = algo.randomize_corrmat(a, n_iter=10000, return_dist=True) - - diag_mean = dist[0, 0].mean() - assert_equal(diag_mean, 1) - - off_diag_mean = dist[0, 1].mean() - nose.tools.assert_greater(0.05, off_diag_mean) - - -def test_randomize_corrmat_correction(): - """Test that FWE correction works.""" - a = rs.randn(3, 20) - p_mat = algo.randomize_corrmat(a, "upper", False) - p_mat_corr = algo.randomize_corrmat(a, "upper", True) - triu = np.triu_indices(3, 1) - npt.assert_array_less(p_mat[triu], p_mat_corr[triu]) - - -def test_randimoize_corrmat_tails(): - """Test that the tail argument works.""" - a = rs.randn(30) - b = a + rs.rand(30) * 8 - c = rs.randn(30) - d = [a, b, c] - - p_mat_b = algo.randomize_corrmat(d, "both", False, random_seed=0) - p_mat_u = algo.randomize_corrmat(d, "upper", False, random_seed=0) - p_mat_l = algo.randomize_corrmat(d, "lower", False, random_seed=0) - assert_equal(p_mat_b[0, 1], p_mat_u[0, 1] * 2) - assert_equal(p_mat_l[0, 1], 1 - p_mat_u[0, 1]) - - -def test_randomise_corrmat_seed(): - """Test that we can seed the corrmat randomization.""" - a = rs.randn(3, 20) - _, dist1 = algo.randomize_corrmat(a, random_seed=0, return_dist=True) - _, dist2 = algo.randomize_corrmat(a, random_seed=0, return_dist=True) - assert_array_equal(dist1, dist2) - - -@raises(ValueError) -def test_randomize_corrmat_tail_error(): - """Test that we are strict about tail paramete.""" - a = rs.randn(3, 30) - algo.randomize_corrmat(a, "hello") From 371edcf62f31b36842b56283dacbec4d03c4d568 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 29 Sep 2017 20:24:09 -0400 Subject: [PATCH 0531/1738] Fix lvplotter setup --- seaborn/tests/test_categorical.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 9b4aed127e..94dbb75106 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -2474,15 +2474,10 @@ class TestLVPlotter(CategoricalFixture): k_depth='proportion', linewidth=None, scale='exponential', outlier_prop=None) - linear_data = np.arange(101) - n = len(linear_data) - expected_k = int(np.log2(n)) - int(np.log2(n*0.007)) + 1 - - expected_edges_l = [edge_calc(i, linear_data) + expected_k = int(np.log2(100)) - int(np.log2(100 * 0.007)) + 1 + expected_edges_l = [edge_calc(i, np.arange(100)) for i in range(expected_k + 2, 1, -1)] - - outlier_data = np.concatenate((np.arange(100), [200])) - expected_edges_o = [edge_calc(i, outlier_data) + expected_edges_o = [edge_calc(i, np._c[np.arange(99), [200]]) for i in range(expected_k + 2, 1, -1)] def ispatch(self, c): From 41451efd92842233c5e38a529f69be76927329b3 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 29 Sep 2017 20:25:01 -0400 Subject: [PATCH 0532/1738] Update travis file --- .travis.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index dfb4af4aa9..e129899ccd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ env: before_install: - sudo apt-get update -yq - - sudo sh -c "echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections" + - sudo sh -c "echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections" # TODO still needed? - sudo apt-get install msttcorefonts -qq # http://conda.pydata.org/docs/travis.html#the-travis-yml-file @@ -33,8 +33,6 @@ install: - conda update conda --yes - source activate testenv - conda install --yes --file testing/deps_${DEPS}.txt - - pip install pep8==1.5 # Later versions get stricter... - - pip install https://github.com/dcramer/pyflakes/tarball/master - pip install . @@ -48,10 +46,8 @@ before_script: script: - - if [ ${PYTHON:0:1} == "2" ]; then - make lint; - fi + - make lint - if [ $DOCTESTS == 'true' ]; - then nosetests --with-doctest; - else nosetests; + then make test; + else make test-nodoctest; fi From 1f0bca9170124386c99e6b8b7040de783e2096d0 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 29 Sep 2017 20:47:35 -0400 Subject: [PATCH 0533/1738] Fix lvplotter tests --- seaborn/tests/test_categorical.py | 49 +++++++++++++++++-------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 94dbb75106..741f70c6c5 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -2458,13 +2458,6 @@ def test_plot_colors(self): plt.close("all") -def edge_calc(n, data): - - q = np.asanyarray([0.5 ** n, 1 - 0.5 ** n]) * 100 - q = list(np.unique(q)) - return np.percentile(data, q) - - class TestLVPlotter(CategoricalFixture): default_kws = dict(x=None, y=None, hue=None, data=None, @@ -2474,16 +2467,16 @@ class TestLVPlotter(CategoricalFixture): k_depth='proportion', linewidth=None, scale='exponential', outlier_prop=None) - expected_k = int(np.log2(100)) - int(np.log2(100 * 0.007)) + 1 - expected_edges_l = [edge_calc(i, np.arange(100)) - for i in range(expected_k + 2, 1, -1)] - expected_edges_o = [edge_calc(i, np._c[np.arange(99), [200]]) - for i in range(expected_k + 2, 1, -1)] - def ispatch(self, c): return isinstance(c, mpl.collections.PatchCollection) + def edge_calc(self, n, data): + + q = np.asanyarray([0.5 ** n, 1 - 0.5 ** n]) * 100 + q = list(np.unique(q)) + return np.percentile(data, q) + def test_box_ends_finite(self): p = cat._LVPlotter(**self.default_kws) p.establish_variables("g", "y", data=self.df) @@ -2509,23 +2502,35 @@ def within(t): npt.assert_equal(np.sum(list(k_f)), len(k_vals)) def test_box_ends_correct(self): - p = cat._LVPlotter(**self.default_kws) - calc_edges, calc_k = p._lv_box_ends(self.linear_data) - npt.assert_equal(list(self.expected_edges_l), calc_edges) + linear_data = np.arange(100) + expected_k = int(np.log2(100)) - int(np.log2(100 * 0.007)) + 1 + expected_edges = [self.edge_calc(i, linear_data) + for i in range(expected_k + 2, 1, -1)] + + p = cat._LVPlotter(**self.default_kws) + calc_edges, calc_k = p._lv_box_ends(linear_data) - npt.assert_equal(self.expected_k, calc_k) + npt.assert_equal(list(expected_edges), calc_edges) + npt.assert_equal(expected_k, calc_k) def test_outliers(self): + + n = 100 + outlier_data = np.append(np.arange(n - 1), 2 * n) + expected_k = int(np.log2(n)) - int(np.log2(n * 0.007)) + 1 + expected_edges = [self.edge_calc(i, outlier_data) + for i in range(expected_k + 2, 1, -1)] + p = cat._LVPlotter(**self.default_kws) - calc_edges, calc_k = p._lv_box_ends(self.outlier_data) + calc_edges, calc_k = p._lv_box_ends(outlier_data) - npt.assert_equal(list(self.expected_edges_o), calc_edges) + npt.assert_equal(list(expected_edges), calc_edges) - npt.assert_equal(self.expected_k, calc_k) + npt.assert_equal(expected_k, calc_k) - out_calc = p._lv_outliers(self.outlier_data, calc_k) - out_exp = p._lv_outliers(self.outlier_data, self.expected_k) + out_calc = p._lv_outliers(outlier_data, calc_k) + out_exp = p._lv_outliers(outlier_data, expected_k) npt.assert_equal(out_exp, out_calc) From 69b43e4ca8f3bd1fe80806c931e36b15f95b6692 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 29 Sep 2017 21:43:09 -0400 Subject: [PATCH 0534/1738] Try not installing arial --- .travis.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index e129899ccd..60aa663dd5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,11 +13,6 @@ env: before_install: - - sudo apt-get update -yq - - sudo sh -c "echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections" # TODO still needed? - - sudo apt-get install msttcorefonts -qq - - # http://conda.pydata.org/docs/travis.html#the-travis-yml-file - wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh - bash miniconda.sh -b -p $HOME/miniconda - export PATH="$HOME/miniconda/bin:$PATH" From 11a3bbdcc017d21d6c4e8354820631efff2c36d5 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Fri, 29 Sep 2017 21:57:49 -0400 Subject: [PATCH 0535/1738] More test-related cleaning --- Makefile | 6 +----- README.md | 4 ++-- doc/installing.rst | 6 +++--- seaborn/tests/__init__.py | 11 ----------- seaborn/tests/test_algorithms.py | 2 -- seaborn/tests/test_axisgrid.py | 9 ++++----- seaborn/tests/test_categorical.py | 8 ++++---- seaborn/tests/test_distributions.py | 3 +-- seaborn/tests/test_matrix.py | 7 +++---- seaborn/tests/test_miscplot.py | 4 +--- seaborn/tests/test_rcmod.py | 3 +-- seaborn/tests/test_regression.py | 7 +++---- seaborn/tests/test_utils.py | 4 +--- 13 files changed, 24 insertions(+), 50 deletions(-) diff --git a/Makefile b/Makefile index 36d426e67b..9c630f82c0 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,13 @@ export SHELL := /bin/bash test: - pytest --doctest-modules seaborn test-nodoctest: - pytest seaborn coverage: - - nosetests --cov=seaborn seaborn + pytest --cov=seaborn seaborn lint: - flake8 --exclude seaborn/__init__.py,seaborn/cm.py,seaborn/tests,seaborn/external seaborn diff --git a/README.md b/README.md index 1fe0fefc71..a7b0a9e060 100644 --- a/README.md +++ b/README.md @@ -82,8 +82,8 @@ Testing [![Build Status](https://travis-ci.org/mwaskom/seaborn.png?branch=master)](https://travis-ci.org/mwaskom/seaborn) -To test seaborn, run `make test` in the source directory. This will run the -unit-test and doctest suite (using `nose`). +To test seaborn, run `make test` in the source directory. This will exercise the +unit-test and doctest suite (using `pytest`). Development diff --git a/doc/installing.rst b/doc/installing.rst index 2e04709996..21541507e2 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -59,9 +59,9 @@ Testing ~~~~~~~ To test seaborn, run ``make test`` in the root directory of the source -distribution. This runs the unit test suite (which can also be exercised -separately by running ``nosetests``). It also runs the example code in function -docstrings to smoke-test a broader and more realistic range of example usage. +distribution. This runs the unit test suite (using ``pytest``). It also runs +the example code in function docstrings to smoke-test a broader and more +realistic range of example usage. The full set of tests requires an internet connection to download the example datasets (if they haven't been previously cached), but the unit tests should diff --git a/seaborn/tests/__init__.py b/seaborn/tests/__init__.py index 0c21ad5dd0..e69de29bb2 100644 --- a/seaborn/tests/__init__.py +++ b/seaborn/tests/__init__.py @@ -1,11 +0,0 @@ -import numpy as np -from matplotlib.pyplot import close - - -class PlotTestCase(object): - - def setUp(self): - np.random.seed(49) - - def tearDown(self): - close('all') diff --git a/seaborn/tests/test_algorithms.py b/seaborn/tests/test_algorithms.py index 5296b0d508..dfd31cac8f 100644 --- a/seaborn/tests/test_algorithms.py +++ b/seaborn/tests/test_algorithms.py @@ -1,8 +1,6 @@ import numpy as np -from scipy import stats from ..external.six.moves import range -import numpy.testing as npt from numpy.testing import assert_array_equal import nose.tools from nose.tools import assert_equal, raises diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 793b4e7f36..2a4825ccfe 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -15,7 +15,6 @@ except ImportError: import pandas.util.testing as tm -from . import PlotTestCase from .. import axisgrid as ag from .. import rcmod from ..palettes import color_palette @@ -29,7 +28,7 @@ pandas_has_categoricals = LooseVersion(pd.__version__) >= "0.15" -class TestFacetGrid(PlotTestCase): +class TestFacetGrid(object): df = pd.DataFrame(dict(x=rs.normal(size=60), y=rs.gamma(4, size=60), @@ -736,7 +735,7 @@ def test_categorical_warning(self): npt.assert_warns(UserWarning, g.map, pointplot, "b", "x") -class TestPairGrid(PlotTestCase): +class TestPairGrid(object): rs = np.random.RandomState(sum(map(ord, "PairGrid"))) df = pd.DataFrame(dict(x=rs.normal(size=80), @@ -1267,7 +1266,7 @@ def test_pairplot_markers(self): g = ag.pairplot(self.df, hue="a", vars=vars, markers=markers[:-2]) -class TestJointGrid(PlotTestCase): +class TestJointGrid(object): rs = np.random.RandomState(sum(map(ord, "JointGrid"))) x = rs.randn(100) @@ -1408,7 +1407,7 @@ def test_space(self): nt.assert_equal(joint_bounds[3], marg_y_bounds[3]) -class TestJointPlot(PlotTestCase): +class TestJointPlot(object): rs = np.random.RandomState(sum(map(ord, "jointplot"))) x = rs.randn(100) diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index 741f70c6c5..c4585ed381 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -12,7 +12,6 @@ import numpy.testing as npt from numpy.testing.decorators import skipif -from . import PlotTestCase from .. import categorical as cat from .. import palettes @@ -21,7 +20,7 @@ mpl_barplot_change = LooseVersion("2.0.1") -class CategoricalFixture(PlotTestCase): +class CategoricalFixture(object): """Test boxplot (also base class for things like violinplots).""" rs = np.random.RandomState(30) n_total = 60 @@ -2503,8 +2502,9 @@ def within(t): def test_box_ends_correct(self): - linear_data = np.arange(100) - expected_k = int(np.log2(100)) - int(np.log2(100 * 0.007)) + 1 + n = 100 + linear_data = np.arange(n) + expected_k = int(np.log2(n)) - int(np.log2(n * 0.007)) + 1 expected_edges = [self.edge_calc(i, linear_data) for i in range(expected_k + 2, 1, -1)] diff --git a/seaborn/tests/test_distributions.py b/seaborn/tests/test_distributions.py index a146d6d39c..1baa1dda92 100644 --- a/seaborn/tests/test_distributions.py +++ b/seaborn/tests/test_distributions.py @@ -8,7 +8,6 @@ import numpy.testing as npt from numpy.testing.decorators import skipif -from . import PlotTestCase from .. import distributions as dist try: @@ -22,7 +21,7 @@ _old_matplotlib = LooseVersion(mpl.__version__) < "1.5" -class TestKDE(PlotTestCase): +class TestKDE(object): rs = np.random.RandomState(0) x = rs.randn(50) diff --git a/seaborn/tests/test_matrix.py b/seaborn/tests/test_matrix.py index ecae4b7903..3c590aafca 100644 --- a/seaborn/tests/test_matrix.py +++ b/seaborn/tests/test_matrix.py @@ -16,7 +16,6 @@ import pandas.util.testing as pdt from numpy.testing.decorators import skipif -from . import PlotTestCase from .. import matrix as mat from .. import color_palette from ..external.six.moves import range @@ -30,7 +29,7 @@ _no_fastcluster = True -class TestHeatmap(PlotTestCase): +class TestHeatmap(object): rs = np.random.RandomState(sum(map(ord, "heatmap"))) x_norm = rs.randn(4, 8) @@ -399,7 +398,7 @@ def test_cbar_ticks(self): plt.close(f) -class TestDendrogram(PlotTestCase): +class TestDendrogram(object): rs = np.random.RandomState(sum(map(ord, "dendrogram"))) x_norm = rs.randn(4, 8) + np.arange(8) @@ -640,7 +639,7 @@ def test_dendrogram_ticklabel_rotation(self): plt.close(f) -class TestClustermap(PlotTestCase): +class TestClustermap(object): rs = np.random.RandomState(sum(map(ord, "clustermap"))) x_norm = rs.randn(4, 8) + np.arange(8) diff --git a/seaborn/tests/test_miscplot.py b/seaborn/tests/test_miscplot.py index 7ca4979af3..f17a83f682 100644 --- a/seaborn/tests/test_miscplot.py +++ b/seaborn/tests/test_miscplot.py @@ -1,13 +1,11 @@ import nose.tools as nt -import numpy.testing as npt import matplotlib.pyplot as plt -from . import PlotTestCase from .. import miscplot as misc from seaborn import color_palette -class TestPalPlot(PlotTestCase): +class TestPalPlot(object): """Test the function that visualizes a color palette.""" def test_palplot_size(self): diff --git a/seaborn/tests/test_rcmod.py b/seaborn/tests/test_rcmod.py index 585d10583f..abcc619df2 100644 --- a/seaborn/tests/test_rcmod.py +++ b/seaborn/tests/test_rcmod.py @@ -6,7 +6,6 @@ import nose.tools as nt import numpy.testing as npt -from . import PlotTestCase from .. import rcmod @@ -176,7 +175,7 @@ def func(): self.assert_rc_params(orig_params) -class TestFonts(PlotTestCase): +class TestFonts(object): def test_set_font(self): diff --git a/seaborn/tests/test_regression.py b/seaborn/tests/test_regression.py index 3a43a0dccd..6628995da6 100644 --- a/seaborn/tests/test_regression.py +++ b/seaborn/tests/test_regression.py @@ -18,14 +18,13 @@ except ImportError: _no_statsmodels = True -from . import PlotTestCase from .. import regression as lm from ..palettes import color_palette rs = np.random.RandomState(0) -class TestLinearPlotter(PlotTestCase): +class TestLinearPlotter(object): rs = np.random.RandomState(77) df = pd.DataFrame(dict(x=rs.normal(size=60), @@ -89,7 +88,7 @@ def test_dropna(self): pdt.assert_series_equal(p.y_na, self.df.y_na[mask]) -class TestRegressionPlotter(PlotTestCase): +class TestRegressionPlotter(object): rs = np.random.RandomState(49) @@ -411,7 +410,7 @@ def test_regression_limits(self): nt.assert_equal(grid.max(), self.df.x.max()) -class TestRegressionPlots(PlotTestCase): +class TestRegressionPlots(object): rs = np.random.RandomState(56) df = pd.DataFrame(dict(x=rs.randn(90), diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index c44835ba74..fe7156cc7a 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -1,5 +1,4 @@ """Tests for plotting utilities.""" -import warnings import tempfile import shutil @@ -25,7 +24,6 @@ except ImportError: BeautifulSoup = None -from . import PlotTestCase from .. import utils, rcmod from ..utils import get_dataset_names, load_dataset, _network @@ -121,7 +119,7 @@ def test_str_to_utf8(): assert_equal(type(u), type(u"\u01ff\u02ff")) -class TestSpineUtils(PlotTestCase): +class TestSpineUtils(object): sides = ["left", "right", "bottom", "top"] outer_sides = ["top", "right"] From 9b514d7a7b604a7e73c70539cf7af95330e58dae Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 30 Sep 2017 10:08:33 -0400 Subject: [PATCH 0536/1738] Update release notes --- doc/releases/v0.9.0.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 doc/releases/v0.9.0.txt diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt new file mode 100644 index 0000000000..5a6039d263 --- /dev/null +++ b/doc/releases/v0.9.0.txt @@ -0,0 +1,5 @@ + +v0.9.0 (Unreleased) +------------------- + +- Updated the testing infrastructure to execute tests with `pytest ` (although many individual tests still use nose assertion). From 806411d65a044ef3f010e062962c223b125c3bb0 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 30 Sep 2017 10:09:40 -0400 Subject: [PATCH 0537/1738] Add test coverage to travis builds Change how codecov uploader is installed --- .coveragerc | 6 ++++-- .gitignore | 1 + .travis.yml | 18 ++++++++++++------ Makefile | 4 ++-- testing/deps_latest.txt | 3 --- testing/deps_minimal.txt | 3 --- testing/deps_pinned.txt | 3 --- testing/utils.txt | 4 ++++ 8 files changed, 23 insertions(+), 19 deletions(-) create mode 100644 testing/utils.txt diff --git a/.coveragerc b/.coveragerc index 55415f6b58..1d5681ad07 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,3 +1,5 @@ [run] -include = seaborn/* -omit = *external* *widgets.py +omit = + seaborn/widgets.py + seaborn/external/* + seaborn/tests/* diff --git a/.gitignore b/.gitignore index 6466f7a6d7..ec2677f315 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ build/ .ipynb_checkpoints/ dist/ seaborn.egg-info/ +.cache/ .coverage cover/ .idea diff --git a/.travis.yml b/.travis.yml index 60aa663dd5..83e5a9ce0d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,11 +23,12 @@ before_install: install: - - conda update conda --yes - - conda create -n testenv --yes pip python=$PYTHON - - conda update conda --yes + - conda update conda + - conda create -n testenv pip python=$PYTHON + - conda update conda - source activate testenv - - conda install --yes --file testing/deps_${DEPS}.txt + - cat testing/deps_${DEPS}.txt testing/utils.txt > deps.txt + - conda install --file deps.txt - pip install . @@ -43,6 +44,11 @@ before_script: script: - make lint - if [ $DOCTESTS == 'true' ]; - then make test; - else make test-nodoctest; + then make coverage; + else make unittests; fi + + +after_success: + - pip install codecov + - codecov diff --git a/Makefile b/Makefile index 9c630f82c0..90a502419f 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,11 @@ export SHELL := /bin/bash test: pytest --doctest-modules seaborn -test-nodoctest: +unittests: pytest seaborn coverage: - pytest --cov=seaborn seaborn + pytest --doctest-modules --cov=seaborn --cov-config=.coveragerc seaborn lint: flake8 --exclude seaborn/__init__.py,seaborn/cm.py,seaborn/tests,seaborn/external seaborn diff --git a/testing/deps_latest.txt b/testing/deps_latest.txt index a8d65bbf35..a8d32f9934 100644 --- a/testing/deps_latest.txt +++ b/testing/deps_latest.txt @@ -3,6 +3,3 @@ scipy matplotlib pandas statsmodels -pytest -flake8 -nose diff --git a/testing/deps_minimal.txt b/testing/deps_minimal.txt index 99298f11e1..8fed041814 100644 --- a/testing/deps_minimal.txt +++ b/testing/deps_minimal.txt @@ -2,6 +2,3 @@ numpy scipy matplotlib pandas -pytest -flake8 -nose diff --git a/testing/deps_pinned.txt b/testing/deps_pinned.txt index 77b750b5f8..a1a7609a6f 100644 --- a/testing/deps_pinned.txt +++ b/testing/deps_pinned.txt @@ -3,6 +3,3 @@ scipy=0.11.0 matplotlib=1.1.1 pandas=0.12.0 statsmodels=0.5.0 -pytest -flake8 -nose diff --git a/testing/utils.txt b/testing/utils.txt new file mode 100644 index 0000000000..e07e015a77 --- /dev/null +++ b/testing/utils.txt @@ -0,0 +1,4 @@ +pytest +pytest-cov +flake8 +nose From f98b43d521101d291ea5768f109b6295b1eadcb9 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 30 Sep 2017 10:47:55 -0400 Subject: [PATCH 0538/1738] Restore microsoft fonts on travis --- .travis.yml | 2 ++ testing/getmsfonts.sh | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 testing/getmsfonts.sh diff --git a/.travis.yml b/.travis.yml index 83e5a9ce0d..c399ea5fa7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,8 @@ env: before_install: + - sudo apt-get update -yq + - sudo sh testing/getmsfonts.sh - wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh - bash miniconda.sh -b -p $HOME/miniconda - export PATH="$HOME/miniconda/bin:$PATH" diff --git a/testing/getmsfonts.sh b/testing/getmsfonts.sh new file mode 100644 index 0000000000..0d1a5eb3e1 --- /dev/null +++ b/testing/getmsfonts.sh @@ -0,0 +1,3 @@ +echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections +apt-get install msttcorefonts -qq + From 88979f7341f96bf7d4e2895827613d770105f340 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 30 Sep 2017 11:02:49 -0400 Subject: [PATCH 0539/1738] Move color dictionaries to submodule and don't evaluate coverage Update linter exclusions Fix color dictionary imports in palette tests --- .coveragerc | 3 +++ Makefile | 2 +- seaborn/__init__.py | 3 +-- seaborn/colors/__init__.py | 2 ++ seaborn/{ => colors}/crayons.py | 0 seaborn/{ => colors}/xkcd_rgb.py | 0 seaborn/palettes.py | 3 +-- seaborn/tests/test_palettes.py | 3 +-- 8 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 seaborn/colors/__init__.py rename seaborn/{ => colors}/crayons.py (100%) rename seaborn/{ => colors}/xkcd_rgb.py (100%) diff --git a/.coveragerc b/.coveragerc index 1d5681ad07..905f7faf66 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,4 +2,7 @@ omit = seaborn/widgets.py seaborn/external/* + seaborn/colors/* + seaborn/cm.py + seaborn/conftest.py seaborn/tests/* diff --git a/Makefile b/Makefile index 90a502419f..3cd57d25e9 100644 --- a/Makefile +++ b/Makefile @@ -10,4 +10,4 @@ coverage: pytest --doctest-modules --cov=seaborn --cov-config=.coveragerc seaborn lint: - flake8 --exclude seaborn/__init__.py,seaborn/cm.py,seaborn/tests,seaborn/external seaborn + flake8 --exclude seaborn/__init__.py,seaborn/colors/__init__.py,seaborn/cm.py,seaborn/tests,seaborn/external seaborn diff --git a/seaborn/__init__.py b/seaborn/__init__.py index cf349b7e93..5fde08572b 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -14,8 +14,7 @@ from .miscplot import * from .axisgrid import * from .widgets import * -from .xkcd_rgb import xkcd_rgb -from .crayons import crayons +from .colors import xkcd_rgb, crayons from . import cm __version__ = "0.9.dev" diff --git a/seaborn/colors/__init__.py b/seaborn/colors/__init__.py new file mode 100644 index 0000000000..191705c6f5 --- /dev/null +++ b/seaborn/colors/__init__.py @@ -0,0 +1,2 @@ +from .xkcd_rgb import xkcd_rgb +from .crayons import crayons diff --git a/seaborn/crayons.py b/seaborn/colors/crayons.py similarity index 100% rename from seaborn/crayons.py rename to seaborn/colors/crayons.py diff --git a/seaborn/xkcd_rgb.py b/seaborn/colors/xkcd_rgb.py similarity index 100% rename from seaborn/xkcd_rgb.py rename to seaborn/colors/xkcd_rgb.py diff --git a/seaborn/palettes.py b/seaborn/palettes.py index c0965b1000..060e43c9b4 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -10,8 +10,7 @@ from .external.six.moves import range from .utils import desaturate, set_hls_values, get_color_cycle -from .xkcd_rgb import xkcd_rgb -from .crayons import crayons +from .colors import xkcd_rgb, crayons __all__ = ["color_palette", "hls_palette", "husl_palette", "mpl_palette", diff --git a/seaborn/tests/test_palettes.py b/seaborn/tests/test_palettes.py index 4b0ef0e3fe..098a946c9a 100644 --- a/seaborn/tests/test_palettes.py +++ b/seaborn/tests/test_palettes.py @@ -8,8 +8,7 @@ from .. import palettes, utils, rcmod from ..external import husl -from ..xkcd_rgb import xkcd_rgb -from ..crayons import crayons +from ..colors import xkcd_rgb, crayons class TestColorPalettes(object): From 8ba910e53ed29610e408a5859201c5ee2adec73e Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 30 Sep 2017 08:38:56 -0700 Subject: [PATCH 0540/1738] Update README and add badges Fix DOI badge --- README.md | 57 +++++++++++++++++++++---------------------------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index a7b0a9e060..b1b0a0f558 100644 --- a/README.md +++ b/README.md @@ -6,44 +6,44 @@ seaborn: statistical data visualization - - + + - - + + - - + + - - + + -Seaborn is a Python visualization library based on matplotlib. It provides a high-level interface for drawing attractive statistical graphics. +-------------------------------------- -Documentation -------------- +[![PyPI Version](https://img.shields.io/pypi/v/seaborn.svg)](https://pypi.org/project/seaborn/) +[![License](https://img.shields.io/pypi/l/seaborn.svg)](https://github.com/mwaskom/seaborn/blob/master/LICENSE) +[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.883859.svg)](https://doi.org/10.5281/zenodo.883859) +[![Build Status](https://travis-ci.org/mwaskom/seaborn.svg?branch=master)](https://travis-ci.org/mwaskom/seaborn) +[![Code Coverage](https://codecov.io/gh/mwaskom/seaborn/branch/master/graph/badge.svg)](https://codecov.io/gh/mwaskom/seaborn) -Online documentation is available [here](http://seaborn.pydata.org/). It includes a high-level tutorial, detailed API documentation, and other useful info. +Seaborn is a Python visualization library based on matplotlib. It provides a high-level interface for drawing attractive statistical graphics. -Examples --------- -The documentation has an [example gallery](http://seaborn.pydata.org/examples/index.html) with short scripts showing how to use different parts of the package. +Documentation +------------- -Citing ------- +Online documentation is available at [seaborn.pydata.org](https://seaborn.pydata.org). The docs include a [high-level tutorial](http://seaborn.pydata.org/tutorial.html), [example gallery](http://seaborn.pydata.org/examples/index.html), [API documentation](http://seaborn.pydata.org/api.html), and other useful information. -Seaborn can be cited using a DOI provided through Zenodo: [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.883859.svg)](https://doi.org/10.5281/zenodo.883859) Dependencies ------------ @@ -68,11 +68,11 @@ Dependencies Installation ------------ -To install the released version, just do +The latest stable release (and older versions) can be installed from PyPI: pip install seaborn -You may instead want to use the development version from Github, by running +You may instead want to use the development version from Github: pip install git+https://github.com/mwaskom/seaborn.git#egg=seaborn @@ -80,10 +80,7 @@ You may instead want to use the development version from Github, by running Testing ------- -[![Build Status](https://travis-ci.org/mwaskom/seaborn.png?branch=master)](https://travis-ci.org/mwaskom/seaborn) - -To test seaborn, run `make test` in the source directory. This will exercise the -unit-test and doctest suite (using `pytest`). +To test seaborn, run `make test` in the source directory. This will exercise both the unit tests and docstring examples (using `pytest`). Development @@ -91,15 +88,5 @@ Development https://github.com/mwaskom/seaborn -Please [submit](https://github.com/mwaskom/seaborn/issues/new) any bugs you encounter to the Github issue tracker. - -License -------- - -Released under a BSD (3-clause) license - - -Celebrity Endorsements ----------------------- +Please submit any reproducible bugs you encounter to the Github [issue tracker](https://github.com/mwaskom/seaborn/issues). -"Those are nice plots" -Hadley Wickham From 8d3537f901208b2da1d3189c73e1451ad9e8f9e6 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 30 Sep 2017 12:04:06 -0400 Subject: [PATCH 0541/1738] Move and update contributing guidelines --- .github/CONTRIBUTING.md | 17 +++++++++++++++++ CONTRIBUTING.md | 38 -------------------------------------- 2 files changed, 17 insertions(+), 38 deletions(-) create mode 100644 .github/CONTRIBUTING.md delete mode 100644 CONTRIBUTING.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000000..dd8fc4a24c --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,17 @@ +Contributing to seaborn +======================= + +General support +--------------- + +General support questions ("how do I do ?") are most at home on [StackOverflow](http://stackoverflow.com/), where they will be seen by more people and are more easily searchable. StackOverflow has a `[seaborn]` tag, which will bring the question to the attention of people who might be able to answer. + +Reporting bugs +-------------- + +If you have encountered a bug in seaborn, please report it on the [Github issue tracker](https://github.com/mwaskom/seaborn/issues/new). It is only really possible to address bug reports if they include a reproducible script using randomly-generated data or one of the example datasets (accessed through `seaborn.load_dataset()`). Please also specify your versions of seaborn and matplotlib, as well as which matplotlib backend you are using. + +New features +------------ + +If you think there is a new feature that should be added to seaborn, you can open an issue to discuss it. However, seaborn's development has become increasingly conservative, and the answer to most feature requests or proposed additions is "no". Polite requests with an explanation of the proposed feature's virtues will usually get an explanation; feature requests that say "I would like feature X, you need to add it" typically won't. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 211e6c4dda..0000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,38 +0,0 @@ -Contributing to seaborn -======================= - -General support ---------------- - -General support questions are most at home on [StackOverflow](http://stackoverflow.com/), where they will be seen by more people and are more easily searchable. StackOverflow has a `seaborn` tag, which will bring the question to the attention of people who might be able to answer. - -Reporting bugs --------------- - -If you think you have encountered a bug in seaborn, please report it on the [Github issue tracker](https://github.com/mwaskom/seaborn/issues/new). It will be most helpful to include a reproducible script with one of the example datasets (accessed through `load_dataset()`) or using some randomly-generated data. - -It is difficult debug any issues without knowing the versions of seaborn and matplotlib you are using, as well as what matplotlib backend you are using to draw the plots, so please include those in your bug report. - -Fixing bugs ------------ - -If you know how to fix a bug you have encountered or see on the issue tracker, that is very appreciated. Please submit a [pull request](https://help.github.com/articles/using-pull-requests/) on the main seaborn repository with the fix. The presence of a bug implies a lack of coverage in the tests, so when fixing a bug, it is best to add a test that fails before the fix and passes after to make sure it does not reappear. See the section on testing below. But if there is an obvious fix and you're not sure how to write a test, don't let that stop you. - -Documentation issues --------------------- - -If you see something wrong or confusing in the documentation, please report it with an issue or fix it and open a pull request. - -New features ------------- - -If you'd like to add a new feature to seaborn, it's best to open an issue to discuss it first. Given the nature of seaborn's goals and approach, it can be hard to write a substantial contribution that is consistent with the rest of the package, and I often lack the bandwidth to help. Also, every new feature represents a new commitment for support. For these reasons, I'm somewhat averse to large feature contributions. Smaller or well-targeted enhancements can be helpful and should be submitted through the normal pull-request workflow. Please include tests for any new features and make sure your changes don't break any existing tests. - -Testing seaborn ---------------- - -Seaborn is primarily tested through a `nose` unit-test suite that interacts with the private objects that actually draw the plots behind the function interface. The basic approach here is to test the numeric information going into and coming out of the matplotlib functions. Currently, there is a general assumption that matplotlib is drawing things properly, and tests are run against the data that ends up in the matplotlib objects but not against the images themselves. See the existing tests for examples of how this works. - -To execute the test suite and doctests, run `make test` in the root source directory. You can also build a test coverage report with `make coverage`. - -The `make lint` command will run `pep8` and `pyflakes` over the codebase to check for style issues. Doing so requires [this](https://github.com/dcramer/pyflakes) fork of pyflakes, which can be installed with `pip install https://github.com/dcramer/pyflakes/tarball/master`. It also currently requires `pep8` 1.5 or older, as the rules got stricter and the codebase has not been updated. This is part of the Travis build, and the build will fail if there are issues, so please do this before submitting a pull request. From 985cf0f8530caf295af900baac50d03d462f8252 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 30 Sep 2017 12:32:16 -0400 Subject: [PATCH 0542/1738] Avoid divide-by-zero warnings --- seaborn/categorical.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 040b6b9ff5..6bbcfc7aae 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -769,19 +769,25 @@ def scale_width(self, density): def scale_count(self, density, counts, scale_hue): """Scale each density curve by the number of observations.""" if self.hue_names is None: - for count, d in zip(counts, density): - d /= d.max() - d *= count / counts.max() + if counts.max() == 0: + d = 0 + else: + for count, d in zip(counts, density): + d /= d.max() + d *= count / counts.max() else: for i, group in enumerate(density): for j, d in enumerate(group): - count = counts[i, j] - if scale_hue: - scaler = count / counts[i].max() + if counts[i].max() == 0: + d = 0 else: - scaler = count / counts.max() - d /= d.max() - d *= scaler + count = counts[i, j] + if scale_hue: + scaler = count / counts[i].max() + else: + scaler = count / counts.max() + d /= d.max() + d *= scaler @property def dwidth(self): From bb4ac6c2e8a6c05abf8f64106a9110335e5c56ab Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 30 Sep 2017 12:34:23 -0400 Subject: [PATCH 0543/1738] Avoid overflow warning when testing perfect separation --- seaborn/tests/test_regression.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seaborn/tests/test_regression.py b/seaborn/tests/test_regression.py index 6628995da6..2e114cca6d 100644 --- a/seaborn/tests/test_regression.py +++ b/seaborn/tests/test_regression.py @@ -359,7 +359,8 @@ def test_logistic_perfect_separation(self): y = self.df.x > self.df.x.mean() p = lm._RegressionPlotter("x", y, data=self.df, logistic=True, n_boot=10) - _, yhat, _ = p.fit_regression(x_range=(-3, 3)) + with np.errstate(all="ignore"): + _, yhat, _ = p.fit_regression(x_range=(-3, 3)) nt.assert_true(np.isnan(yhat).all()) @skipif(_no_statsmodels) From d1813a2b4f8e320afebdbbf34ac5f565085eebef Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 30 Sep 2017 12:51:26 -0400 Subject: [PATCH 0544/1738] Avoid legend warning in kdeplot --- seaborn/distributions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index ce50ae9124..dde8fea6f4 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -334,7 +334,8 @@ def _univariate_kdeplot(data, shade, vertical, kernel, bw, gridsize, cut, ax.set_ylim(0, max(ax.get_ylim()[1], (1 + ymargin) * y.max())) # Draw the legend here - if legend: + handles, labels = ax.get_legend_handles_labels() + if legend and handles: ax.legend(loc="best") return ax From 135fd2a8eebd3489899a3212e96c851b65ab3d77 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 30 Sep 2017 13:44:15 -0400 Subject: [PATCH 0545/1738] Update README --- README.md | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index b1b0a0f558..877be38eaa 100644 --- a/README.md +++ b/README.md @@ -42,27 +42,17 @@ Seaborn is a Python visualization library based on matplotlib. It provides a hig Documentation ------------- -Online documentation is available at [seaborn.pydata.org](https://seaborn.pydata.org). The docs include a [high-level tutorial](http://seaborn.pydata.org/tutorial.html), [example gallery](http://seaborn.pydata.org/examples/index.html), [API documentation](http://seaborn.pydata.org/api.html), and other useful information. +Online documentation is available at [seaborn.pydata.org](https://seaborn.pydata.org). + +The docs include a [tutorial](http://seaborn.pydata.org/tutorial.html), [example gallery](http://seaborn.pydata.org/examples/index.html), [API reference](http://seaborn.pydata.org/api.html), and other useful information. Dependencies ------------ -- Python 2.7 or 3.4+ - -### Mandatory - -- [numpy](http://www.numpy.org/) - -- [scipy](http://www.scipy.org/) +Seaborn supports Python 2.7 and 3.4+. -- [matplotlib](http://matplotlib.org/) - -- [pandas](http://pandas.pydata.org/) - -### Recommended - -- [statsmodels](http://statsmodels.sourceforge.net/) +Installation requires [numpy](http://www.numpy.org/), [scipy](http://www.scipy.org/), [pandas](http://pandas.pydata.org/), and [matplotlib](http://matplotlib.org/). Some functions will optionally use [statsmodels](http://statsmodels.sourceforge.net/) if it is installed. Installation @@ -80,13 +70,15 @@ You may instead want to use the development version from Github: Testing ------- -To test seaborn, run `make test` in the source directory. This will exercise both the unit tests and docstring examples (using `pytest`). +To test seaborn, run `make test` in the source directory. + +This will exercise both the unit tests and docstring examples (using `pytest`). Development ----------- -https://github.com/mwaskom/seaborn +Seaborn development takes place on Github: https://github.com/mwaskom/seaborn -Please submit any reproducible bugs you encounter to the Github [issue tracker](https://github.com/mwaskom/seaborn/issues). +Please submit any reproducible bugs you encounter to the [issue tracker](https://github.com/mwaskom/seaborn/issues). From cbcf7a556752409681725bdebb8bad27c68abf6c Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 30 Sep 2017 13:44:39 -0400 Subject: [PATCH 0546/1738] Final removal of coefplot and interactplot --- doc/releases/v0.9.0.txt | 2 + seaborn/regression.py | 232 ---------------------------------------- 2 files changed, 2 insertions(+), 232 deletions(-) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index 5a6039d263..c664e6c2e5 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -2,4 +2,6 @@ v0.9.0 (Unreleased) ------------------- +- Final removal of the previously-deprecated ``coefcplot`` and ``interactplot`` functions. + - Updated the testing infrastructure to execute tests with `pytest ` (although many individual tests still use nose assertion). diff --git a/seaborn/regression.py b/seaborn/regression.py index 365f489668..99deb70cf3 100644 --- a/seaborn/regression.py +++ b/seaborn/regression.py @@ -1,7 +1,6 @@ """Plotting functions for linear models (broadly construed).""" from __future__ import division import copy -import itertools from textwrap import dedent import numpy as np import pandas as pd @@ -9,8 +8,6 @@ import matplotlib as mpl import matplotlib.pyplot as plt -import warnings - try: import statsmodels assert statsmodels @@ -19,11 +16,9 @@ _has_statsmodels = False from .external.six import string_types -from .external.six.moves import range from . import utils from . import algorithms as algo -from .palettes import color_palette from .axisgrid import FacetGrid, _facet_docs @@ -1043,230 +1038,3 @@ def residplot(x, y, data=None, lowess=False, x_partial=None, y_partial=None, line_kws = {} if line_kws is None else line_kws plotter.plot(ax, scatter_kws, line_kws) return ax - - -def coefplot(formula, data, groupby=None, intercept=False, ci=95, - palette="husl"): - """Plot the coefficients from a linear model. - - Parameters - ---------- - formula : string - patsy formula for ols model - data : dataframe - data for the plot; formula terms must appear in columns - groupby : grouping object, optional - object to group data with to fit conditional models - intercept : bool, optional - if False, strips the intercept term before plotting - ci : float, optional - size of confidence intervals - palette : seaborn color palette, optional - palette for the horizonal plots - - """ - msg = ( - "The `coefplot` function has been deprecated and will be removed " - "in a future version." - ) - warnings.warn(msg, UserWarning) - if not _has_statsmodels: - raise ImportError("The `coefplot` function requires statsmodels") - import statsmodels.formula.api as sf - - alpha = 1 - ci / 100 - if groupby is None: - coefs = sf.ols(formula, data).fit().params - cis = sf.ols(formula, data).fit().conf_int(alpha) - else: - grouped = data.groupby(groupby) - coefs = grouped.apply(lambda d: sf.ols(formula, d).fit().params).T - cis = grouped.apply(lambda d: sf.ols(formula, d).fit().conf_int(alpha)) - - # Possibly ignore the intercept - if not intercept: - coefs = coefs.ix[1:] - - n_terms = len(coefs) - - # Plot seperately depending on groupby - def hsize(n): - return n * (h / 2) - - def wsize(n): - return n * (w / (4 * (n / 5))) - - w, h = mpl.rcParams["figure.figsize"] - if groupby is None: - colors = itertools.cycle(color_palette(palette, n_terms)) - f, ax = plt.subplots(1, 1, figsize=(wsize(n_terms), hsize(1))) - for i, term in enumerate(coefs.index): - color = next(colors) - low, high = cis.ix[term] - ax.plot([i, i], [low, high], c=color, - solid_capstyle="round", lw=2.5) - ax.plot(i, coefs.ix[term], "o", c=color, ms=8) - ax.set_xlim(-.5, n_terms - .5) - ax.axhline(0, ls="--", c="dimgray") - ax.set_xticks(range(n_terms)) - ax.set_xticklabels(coefs.index) - - else: - n_groups = len(coefs.columns) - f, axes = plt.subplots(n_terms, 1, sharex=True, - figsize=(wsize(n_groups), hsize(n_terms))) - if n_terms == 1: - axes = [axes] - colors = itertools.cycle(color_palette(palette, n_groups)) - for ax, term in zip(axes, coefs.index): - for i, group in enumerate(coefs.columns): - color = next(colors) - low, high = cis.ix[(group, term)] - ax.plot([i, i], [low, high], c=color, - solid_capstyle="round", lw=2.5) - ax.plot(i, coefs.loc[term, group], "o", c=color, ms=8) - ax.set_xlim(-.5, n_groups - .5) - ax.axhline(0, ls="--", c="dimgray") - ax.set_title(term) - ax.set_xlabel(groupby) - ax.set_xticks(range(n_groups)) - ax.set_xticklabels(coefs.columns) - - -def interactplot(x1, x2, y, data=None, filled=False, cmap="RdBu_r", - colorbar=True, levels=30, logistic=False, - contour_kws=None, scatter_kws=None, ax=None, **kwargs): - """Visualize a continuous two-way interaction with a contour plot. - - Parameters - ---------- - x1, x2, y, strings or array-like - Either the two independent variables and the dependent variable, - or keys to extract them from `data` - data : DataFrame - Pandas DataFrame with the data in the columns. - filled : bool - Whether to plot with filled or unfilled contours - cmap : matplotlib colormap - Colormap to represent yhat in the countour plot. - colorbar : bool - Whether to draw the colorbar for interpreting the color values. - levels : int or sequence - Number or position of contour plot levels. - logistic : bool - Fit a logistic regression model instead of linear regression. - contour_kws : dictionary - Keyword arguments for contour[f](). - scatter_kws : dictionary - Keyword arguments for plot(). - ax : matplotlib axis - Axis to draw plot in. - - Returns - ------- - ax : Matplotlib axis - Axis with the contour plot. - - """ - msg = ( - "The `interactplot` function has been deprecated and will be removed " - "in a future version." - ) - warnings.warn(msg, UserWarning) - if not _has_statsmodels: - raise ImportError("The `interactplot` function requires statsmodels") - from statsmodels.regression.linear_model import OLS - from statsmodels.genmod.generalized_linear_model import GLM - from statsmodels.genmod.families import Binomial - - # Handle the form of the data - if data is not None: - x1 = data[x1] - x2 = data[x2] - y = data[y] - if hasattr(x1, "name"): - xlabel = x1.name - else: - xlabel = None - if hasattr(x2, "name"): - ylabel = x2.name - else: - ylabel = None - if hasattr(y, "name"): - clabel = y.name - else: - clabel = None - x1 = np.asarray(x1) - x2 = np.asarray(x2) - y = np.asarray(y) - - # Initialize the scatter keyword dictionary - if scatter_kws is None: - scatter_kws = {} - if not ("color" in scatter_kws or "c" in scatter_kws): - scatter_kws["color"] = "#222222" - if "alpha" not in scatter_kws: - scatter_kws["alpha"] = 0.75 - - # Intialize the contour keyword dictionary - if contour_kws is None: - contour_kws = {} - - # Initialize the axis - if ax is None: - ax = plt.gca() - - # Plot once to let matplotlib sort out the axis limits - ax.plot(x1, x2, "o", **scatter_kws) - - # Find the plot limits - x1min, x1max = ax.get_xlim() - x2min, x2max = ax.get_ylim() - - # Make the grid for the contour plot - x1_points = np.linspace(x1min, x1max, 100) - x2_points = np.linspace(x2min, x2max, 100) - xx1, xx2 = np.meshgrid(x1_points, x2_points) - - # Fit the model with an interaction - X = np.c_[np.ones(x1.size), x1, x2, x1 * x2] - if logistic: - lm = GLM(y, X, family=Binomial()).fit() - else: - lm = OLS(y, X).fit() - - # Evaluate the model on the grid - eval = np.vectorize(lambda x1_, x2_: lm.predict([1, x1_, x2_, x1_ * x2_])) - yhat = eval(xx1, xx2) - - # Default color limits put the midpoint at mean(y) - y_bar = y.mean() - c_min = min(np.percentile(y, 2), yhat.min()) - c_max = max(np.percentile(y, 98), yhat.max()) - delta = max(c_max - y_bar, y_bar - c_min) - c_min, c_max = y_bar - delta, y_bar + delta - contour_kws.setdefault("vmin", c_min) - contour_kws.setdefault("vmax", c_max) - - # Draw the contour plot - func_name = "contourf" if filled else "contour" - contour = getattr(ax, func_name) - c = contour(xx1, xx2, yhat, levels, cmap=cmap, **contour_kws) - - # Draw the scatter again so it's visible - ax.plot(x1, x2, "o", **scatter_kws) - - # Draw a colorbar, maybe - if colorbar: - bar = plt.colorbar(c) - - # Label the axes - if xlabel is not None: - ax.set_xlabel(xlabel) - if ylabel is not None: - ax.set_ylabel(ylabel) - if clabel is not None and colorbar: - clabel = "P(%s)" % clabel if logistic else clabel - bar.set_label(clabel, labelpad=15, rotation=270) - - return ax From 11d3ffa6c2da280f65ac5eff454fa582d59ae382 Mon Sep 17 00:00:00 2001 From: Joel Ostblom Date: Fri, 6 Oct 2017 11:21:31 -0400 Subject: [PATCH 0547/1738] typo lvplot docstring --- seaborn/categorical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 6bbcfc7aae..02117cf2c2 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -3743,7 +3743,7 @@ def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, assumptions about the number of outliers and leverages different statistical properties. {linewidth} - scale : "linear" | "exonential" | "area" + scale : "linear" | "exponential" | "area" Method to use for the width of the letter value boxes. All give similar results visually. "linear" reduces the width by a constant linear factor, "exponential" uses the proportion of data not covered, "area" From 2f5ddf5504769b54310a1e177d1d8de811e8ea88 Mon Sep 17 00:00:00 2001 From: Joel Ostblom Date: Fri, 6 Oct 2017 15:06:13 -0400 Subject: [PATCH 0548/1738] One more typo in lvplot docstring Also corrected some typos in the comments --- seaborn/categorical.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 02117cf2c2..b84ecd1e4e 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -321,7 +321,7 @@ def infer_orient(self, x, y, orient=None): def is_categorical(s): try: - # Correct way, but doesnt exist in older Pandas + # Correct way, but does not exist in older Pandas try: return pd.api.types.is_categorical_dtype(s) except AttributeError: @@ -414,7 +414,7 @@ def annotate_axes(self, ax): leg.set_title(self.hue_title) # Set the title size a roundabout way to maintain - # compatability with matplotlib 1.1 + # compatibility with matplotlib 1.1 try: title_size = mpl.rcParams["axes.labelsize"] * .85 except TypeError: # labelsize is something like "large" @@ -1303,7 +1303,7 @@ def beeswarm(self, orig_xy, d): offsets = np.abs(candidates[:, 0] - midline) candidates = candidates[np.argsort(offsets)] - # Find the first candidate that doesn't overlap any neighbours + # Find the first candidate that does not overlap any neighbours new_xy_i = self.first_non_overlapping_candidate(candidates, neighbors, d) @@ -1338,7 +1338,7 @@ def swarm_points(self, ax, points, center, width, s, **kws): # and then convert back to data coordinates and replot orig_xy = ax.transData.transform(points.get_offsets()) - # Order the variables so that x is the caegorical axis + # Order the variables so that x is the categorical axis if self.orient == "h": orig_xy = orig_xy[:, [1, 0]] @@ -1815,7 +1815,7 @@ def __init__(self, x, y, hue, data, order, hue_order, orient, color, palette, saturation, width, dodge, k_depth, linewidth, scale, outlier_prop): - # TODO assigning variables for None is unceccesary + # TODO assigning variables for None is unneccesary if width is None: width = .8 self.width = width @@ -3749,7 +3749,7 @@ def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, factor, "exponential" uses the proportion of data not covered, "area" is proportional to the percentage of data covered. outlier_prop : float, optional - Proportion of data believed to be outliers. Is used in conjuction with + Proportion of data believed to be outliers. Used in conjunction with k_depth to determine the number of percentiles to draw. Defaults to 0.007 as a proportion of outliers. Should be in range [0, 1]. {ax_in} From 56de20d930ec4751c7da7f3dd29c462f62e97d5a Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 12 Oct 2017 09:06:18 -0400 Subject: [PATCH 0549/1738] Include colors submodule --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b980d42f30..23910e1e99 100644 --- a/setup.py +++ b/setup.py @@ -77,7 +77,8 @@ def check_dependencies(): version=VERSION, download_url=DOWNLOAD_URL, install_requires=install_requires, - packages=['seaborn', 'seaborn.external', 'seaborn.tests'], + packages=['seaborn', 'seaborn.colors', + 'seaborn.external', 'seaborn.tests'], classifiers=[ 'Intended Audience :: Science/Research', 'Programming Language :: Python :: 2.7', From 0799f5f9a24d08641838ad70f95cca24c7a01ab9 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 7 Sep 2017 21:17:20 -0400 Subject: [PATCH 0550/1738] Begin developing lineplot --- seaborn/basic.py | 248 +++++++++++++++++++++++++++++++++++++++++++++++ seaborn/utils.py | 12 +++ 2 files changed, 260 insertions(+) create mode 100644 seaborn/basic.py diff --git a/seaborn/basic.py b/seaborn/basic.py new file mode 100644 index 0000000000..72e4bf2b21 --- /dev/null +++ b/seaborn/basic.py @@ -0,0 +1,248 @@ +from textwrap import dedent + +import numpy as np +import pandas as pd +import matplotlib as mpl +import matplotlib.pyplot as plt + +from .external.six import string_types + +from .utils import categorical_order, hue_type, get_color_cycle +from .palettes import color_palette, husl_palette + +class _BasicPlotter(object): + + # TODO use different lists for mpl 1 and 2 + default_markers = ["o", "s", "D", "v", "^", "p"] + marker_scales = {"o": 1, "s": .85, "D": .9, "v": 1.3, "^": 1.3, "p": 1.25} + default_dashes = [(np.inf, 1), (5, 1), (4, 1, 2, 1), + (2, 1), (5, 1, 1, 1), (5, 1, 2, 1, 2, 1)] + + def establish_variables(self, x=None, y=None, + hue=None, style=None, size=None, + data=None): + + # Option 1: + # We have a wide-form datast + # -------------------------- + + if x is None and y is None: + + + # Option 1a: + # The input data is a Pandas DataFrame + # ------------------------------------ + # We will assign the index to x, the values to y, + # and the columns names to both hue and style + + if isinstance(data, pd.DataFrame): + + # Enforce numeric values + try: + data.astype(np.float) + except ValueError: + raise ValueError("A wide-form dataframe must have only " + "numeric values.") + + plot_data = pd.melt(data.assign(x=data.index), "x", + var_name="hue", value_name="y") + plot_data["style"] = plot_data["hue"] + + # Option 1b: + # The input data is an array or list + # ---------------------------------- + + else: + + # The input data is an array: + # We will assign a numeric index to x, the values to y, and + # numeric values to both hue and style + + if hasattr(data, "shape"): + + + plot_data = pd.DataFrame(data) + plot_data = pd.melt(plot_data.assign(x="data.index"), "x", + var_name="hue", value_name="y") + plot_data["style"] = plot_data["hue"] + + # The input data is a flat list: + # We will assign a numeric index for x, and use the values for y + + elif np.isscalar(data[0]): + + plot_data = pd.DataFrame(dict(x=np.arange(len(data)), + y=data)) + + # The input data is a nested list: + # We will assign a numeric index for x, use the values for, y + # and use numieric hue/style identifiers for each entry. + + else: + + plot_data = pd.concat([ + pd.DataFrame(dict(x=np.arange(len(data_i)), + y=data_i, hue=i, style=i)) + for i, data_i in enumerate(data) + ]) + + # Option 2: + # We have long-form data + # ---------------------- + + else: + + # See if we need to get variables from `data` + if data is not None: + x = data.get(x, x) + y = data.get(y, y) + hue = data.get(hue, hue) + style = data.get(style, style) + size = data.get(size, size) + + # Validate the inputs + for input in [x, y, hue, style, size]: + if isinstance(input, string_types): + err = "Could not interpret input '{}'".format(input) + raise ValueError(err) + + plot_data = dict(x=x, y=y, hue=hue, style=style, size=size) + plot_data = {k: v for k, v in plot_data.items() if v is not None} + plot_data = pd.DataFrame(plot_data) + + self.plot_data = plot_data + + def determine_attributes(self, palette=None, clims=None, markers=None, + dashes=None, slims=None): + + hue_subset_masks = [] + + if "hue" in self.plot_data: + + hue_data = self.plot_data["hue"] + type = hue_type(hue_data) + + if type == "categorical": + + hue_levels = categorical_order(hue_data) + n_colors = len(hue_levels) + + if isinstance(palette, dict): + missing = set(hue_levels) - set(palette) + if any(missing): + msg = ("palette dictionary is missing keys: {}" + .format(missing)) + raise ValueError(msg) + else: + if palette is None: + + # Determine whether the current palette will have + # enough values If not, we'll default to the husl + # palette so each is distinct + if n_colors <= len(get_color_cycle()): + colors = color_palette(n_colors=n_colors) + else: + colors = husl_palette(n_colors, l=.7) + else: + colors = color_palette(palette, n_colors) + self.palette = dict(zip(hue_levels, colors)) + + + for hue in hue_levels: + hue_subset_masks.append( + (hue, self.plot_data["hue"] == hue) + ) + else: + + if palette is None: + cmap_name = plt.rcParams["image.cmap"] + + elif isinstance(palette, mpl.colors.Colormap): + + pass # TODO + + self.subset_masks = hue_subset_masks + + +class _LinePlotter(_BasicPlotter): + + def __init__(self, + x=None, y=None, hue=None, style=None, size=None, data=None, + x_estimator=None, x_ci=95, y_estimator=None, y_ci=None, + palette=None, clims=None, markers=None, dashes=None, slims=None, + sort=True, errstyle="bars"): + + self.establish_variables(x, y, hue, style, size, data) + self.determine_attributes(palette, clims, markers, dashes, slims) + + self.sort = sort + + + def plot(self, ax, kws): + + orig_color = kws.pop("color", None) + + for (hue, subset_mask) in self.subset_masks: + + subset_data = self.plot_data.loc[subset_mask] + + if self.sort: + subset_data = subset_data.sort_values(["x", "y"]) + + kws["color"] = self.palette[hue] + + ax.plot(subset_data["x"], subset_data["y"], + **kws) + + + + +class _ScatterPlotter(_BasicPlotter): + + def __init__(self): + pass + + def plot(self, ax=None): + pass + + +_basic_docs = dict( + + main_api_narrative=dedent("""\ + """), + +) + + +def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, + x_estimator=None, x_ci=95, y_estimator=None, y_ci=None, + palette=None, clims=None, markers=None, dashes=None, slims=None, + sort=True, errstyle="bars", + ax=None, **kwargs): + + p = _LinePlotter( + x=x, y=y, hue=hue, style=style, size=size, data=data, + x_estimator=x_estimator, x_ci=x_ci, + y_estimator=y_estimator, y_ci=y_ci, + palette=palette, clims=clims, + markers=markers, dashes=dashes, + slims=slims, + sort=sort, errstyle=errstyle + ) + + if ax is None: + ax = plt.gca() + + p.plot(ax, kwargs) + + return ax, p # TODO + + + +def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, + x_estimator=None, x_ci=95, y_estimator=None, y_ci=None, + palette=None, clims=None, markers=None, slims=None, + errstyle="bars", alpha="auto", + ax=None, **kwargs): + + pass diff --git a/seaborn/utils.py b/seaborn/utils.py index db329d6707..3abb5d652d 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -521,6 +521,18 @@ def categorical_order(values, order=None): return list(order) +def hue_type(data): + + data = remove_na(np.asarray(data)) + try: + int_data = data.astype(np.int) + if np.array_equal(data, int_data): + return "discrete" + else: + return "numeric" + except ValueError: + return "categorical" + def get_color_cycle(): if mpl_ge_150: cyl = mpl.rcParams['axes.prop_cycle'] From 0289ea6cb170a6b2f7f265bda5ece18cfcaf4926 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 10 Sep 2017 12:58:16 -0400 Subject: [PATCH 0551/1738] Something of a workable approach to lineplot attributes --- seaborn/basic.py | 141 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 102 insertions(+), 39 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 72e4bf2b21..c38f8ee32a 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -1,3 +1,4 @@ +from itertools import product from textwrap import dedent import numpy as np @@ -10,6 +11,7 @@ from .utils import categorical_order, hue_type, get_color_cycle from .palettes import color_palette, husl_palette + class _BasicPlotter(object): # TODO use different lists for mpl 1 and 2 @@ -22,13 +24,12 @@ def establish_variables(self, x=None, y=None, hue=None, style=None, size=None, data=None): - # Option 1: + # Option 1: # We have a wide-form datast # -------------------------- if x is None and y is None: - # Option 1a: # The input data is a Pandas DataFrame # ------------------------------------ @@ -45,7 +46,7 @@ def establish_variables(self, x=None, y=None, "numeric values.") plot_data = pd.melt(data.assign(x=data.index), "x", - var_name="hue", value_name="y") + var_name="hue", value_name="y") plot_data["style"] = plot_data["hue"] # Option 1b: @@ -60,14 +61,13 @@ def establish_variables(self, x=None, y=None, if hasattr(data, "shape"): - plot_data = pd.DataFrame(data) plot_data = pd.melt(plot_data.assign(x="data.index"), "x", var_name="hue", value_name="y") plot_data["style"] = plot_data["hue"] # The input data is a flat list: - # We will assign a numeric index for x, and use the values for y + # We will assign a numeric index for x and use the values for y elif np.isscalar(data[0]): @@ -86,7 +86,7 @@ def establish_variables(self, x=None, y=None, for i, data_i in enumerate(data) ]) - # Option 2: + # Option 2: # We have long-form data # ---------------------- @@ -107,24 +107,41 @@ def establish_variables(self, x=None, y=None, raise ValueError(err) plot_data = dict(x=x, y=y, hue=hue, style=style, size=size) - plot_data = {k: v for k, v in plot_data.items() if v is not None} plot_data = pd.DataFrame(plot_data) self.plot_data = plot_data - def determine_attributes(self, palette=None, clims=None, markers=None, - dashes=None, slims=None): - hue_subset_masks = [] +class _LinePlotter(_BasicPlotter): - if "hue" in self.plot_data: + def __init__(self, + x=None, y=None, hue=None, style=None, size=None, data=None, + x_estimator=None, x_ci=95, y_estimator=None, y_ci=None, + palette=None, clims=None, + markers=None, dashes=None, slims=None, + sort=True, errstyle="bars"): - hue_data = self.plot_data["hue"] - type = hue_type(hue_data) + self.establish_variables(x, y, hue, style, size, data) + self.determine_attributes(palette, clims, markers, dashes, slims) + + self.sort = sort + + def determine_attributes(self, + palette=None, clims=None, + markers=None, dashes=None, + slims=None): + + data = self.plot_data + + if data["hue"].isnull().all(): + hue_levels = [None] + palette = {} + else: + type = hue_type(data["hue"]) if type == "categorical": - hue_levels = categorical_order(hue_data) + hue_levels = categorical_order(data["hue"]) n_colors = len(hue_levels) if isinstance(palette, dict): @@ -133,9 +150,9 @@ def determine_attributes(self, palette=None, clims=None, markers=None, msg = ("palette dictionary is missing keys: {}" .format(missing)) raise ValueError(msg) - else: + else: if palette is None: - + # Determine whether the current palette will have # enough values If not, we'll default to the husl # palette so each is distinct @@ -145,13 +162,8 @@ def determine_attributes(self, palette=None, clims=None, markers=None, colors = husl_palette(n_colors, l=.7) else: colors = color_palette(palette, n_colors) - self.palette = dict(zip(hue_levels, colors)) + palette = dict(zip(hue_levels, colors)) - - for hue in hue_levels: - hue_subset_masks.append( - (hue, self.plot_data["hue"] == hue) - ) else: if palette is None: @@ -161,41 +173,93 @@ def determine_attributes(self, palette=None, clims=None, markers=None, pass # TODO - self.subset_masks = hue_subset_masks - + if data["size"].isnull().all(): + size_levels = [None] + sizes = {} -class _LinePlotter(_BasicPlotter): + else: + size_levels = categorical_order(data["size"]) - def __init__(self, - x=None, y=None, hue=None, style=None, size=None, data=None, - x_estimator=None, x_ci=95, y_estimator=None, y_ci=None, - palette=None, clims=None, markers=None, dashes=None, slims=None, - sort=True, errstyle="bars"): + if slims is None: + smin, smax = 1, 3 + else: + smin, smax = slims + smax -= smin + norm = mpl.colors.Normalize(data["size"].min(), data["size"].max()) + sizes = {s: smin + (norm(s) * smax) for s in size_levels} - self.establish_variables(x, y, hue, style, size, data) - self.determine_attributes(palette, clims, markers, dashes, slims) + if data["style"].isnull().all(): + style_levels = [None] + dashes = {} + markers = {} - self.sort = sort + else: + style_levels = categorical_order(data["style"]) + + if dashes is True: + # TODO error on too many levels + dashes = dict(zip(style_levels, self.default_dashes)) + elif isinstance(dashes, dict): + # TODO error on missing levels + pass + else: + dashes = dict(zip(style_levels, dashes)) + + if markers is True: + # TODO error on too many levels + markers = dict(zip(style_levels, self.default_markers)) + elif isinstance(markers, dict): + # TODO error on missing levels + pass + else: + markers = dict(zip(style_levels, markers)) + + self.attributes = product(hue_levels, style_levels, size_levels) + + self.palette = palette + self.dashes = dashes + self.markers = markers + self.sizes = sizes def plot(self, ax, kws): orig_color = kws.pop("color", None) + orig_dashes = kws.pop("dashes", None) + orig_marker = kws.pop("marker", None) + orig_linewidth = kws.pop("linewidth", kws.pop("lw", None)) + + kws.setdefault("markeredgewidth", .75) + kws.setdefault("markeredgecolor", "w") - for (hue, subset_mask) in self.subset_masks: + data = self.plot_data + all_true = pd.Series(True, data.index) - subset_data = self.plot_data.loc[subset_mask] + for hue, style, size in self.attributes: + + rows = ( + all_true + & (all_true if hue is None else data["hue"] == hue) + & (all_true if style is None else data["style"] == style) + & (all_true if size is None else data["size"] == size) + ) + + subset_data = data.loc[rows] if self.sort: subset_data = subset_data.sort_values(["x", "y"]) - kws["color"] = self.palette[hue] + kws["color"] = self.palette.get(hue, orig_color) + kws["dashes"] = self.dashes.get(style, orig_dashes) + kws["marker"] = self.markers.get(style, orig_marker) + kws["linewidth"] = self.sizes.get(size, orig_linewidth) + + # TODO handle marker size adjustment + # TODO Add white edges to markers ax.plot(subset_data["x"], subset_data["y"], **kws) - - class _ScatterPlotter(_BasicPlotter): @@ -236,7 +300,6 @@ def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, p.plot(ax, kwargs) return ax, p # TODO - def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, From 498d1eccd19dde797f367b3e66f0bc57e56db29f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 10 Sep 2017 16:20:09 -0400 Subject: [PATCH 0552/1738] Some progress on estimates/cis in lineplot --- seaborn/basic.py | 62 ++++++++++++++++++++++++++++++++++++------------ seaborn/utils.py | 5 +++- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index c38f8ee32a..eb93d504a6 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -8,7 +8,9 @@ from .external.six import string_types +from . import utils from .utils import categorical_order, hue_type, get_color_cycle +from .algorithms import bootstrap from .palettes import color_palette, husl_palette @@ -116,15 +118,18 @@ class _LinePlotter(_BasicPlotter): def __init__(self, x=None, y=None, hue=None, style=None, size=None, data=None, - x_estimator=None, x_ci=95, y_estimator=None, y_ci=None, palette=None, clims=None, markers=None, dashes=None, slims=None, - sort=True, errstyle="bars"): + estimator=None, ci=None, n_boot=None, + sort=True, errstyle="band"): self.establish_variables(x, y, hue, style, size, data) self.determine_attributes(palette, clims, markers, dashes, slims) self.sort = sort + self.estimator = estimator + self.ci = ci + self.n_boot = n_boot def determine_attributes(self, palette=None, clims=None, @@ -200,20 +205,24 @@ def determine_attributes(self, if dashes is True: # TODO error on too many levels dashes = dict(zip(style_levels, self.default_dashes)) - elif isinstance(dashes, dict): + elif dashes and isinstance(dashes, dict): # TODO error on missing levels pass - else: + elif dashes: dashes = dict(zip(style_levels, dashes)) + else: + dashes = {} if markers is True: # TODO error on too many levels markers = dict(zip(style_levels, self.default_markers)) - elif isinstance(markers, dict): + elif markers and isinstance(markers, dict): # TODO error on missing levels pass - else: + elif markers: markers = dict(zip(style_levels, markers)) + else: + markers = {} self.attributes = product(hue_levels, style_levels, size_levels) @@ -222,10 +231,26 @@ def determine_attributes(self, self.markers = markers self.sizes = sizes + def estimate(self, vals, grouper, func, ci): + + n_boot = self.n_boot + + def bootstrapped_cis(vals): + boots = bootstrap(vals, n_boot=n_boot) + cis = utils.ci(boots, ci) + return pd.Series(cis, ["low", "high"]) + + # TODO handle ci="sd" + + grouped = vals.groupby(grouper) + est = grouped.apply(func) + cis = grouped.apply(bootstrapped_cis) + return est.index, est, cis.unstack() + def plot(self, ax, kws): orig_color = kws.pop("color", None) - orig_dashes = kws.pop("dashes", None) + orig_dashes = kws.pop("dashes", (np.inf, 1)) orig_marker = kws.pop("marker", None) orig_linewidth = kws.pop("linewidth", kws.pop("lw", None)) @@ -249,16 +274,25 @@ def plot(self, ax, kws): if self.sort: subset_data = subset_data.sort_values(["x", "y"]) + x, y = subset_data["x"], subset_data["y"] + + if self.estimator is not None: + x, y, y_ci = self.estimate(y, x, self.estimator, self.ci) + else: + y_ci = None + kws["color"] = self.palette.get(hue, orig_color) kws["dashes"] = self.dashes.get(style, orig_dashes) kws["marker"] = self.markers.get(style, orig_marker) kws["linewidth"] = self.sizes.get(size, orig_linewidth) # TODO handle marker size adjustment - # TODO Add white edges to markers - ax.plot(subset_data["x"], subset_data["y"], - **kws) + if y_ci is not None: + ax.fill_between(x, y_ci["low"], y_ci["high"], + color=kws["color"], alpha=.2) + + ax.plot(x, y, **kws) class _ScatterPlotter(_BasicPlotter): @@ -279,18 +313,16 @@ def plot(self, ax=None): def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, - x_estimator=None, x_ci=95, y_estimator=None, y_ci=None, palette=None, clims=None, markers=None, dashes=None, slims=None, - sort=True, errstyle="bars", + estimator=None, ci=95, n_boot=1000, sort=True, errstyle="bars", ax=None, **kwargs): p = _LinePlotter( x=x, y=y, hue=hue, style=style, size=size, data=data, - x_estimator=x_estimator, x_ci=x_ci, - y_estimator=y_estimator, y_ci=y_ci, palette=palette, clims=clims, markers=markers, dashes=dashes, slims=slims, + estimator=estimator, ci=ci, n_boot=n_boot, sort=sort, errstyle=errstyle ) @@ -303,8 +335,8 @@ def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, - x_estimator=None, x_ci=95, y_estimator=None, y_ci=None, palette=None, clims=None, markers=None, slims=None, + x_bins=None, n_bins=None, estimator=None, ci=95, n_boot=1000, errstyle="bars", alpha="auto", ax=None, **kwargs): diff --git a/seaborn/utils.py b/seaborn/utils.py index 3abb5d652d..82a3fcf5f9 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -522,7 +522,7 @@ def categorical_order(values, order=None): def hue_type(data): - + """Determine whether data is discrete, numeric, or categorical.""" data = remove_na(np.asarray(data)) try: int_data = data.astype(np.int) @@ -533,7 +533,9 @@ def hue_type(data): except ValueError: return "categorical" + def get_color_cycle(): + """Return the list of colors in the current matplotlib color cycle.""" if mpl_ge_150: cyl = mpl.rcParams['axes.prop_cycle'] # matplotlib 1.5 verifies that axes.prop_cycle *is* a cycler @@ -543,6 +545,7 @@ def get_color_cycle(): return [x['color'] for x in cyl] except KeyError: pass # just return axes.color style below + return mpl.rcParams['axes.color_cycle'] From 9e1102a014beef11a5e93766837b1cbf073126d8 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 14 Sep 2017 20:26:59 -0400 Subject: [PATCH 0553/1738] Various small fixes to get a reasonable set of examples working --- seaborn/basic.py | 66 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index eb93d504a6..79b3c6b4eb 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -9,7 +9,7 @@ from .external.six import string_types from . import utils -from .utils import categorical_order, hue_type, get_color_cycle +from .utils import categorical_order, remove_na, hue_type, get_color_cycle from .algorithms import bootstrap from .palettes import color_palette, husl_palette @@ -19,8 +19,8 @@ class _BasicPlotter(object): # TODO use different lists for mpl 1 and 2 default_markers = ["o", "s", "D", "v", "^", "p"] marker_scales = {"o": 1, "s": .85, "D": .9, "v": 1.3, "^": 1.3, "p": 1.25} - default_dashes = [(np.inf, 1), (5, 1), (4, 1, 2, 1), - (2, 1), (5, 1, 1, 1), (5, 1, 2, 1, 2, 1)] + default_dashes = [(np.inf, 1), (4, 1), (1, 1), + (4, 1, 2, 1), (5, 1, 1, 1), (5, 1, 2, 1, 2, 1)] def establish_variables(self, x=None, y=None, hue=None, style=None, size=None, @@ -51,6 +51,8 @@ def establish_variables(self, x=None, y=None, var_name="hue", value_name="y") plot_data["style"] = plot_data["hue"] + # TODO accept a dict and try to coerce to a dataframe? + # Option 1b: # The input data is an array or list # ---------------------------------- @@ -64,7 +66,8 @@ def establish_variables(self, x=None, y=None, if hasattr(data, "shape"): plot_data = pd.DataFrame(data) - plot_data = pd.melt(plot_data.assign(x="data.index"), "x", + plot_data = plot_data.assign(x=plot_data.index) + plot_data = pd.melt(plot_data, "x", var_name="hue", value_name="y") plot_data["style"] = plot_data["hue"] @@ -84,7 +87,7 @@ def establish_variables(self, x=None, y=None, plot_data = pd.concat([ pd.DataFrame(dict(x=np.arange(len(data_i)), - y=data_i, hue=i, style=i)) + y=data_i, hue=i, style=i, size=None)) for i, data_i in enumerate(data) ]) @@ -111,6 +114,11 @@ def establish_variables(self, x=None, y=None, plot_data = dict(x=x, y=y, hue=hue, style=style, size=size) plot_data = pd.DataFrame(plot_data) + for attr in ["hue", "style", "size"]: + if attr not in plot_data: + plot_data[attr] = None + + # TODO handle empty data more gracefully self.plot_data = plot_data @@ -119,7 +127,7 @@ class _LinePlotter(_BasicPlotter): def __init__(self, x=None, y=None, hue=None, style=None, size=None, data=None, palette=None, clims=None, - markers=None, dashes=None, slims=None, + dashes=None, markers=None, slims=None, estimator=None, ci=None, n_boot=None, sort=True, errstyle="band"): @@ -169,14 +177,27 @@ def determine_attributes(self, colors = color_palette(palette, n_colors) palette = dict(zip(hue_levels, colors)) - else: + elif type == "discrete": + + # TODO wide-form array data will have an discrete hue type + # but we probably want to default to a categorical palette + + hue_levels = np.arange(data["hue"].min(), + data["hue"].max() + 1) if palette is None: - cmap_name = plt.rcParams["image.cmap"] + vals = np.linspace(0, 1, len(hue_levels)) + colors = mpl.cm.viridis(vals) + palette = dict(zip(hue_levels, colors)) + if palette is None: + cmap_name = plt.rcParams["image.cmap"] elif isinstance(palette, mpl.colors.Colormap): - pass # TODO + else: + vals = np.linspace(0, 1, len(hue_levels)) + colors = mpl.cm.get_cmap(palette)(vals) + palette = dict(zip(hue_levels, colors)) if data["size"].isnull().all(): size_levels = [None] @@ -224,8 +245,10 @@ def determine_attributes(self, else: markers = {} - self.attributes = product(hue_levels, style_levels, size_levels) + # TODO This doesn't work when attributes share a variable + attributes = product(hue_levels, style_levels, size_levels) + self.attributes = attributes self.palette = palette self.dashes = dashes self.markers = markers @@ -236,7 +259,7 @@ def estimate(self, vals, grouper, func, ci): n_boot = self.n_boot def bootstrapped_cis(vals): - boots = bootstrap(vals, n_boot=n_boot) + boots = bootstrap(vals, func=func, n_boot=n_boot) cis = utils.ci(boots, ci) return pd.Series(cis, ["low", "high"]) @@ -244,6 +267,8 @@ def bootstrapped_cis(vals): grouped = vals.groupby(grouper) est = grouped.apply(func) + if ci is None: + return est.index, est, None cis = grouped.apply(bootstrapped_cis) return est.index, est, cis.unstack() @@ -254,8 +279,8 @@ def plot(self, ax, kws): orig_marker = kws.pop("marker", None) orig_linewidth = kws.pop("linewidth", kws.pop("lw", None)) - kws.setdefault("markeredgewidth", .75) - kws.setdefault("markeredgecolor", "w") + kws.setdefault("markeredgewidth", kws.pop("mew", .75)) + kws.setdefault("markeredgecolor", kws.pop("mec", "w")) data = self.plot_data all_true = pd.Series(True, data.index) @@ -271,6 +296,10 @@ def plot(self, ax, kws): subset_data = data.loc[rows] + # TODO dumb way to handle shared attributes + if not len(subset_data): + continue + if self.sort: subset_data = subset_data.sort_values(["x", "y"]) @@ -281,6 +310,8 @@ def plot(self, ax, kws): else: y_ci = None + # TODO convert from None to (inf, 0) for dash spec? + kws["color"] = self.palette.get(hue, orig_color) kws["dashes"] = self.dashes.get(style, orig_dashes) kws["marker"] = self.markers.get(style, orig_marker) @@ -289,6 +320,7 @@ def plot(self, ax, kws): # TODO handle marker size adjustment if y_ci is not None: + # TODO fill_between doesn't follow color cylce with no palette ax.fill_between(x, y_ci["low"], y_ci["high"], color=kws["color"], alpha=.2) @@ -313,14 +345,14 @@ def plot(self, ax=None): def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, - palette=None, clims=None, markers=None, dashes=None, slims=None, - estimator=None, ci=95, n_boot=1000, sort=True, errstyle="bars", + palette=None, clims=None, dashes=True, markers=None, slims=None, + estimator=None, ci=95, n_boot=1000, sort=True, errstyle="band", ax=None, **kwargs): p = _LinePlotter( x=x, y=y, hue=hue, style=style, size=size, data=data, palette=palette, clims=clims, - markers=markers, dashes=dashes, + dashes=dashes, markers=markers, slims=slims, estimator=estimator, ci=ci, n_boot=n_boot, sort=sort, errstyle=errstyle @@ -336,7 +368,7 @@ def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, palette=None, clims=None, markers=None, slims=None, - x_bins=None, n_bins=None, estimator=None, ci=95, n_boot=1000, + x_bins=None, y_bins=None, estimator=None, ci=95, n_boot=1000, errstyle="bars", alpha="auto", ax=None, **kwargs): From cbd7b5faef3f3125608f22b27899ba6d8a1041e5 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 17 Sep 2017 16:31:14 -0400 Subject: [PATCH 0554/1738] Move parse_hue into its own function, lots of other stuff too... --- seaborn/__init__.py | 1 + seaborn/basic.py | 266 +++++++++++++++++++++++++++++--------------- seaborn/utils.py | 2 +- 3 files changed, 179 insertions(+), 90 deletions(-) diff --git a/seaborn/__init__.py b/seaborn/__init__.py index 5fde08572b..4ed49940cf 100644 --- a/seaborn/__init__.py +++ b/seaborn/__init__.py @@ -6,6 +6,7 @@ from .rcmod import * from .utils import * from .palettes import * +from .basic import * from .regression import * from .categorical import * from .distributions import * diff --git a/seaborn/basic.py b/seaborn/basic.py index 79b3c6b4eb..74a5824432 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -5,6 +5,7 @@ import pandas as pd import matplotlib as mpl import matplotlib.pyplot as plt +from matplotlib.collections import LineCollection from .external.six import string_types @@ -14,9 +15,12 @@ from .palettes import color_palette, husl_palette +__all__ = ["lineplot"] + + class _BasicPlotter(object): - # TODO use different lists for mpl 1 and 2 + # TODO use different lists for mpl 1 and 2? default_markers = ["o", "s", "D", "v", "^", "p"] marker_scales = {"o": 1, "s": .85, "D": .9, "v": 1.3, "^": 1.3, "p": 1.25} default_dashes = [(np.inf, 1), (4, 1), (1, 1), @@ -32,6 +36,8 @@ def establish_variables(self, x=None, y=None, if x is None and y is None: + self.input_format = "wide" + # Option 1a: # The input data is a Pandas DataFrame # ------------------------------------ @@ -59,32 +65,32 @@ def establish_variables(self, x=None, y=None, else: - # The input data is an array: - # We will assign a numeric index to x, the values to y, and - # numeric values to both hue and style - if hasattr(data, "shape"): + # The input data is an array(like): + # We assign a numeric index to x, the values to y, and + # numeric values to both hue and style + plot_data = pd.DataFrame(data) plot_data = plot_data.assign(x=plot_data.index) plot_data = pd.melt(plot_data, "x", var_name="hue", value_name="y") plot_data["style"] = plot_data["hue"] - # The input data is a flat list: - # We will assign a numeric index for x and use the values for y - elif np.isscalar(data[0]): + # The input data is a flat list(like): + # We assign a numeric index for x and use the values for y + plot_data = pd.DataFrame(dict(x=np.arange(len(data)), y=data)) - # The input data is a nested list: - # We will assign a numeric index for x, use the values for, y - # and use numieric hue/style identifiers for each entry. - else: + # The input data is a nested list: We will assign a numeric + # index for x, use the values for, y and use numieric + # hue/style identifiers for each entry. + plot_data = pd.concat([ pd.DataFrame(dict(x=np.arange(len(data_i)), y=data_i, hue=i, style=i, size=None)) @@ -95,9 +101,11 @@ def establish_variables(self, x=None, y=None, # We have long-form data # ---------------------- - else: + elif x is not None and y is not None: + + self.input_format = "long" - # See if we need to get variables from `data` + # Use variables as from the dataframe if specified if data is not None: x = data.get(x, x) y = data.get(y, y) @@ -111,14 +119,30 @@ def establish_variables(self, x=None, y=None, err = "Could not interpret input '{}'".format(input) raise ValueError(err) + # Reassemble into a DataFrame plot_data = dict(x=x, y=y, hue=hue, style=style, size=size) plot_data = pd.DataFrame(plot_data) + # Option 3: + # Only one variable arugment + # -------------------------- + + else: + err = ("Either both or neither of `x` and `y` must be specified " + "(but try passing to `data`, which is more flexible).") + raise ValueError(err) + + # ---- Post-processing + + # Assign default values for missing attribute variables for attr in ["hue", "style", "size"]: if attr not in plot_data: plot_data[attr] = None - # TODO handle empty data more gracefully + if not plot_data.size: + err = "Input data (after processing) was empty" + raise ValueError(err) + self.plot_data = plot_data @@ -126,78 +150,28 @@ class _LinePlotter(_BasicPlotter): def __init__(self, x=None, y=None, hue=None, style=None, size=None, data=None, - palette=None, clims=None, - dashes=None, markers=None, slims=None, + palette=None, clim=None, + dashes=None, markers=None, slim=None, estimator=None, ci=None, n_boot=None, - sort=True, errstyle="band"): + sort=True, errstyle=None): self.establish_variables(x, y, hue, style, size, data) - self.determine_attributes(palette, clims, markers, dashes, slims) + self.determine_attributes(palette, clim, markers, dashes, slim) self.sort = sort self.estimator = estimator self.ci = ci self.n_boot = n_boot + self.errstyle = errstyle def determine_attributes(self, - palette=None, clims=None, + palette=None, clim=None, markers=None, dashes=None, - slims=None): + slim=None): data = self.plot_data - if data["hue"].isnull().all(): - hue_levels = [None] - palette = {} - else: - type = hue_type(data["hue"]) - - if type == "categorical": - - hue_levels = categorical_order(data["hue"]) - n_colors = len(hue_levels) - - if isinstance(palette, dict): - missing = set(hue_levels) - set(palette) - if any(missing): - msg = ("palette dictionary is missing keys: {}" - .format(missing)) - raise ValueError(msg) - else: - if palette is None: - - # Determine whether the current palette will have - # enough values If not, we'll default to the husl - # palette so each is distinct - if n_colors <= len(get_color_cycle()): - colors = color_palette(n_colors=n_colors) - else: - colors = husl_palette(n_colors, l=.7) - else: - colors = color_palette(palette, n_colors) - palette = dict(zip(hue_levels, colors)) - - elif type == "discrete": - - # TODO wide-form array data will have an discrete hue type - # but we probably want to default to a categorical palette - - hue_levels = np.arange(data["hue"].min(), - data["hue"].max() + 1) - - if palette is None: - vals = np.linspace(0, 1, len(hue_levels)) - colors = mpl.cm.viridis(vals) - palette = dict(zip(hue_levels, colors)) - - if palette is None: - cmap_name = plt.rcParams["image.cmap"] - elif isinstance(palette, mpl.colors.Colormap): - pass # TODO - else: - vals = np.linspace(0, 1, len(hue_levels)) - colors = mpl.cm.get_cmap(palette)(vals) - palette = dict(zip(hue_levels, colors)) + self.parse_hue(self.plot_data["hue"], palette, clim) if data["size"].isnull().all(): size_levels = [None] @@ -206,10 +180,10 @@ def determine_attributes(self, else: size_levels = categorical_order(data["size"]) - if slims is None: + if slim is None: smin, smax = 1, 3 else: - smin, smax = slims + smin, smax = slim smax -= smin norm = mpl.colors.Normalize(data["size"].min(), data["size"].max()) sizes = {s: smin + (norm(s) * smax) for s in size_levels} @@ -246,31 +220,128 @@ def determine_attributes(self, markers = {} # TODO This doesn't work when attributes share a variable - attributes = product(hue_levels, style_levels, size_levels) + attributes = product(self.hue_levels, + style_levels, size_levels) self.attributes = attributes - self.palette = palette self.dashes = dashes self.markers = markers self.sizes = sizes + def parse_hue(self, data, palette, clim): + """Determine what color palette to use given data characteristics.""" + + if data.isnull().all(): + + # -- Set default values when not using a hue mapping + + hue_levels = [None] + palette = {} + palette_type = None + cmap = None + + else: + + # -- Determine what kind of hue mapping we want + + # (Default to categorical for wide-form inputs because the hue + # variable will usually be default integer index values) + + if self.input_format == "wide": + palette_type = "categorical" + else: + palette_type = hue_type(data) + + # -- Option 1: categorical color palette + + if palette_type == "categorical": + + hue_levels = categorical_order(data) + n_colors = len(hue_levels) + cmap = None + + if isinstance(palette, dict): + + missing = set(hue_levels) - set(palette) + if any(missing): + msg = ("The palette dictionary is missing keys: {}" + .format(missing)) + raise ValueError(msg) + + elif palette is None: + + if n_colors <= len(get_color_cycle()): + colors = color_palette(n_colors=n_colors) + else: + colors = husl_palette(n_colors, l=.7) + + else: + colors = color_palette(palette, n_colors) + + palette = dict(zip(hue_levels, colors)) + + # -- Option 2: sequential color palette + + elif palette_type is not None: + + if palette is None: + cmap = mpl.cm.get_cmap(plt.rcParams["image.cmap"]) + elif isinstance(palette, mpl.colors.Colormap): + cmap = palette + else: + cmap = mpl.cm.get_cmap(palette) + + # -- Option 2a: a discrete colormap + + if palette_type == "discrete": + + if clim is None: + hue_levels = np.arange(data.min(), data.max() + 1) + else: + hue_levels = np.arange(clim[0], clim[1] + 1) + + vals = np.linspace(0, 1, len(hue_levels)) + colors = cmap(vals) + palette = dict(zip(hue_levels, colors)) + + # -- Option 2b: a continuous colormap + + elif palette_type == "continuous": + + hue_levels = data.unique() + vals = mpl.colors.Normalize(*clim)(data) + colors = cmap(vals) + palette = dict(zip(hue_levels, colors)) + + self.hue_levels = hue_levels + self.palette = palette + self.palette_type = palette_type + self.cmap = cmap + def estimate(self, vals, grouper, func, ci): n_boot = self.n_boot def bootstrapped_cis(vals): + if len(vals) == 1: + return None boots = bootstrap(vals, func=func, n_boot=n_boot) cis = utils.ci(boots, ci) return pd.Series(cis, ["low", "high"]) # TODO handle ci="sd" - grouped = vals.groupby(grouper) + grouped = vals.groupby(grouper, sort=self.sort) est = grouped.apply(func) if ci is None: return est.index, est, None cis = grouped.apply(bootstrapped_cis) - return est.index, est, cis.unstack() + if cis.notnull().any(): + cis = cis.unstack() + else: + cis = None + + return est.index, est, cis def plot(self, ax, kws): @@ -300,6 +371,8 @@ def plot(self, ax, kws): if not len(subset_data): continue + subset_data = remove_na(subset_data) + if self.sort: subset_data = subset_data.sort_values(["x", "y"]) @@ -319,12 +392,27 @@ def plot(self, ax, kws): # TODO handle marker size adjustment + line, = ax.plot(x, y, **kws) + line_color = line.get_color() + line_alpha = line.get_alpha() + if y_ci is not None: - # TODO fill_between doesn't follow color cylce with no palette - ax.fill_between(x, y_ci["low"], y_ci["high"], - color=kws["color"], alpha=.2) - ax.plot(x, y, **kws) + if self.errstyle == "band": + + ax.fill_between(x, y_ci["low"], y_ci["high"], + color=line_color, alpha=.2) + + elif self.errstyle == "bars": + + ci_xy = np.empty((len(x), 2, 2)) + ci_xy[:, :, 0] = x[:, np.newaxis] + ci_xy[:, :, 1] = y_ci.values + lines = LineCollection(ci_xy, + color=line_color, + alpha=line_alpha) + ax.add_collection(lines) + ax.autoscale_view() class _ScatterPlotter(_BasicPlotter): @@ -345,15 +433,15 @@ def plot(self, ax=None): def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, - palette=None, clims=None, dashes=True, markers=None, slims=None, - estimator=None, ci=95, n_boot=1000, sort=True, errstyle="band", + palette=None, clim=None, dashes=True, markers=None, slim=None, + estimator=np.mean, ci=95, n_boot=1000, sort=True, errstyle="band", ax=None, **kwargs): p = _LinePlotter( x=x, y=y, hue=hue, style=style, size=size, data=data, - palette=palette, clims=clims, + palette=palette, clim=clim, dashes=dashes, markers=markers, - slims=slims, + slim=slim, estimator=estimator, ci=ci, n_boot=n_boot, sort=sort, errstyle=errstyle ) @@ -367,9 +455,9 @@ def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, - palette=None, clims=None, markers=None, slims=None, + palette=None, clim=None, markers=None, slim=None, x_bins=None, y_bins=None, estimator=None, ci=95, n_boot=1000, - errstyle="bars", alpha="auto", + errstyle="bars", alpha="auto", x_jitter=None, y_jitter=None, ax=None, **kwargs): pass diff --git a/seaborn/utils.py b/seaborn/utils.py index 82a3fcf5f9..616520fb51 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -529,7 +529,7 @@ def hue_type(data): if np.array_equal(data, int_data): return "discrete" else: - return "numeric" + return "continuous" except ValueError: return "categorical" From d4d740c25c132a5cb258cf3f2b0d76ccc5d06ac2 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 17 Sep 2017 16:36:01 -0400 Subject: [PATCH 0555/1738] Slightly different default dashes --- seaborn/basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 74a5824432..75bb9e1698 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -24,7 +24,7 @@ class _BasicPlotter(object): default_markers = ["o", "s", "D", "v", "^", "p"] marker_scales = {"o": 1, "s": .85, "D": .9, "v": 1.3, "^": 1.3, "p": 1.25} default_dashes = [(np.inf, 1), (4, 1), (1, 1), - (4, 1, 2, 1), (5, 1, 1, 1), (5, 1, 2, 1, 2, 1)] + (3, 1, 1.5, 1), (5, 1, 1, 1), (5, 1, 2, 1, 2, 1)] def establish_variables(self, x=None, y=None, hue=None, style=None, size=None, From 8459b6ede4abdbe32c2ccd26115a588c2b81e13f Mon Sep 17 00:00:00 2001 From: mwaskom Date: Mon, 18 Sep 2017 09:44:41 -0400 Subject: [PATCH 0556/1738] More organization of attribute code --- seaborn/basic.py | 140 +++++++++++++++++++++++++++-------------------- 1 file changed, 82 insertions(+), 58 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 75bb9e1698..9ab9786195 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -152,7 +152,7 @@ def __init__(self, x=None, y=None, hue=None, style=None, size=None, data=None, palette=None, clim=None, dashes=None, markers=None, slim=None, - estimator=None, ci=None, n_boot=None, + estimator=None, ci=None, n_boot=None, units=None, sort=True, errstyle=None): self.establish_variables(x, y, hue, style, size, data) @@ -169,64 +169,20 @@ def determine_attributes(self, markers=None, dashes=None, slim=None): - data = self.plot_data + # TODO going to need some way to specify attribute order + # TODO also should things generally be expected to take dicts + # as the most direct way of exact specification? + # TODO also need better names! Naming things is hard. self.parse_hue(self.plot_data["hue"], palette, clim) - - if data["size"].isnull().all(): - size_levels = [None] - sizes = {} - - else: - size_levels = categorical_order(data["size"]) - - if slim is None: - smin, smax = 1, 3 - else: - smin, smax = slim - smax -= smin - norm = mpl.colors.Normalize(data["size"].min(), data["size"].max()) - sizes = {s: smin + (norm(s) * smax) for s in size_levels} - - if data["style"].isnull().all(): - style_levels = [None] - dashes = {} - markers = {} - - else: - - style_levels = categorical_order(data["style"]) - - if dashes is True: - # TODO error on too many levels - dashes = dict(zip(style_levels, self.default_dashes)) - elif dashes and isinstance(dashes, dict): - # TODO error on missing levels - pass - elif dashes: - dashes = dict(zip(style_levels, dashes)) - else: - dashes = {} - - if markers is True: - # TODO error on too many levels - markers = dict(zip(style_levels, self.default_markers)) - elif markers and isinstance(markers, dict): - # TODO error on missing levels - pass - elif markers: - markers = dict(zip(style_levels, markers)) - else: - markers = {} + self.parse_size(self.plot_data["size"], slim) + self.parse_style(self.plot_data["style"], markers, dashes) # TODO This doesn't work when attributes share a variable - attributes = product(self.hue_levels, - style_levels, size_levels) - - self.attributes = attributes - self.dashes = dashes - self.markers = markers - self.sizes = sizes + # (but it is kind of handled in plot()) + self.attributes = product(self.hue_levels, + self.style_levels, + self.size_levels) def parse_hue(self, data, palette, clim): """Determine what color palette to use given data characteristics.""" @@ -291,11 +247,16 @@ def parse_hue(self, data, palette, clim): else: cmap = mpl.cm.get_cmap(palette) + # TODO do we want to do something complicated to ensure contrast + # at the extremes of the colormap against the background? + # -- Option 2a: a discrete colormap if palette_type == "discrete": if clim is None: + # TODO make sure this is handled properly in a + # faceted context hue_levels = np.arange(data.min(), data.max() + 1) else: hue_levels = np.arange(clim[0], clim[1] + 1) @@ -318,6 +279,69 @@ def parse_hue(self, data, palette, clim): self.palette_type = palette_type self.cmap = cmap + def parse_size(self, data, slim): + + # TODO note that as currently written, slim and clim are different + # slim is specified in marker units while clim is specified in data + # units. This needs to be remedied (not sure how). + + if data.isnull().all(): + size_levels = [None] + sizes = {} + + else: + + size_levels = categorical_order(data) + + if slim is None: + # TODO ensure that this works properly in faceted context + smin, smax = 1, 3 + else: + smin, smax = slim + smax -= smin + norm = mpl.colors.Normalize(data.min(), data.max()) + sizes = {s: smin + (norm(s) * smax) for s in size_levels} + + self.size_levels = size_levels + self.sizes = sizes + + def parse_style(self, data, markers, dashes): + + if data.isnull().all(): + style_levels = [None] + dashes = {} + markers = {} + + else: + + style_levels = categorical_order(data) + + if dashes is True: + # TODO error on too many levels + dashes = dict(zip(style_levels, self.default_dashes)) + elif dashes and isinstance(dashes, dict): + # TODO error on missing levels + pass + elif dashes: + dashes = dict(zip(style_levels, dashes)) + else: + dashes = {} + + if markers is True: + # TODO error on too many levels + markers = dict(zip(style_levels, self.default_markers)) + elif markers and isinstance(markers, dict): + # TODO error on missing levels + pass + elif markers: + markers = dict(zip(style_levels, markers)) + else: + markers = {} + + self.style_levels = style_levels + self.dashes = dashes + self.markers = markers + def estimate(self, vals, grouper, func, ci): n_boot = self.n_boot @@ -434,15 +458,15 @@ def plot(self, ax=None): def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, palette=None, clim=None, dashes=True, markers=None, slim=None, - estimator=np.mean, ci=95, n_boot=1000, sort=True, errstyle="band", - ax=None, **kwargs): + estimator=np.mean, ci=95, n_boot=1000, units=None, + sort=True, errstyle="band", ax=None, **kwargs): p = _LinePlotter( x=x, y=y, hue=hue, style=style, size=size, data=data, palette=palette, clim=clim, dashes=dashes, markers=markers, slim=slim, - estimator=estimator, ci=ci, n_boot=n_boot, + estimator=estimator, ci=ci, n_boot=n_boot, units=None, sort=sort, errstyle=errstyle ) From 0f15267f6d6fe3f4c8a5846f53eff54ec8d679c9 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Wed, 27 Sep 2017 16:07:15 -0400 Subject: [PATCH 0557/1738] Better handling of null data --- seaborn/basic.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 9ab9786195..9097e92eda 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -10,7 +10,7 @@ from .external.six import string_types from . import utils -from .utils import categorical_order, remove_na, hue_type, get_color_cycle +from .utils import categorical_order, hue_type, get_color_cycle from .algorithms import bootstrap from .palettes import color_palette, husl_palette @@ -389,14 +389,12 @@ def plot(self, ax, kws): & (all_true if size is None else data["size"] == size) ) - subset_data = data.loc[rows] + subset_data = data.loc[rows, ["x", "y"]].dropna() # TODO dumb way to handle shared attributes if not len(subset_data): continue - subset_data = remove_na(subset_data) - if self.sort: subset_data = subset_data.sort_values(["x", "y"]) From 94d0076da78f45dfcaa3610b8f90465c1c59927d Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 30 Sep 2017 18:00:53 -0400 Subject: [PATCH 0558/1738] Add tests for basic plot input variable parsing --- doc/releases/v0.9.0.txt | 2 +- seaborn/basic.py | 38 +++++++++++++++++--------------------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index c664e6c2e5..83d9c33ecf 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -2,6 +2,6 @@ v0.9.0 (Unreleased) ------------------- -- Final removal of the previously-deprecated ``coefcplot`` and ``interactplot`` functions. +- Final removal of the previously-deprecated ``coefplot`` and ``interactplot`` functions. - Updated the testing infrastructure to execute tests with `pytest ` (although many individual tests still use nose assertion). diff --git a/seaborn/basic.py b/seaborn/basic.py index 9097e92eda..30dc5b2f42 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -44,28 +44,35 @@ def establish_variables(self, x=None, y=None, # We will assign the index to x, the values to y, # and the columns names to both hue and style + # TODO accept a dict and try to coerce to a dataframe? + if isinstance(data, pd.DataFrame): # Enforce numeric values try: data.astype(np.float) except ValueError: - raise ValueError("A wide-form dataframe must have only " - "numeric values.") + err = "A wide-form input must have only numeric values." + raise ValueError(err) plot_data = pd.melt(data.assign(x=data.index), "x", var_name="hue", value_name="y") plot_data["style"] = plot_data["hue"] - # TODO accept a dict and try to coerce to a dataframe? - # Option 1b: # The input data is an array or list # ---------------------------------- else: - if hasattr(data, "shape"): + if np.isscalar(data[0]): + + # The input data is a flat list(like): + # We assign a numeric index for x and use the values for y + + plot_data = pd.DataFrame(dict(x=np.arange(len(data)), + y=data)) + elif hasattr(data, "shape"): # The input data is an array(like): # We assign a numeric index to x, the values to y, and @@ -77,18 +84,10 @@ def establish_variables(self, x=None, y=None, var_name="hue", value_name="y") plot_data["style"] = plot_data["hue"] - elif np.isscalar(data[0]): - - # The input data is a flat list(like): - # We assign a numeric index for x and use the values for y - - plot_data = pd.DataFrame(dict(x=np.arange(len(data)), - y=data)) - else: # The input data is a nested list: We will assign a numeric - # index for x, use the values for, y and use numieric + # index for x, use the values for, y and use numeric # hue/style identifiers for each entry. plot_data = pd.concat([ @@ -382,13 +381,10 @@ def plot(self, ax, kws): for hue, style, size in self.attributes: - rows = ( - all_true - & (all_true if hue is None else data["hue"] == hue) - & (all_true if style is None else data["style"] == style) - & (all_true if size is None else data["size"] == size) - ) - + hue_rows = all_true if hue is None else data["hue"] == hue + style_rows = all_true if style is None else data["style"] == style + size_rows = all_true if size is None else data["size"] == size + rows = hue_rows & style_rows & size_rows subset_data = data.loc[rows, ["x", "y"]].dropna() # TODO dumb way to handle shared attributes From c1a35f42a6bbcc2b24fa350041cc69be930e4a49 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 1 Oct 2017 12:30:16 -0400 Subject: [PATCH 0559/1738] Add tests for lineplot categorical palette --- seaborn/basic.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 30dc5b2f42..14a6ce9631 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -12,7 +12,7 @@ from . import utils from .utils import categorical_order, hue_type, get_color_cycle from .algorithms import bootstrap -from .palettes import color_palette, husl_palette +from .palettes import color_palette __all__ = ["lineplot"] @@ -223,17 +223,17 @@ def parse_hue(self, data, palette, clim): .format(missing)) raise ValueError(msg) - elif palette is None: + else: - if n_colors <= len(get_color_cycle()): - colors = color_palette(n_colors=n_colors) + if palette is None: + if n_colors <= len(get_color_cycle()): + colors = color_palette(None, n_colors) + else: + colors = color_palette("husl", n_colors) else: - colors = husl_palette(n_colors, l=.7) - - else: - colors = color_palette(palette, n_colors) + colors = color_palette(palette, n_colors) - palette = dict(zip(hue_levels, colors)) + palette = dict(zip(hue_levels, colors)) # -- Option 2: sequential color palette @@ -250,6 +250,12 @@ def parse_hue(self, data, palette, clim): # at the extremes of the colormap against the background? # -- Option 2a: a discrete colormap + # TODO is there any real reason to separate discrete/continuous? + # Originally I think the idea was to have the former in a legend + # and the latter in a colorbar. But doing colorbars adds a lot of + # complexity ... perhaps it's easier just to do both in a legend. + # Note that there are also some considerations for using this + # in scatterplot too... if palette_type == "discrete": From cff73638cde07622e2bf7e3f935373ba245f8ead Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 7 Oct 2017 14:49:17 -0400 Subject: [PATCH 0560/1738] Add order parameters and rename limit parameters --- seaborn/basic.py | 64 +++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 14a6ce9631..c90a53429a 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -149,13 +149,16 @@ class _LinePlotter(_BasicPlotter): def __init__(self, x=None, y=None, hue=None, style=None, size=None, data=None, - palette=None, clim=None, - dashes=None, markers=None, slim=None, + palette=None, hue_order=None, hue_limits=None, + dashes=None, markers=None, style_order=None, + size_limits=None, estimator=None, ci=None, n_boot=None, units=None, sort=True, errstyle=None): self.establish_variables(x, y, hue, style, size, data) - self.determine_attributes(palette, clim, markers, dashes, slim) + self.determine_attributes(palette, hue_order, hue_limits, + markers, dashes, style_order, + size_limits) self.sort = sort self.estimator = estimator @@ -164,18 +167,18 @@ def __init__(self, self.errstyle = errstyle def determine_attributes(self, - palette=None, clim=None, - markers=None, dashes=None, - slim=None): + palette=None, hue_order=None, hue_limits=None, + markers=None, dashes=None, style_order=None, + size_limits=None): # TODO going to need some way to specify attribute order # TODO also should things generally be expected to take dicts # as the most direct way of exact specification? # TODO also need better names! Naming things is hard. - self.parse_hue(self.plot_data["hue"], palette, clim) - self.parse_size(self.plot_data["size"], slim) - self.parse_style(self.plot_data["style"], markers, dashes) + self.parse_hue(self.plot_data["hue"], palette, hue_order, hue_limits) + self.parse_style(self.plot_data["style"], markers, dashes, style_order) + self.parse_size(self.plot_data["size"], size_limits) # TODO This doesn't work when attributes share a variable # (but it is kind of handled in plot()) @@ -183,7 +186,7 @@ def determine_attributes(self, self.style_levels, self.size_levels) - def parse_hue(self, data, palette, clim): + def parse_hue(self, data, palette, hue_order, hue_limits): """Determine what color palette to use given data characteristics.""" if data.isnull().all(): @@ -211,7 +214,10 @@ def parse_hue(self, data, palette, clim): if palette_type == "categorical": - hue_levels = categorical_order(data) + if hue_order is None: + hue_levels = categorical_order(data) + else: + hue_levels = hue_order n_colors = len(hue_levels) cmap = None @@ -259,12 +265,12 @@ def parse_hue(self, data, palette, clim): if palette_type == "discrete": - if clim is None: + if hue_limits is None: # TODO make sure this is handled properly in a # faceted context hue_levels = np.arange(data.min(), data.max() + 1) else: - hue_levels = np.arange(clim[0], clim[1] + 1) + hue_levels = np.arange(hue_limits[0], hue_limits[1] + 1) vals = np.linspace(0, 1, len(hue_levels)) colors = cmap(vals) @@ -275,7 +281,7 @@ def parse_hue(self, data, palette, clim): elif palette_type == "continuous": hue_levels = data.unique() - vals = mpl.colors.Normalize(*clim)(data) + vals = mpl.colors.Normalize(*hue_limits)(data) colors = cmap(vals) palette = dict(zip(hue_levels, colors)) @@ -284,11 +290,11 @@ def parse_hue(self, data, palette, clim): self.palette_type = palette_type self.cmap = cmap - def parse_size(self, data, slim): + def parse_size(self, data, size_limits): - # TODO note that as currently written, slim and clim are different - # slim is specified in marker units while clim is specified in data - # units. This needs to be remedied (not sure how). + # TODO note that as currently written, size_limits and hue_limits are + # different size_limits is specified in marker units while hue_limits + # is specified in data units. This needs to be remedied (not sure how). if data.isnull().all(): size_levels = [None] @@ -298,11 +304,11 @@ def parse_size(self, data, slim): size_levels = categorical_order(data) - if slim is None: + if size_limits is None: # TODO ensure that this works properly in faceted context smin, smax = 1, 3 else: - smin, smax = slim + smin, smax = size_limits smax -= smin norm = mpl.colors.Normalize(data.min(), data.max()) sizes = {s: smin + (norm(s) * smax) for s in size_levels} @@ -310,7 +316,7 @@ def parse_size(self, data, slim): self.size_levels = size_levels self.sizes = sizes - def parse_style(self, data, markers, dashes): + def parse_style(self, data, markers, dashes, style_order): if data.isnull().all(): style_levels = [None] @@ -457,15 +463,16 @@ def plot(self, ax=None): def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, - palette=None, clim=None, dashes=True, markers=None, slim=None, + palette=None, hue_order=None, hue_limits=None, + dashes=True, markers=None, style_order=None, size_limits=None, estimator=np.mean, ci=95, n_boot=1000, units=None, sort=True, errstyle="band", ax=None, **kwargs): p = _LinePlotter( x=x, y=y, hue=hue, style=style, size=size, data=data, - palette=palette, clim=clim, - dashes=dashes, markers=markers, - slim=slim, + palette=palette, hue_order=hue_order, hue_limits=hue_limits, + dashes=dashes, markers=markers, style_order=style_order, + size_limits=size_limits, estimator=estimator, ci=ci, n_boot=n_boot, units=None, sort=sort, errstyle=errstyle ) @@ -475,12 +482,13 @@ def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, p.plot(ax, kwargs) - return ax, p # TODO + return ax def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, - palette=None, clim=None, markers=None, slim=None, - x_bins=None, y_bins=None, estimator=None, ci=95, n_boot=1000, + palette=None, hue_order=None, hue_limits=None, + markers=None, size_limits=None, x_bins=None, y_bins=None, + estimator=None, ci=95, n_boot=1000, units=None, errstyle="bars", alpha="auto", x_jitter=None, y_jitter=None, ax=None, **kwargs): From 1763b00efeedcfa5d10f764cd6a3ec8dc360aef4 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 7 Oct 2017 15:54:54 -0400 Subject: [PATCH 0561/1738] Improve hue parsing to include handling numeric hue data --- seaborn/basic.py | 99 +++++++++++++++++++++--------------------------- seaborn/utils.py | 13 ------- 2 files changed, 43 insertions(+), 69 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index c90a53429a..223c78ba4d 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -1,3 +1,4 @@ +from __future__ import division from itertools import product from textwrap import dedent @@ -10,7 +11,7 @@ from .external.six import string_types from . import utils -from .utils import categorical_order, hue_type, get_color_cycle +from .utils import categorical_order, get_color_cycle from .algorithms import bootstrap from .palettes import color_palette @@ -144,6 +145,17 @@ def establish_variables(self, x=None, y=None, self.plot_data = plot_data + def _attribute_type(self, data): + + if self.input_format == "wide": + return "categorical" + else: + try: + data.astype(np.float) + return "numeric" + except ValueError: + return "categorical" + class _LinePlotter(_BasicPlotter): @@ -171,9 +183,6 @@ def determine_attributes(self, markers=None, dashes=None, style_order=None, size_limits=None): - # TODO going to need some way to specify attribute order - # TODO also should things generally be expected to take dicts - # as the most direct way of exact specification? # TODO also need better names! Naming things is hard. self.parse_hue(self.plot_data["hue"], palette, hue_order, hue_limits) @@ -187,39 +196,34 @@ def determine_attributes(self, self.size_levels) def parse_hue(self, data, palette, hue_order, hue_limits): - """Determine what color palette to use given data characteristics.""" - + """Determine what colors to use given data characteristics.""" if data.isnull().all(): - # -- Set default values when not using a hue mapping - + # Set default values when not using a hue mapping hue_levels = [None] palette = {} - palette_type = None + hue_type = None cmap = None else: - # -- Determine what kind of hue mapping we want - - # (Default to categorical for wide-form inputs because the hue - # variable will usually be default integer index values) - - if self.input_format == "wide": - palette_type = "categorical" - else: - palette_type = hue_type(data) + # Determine what kind of hue mapping we want + hue_type = self._attribute_type(data) # -- Option 1: categorical color palette - if palette_type == "categorical": + if hue_type == "categorical": + + # -- Identify the order and name of the levels + cmap = None if hue_order is None: hue_levels = categorical_order(data) else: hue_levels = hue_order n_colors = len(hue_levels) - cmap = None + + # -- Identify the set of colors to use if isinstance(palette, dict): @@ -243,59 +247,42 @@ def parse_hue(self, data, palette, hue_order, hue_limits): # -- Option 2: sequential color palette - elif palette_type is not None: + elif hue_type == "numeric": + # Identify the colormap to use if palette is None: cmap = mpl.cm.get_cmap(plt.rcParams["image.cmap"]) elif isinstance(palette, mpl.colors.Colormap): cmap = palette else: - cmap = mpl.cm.get_cmap(palette) + try: + cmap = mpl.cm.get_cmap(palette) + except (TypeError, ValueError): + cmap = mpl.colors.ListedColormap(color_palette(palette)) # TODO do we want to do something complicated to ensure contrast # at the extremes of the colormap against the background? - # -- Option 2a: a discrete colormap - # TODO is there any real reason to separate discrete/continuous? - # Originally I think the idea was to have the former in a legend - # and the latter in a colorbar. But doing colorbars adds a lot of - # complexity ... perhaps it's easier just to do both in a legend. - # Note that there are also some considerations for using this - # in scatterplot too... - - if palette_type == "discrete": - - if hue_limits is None: - # TODO make sure this is handled properly in a - # faceted context - hue_levels = np.arange(data.min(), data.max() + 1) - else: - hue_levels = np.arange(hue_limits[0], hue_limits[1] + 1) - - vals = np.linspace(0, 1, len(hue_levels)) - colors = cmap(vals) - palette = dict(zip(hue_levels, colors)) - - # -- Option 2b: a continuous colormap + if hue_limits is None: + hue_limits = data.min(), data.max() + else: + hue_min, hue_max = hue_limits + hue_min = data.min() if hue_min is None else hue_min + hue_max = data.max() if hue_max is None else hue_max + hue_limits = hue_min, hue_max - elif palette_type == "continuous": - - hue_levels = data.unique() - vals = mpl.colors.Normalize(*hue_limits)(data) - colors = cmap(vals) - palette = dict(zip(hue_levels, colors)) + hue_levels = list(np.sort(data.unique())) + norm = mpl.colors.Normalize(*hue_limits) + palette = {level: cmap(norm(level)) for level in hue_levels} self.hue_levels = hue_levels + self.hue_limits = hue_limits self.palette = palette - self.palette_type = palette_type + self.hue_type = hue_type self.cmap = cmap def parse_size(self, data, size_limits): - # TODO note that as currently written, size_limits and hue_limits are - # different size_limits is specified in marker units while hue_limits - # is specified in data units. This needs to be remedied (not sure how). - if data.isnull().all(): size_levels = [None] sizes = {} @@ -306,7 +293,7 @@ def parse_size(self, data, size_limits): if size_limits is None: # TODO ensure that this works properly in faceted context - smin, smax = 1, 3 + smin, smax = data.min(), data.max() else: smin, smax = size_limits smax -= smin diff --git a/seaborn/utils.py b/seaborn/utils.py index 616520fb51..89cb9baa47 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -521,19 +521,6 @@ def categorical_order(values, order=None): return list(order) -def hue_type(data): - """Determine whether data is discrete, numeric, or categorical.""" - data = remove_na(np.asarray(data)) - try: - int_data = data.astype(np.int) - if np.array_equal(data, int_data): - return "discrete" - else: - return "continuous" - except ValueError: - return "categorical" - - def get_color_cycle(): """Return the list of colors in the current matplotlib color cycle.""" if mpl_ge_150: From aafff191877d301fbb1376b3c870341f5d674cf7 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 7 Oct 2017 20:57:28 -0400 Subject: [PATCH 0562/1738] Fix CI with nulls --- seaborn/basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 223c78ba4d..5b44bad539 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -346,7 +346,7 @@ def estimate(self, vals, grouper, func, ci): def bootstrapped_cis(vals): if len(vals) == 1: - return None + return pd.Series(index=["low", "high"], dtype=np.float) boots = bootstrap(vals, func=func, n_boot=n_boot) cis = utils.ci(boots, ci) return pd.Series(cis, ["low", "high"]) From 3ce321505d9e56fcf567a7bcd475c2b8dc4ccfef Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 7 Oct 2017 22:01:53 -0400 Subject: [PATCH 0563/1738] Allow specified colors with numeric hue type using list or dict --- seaborn/basic.py | 49 +++++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 5b44bad539..0b682b8acf 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -229,9 +229,8 @@ def parse_hue(self, data, palette, hue_order, hue_limits): missing = set(hue_levels) - set(palette) if any(missing): - msg = ("The palette dictionary is missing keys: {}" - .format(missing)) - raise ValueError(msg) + err = "The palette dictionary is missing keys: {}" + raise ValueError(err.format(missing)) else: @@ -249,31 +248,47 @@ def parse_hue(self, data, palette, hue_order, hue_limits): elif hue_type == "numeric": + hue_levels = list(np.sort(data.unique())) + + # TODO do we want to do something complicated to ensure contrast + # at the extremes of the colormap against the background? + # Identify the colormap to use if palette is None: cmap = mpl.cm.get_cmap(plt.rcParams["image.cmap"]) + elif isinstance(palette, dict): + missing = set(hue_levels) - set(palette) + if any(missing): + err = "The palette dictionary is missing keys: {}" + raise ValueError(err.format(missing)) + cmap = None + elif isinstance(palette, list): + if len(palette) != len(hue_levels): + err = "The palette has the wrong number of colors" + raise ValueError(err) + palette = dict(zip(hue_levels, palette)) + cmap = None elif isinstance(palette, mpl.colors.Colormap): cmap = palette else: try: cmap = mpl.cm.get_cmap(palette) - except (TypeError, ValueError): - cmap = mpl.colors.ListedColormap(color_palette(palette)) + except (ValueError, TypeError): + err = "Palette {} not understood" + raise ValueError(err) - # TODO do we want to do something complicated to ensure contrast - # at the extremes of the colormap against the background? + if cmap is not None: - if hue_limits is None: - hue_limits = data.min(), data.max() - else: - hue_min, hue_max = hue_limits - hue_min = data.min() if hue_min is None else hue_min - hue_max = data.max() if hue_max is None else hue_max - hue_limits = hue_min, hue_max + if hue_limits is None: + hue_limits = data.min(), data.max() + else: + hue_min, hue_max = hue_limits + hue_min = data.min() if hue_min is None else hue_min + hue_max = data.max() if hue_max is None else hue_max + hue_limits = hue_min, hue_max - hue_levels = list(np.sort(data.unique())) - norm = mpl.colors.Normalize(*hue_limits) - palette = {level: cmap(norm(level)) for level in hue_levels} + norm = mpl.colors.Normalize(*hue_limits) + palette = {level: cmap(norm(level)) for level in hue_levels} self.hue_levels = hue_levels self.hue_limits = hue_limits From f133034e4570c9950f43bfe8a046b754b0deaac1 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sat, 7 Oct 2017 22:02:07 -0400 Subject: [PATCH 0564/1738] Add test module for basic plots --- seaborn/tests/test_basic.py | 349 ++++++++++++++++++++++++++++++++++++ 1 file changed, 349 insertions(+) create mode 100644 seaborn/tests/test_basic.py diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py new file mode 100644 index 0000000000..96a0e2d7a0 --- /dev/null +++ b/seaborn/tests/test_basic.py @@ -0,0 +1,349 @@ +from __future__ import division +import numpy as np +import pandas as pd +import matplotlib as mpl +import pytest +from .. import basic +from ..palettes import color_palette +from ..utils import categorical_order + + +class TestBasicPlotter(object): + + @pytest.fixture + def wide_df(self): + + columns = list("abc") + index = np.arange(10, 50, 2) + values = np.random.randn(len(index), len(columns)) + return pd.DataFrame(values, index=index, columns=columns) + + @pytest.fixture + def wide_array(self): + + return np.random.randn(20, 3) + + @pytest.fixture + def flat_array(self): + + return np.random.randn(20) + + @pytest.fixture + def wide_list(self): + + return [np.random.randn(20), np.random.randn(10)] + + @pytest.fixture + def long_df(self): + + n = 100 + rs = np.random.RandomState() + return pd.DataFrame(dict( + x=rs.randint(0, 20, n), + y=rs.randn(n), + a=np.take(list("abc"), rs.randint(0, 3, n)), + b=np.take(list("mnop"), rs.randint(0, 4, n)), + s=np.take([2, 4, 8], rs.randint(0, 3, n)), + )) + + @pytest.fixture + def null_column(self): + + return pd.Series(index=pd.RangeIndex(0, 20)) + + def test_wide_df_variables(self, wide_df): + + p = basic._BasicPlotter() + p.establish_variables(data=wide_df) + assert p.input_format == "wide" + assert len(p.plot_data) == np.product(wide_df.shape) + + x = p.plot_data["x"] + expected_x = np.tile(wide_df.index, wide_df.shape[1]) + assert np.array_equal(x, expected_x) + + y = p.plot_data["y"] + expected_y = wide_df.values.ravel(order="f") + assert np.array_equal(y, expected_y) + + hue = p.plot_data["hue"] + expected_hue = np.repeat(wide_df.columns, wide_df.shape[0]) + assert np.array_equal(hue, expected_hue) + + style = p.plot_data["style"] + expected_style = expected_hue + assert np.array_equal(style, expected_style) + + assert p.plot_data["size"].isnull().all() + + def test_wide_df_variables_check(self, wide_df): + + p = basic._BasicPlotter() + wide_df = wide_df.copy() + wide_df.loc[:, "not_numeric"] = "a" + with pytest.raises(ValueError): + p.establish_variables(data=wide_df) + + def test_wide_array_variables(self, wide_array): + + p = basic._BasicPlotter() + p.establish_variables(data=wide_array) + assert p.input_format == "wide" + assert len(p.plot_data) == np.product(wide_array.shape) + + nrow, ncol = wide_array.shape + + x = p.plot_data["x"] + expected_x = np.tile(np.arange(nrow), ncol) + assert np.array_equal(x, expected_x) + + y = p.plot_data["y"] + expected_y = wide_array.ravel(order="f") + assert np.array_equal(y, expected_y) + + hue = p.plot_data["hue"] + expected_hue = np.repeat(np.arange(ncol), nrow) + assert np.array_equal(hue, expected_hue) + + style = p.plot_data["style"] + expected_style = expected_hue + assert np.array_equal(style, expected_style) + + assert p.plot_data["size"].isnull().all() + + def test_flat_array_variables(self, flat_array): + + p = basic._BasicPlotter() + p.establish_variables(data=flat_array) + assert p.input_format == "wide" + assert len(p.plot_data) == np.product(flat_array.shape) + + x = p.plot_data["x"] + expected_x = np.arange(flat_array.shape[0]) + assert np.array_equal(x, expected_x) + + y = p.plot_data["y"] + expected_y = flat_array + assert np.array_equal(y, expected_y) + + assert p.plot_data["hue"].isnull().all() + assert p.plot_data["style"].isnull().all() + assert p.plot_data["size"].isnull().all() + + def test_wide_list_variables(self, wide_list): + + p = basic._BasicPlotter() + p.establish_variables(data=wide_list) + assert p.input_format == "wide" + assert len(p.plot_data) == sum(len(l) for l in wide_list) + + x = p.plot_data["x"] + expected_x = np.concatenate([np.arange(len(l)) for l in wide_list]) + assert np.array_equal(x, expected_x) + + y = p.plot_data["y"] + expected_y = np.concatenate(wide_list) + assert np.array_equal(y, expected_y) + + hue = p.plot_data["hue"] + expected_hue = np.concatenate([ + np.ones_like(l) * i for i, l in enumerate(wide_list) + ]) + assert np.array_equal(hue, expected_hue) + + style = p.plot_data["style"] + expected_style = expected_hue + assert np.array_equal(style, expected_style) + + assert p.plot_data["size"].isnull().all() + + def test_long_df(self, long_df): + + p = basic._BasicPlotter() + p.establish_variables(x="x", y="y", data=long_df) + assert p.input_format == "long" + + assert np.array_equal(p.plot_data["x"], long_df["x"]) + assert np.array_equal(p.plot_data["y"], long_df["y"]) + for col in ["hue", "style", "size"]: + assert p.plot_data[col].isnull().all() + + p.establish_variables(x=long_df.x, y="y", data=long_df) + assert np.array_equal(p.plot_data["x"], long_df["x"]) + assert np.array_equal(p.plot_data["y"], long_df["y"]) + + p.establish_variables(x="x", y=long_df.y, data=long_df) + assert np.array_equal(p.plot_data["x"], long_df["x"]) + assert np.array_equal(p.plot_data["y"], long_df["y"]) + + p.establish_variables(x="x", y="y", hue="a", data=long_df) + assert np.array_equal(p.plot_data["hue"], long_df["a"]) + for col in ["style", "size"]: + assert p.plot_data[col].isnull().all() + + p.establish_variables(x="x", y="y", hue="a", style="a", data=long_df) + assert np.array_equal(p.plot_data["hue"], long_df["a"]) + assert np.array_equal(p.plot_data["style"], long_df["a"]) + assert p.plot_data["size"].isnull().all() + + p.establish_variables(x="x", y="y", hue="a", style="b", data=long_df) + assert np.array_equal(p.plot_data["hue"], long_df["a"]) + assert np.array_equal(p.plot_data["style"], long_df["b"]) + assert p.plot_data["size"].isnull().all() + + p.establish_variables(x="x", y="y", size="y", data=long_df) + assert np.array_equal(p.plot_data["size"], long_df["y"]) + + def test_bad_input(self, long_df): + + p = basic._BasicPlotter() + + with pytest.raises(ValueError): + p.establish_variables(x=long_df.x) + + with pytest.raises(ValueError): + p.establish_variables(y=long_df.y) + + with pytest.raises(ValueError): + p.establish_variables(x="not_in_df", data=long_df) + + with pytest.raises(ValueError): + p.establish_variables(x="x", y="not_in_df", data=long_df) + + with pytest.raises(ValueError): + p.establish_variables(x="x", y="not_in_df", data=long_df) + + with pytest.raises(ValueError): + p.establish_variables(data=np.array([[], []])) + + +class TestLinePlotter(TestBasicPlotter): + + def test_parse_hue_null(self, wide_df, null_column): + + p = basic._LinePlotter(data=wide_df) + p.parse_hue(null_column, "Blues", None, None) + assert p.hue_levels == [None] + assert p.palette == {} + assert p.hue_type is None + assert p.cmap is None + + def test_parse_hue_categorical(self, wide_df, long_df): + + p = basic._LinePlotter(data=wide_df) + assert p.hue_levels == wide_df.columns.tolist() + assert p.hue_type is "categorical" + assert p.cmap is None + + # Test named palette + palette = "Blues" + expected_colors = color_palette(palette, wide_df.shape[1]) + expected_palette = dict(zip(wide_df.columns, expected_colors)) + p.parse_hue(p.plot_data.hue, palette, None, None) + assert p.palette == expected_palette + + # Test list palette + palette = color_palette("Reds", wide_df.shape[1] + 2) + p.parse_hue(p.plot_data.hue, palette, None, None) + expected_palette = dict(zip(wide_df.columns, palette)) + assert p.palette == expected_palette + + # Test dict palette + colors = color_palette("Set1", 8) + palette = dict(zip(wide_df.columns, colors)) + p.parse_hue(p.plot_data.hue, palette, None, None) + assert p.palette == palette + + # Test dict with missing keys + palette = dict(zip(wide_df.columns[:-1], colors)) + with pytest.raises(ValueError): + p.parse_hue(p.plot_data.hue, palette, None, None) + + # Test hue order + hue_order = ["a", "c", "d"] + p.parse_hue(p.plot_data.hue, None, hue_order, None) + assert p.hue_levels == hue_order + + # Test long data + p = basic._LinePlotter(x="x", y="y", hue="a", data=long_df) + assert p.hue_levels == categorical_order(long_df.a) + assert p.hue_type is "categorical" + assert p.cmap is None + + # Test default palette + p.parse_hue(p.plot_data.hue, None, None, None) + hue_levels = categorical_order(long_df.a) + expected_colors = color_palette(n_colors=len(hue_levels)) + expected_palette = dict(zip(hue_levels, expected_colors)) + assert p.palette == expected_palette + + # Test default palette with many levels + levels = pd.Series(list("abcdefghijklmnopqrstuvwxyz")) + p.parse_hue(levels, None, None, None) + expected_colors = color_palette("husl", n_colors=len(levels)) + expected_palette = dict(zip(levels, expected_colors)) + assert p.palette == expected_palette + + def test_parse_hue_numeric(self, long_df): + + p = basic._LinePlotter(x="x", y="y", hue="s", data=long_df) + hue_levels = list(np.sort(long_df.s.unique())) + assert p.hue_levels == hue_levels + assert p.hue_type is "numeric" + assert p.cmap is mpl.cm.get_cmap(mpl.rcParams["image.cmap"]) + + # Test named colormap + palette = "Purples" + p.parse_hue(p.plot_data.hue, palette, None, None) + assert p.cmap is mpl.cm.get_cmap(palette) + + # Test colormap object + palette = mpl.cm.get_cmap("Greens") + p.parse_hue(p.plot_data.hue, palette, None, None) + assert p.cmap is palette + + # Test default hue limits + p.parse_hue(p.plot_data.hue, None, None, None) + assert p.hue_limits == (p.plot_data.hue.min(), p.plot_data.hue.max()) + + # Test specified hue limits + hue_limits = 1, 4 + p.parse_hue(p.plot_data.hue, None, None, hue_limits) + assert p.hue_limits == hue_limits + + # Test default colormap values + min, max = p.plot_data.hue.min(), p.plot_data.hue.max() + p.parse_hue(p.plot_data.hue, None, None, None) + assert p.palette[min] == pytest.approx(p.cmap(0.0)) + assert p.palette[max] == pytest.approx(p.cmap(1.0)) + + # Test specified colormap values + hue_limits = min - 1, max - 1 + p.parse_hue(p.plot_data.hue, None, None, hue_limits) + norm_min = (min - hue_limits[0]) / (hue_limits[1] - hue_limits[0]) + assert p.palette[min] == pytest.approx(p.cmap(norm_min)) + assert p.palette[max] == pytest.approx(p.cmap(1.0)) + + # Test list of colors + hue_levels = list(np.sort(long_df.s.unique())) + palette = color_palette("Blues", len(hue_levels)) + p.parse_hue(p.plot_data.hue, palette, None, None) + assert p.palette == dict(zip(hue_levels, palette)) + + palette = color_palette("Blues", len(hue_levels) + 1) + with pytest.raises(ValueError): + p.parse_hue(p.plot_data.hue, palette, None, None) + + # Test dictionary of colors + palette = dict(zip(hue_levels, color_palette("Reds"))) + p.parse_hue(p.plot_data.hue, palette, None, None) + assert p.palette == palette + + palette.pop(hue_levels[0]) + with pytest.raises(ValueError): + p.parse_hue(p.plot_data.hue, palette, None, None) + + # Test invalid palette + palette = "not_a_valid_palette" + with pytest.raises(ValueError): + p.parse_hue(p.plot_data.hue, palette, None, None) From 97edfe6c4ef6a32dda9824d2122ba5c5c53b0eea Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 8 Oct 2017 10:57:45 -0400 Subject: [PATCH 0565/1738] Compat with older pandas --- seaborn/basic.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 0b682b8acf..f7fbd68d73 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -56,7 +56,9 @@ def establish_variables(self, x=None, y=None, err = "A wide-form input must have only numeric values." raise ValueError(err) - plot_data = pd.melt(data.assign(x=data.index), "x", + plot_data = data.copy() + plot_data.loc[:, "x"] = data.index + plot_data = pd.melt(plot_data, "x", var_name="hue", value_name="y") plot_data["style"] = plot_data["hue"] From 1e6c680bd5202375124d0b58a3edbb8cce04db29 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 8 Oct 2017 10:57:55 -0400 Subject: [PATCH 0566/1738] Don't raise on empty input --- seaborn/basic.py | 18 +++++++++--------- seaborn/tests/test_basic.py | 14 ++++++++++++-- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index f7fbd68d73..9af4d8e815 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -68,7 +68,11 @@ def establish_variables(self, x=None, y=None, else: - if np.isscalar(data[0]): + if not len(data): + + plot_data = pd.DataFrame(columns=["x", "y"]) + + elif np.isscalar(data[0]): # The input data is a flat list(like): # We assign a numeric index for x and use the values for y @@ -141,10 +145,6 @@ def establish_variables(self, x=None, y=None, if attr not in plot_data: plot_data[attr] = None - if not plot_data.size: - err = "Input data (after processing) was empty" - raise ValueError(err) - self.plot_data = plot_data def _attribute_type(self, data): @@ -188,8 +188,8 @@ def determine_attributes(self, # TODO also need better names! Naming things is hard. self.parse_hue(self.plot_data["hue"], palette, hue_order, hue_limits) - self.parse_style(self.plot_data["style"], markers, dashes, style_order) self.parse_size(self.plot_data["size"], size_limits) + self.parse_style(self.plot_data["style"], markers, dashes, style_order) # TODO This doesn't work when attributes share a variable # (but it is kind of handled in plot()) @@ -282,14 +282,14 @@ def parse_hue(self, data, palette, hue_order, hue_limits): if cmap is not None: if hue_limits is None: - hue_limits = data.min(), data.max() + hue_min, hue_max = data.min(), data.max() else: hue_min, hue_max = hue_limits hue_min = data.min() if hue_min is None else hue_min hue_max = data.max() if hue_max is None else hue_max - hue_limits = hue_min, hue_max - norm = mpl.colors.Normalize(*hue_limits) + hue_limits = hue_min, hue_max + norm = mpl.colors.Normalize(hue_min, hue_max) palette = {level: cmap(norm(level)) for level in hue_levels} self.hue_levels = hue_levels diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 96a0e2d7a0..3fc3634361 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -213,8 +213,14 @@ def test_bad_input(self, long_df): with pytest.raises(ValueError): p.establish_variables(x="x", y="not_in_df", data=long_df) - with pytest.raises(ValueError): - p.establish_variables(data=np.array([[], []])) + def test_empty_input(self): + + p = basic._BasicPlotter() + + p.establish_variables(data=[]) + p.establish_variables(data=np.array([])) + p.establish_variables(data=pd.DataFrame()) + p.establish_variables(x=[], y=[]) class TestLinePlotter(TestBasicPlotter): @@ -347,3 +353,7 @@ def test_parse_hue_numeric(self, long_df): palette = "not_a_valid_palette" with pytest.raises(ValueError): p.parse_hue(p.plot_data.hue, palette, None, None) + + def test_parse_size(self, long_df): + + p = basic._LinePlotter(x="x", y="y", hue="s", data=long_df) From c37ba389492407500de4471b7fe0b4664aa3bcc5 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Thu, 12 Oct 2017 09:02:09 -0400 Subject: [PATCH 0567/1738] Tweak default dashes --- seaborn/basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 9af4d8e815..bba957fe9b 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -24,7 +24,7 @@ class _BasicPlotter(object): # TODO use different lists for mpl 1 and 2? default_markers = ["o", "s", "D", "v", "^", "p"] marker_scales = {"o": 1, "s": .85, "D": .9, "v": 1.3, "^": 1.3, "p": 1.25} - default_dashes = [(np.inf, 1), (4, 1), (1, 1), + default_dashes = [(np.inf, 1), (4, 1.5), (1, 1), (3, 1, 1.5, 1), (5, 1, 1, 1), (5, 1, 2, 1, 2, 1)] def establish_variables(self, x=None, y=None, From 30898bb9c2757f87d560f6eb134100a167f5c2d4 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Sat, 21 Oct 2017 13:02:57 -0400 Subject: [PATCH 0568/1738] Avoid turning off axes autoscaling --- seaborn/distributions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index dde8fea6f4..4e24b0ad95 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -329,9 +329,9 @@ def _univariate_kdeplot(data, shade, vertical, kernel, bw, gridsize, cut, # Set the density axis minimum to 0 xmargin, ymargin = ax.margins() if vertical: - ax.set_xlim(0, max(ax.get_xlim()[1], (1 + xmargin) * x.max())) + ax.set_xlim(0, auto=None) else: - ax.set_ylim(0, max(ax.get_ylim()[1], (1 + ymargin) * y.max())) + ax.set_ylim(0, auto=None) # Draw the legend here handles, labels = ax.get_legend_handles_labels() From efe6543a98bb780979af7cb5811ad6ff86cb38a1 Mon Sep 17 00:00:00 2001 From: mwaskom Date: Sun, 22 Oct 2017 16:40:42 -0400 Subject: [PATCH 0569/1738] Add two TODOs --- seaborn/basic.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/seaborn/basic.py b/seaborn/basic.py index bba957fe9b..64973bcc1e 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -472,6 +472,10 @@ def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, estimator=np.mean, ci=95, n_boot=1000, units=None, sort=True, errstyle="band", ax=None, **kwargs): + # TODO add a "brief_legend" or similar that handles many legend entries + # using a matplotlib ticker ... maybe have it take threshold where you + # flip from itemizing levels to showing level ticks + p = _LinePlotter( x=x, y=y, hue=hue, style=style, size=size, data=data, palette=palette, hue_order=hue_order, hue_limits=hue_limits, @@ -496,4 +500,6 @@ def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, errstyle="bars", alpha="auto", x_jitter=None, y_jitter=None, ax=None, **kwargs): + # TODO auto alpha + pass From a50e143ec22197059afba8bbc39674841e91956d Mon Sep 17 00:00:00 2001 From: joelostblom Date: Sun, 29 Oct 2017 11:04:41 -0400 Subject: [PATCH 0570/1738] No need to define x/ymargin with auto=None --- seaborn/distributions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 4e24b0ad95..131bf69b3f 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -327,7 +327,6 @@ def _univariate_kdeplot(data, shade, vertical, kernel, bw, gridsize, cut, ax.fill_between(x, 0, y, **shade_kws) # Set the density axis minimum to 0 - xmargin, ymargin = ax.margins() if vertical: ax.set_xlim(0, auto=None) else: From 888fc510cb40af544b1f783d2eb19372ef2a6bf4 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 29 Oct 2017 14:13:41 -0400 Subject: [PATCH 0571/1738] Add better handling and tests of size variable --- seaborn/basic.py | 72 ++++++++++++++++++++++++++----------- seaborn/tests/test_basic.py | 47 +++++++++++++++++++----- 2 files changed, 90 insertions(+), 29 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 64973bcc1e..2dc31bce9b 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -147,6 +147,11 @@ def establish_variables(self, x=None, y=None, self.plot_data = plot_data + def _empty_data(self, data): + + empty_data = data.isnull().all() + return empty_data + def _attribute_type(self, data): if self.input_format == "wide": @@ -165,14 +170,14 @@ def __init__(self, x=None, y=None, hue=None, style=None, size=None, data=None, palette=None, hue_order=None, hue_limits=None, dashes=None, markers=None, style_order=None, - size_limits=None, + size_limits=None, size_range=None, estimator=None, ci=None, n_boot=None, units=None, sort=True, errstyle=None): self.establish_variables(x, y, hue, style, size, data) self.determine_attributes(palette, hue_order, hue_limits, markers, dashes, style_order, - size_limits) + size_limits, size_range) self.sort = sort self.estimator = estimator @@ -183,12 +188,12 @@ def __init__(self, def determine_attributes(self, palette=None, hue_order=None, hue_limits=None, markers=None, dashes=None, style_order=None, - size_limits=None): + size_limits=None, size_range=None): # TODO also need better names! Naming things is hard. self.parse_hue(self.plot_data["hue"], palette, hue_order, hue_limits) - self.parse_size(self.plot_data["size"], size_limits) + self.parse_size(self.plot_data["size"], size_limits, size_range) self.parse_style(self.plot_data["style"], markers, dashes, style_order) # TODO This doesn't work when attributes share a variable @@ -199,7 +204,7 @@ def determine_attributes(self, def parse_hue(self, data, palette, hue_order, hue_limits): """Determine what colors to use given data characteristics.""" - if data.isnull().all(): + if self._empty_data(data): # Set default values when not using a hue mapping hue_levels = [None] @@ -289,8 +294,8 @@ def parse_hue(self, data, palette, hue_order, hue_limits): hue_max = data.max() if hue_max is None else hue_max hue_limits = hue_min, hue_max - norm = mpl.colors.Normalize(hue_min, hue_max) - palette = {level: cmap(norm(level)) for level in hue_levels} + normalize = mpl.colors.Normalize(hue_min, hue_max, clip=True) + palette = {l: cmap(normalize(l)) for l in hue_levels} self.hue_levels = hue_levels self.hue_limits = hue_limits @@ -298,31 +303,52 @@ def parse_hue(self, data, palette, hue_order, hue_limits): self.hue_type = hue_type self.cmap = cmap - def parse_size(self, data, size_limits): + def parse_size(self, data, size_limits, size_range): - if data.isnull().all(): + if self._empty_data(data): size_levels = [None] sizes = {} else: + if self._attribute_type(data) == "categorical": + err = "The variable that determines size must be numeric." + raise ValueError(err) + size_levels = categorical_order(data) + if size_range is None: + default = plt.rcParams["lines.linewidth"] + min_width, max_width = default * .5, default * 2 + else: + min_width, max_width = size_range + if size_limits is None: - # TODO ensure that this works properly in faceted context - smin, smax = data.min(), data.max() + s_min, s_max = data.min(), data.max() else: - smin, smax = size_limits - smax -= smin - norm = mpl.colors.Normalize(data.min(), data.max()) - sizes = {s: smin + (norm(s) * smax) for s in size_levels} + s_min, s_max = size_limits + s_min = data.min() if s_min is None else s_min + s_max = data.max() if s_max is None else s_max + + size_limits = s_min, s_max + size_range = min_width, max_width + normalize = mpl.colors.Normalize(s_min, s_max, clip=True) + sizes = {l: min_width + (normalize(l) * max_width) + for l in size_levels} self.size_levels = size_levels + self.size_limits = size_limits + self.size_range = size_range self.sizes = sizes def parse_style(self, data, markers, dashes, style_order): - if data.isnull().all(): + try: + empty_data = data.isnull().all() + except AttributeError: + empty_data = False + + if empty_data: style_levels = [None] dashes = {} markers = {} @@ -468,7 +494,8 @@ def plot(self, ax=None): def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, palette=None, hue_order=None, hue_limits=None, - dashes=True, markers=None, style_order=None, size_limits=None, + dashes=True, markers=None, style_order=None, + size_limits=None, size_range=None, estimator=np.mean, ci=95, n_boot=1000, units=None, sort=True, errstyle="band", ax=None, **kwargs): @@ -480,8 +507,8 @@ def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, x=x, y=y, hue=hue, style=style, size=size, data=data, palette=palette, hue_order=hue_order, hue_limits=hue_limits, dashes=dashes, markers=markers, style_order=style_order, - size_limits=size_limits, - estimator=estimator, ci=ci, n_boot=n_boot, units=None, + size_range=size_range, size_limits=size_limits, + estimator=estimator, ci=ci, n_boot=n_boot, units=units, sort=sort, errstyle=errstyle ) @@ -495,9 +522,12 @@ def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, palette=None, hue_order=None, hue_limits=None, - markers=None, size_limits=None, x_bins=None, y_bins=None, + markers=None, style_order=None, + size_range=None, size_limits=None, + x_bins=None, y_bins=None, estimator=None, ci=95, n_boot=1000, units=None, - errstyle="bars", alpha="auto", x_jitter=None, y_jitter=None, + errstyle="bars", alpha="auto", + x_jitter=None, y_jitter=None, ax=None, **kwargs): # TODO auto alpha diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 3fc3634361..7684f3509e 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -318,17 +318,17 @@ def test_parse_hue_numeric(self, long_df): assert p.hue_limits == hue_limits # Test default colormap values - min, max = p.plot_data.hue.min(), p.plot_data.hue.max() + hmin, hmax = p.plot_data.hue.min(), p.plot_data.hue.max() p.parse_hue(p.plot_data.hue, None, None, None) - assert p.palette[min] == pytest.approx(p.cmap(0.0)) - assert p.palette[max] == pytest.approx(p.cmap(1.0)) + assert p.palette[hmin] == pytest.approx(p.cmap(0.0)) + assert p.palette[hmax] == pytest.approx(p.cmap(1.0)) # Test specified colormap values - hue_limits = min - 1, max - 1 + hue_limits = hmin - 1, hmax - 1 p.parse_hue(p.plot_data.hue, None, None, hue_limits) - norm_min = (min - hue_limits[0]) / (hue_limits[1] - hue_limits[0]) - assert p.palette[min] == pytest.approx(p.cmap(norm_min)) - assert p.palette[max] == pytest.approx(p.cmap(1.0)) + norm_min = (hmin - hue_limits[0]) / (hue_limits[1] - hue_limits[0]) + assert p.palette[hmin] == pytest.approx(p.cmap(norm_min)) + assert p.palette[hmax] == pytest.approx(p.cmap(1.0)) # Test list of colors hue_levels = list(np.sort(long_df.s.unique())) @@ -356,4 +356,35 @@ def test_parse_hue_numeric(self, long_df): def test_parse_size(self, long_df): - p = basic._LinePlotter(x="x", y="y", hue="s", data=long_df) + p = basic._LinePlotter(x="x", y="y", size="s", data=long_df) + + # Test default size limits and range + default_linewidth = mpl.rcParams["lines.linewidth"] + default_limits = p.plot_data["size"].min(), p.plot_data["size"].max() + default_range = .5 * default_linewidth, 2 * default_linewidth + p.parse_size(p.plot_data["size"], None, None) + assert p.size_limits == default_limits + assert p.size_range == default_range + + # Test specified size limits + size_limits = (1, 5) + p.parse_size(p.plot_data["size"], size_limits, None) + assert p.size_limits == size_limits + assert p.size_range == default_range + + # Test specified size range + size_range = (.1, .5) + p.parse_size(p.plot_data["size"], None, size_range) + assert p.size_limits == default_limits + assert p.size_range == size_range + + # Test size values + size_limits = (1, 10) + size_range = (1, 5) + p.parse_size(p.plot_data["size"], size_limits, size_range) + normalize = mpl.colors.Normalize(*size_limits, clip=False) + for level, width in p.sizes.items(): + assert width == size_range[0] + size_range[1] * normalize(level) + + with pytest.raises(ValueError): + basic._LinePlotter(x="x", y="y", size="a", data=long_df) From 0281a9096946aff38f2ceaaab2b6ba0f1b7db853 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 29 Oct 2017 15:47:26 -0400 Subject: [PATCH 0572/1738] Some style tests (but incomplete coverage) --- seaborn/basic.py | 44 ++++++++++++++++++++++--------------- seaborn/tests/test_basic.py | 32 +++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 2dc31bce9b..801e79cf13 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -343,12 +343,18 @@ def parse_size(self, data, size_limits, size_range): def parse_style(self, data, markers, dashes, style_order): - try: - empty_data = data.isnull().all() - except AttributeError: - empty_data = False + def _validate_style_dicts(levels, attrdict, attr): - if empty_data: + if len(levels) > len(attrdict): + err = "Too many levels in `style` for the {}" + raise ValueError(err.format(attr)) + + missing_levels = set(levels) - set(attrdict) + if any(missing_levels): + err = "These `style` levels are missing {}: {}" + raise ValueError(err.format(attr, missing_levels)) + + if self._empty_data(data): style_levels = [None] dashes = {} markers = {} @@ -357,28 +363,30 @@ def parse_style(self, data, markers, dashes, style_order): style_levels = categorical_order(data) - if dashes is True: - # TODO error on too many levels - dashes = dict(zip(style_levels, self.default_dashes)) - elif dashes and isinstance(dashes, dict): - # TODO error on missing levels - pass - elif dashes: - dashes = dict(zip(style_levels, dashes)) - else: - dashes = {} - if markers is True: - # TODO error on too many levels markers = dict(zip(style_levels, self.default_markers)) elif markers and isinstance(markers, dict): - # TODO error on missing levels pass elif markers: markers = dict(zip(style_levels, markers)) + + if markers: + _validate_style_dicts(style_levels, markers, "markers") else: markers = {} + if dashes is True: + dashes = dict(zip(style_levels, self.default_dashes)) + elif dashes and isinstance(dashes, dict): + pass + elif dashes: + dashes = dict(zip(style_levels, dashes)) + + if dashes: + _validate_style_dicts(style_levels, dashes, "dashes") + else: + dashes = {} + self.style_levels = style_levels self.dashes = dashes self.markers = markers diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 7684f3509e..a9f2dd9976 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -388,3 +388,35 @@ def test_parse_size(self, long_df): with pytest.raises(ValueError): basic._LinePlotter(x="x", y="y", size="a", data=long_df) + + def test_parse_style(self, long_df): + + p = basic._LinePlotter(x="x", y="y", style="a", data=long_df) + + # Test defaults + markers, dashes = True, True + p.parse_style(p.plot_data["style"], markers, dashes, None) + assert p.markers == dict(zip(p.style_levels, p.default_markers)) + assert p.dashes == dict(zip(p.style_levels, p.default_dashes)) + + # Test lists + markers, dashes = ["o", "s", "d"], [(1, 0), (1, 1), (2, 1, 3, 1)] + p.parse_style(p.plot_data["style"], markers, dashes, None) + assert p.markers == dict(zip(p.style_levels, markers)) + assert p.dashes == dict(zip(p.style_levels, dashes)) + + # Test dicts + markers = dict(zip(p.style_levels, markers)) + dashes = dict(zip(p.style_levels, dashes)) + p.parse_style(p.plot_data["style"], markers, dashes, None) + assert p.markers == markers + assert p.dashes == dashes + + # Test too many levels with defaults + markers, dashes = False, [(2, 1)] + with pytest.raises(ValueError): + p.parse_style(p.plot_data["style"], markers, dashes, None) + + markers, dashes = ["o", "s"], False + with pytest.raises(ValueError): + p.parse_style(p.plot_data["style"], markers, dashes, None) From c4437818eda3b7ba3608c4bb3aee745227b72fb9 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 30 Oct 2017 09:56:55 -0400 Subject: [PATCH 0573/1738] Simplify style specification check --- seaborn/basic.py | 4 ---- seaborn/tests/test_basic.py | 13 +++++++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 801e79cf13..4010c18c45 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -345,10 +345,6 @@ def parse_style(self, data, markers, dashes, style_order): def _validate_style_dicts(levels, attrdict, attr): - if len(levels) > len(attrdict): - err = "Too many levels in `style` for the {}" - raise ValueError(err.format(attr)) - missing_levels = set(levels) - set(attrdict) if any(missing_levels): err = "These `style` levels are missing {}: {}" diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index a9f2dd9976..3a8baf14aa 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -412,11 +412,20 @@ def test_parse_style(self, long_df): assert p.markers == markers assert p.dashes == dashes - # Test too many levels with defaults + # Test too many levels with style lists + markers, dashes = ["o", "s"], False + with pytest.raises(ValueError): + p.parse_style(p.plot_data["style"], markers, dashes, None) + markers, dashes = False, [(2, 1)] with pytest.raises(ValueError): p.parse_style(p.plot_data["style"], markers, dashes, None) - markers, dashes = ["o", "s"], False + # Test too many levels with style dicts + markers, dashes = {"a": "o", "b": "s"}, False + with pytest.raises(ValueError): + p.parse_style(p.plot_data["style"], markers, dashes, None) + + markers, dashes = False, {"a": (1, 0), "b": (2, 1)} with pytest.raises(ValueError): p.parse_style(p.plot_data["style"], markers, dashes, None) From c26a237f5e15157569bb9aa5b1d1268e3df48ab1 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 30 Oct 2017 10:02:43 -0400 Subject: [PATCH 0574/1738] Compat with older pandas --- seaborn/basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 4010c18c45..90be601055 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -86,7 +86,7 @@ def establish_variables(self, x=None, y=None, # numeric values to both hue and style plot_data = pd.DataFrame(data) - plot_data = plot_data.assign(x=plot_data.index) + plot_data.loc[:, "x"] = plot_data.index plot_data = pd.melt(plot_data, "x", var_name="hue", value_name="y") plot_data["style"] = plot_data["hue"] From 98b5131cb528327a218b404fccb0b5298247beca Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 30 Oct 2017 10:10:26 -0400 Subject: [PATCH 0575/1738] Try bumping minimal pinned versions --- .travis.yml | 2 +- testing/deps_pinned.txt | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index c399ea5fa7..6eac8ddf3d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ install: - conda update conda - source activate testenv - cat testing/deps_${DEPS}.txt testing/utils.txt > deps.txt - - conda install --file deps.txt + - conda install -c conda-forge --file deps.txt - pip install . diff --git a/testing/deps_pinned.txt b/testing/deps_pinned.txt index a1a7609a6f..1ad6eb140b 100644 --- a/testing/deps_pinned.txt +++ b/testing/deps_pinned.txt @@ -1,5 +1,5 @@ -numpy=1.6.2 -scipy=0.11.0 -matplotlib=1.1.1 -pandas=0.12.0 +numpy=1.9.3 +scipy=0.14.0 +matplotlib=1.4.3 +pandas=0.14.1 statsmodels=0.5.0 From 3788034fdb9d534e0c711d96d4116d3c652f163c Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 30 Oct 2017 16:53:06 -0400 Subject: [PATCH 0576/1738] Handle style_order --- seaborn/basic.py | 6 +++++- seaborn/tests/test_basic.py | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 90be601055..46e77628b2 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -351,13 +351,17 @@ def _validate_style_dicts(levels, attrdict, attr): raise ValueError(err.format(attr, missing_levels)) if self._empty_data(data): + style_levels = [None] dashes = {} markers = {} else: - style_levels = categorical_order(data) + if style_order is None: + style_levels = categorical_order(data) + else: + style_levels = style_order if markers is True: markers = dict(zip(style_levels, self.default_markers)) diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 3a8baf14aa..018560b68a 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -412,6 +412,13 @@ def test_parse_style(self, long_df): assert p.markers == markers assert p.dashes == dashes + # Test style order with defaults + style_order = np.take(p.style_levels, [1, 2, 0]) + markers = dashes = True + p.parse_style(p.plot_data["style"], markers, dashes, style_order) + assert p.markers == dict(zip(style_order, p.default_markers)) + assert p.dashes == dict(zip(style_order, p.default_dashes)) + # Test too many levels with style lists markers, dashes = ["o", "s"], False with pytest.raises(ValueError): From 24542c79aafd6d59795204cd7ceeb1a5c5192053 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 30 Oct 2017 17:08:23 -0400 Subject: [PATCH 0577/1738] Don't use conda-forge --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6eac8ddf3d..c399ea5fa7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ install: - conda update conda - source activate testenv - cat testing/deps_${DEPS}.txt testing/utils.txt > deps.txt - - conda install -c conda-forge --file deps.txt + - conda install --file deps.txt - pip install . From c70ca8865ff26d5a09f065112319e83ade2b1d62 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 30 Oct 2017 17:10:25 -0400 Subject: [PATCH 0578/1738] Ignore new pyflakes warning about l as variable --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3cd57d25e9..c22a8ec490 100644 --- a/Makefile +++ b/Makefile @@ -10,4 +10,4 @@ coverage: pytest --doctest-modules --cov=seaborn --cov-config=.coveragerc seaborn lint: - flake8 --exclude seaborn/__init__.py,seaborn/colors/__init__.py,seaborn/cm.py,seaborn/tests,seaborn/external seaborn + flake8 --ignore E121,E123,E126,E226,E24,E704,E741,W503,W504 --exclude seaborn/__init__.py,seaborn/colors/__init__.py,seaborn/cm.py,seaborn/tests,seaborn/external seaborn From 9e2f6a8537e371f21fa072b80e2f1ab2368553db Mon Sep 17 00:00:00 2001 From: Joel Nothman Date: Tue, 31 Oct 2017 16:03:02 +1100 Subject: [PATCH 0579/1738] DOC different metrics for axes in clustermap --- seaborn/matrix.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index f64170bd08..f13c010e54 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -1164,6 +1164,8 @@ def clustermap(data, pivot_kws=None, method='average', metric='euclidean', Distance metric to use for the data. See scipy.spatial.distance.pdist documentation for more options http://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.pdist.html + To use different metrics (or methods) for rows and columns, provide + {row,col}_linkage instead. z_score : int or None, optional Either 0 (rows) or 1 (columns). Whether or not to calculate z-scores for the rows or the columns. Z scores are: z = (x - mean)/std, so From fddba73e8f5f056c5d6f9d6caf1431f2d0d5cceb Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Tue, 31 Oct 2017 10:23:42 -0400 Subject: [PATCH 0580/1738] Include libgfortran in pinned build See https://github.com/ContinuumIO/anaconda-issues/issues/445 --- .travis.yml | 2 -- testing/deps_pinned.txt | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c399ea5fa7..b0c5faac7e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,9 +25,7 @@ before_install: install: - - conda update conda - conda create -n testenv pip python=$PYTHON - - conda update conda - source activate testenv - cat testing/deps_${DEPS}.txt testing/utils.txt > deps.txt - conda install --file deps.txt diff --git a/testing/deps_pinned.txt b/testing/deps_pinned.txt index 1ad6eb140b..03edc51886 100644 --- a/testing/deps_pinned.txt +++ b/testing/deps_pinned.txt @@ -3,3 +3,6 @@ scipy=0.14.0 matplotlib=1.4.3 pandas=0.14.1 statsmodels=0.5.0 + +# Needed due to incomplete scipy recipe +libgfortran=1.0 From 342e002b3a6535971466719c0c5229e390a7934a Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Tue, 31 Oct 2017 11:18:22 -0400 Subject: [PATCH 0581/1738] Pandas compat and bump minimal pandas --- seaborn/tests/test_basic.py | 4 ++-- testing/deps_pinned.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 018560b68a..cd858e565f 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -49,7 +49,7 @@ def long_df(self): @pytest.fixture def null_column(self): - return pd.Series(index=pd.RangeIndex(0, 20)) + return pd.Series(index=np.arange(20)) def test_wide_df_variables(self, wide_df): @@ -67,7 +67,7 @@ def test_wide_df_variables(self, wide_df): assert np.array_equal(y, expected_y) hue = p.plot_data["hue"] - expected_hue = np.repeat(wide_df.columns, wide_df.shape[0]) + expected_hue = np.repeat(wide_df.columns.values, wide_df.shape[0]) assert np.array_equal(hue, expected_hue) style = p.plot_data["style"] diff --git a/testing/deps_pinned.txt b/testing/deps_pinned.txt index 03edc51886..cd5035940e 100644 --- a/testing/deps_pinned.txt +++ b/testing/deps_pinned.txt @@ -1,7 +1,7 @@ numpy=1.9.3 scipy=0.14.0 matplotlib=1.4.3 -pandas=0.14.1 +pandas=0.15.2 statsmodels=0.5.0 # Needed due to incomplete scipy recipe From 147a389d5bf39e548a19b458335ce5a957eb686d Mon Sep 17 00:00:00 2001 From: Joel Nothman Date: Wed, 1 Nov 2017 07:30:07 +1100 Subject: [PATCH 0582/1738] Clarify --- seaborn/matrix.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index f13c010e54..0957278159 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -1164,8 +1164,9 @@ def clustermap(data, pivot_kws=None, method='average', metric='euclidean', Distance metric to use for the data. See scipy.spatial.distance.pdist documentation for more options http://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.pdist.html - To use different metrics (or methods) for rows and columns, provide - {row,col}_linkage instead. + To use different metrics (or methods) for rows and columns, you may + construct each linkage matrix yourself and provide them as + {row,col}_linkage. z_score : int or None, optional Either 0 (rows) or 1 (columns). Whether or not to calculate z-scores for the rows or the columns. Z scores are: z = (x - mean)/std, so From 3debde1626ea6180a026c3b42dd5a08c893dd24e Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 2 Nov 2017 12:06:14 -0400 Subject: [PATCH 0583/1738] Add axes labels --- seaborn/basic.py | 18 ++++++++++++++++++ seaborn/tests/test_basic.py | 17 ++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 46e77628b2..7daccc9d89 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -31,6 +31,9 @@ def establish_variables(self, x=None, y=None, hue=None, style=None, size=None, data=None): + # Initialize label variables + x_label = y_label = None + # Option 1: # We have a wide-form datast # -------------------------- @@ -62,6 +65,8 @@ def establish_variables(self, x=None, y=None, var_name="hue", value_name="y") plot_data["style"] = plot_data["hue"] + x_label = getattr(data.index, "name", None) + # Option 1b: # The input data is an array or list # ---------------------------------- @@ -79,6 +84,7 @@ def establish_variables(self, x=None, y=None, plot_data = pd.DataFrame(dict(x=np.arange(len(data)), y=data)) + elif hasattr(data, "shape"): # The input data is an array(like): @@ -125,6 +131,10 @@ def establish_variables(self, x=None, y=None, err = "Could not interpret input '{}'".format(input) raise ValueError(err) + # Extract variable names + x_label = getattr(x, "name", None) + y_label = getattr(y, "name", None) + # Reassemble into a DataFrame plot_data = dict(x=x, y=y, hue=hue, style=style, size=size) plot_data = pd.DataFrame(plot_data) @@ -145,6 +155,8 @@ def establish_variables(self, x=None, y=None, if attr not in plot_data: plot_data[attr] = None + self.x_label = x_label + self.y_label = y_label self.plot_data = plot_data def _empty_data(self, data): @@ -482,6 +494,12 @@ def plot(self, ax, kws): ax.add_collection(lines) ax.autoscale_view() + # TODO this should go in its own method? + if self.x_label is not None: + ax.set_xlabel(self.x_label) + if self.y_label is not None: + ax.set_ylabel(self.y_label) + class _ScatterPlotter(_BasicPlotter): diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index cd858e565f..f1cac72954 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -14,7 +14,7 @@ class TestBasicPlotter(object): def wide_df(self): columns = list("abc") - index = np.arange(10, 50, 2) + index = pd.Int64Index(np.arange(10, 50, 2), name="wide_index") values = np.random.randn(len(index), len(columns)) return pd.DataFrame(values, index=index, columns=columns) @@ -76,6 +76,9 @@ def test_wide_df_variables(self, wide_df): assert p.plot_data["size"].isnull().all() + assert p.x_label == wide_df.index.name + assert p.y_label is None + def test_wide_df_variables_check(self, wide_df): p = basic._BasicPlotter() @@ -111,6 +114,9 @@ def test_wide_array_variables(self, wide_array): assert p.plot_data["size"].isnull().all() + assert p.x_label is None + assert p.y_label is None + def test_flat_array_variables(self, flat_array): p = basic._BasicPlotter() @@ -130,6 +136,9 @@ def test_flat_array_variables(self, flat_array): assert p.plot_data["style"].isnull().all() assert p.plot_data["size"].isnull().all() + assert p.x_label is None + assert p.y_label is None + def test_wide_list_variables(self, wide_list): p = basic._BasicPlotter() @@ -157,6 +166,9 @@ def test_wide_list_variables(self, wide_list): assert p.plot_data["size"].isnull().all() + assert p.x_label is None + assert p.y_label is None + def test_long_df(self, long_df): p = basic._BasicPlotter() @@ -167,14 +179,17 @@ def test_long_df(self, long_df): assert np.array_equal(p.plot_data["y"], long_df["y"]) for col in ["hue", "style", "size"]: assert p.plot_data[col].isnull().all() + assert (p.x_label, p.y_label) == ("x", "y") p.establish_variables(x=long_df.x, y="y", data=long_df) assert np.array_equal(p.plot_data["x"], long_df["x"]) assert np.array_equal(p.plot_data["y"], long_df["y"]) + assert (p.x_label, p.y_label) == ("x", "y") p.establish_variables(x="x", y=long_df.y, data=long_df) assert np.array_equal(p.plot_data["x"], long_df["x"]) assert np.array_equal(p.plot_data["y"], long_df["y"]) + assert (p.x_label, p.y_label) == ("x", "y") p.establish_variables(x="x", y="y", hue="a", data=long_df) assert np.array_equal(p.plot_data["hue"], long_df["a"]) From efb5860f369e91549512a60c27deb72b13d87582 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 5 Nov 2017 10:17:12 -0500 Subject: [PATCH 0584/1738] Add basic legend code --- seaborn/basic.py | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 7daccc9d89..a58f4abc97 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -184,7 +184,7 @@ def __init__(self, dashes=None, markers=None, style_order=None, size_limits=None, size_range=None, estimator=None, ci=None, n_boot=None, units=None, - sort=True, errstyle=None): + sort=True, errstyle=None, legend=None): self.establish_variables(x, y, hue, style, size, data) self.determine_attributes(palette, hue_order, hue_limits, @@ -316,7 +316,7 @@ def parse_hue(self, data, palette, hue_order, hue_limits): self.cmap = cmap def parse_size(self, data, size_limits, size_range): - + """Determine the widths of the lines.""" if self._empty_data(data): size_levels = [None] sizes = {} @@ -354,7 +354,7 @@ def parse_size(self, data, size_limits, size_range): self.sizes = sizes def parse_style(self, data, markers, dashes, style_order): - + """Determine the markers and line dashes.""" def _validate_style_dicts(levels, attrdict, attr): missing_levels = set(levels) - set(attrdict) @@ -428,7 +428,29 @@ def bootstrapped_cis(vals): return est.index, est, cis - def plot(self, ax, kws): + def add_legend_data(self, ax, legend): + + if legend not in ["brief", "full"]: + err = "`legend` must be 'brief', 'full', or False" + raise ValueError(err) + + for level in self.hue_levels: + if level is not None: + ax.plot([], [], color=self.palette[level], label=level) + + for level in self.size_levels: + if level is not None: + ax.plot([], [], color=".2", + linewidth=self.sizes[level], label=level) + + for level in self.style_levels: + if level is not None: + marker = self.markers.get(level, "") + dashes = self.dashes.get(level, (np.inf, 0)) + ax.plot([], [], color=".2", + marker=marker, dashes=dashes, label=level) + + def plot(self, ax, legend, kws): orig_color = kws.pop("color", None) orig_dashes = kws.pop("dashes", (np.inf, 1)) @@ -472,7 +494,7 @@ def plot(self, ax, kws): # TODO handle marker size adjustment - line, = ax.plot(x, y, **kws) + line, = ax.plot(x.values, y.values, **kws) line_color = line.get_color() line_alpha = line.get_alpha() @@ -500,6 +522,11 @@ def plot(self, ax, kws): if self.y_label is not None: ax.set_ylabel(self.y_label) + # Add legend data + if legend: + self.add_legend_data(ax, legend) + ax.legend() + class _ScatterPlotter(_BasicPlotter): @@ -523,7 +550,8 @@ def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, dashes=True, markers=None, style_order=None, size_limits=None, size_range=None, estimator=np.mean, ci=95, n_boot=1000, units=None, - sort=True, errstyle="band", ax=None, **kwargs): + sort=True, errstyle="band", + legend="brief", ax=None, **kwargs): # TODO add a "brief_legend" or similar that handles many legend entries # using a matplotlib ticker ... maybe have it take threshold where you @@ -535,13 +563,13 @@ def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, dashes=dashes, markers=markers, style_order=style_order, size_range=size_range, size_limits=size_limits, estimator=estimator, ci=ci, n_boot=n_boot, units=units, - sort=sort, errstyle=errstyle + sort=sort, errstyle=errstyle, ) if ax is None: ax = plt.gca() - p.plot(ax, kwargs) + p.plot(ax, legend, kwargs) return ax From def7f145b7d5620caa1b80b79832d6083e92faa3 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Fri, 24 Nov 2017 11:37:00 -0500 Subject: [PATCH 0585/1738] Renaming and reordering --- seaborn/basic.py | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index a58f4abc97..168d59c2cf 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -183,13 +183,13 @@ def __init__(self, palette=None, hue_order=None, hue_limits=None, dashes=None, markers=None, style_order=None, size_limits=None, size_range=None, - estimator=None, ci=None, n_boot=None, units=None, + units=None, estimator=None, ci=None, n_boot=None, sort=True, errstyle=None, legend=None): self.establish_variables(x, y, hue, style, size, data) - self.determine_attributes(palette, hue_order, hue_limits, - markers, dashes, style_order, - size_limits, size_range) + self.determine_semantics(palette, hue_order, hue_limits, + markers, dashes, style_order, + size_limits, size_range) self.sort = sort self.estimator = estimator @@ -197,12 +197,10 @@ def __init__(self, self.n_boot = n_boot self.errstyle = errstyle - def determine_attributes(self, - palette=None, hue_order=None, hue_limits=None, - markers=None, dashes=None, style_order=None, - size_limits=None, size_range=None): - - # TODO also need better names! Naming things is hard. + def determine_semantics(self, + palette=None, hue_order=None, hue_limits=None, + markers=None, dashes=None, style_order=None, + size_limits=None, size_range=None): self.parse_hue(self.plot_data["hue"], palette, hue_order, hue_limits) self.parse_size(self.plot_data["size"], size_limits, size_range) @@ -210,9 +208,9 @@ def determine_attributes(self, # TODO This doesn't work when attributes share a variable # (but it is kind of handled in plot()) - self.attributes = product(self.hue_levels, - self.style_levels, - self.size_levels) + self.semantics = product(self.hue_levels, + self.style_levels, + self.size_levels) def parse_hue(self, data, palette, hue_order, hue_limits): """Determine what colors to use given data characteristics.""" @@ -549,7 +547,7 @@ def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, palette=None, hue_order=None, hue_limits=None, dashes=True, markers=None, style_order=None, size_limits=None, size_range=None, - estimator=np.mean, ci=95, n_boot=1000, units=None, + units=None, estimator=np.mean, ci=95, n_boot=1000, sort=True, errstyle="band", legend="brief", ax=None, **kwargs): @@ -562,7 +560,7 @@ def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, palette=palette, hue_order=hue_order, hue_limits=hue_limits, dashes=dashes, markers=markers, style_order=style_order, size_range=size_range, size_limits=size_limits, - estimator=estimator, ci=ci, n_boot=n_boot, units=units, + units=units, estimator=estimator, ci=ci, n_boot=n_boot, sort=sort, errstyle=errstyle, ) From 74982beeea17b38f951c622ac74864684d46e17a Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Fri, 24 Nov 2017 11:43:35 -0500 Subject: [PATCH 0586/1738] Change where semantic parsing and data subsetting happens --- seaborn/basic.py | 62 ++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 168d59c2cf..813b6b83f0 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -28,7 +28,7 @@ class _BasicPlotter(object): (3, 1, 1.5, 1), (5, 1, 1, 1), (5, 1, 2, 1, 2, 1)] def establish_variables(self, x=None, y=None, - hue=None, style=None, size=None, + hue=None, size=None, style=None, data=None): # Initialize label variables @@ -122,11 +122,11 @@ def establish_variables(self, x=None, y=None, x = data.get(x, x) y = data.get(y, y) hue = data.get(hue, hue) - style = data.get(style, style) size = data.get(size, size) + style = data.get(style, style) # Validate the inputs - for input in [x, y, hue, style, size]: + for input in [x, y, hue, size, style]: if isinstance(input, string_types): err = "Could not interpret input '{}'".format(input) raise ValueError(err) @@ -186,10 +186,11 @@ def __init__(self, units=None, estimator=None, ci=None, n_boot=None, sort=True, errstyle=None, legend=None): - self.establish_variables(x, y, hue, style, size, data) - self.determine_semantics(palette, hue_order, hue_limits, - markers, dashes, style_order, - size_limits, size_range) + self.establish_variables(x, y, hue, size, style, data) + + self.parse_hue(self.plot_data["hue"], palette, hue_order, hue_limits) + self.parse_size(self.plot_data["size"], size_limits, size_range) + self.parse_style(self.plot_data["style"], markers, dashes, style_order) self.sort = sort self.estimator = estimator @@ -197,20 +198,28 @@ def __init__(self, self.n_boot = n_boot self.errstyle = errstyle - def determine_semantics(self, - palette=None, hue_order=None, hue_limits=None, - markers=None, dashes=None, style_order=None, - size_limits=None, size_range=None): + def subset_data(self): - self.parse_hue(self.plot_data["hue"], palette, hue_order, hue_limits) - self.parse_size(self.plot_data["size"], size_limits, size_range) - self.parse_style(self.plot_data["style"], markers, dashes, style_order) + data = self.plot_data + all_true = pd.Series(True, data.index) + + iter_levels = product(self.hue_levels, + self.size_levels, + self.style_levels) + + for hue, size, style in iter_levels: - # TODO This doesn't work when attributes share a variable - # (but it is kind of handled in plot()) - self.semantics = product(self.hue_levels, - self.style_levels, - self.size_levels) + hue_rows = all_true if hue is None else data["hue"] == hue + size_rows = all_true if size is None else data["size"] == size + style_rows = all_true if style is None else data["style"] == style + + rows = hue_rows & size_rows & style_rows + subset_data = data.loc[rows, ["x", "y"]].dropna() + + if not len(subset_data): + continue + + yield (hue, size, style), subset_data def parse_hue(self, data, palette, hue_order, hue_limits): """Determine what colors to use given data characteristics.""" @@ -458,20 +467,7 @@ def plot(self, ax, legend, kws): kws.setdefault("markeredgewidth", kws.pop("mew", .75)) kws.setdefault("markeredgecolor", kws.pop("mec", "w")) - data = self.plot_data - all_true = pd.Series(True, data.index) - - for hue, style, size in self.attributes: - - hue_rows = all_true if hue is None else data["hue"] == hue - style_rows = all_true if style is None else data["style"] == style - size_rows = all_true if size is None else data["size"] == size - rows = hue_rows & style_rows & size_rows - subset_data = data.loc[rows, ["x", "y"]].dropna() - - # TODO dumb way to handle shared attributes - if not len(subset_data): - continue + for (hue, size, style), subset_data in self.subset_data(): if self.sort: subset_data = subset_data.sort_values(["x", "y"]) From dbfb10a18edfea71e71d774fe5c1beb96e38491f Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Fri, 24 Nov 2017 12:00:20 -0500 Subject: [PATCH 0587/1738] Change "dashes" for a solid line --- seaborn/basic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 813b6b83f0..aff415eab4 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -24,7 +24,7 @@ class _BasicPlotter(object): # TODO use different lists for mpl 1 and 2? default_markers = ["o", "s", "D", "v", "^", "p"] marker_scales = {"o": 1, "s": .85, "D": .9, "v": 1.3, "^": 1.3, "p": 1.25} - default_dashes = [(np.inf, 1), (4, 1.5), (1, 1), + default_dashes = ["", (4, 1.5), (1, 1), (3, 1, 1.5, 1), (5, 1, 1, 1), (5, 1, 2, 1, 2, 1)] def establish_variables(self, x=None, y=None, @@ -453,7 +453,7 @@ def add_legend_data(self, ax, legend): for level in self.style_levels: if level is not None: marker = self.markers.get(level, "") - dashes = self.dashes.get(level, (np.inf, 0)) + dashes = self.dashes.get(level, "") ax.plot([], [], color=".2", marker=marker, dashes=dashes, label=level) From d8fbfb4e5450b66c5559be3bd44fe79fecd71c9d Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Fri, 24 Nov 2017 13:42:19 -0500 Subject: [PATCH 0588/1738] Add test for the number of subsets to plot --- seaborn/tests/test_basic.py | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index f1cac72954..4a90f99803 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -451,3 +451,56 @@ def test_parse_style(self, long_df): markers, dashes = False, {"a": (1, 0), "b": (2, 1)} with pytest.raises(ValueError): p.parse_style(p.plot_data["style"], markers, dashes, None) + + def test_subset_data_quantities(self, long_df): + + p = basic._LinePlotter(x="x", y="y", data=long_df) + assert len(list(p.subset_data())) == 1 + + # -- + + var = "a" + n_subsets = len(long_df[var].unique()) + + p = basic._LinePlotter(x="x", y="y", hue=var, data=long_df) + assert len(list(p.subset_data())) == n_subsets + + p = basic._LinePlotter(x="x", y="y", style=var, data=long_df) + assert len(list(p.subset_data())) == n_subsets + + var = "s" + n_subsets = len(long_df[var].unique()) + + p = basic._LinePlotter(x="x", y="y", size=var, data=long_df) + assert len(list(p.subset_data())) == n_subsets + + # -- + + var = "a" + n_subsets = len(long_df[var].unique()) + + p = basic._LinePlotter(x="x", y="y", hue=var, style=var, data=long_df) + assert len(list(p.subset_data())) == n_subsets + + # -- + + var1, var2 = "a", "s" + n_subsets = len(set(list(map(tuple, long_df[[var1, var2]].values)))) + + p = basic._LinePlotter(x="x", y="y", hue=var1, style=var2, + data=long_df) + assert len(list(p.subset_data())) == n_subsets + + p = basic._LinePlotter(x="x", y="y", hue=var1, size=var2, style=var1, + data=long_df) + assert len(list(p.subset_data())) == n_subsets + + # -- + + var1, var2, var3 = "a", "s", "b" + cols = [var1, var2, var3] + n_subsets = len(set(list(map(tuple, long_df[cols].values)))) + + p = basic._LinePlotter(x="x", y="y", hue=var1, size=var2, style=var3, + data=long_df) + assert len(list(p.subset_data())) == n_subsets From 7e8909dbff64401f7e0c01c13056264c711aa983 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Fri, 24 Nov 2017 14:50:44 -0500 Subject: [PATCH 0589/1738] More tests for subset data --- seaborn/basic.py | 8 ++--- seaborn/tests/test_basic.py | 70 +++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index aff415eab4..b58df2ab37 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -479,8 +479,6 @@ def plot(self, ax, legend, kws): else: y_ci = None - # TODO convert from None to (inf, 0) for dash spec? - kws["color"] = self.palette.get(hue, orig_color) kws["dashes"] = self.dashes.get(style, orig_dashes) kws["marker"] = self.markers.get(style, orig_marker) @@ -512,9 +510,11 @@ def plot(self, ax, legend, kws): # TODO this should go in its own method? if self.x_label is not None: - ax.set_xlabel(self.x_label) + x_visible = any(t.get_visible() for t in ax.get_xticklabels()) + ax.set_xlabel(self.x_label, visible=x_visible) if self.y_label is not None: - ax.set_ylabel(self.y_label) + y_visible = any(t.get_visible() for t in ax.get_yticklabels()) + ax.set_ylabel(self.y_label, visible=y_visible) # Add legend data if legend: diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 4a90f99803..14e67d7697 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -504,3 +504,73 @@ def test_subset_data_quantities(self, long_df): p = basic._LinePlotter(x="x", y="y", hue=var1, size=var2, style=var3, data=long_df) assert len(list(p.subset_data())) == n_subsets + + def test_subset_data_keys(self, long_df): + + p = basic._LinePlotter(x="x", y="y", data=long_df) + for (hue, size, style), _ in p.subset_data(): + assert hue is None + assert size is None + assert style is None + + # -- + + var = "a" + + p = basic._LinePlotter(x="x", y="y", hue=var, data=long_df) + for (hue, size, style), _ in p.subset_data(): + assert hue in long_df[var].values + assert size is None + assert style is None + + p = basic._LinePlotter(x="x", y="y", style=var, data=long_df) + for (hue, size, style), _ in p.subset_data(): + assert hue is None + assert size is None + assert style in long_df[var].values + + p = basic._LinePlotter(x="x", y="y", hue=var, style=var, data=long_df) + for (hue, size, style), _ in p.subset_data(): + assert hue in long_df[var].values + assert size is None + assert style in long_df[var].values + + var = "s" + + p = basic._LinePlotter(x="x", y="y", size=var, data=long_df) + for (hue, size, style), _ in p.subset_data(): + assert hue is None + assert size in long_df[var].values + assert style is None + + # -- + + var1, var2 = "a", "s" + + p = basic._LinePlotter(x="x", y="y", hue=var1, size=var2, data=long_df) + for (hue, size, style), _ in p.subset_data(): + assert hue in long_df[var1].values + assert size in long_df[var2].values + assert style is None + + def test_subset_data_values(self, long_df): + + p = basic._LinePlotter(x="x", y="y", data=long_df) + _, data = next(p.subset_data()) + assert np.array_equal(data.values, p.plot_data[["x", "y"]].values) + + p = basic._LinePlotter(x="x", y="y", hue="a", data=long_df) + for (hue, _, _), data in p.subset_data(): + expected = p.plot_data.loc[p.plot_data["hue"] == hue, ["x", "y"]] + assert np.array_equal(data.values, expected.values) + + p = basic._LinePlotter(x="x", y="y", hue="a", style="a", data=long_df) + for (hue, _, _), data in p.subset_data(): + expected = p.plot_data.loc[p.plot_data["hue"] == hue, ["x", "y"]] + assert np.array_equal(data.values, expected.values) + + p = basic._LinePlotter(x="x", y="y", hue="a", size="s", data=long_df) + for (hue, size, _), data in p.subset_data(): + rows = (p.plot_data["hue"] == hue) & (p.plot_data["size"] == size) + expected = p.plot_data.loc[rows, ["x", "y"]] + assert np.array_equal(data.values, expected.values) From f2f29735567eefe9030fdbbcddc199fe8e48e37c Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Fri, 24 Nov 2017 15:04:12 -0500 Subject: [PATCH 0590/1738] Reorganize methods --- seaborn/basic.py | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index b58df2ab37..b7ca7648e2 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -435,28 +435,6 @@ def bootstrapped_cis(vals): return est.index, est, cis - def add_legend_data(self, ax, legend): - - if legend not in ["brief", "full"]: - err = "`legend` must be 'brief', 'full', or False" - raise ValueError(err) - - for level in self.hue_levels: - if level is not None: - ax.plot([], [], color=self.palette[level], label=level) - - for level in self.size_levels: - if level is not None: - ax.plot([], [], color=".2", - linewidth=self.sizes[level], label=level) - - for level in self.style_levels: - if level is not None: - marker = self.markers.get(level, "") - dashes = self.dashes.get(level, "") - ax.plot([], [], color=".2", - marker=marker, dashes=dashes, label=level) - def plot(self, ax, legend, kws): orig_color = kws.pop("color", None) @@ -521,6 +499,28 @@ def plot(self, ax, legend, kws): self.add_legend_data(ax, legend) ax.legend() + def add_legend_data(self, ax, legend): + + if legend not in ["brief", "full"]: + err = "`legend` must be 'brief', 'full', or False" + raise ValueError(err) + + for level in self.hue_levels: + if level is not None: + ax.plot([], [], color=self.palette[level], label=level) + + for level in self.size_levels: + if level is not None: + ax.plot([], [], color=".2", + linewidth=self.sizes[level], label=level) + + for level in self.style_levels: + if level is not None: + marker = self.markers.get(level, "") + dashes = self.dashes.get(level, "") + ax.plot([], [], color=".2", + marker=marker, dashes=dashes, label=level) + class _ScatterPlotter(_BasicPlotter): From 23765d1258e173372f24ce514be1f825fc701502 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Fri, 24 Nov 2017 15:18:16 -0500 Subject: [PATCH 0591/1738] Reorder hue/size/style in more places --- seaborn/basic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index b7ca7648e2..12153ecaf8 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -179,7 +179,7 @@ def _attribute_type(self, data): class _LinePlotter(_BasicPlotter): def __init__(self, - x=None, y=None, hue=None, style=None, size=None, data=None, + x=None, y=None, hue=None, size=None, style=None, data=None, palette=None, hue_order=None, hue_limits=None, dashes=None, markers=None, style_order=None, size_limits=None, size_range=None, @@ -539,7 +539,7 @@ def plot(self, ax=None): ) -def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, +def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, palette=None, hue_order=None, hue_limits=None, dashes=True, markers=None, style_order=None, size_limits=None, size_range=None, @@ -552,7 +552,7 @@ def lineplot(x=None, y=None, hue=None, style=None, size=None, data=None, # flip from itemizing levels to showing level ticks p = _LinePlotter( - x=x, y=y, hue=hue, style=style, size=size, data=data, + x=x, y=y, hue=hue, size=size, style=style, data=data, palette=palette, hue_order=hue_order, hue_limits=hue_limits, dashes=dashes, markers=markers, style_order=style_order, size_range=size_range, size_limits=size_limits, From 5d27bef916d5033d2e3751bd6b873969ad9c19cb Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Fri, 24 Nov 2017 15:18:26 -0500 Subject: [PATCH 0592/1738] Only add legend if we have legend data --- seaborn/basic.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 12153ecaf8..33cfac751e 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -497,10 +497,14 @@ def plot(self, ax, legend, kws): # Add legend data if legend: self.add_legend_data(ax, legend) - ax.legend() + handles, _ = ax.get_legend_handles_labels() + if handles: + ax.legend() def add_legend_data(self, ax, legend): + # TODO doesn't handle overlapping keys (i.e. hue="a", style="a") well + if legend not in ["brief", "full"]: err = "`legend` must be 'brief', 'full', or False" raise ValueError(err) From abb6d6f5611e64f4536b3ad662866c7ff5a29a42 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Fri, 24 Nov 2017 15:41:43 -0500 Subject: [PATCH 0593/1738] Support named methods as estimators and 'sd' as ci --- seaborn/basic.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 33cfac751e..07f8b843a2 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -414,20 +414,35 @@ def estimate(self, vals, grouper, func, ci): n_boot = self.n_boot + # TODO rework this logic, which is a mess + if callable(func): + def f(x): + return func(x) + else: + def f(x): + return getattr(x, func)() + def bootstrapped_cis(vals): if len(vals) == 1: return pd.Series(index=["low", "high"], dtype=np.float) - boots = bootstrap(vals, func=func, n_boot=n_boot) + boots = bootstrap(vals, func=f, n_boot=n_boot) cis = utils.ci(boots, ci) return pd.Series(cis, ["low", "high"]) - # TODO handle ci="sd" - grouped = vals.groupby(grouper, sort=self.sort) - est = grouped.apply(func) + + est = f(grouped) + if ci is None: return est.index, est, None - cis = grouped.apply(bootstrapped_cis) + elif ci == "sd": + sd = grouped.std() + cis = pd.DataFrame(np.c_[est - sd, est + sd], + index=est.index, + columns=["low", "high"]).stack() + else: + cis = grouped.apply(bootstrapped_cis) + if cis.notnull().any(): cis = cis.unstack() else: @@ -547,7 +562,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, palette=None, hue_order=None, hue_limits=None, dashes=True, markers=None, style_order=None, size_limits=None, size_range=None, - units=None, estimator=np.mean, ci=95, n_boot=1000, + units=None, estimator="mean", ci=95, n_boot=1000, sort=True, errstyle="band", legend="brief", ax=None, **kwargs): From 646f7685c579e128c11d6b5896b8bfca559b31ce Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Fri, 24 Nov 2017 16:28:48 -0500 Subject: [PATCH 0594/1738] Add aggregation tests --- seaborn/basic.py | 16 +++++++++--- seaborn/tests/test_basic.py | 50 +++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 07f8b843a2..2520079b92 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -410,21 +410,29 @@ def _validate_style_dicts(levels, attrdict, attr): self.dashes = dashes self.markers = markers - def estimate(self, vals, grouper, func, ci): + def aggregate(self, vals, grouper, func, ci): n_boot = self.n_boot # TODO rework this logic, which is a mess if callable(func): def f(x): - return func(x) + try: + return x.apply(func) + except AttributeError: + return func(x) + else: def f(x): return getattr(x, func)() + null_ci = pd.Series(index=["low", "high"], dtype=np.float) + def bootstrapped_cis(vals): + if len(vals) == 1: - return pd.Series(index=["low", "high"], dtype=np.float) + return null_ci + boots = bootstrap(vals, func=f, n_boot=n_boot) cis = utils.ci(boots, ci) return pd.Series(cis, ["low", "high"]) @@ -468,7 +476,7 @@ def plot(self, ax, legend, kws): x, y = subset_data["x"], subset_data["y"] if self.estimator is not None: - x, y, y_ci = self.estimate(y, x, self.estimator, self.ci) + x, y, y_ci = self.aggregate(y, x, self.estimator, self.ci) else: y_ci = None diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 14e67d7697..0ab7ca64af 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -574,3 +574,53 @@ def test_subset_data_values(self, long_df): rows = (p.plot_data["hue"] == hue) & (p.plot_data["size"] == size) expected = p.plot_data.loc[rows, ["x", "y"]] assert np.array_equal(data.values, expected.values) + + def test_aggregate(self, long_df): + + p = basic._LinePlotter(x="x", y="y", data=long_df) + p.n_boot = 10000 + p.sort = False + + x = pd.Series(np.tile([1, 2], 100)) + y = pd.Series(np.random.randn(200)) + y_mean = y.groupby(x).mean() + + def sem(x): + return np.std(x) / np.sqrt(len(x)) + + y_sem = y.groupby(x).apply(sem) + y_cis = pd.DataFrame(dict(low=y_mean - y_sem, + high=y_mean + y_sem), + columns=["low", "high"]) + + index, est, cis = p.aggregate(y, x, "mean", 68) + assert np.array_equal(index.values, x.unique()) + assert est.index.equals(index) + assert est.values == pytest.approx(y_mean.values) + assert cis.values == pytest.approx(y_cis.values, 4) + + index, est, cis = p.aggregate(y, x, np.mean, 68) + assert np.array_equal(index.values, x.unique()) + assert est.index.equals(index) + assert est.values == pytest.approx(y_mean.values) + assert cis.values == pytest.approx(y_cis.values, 4) + + y_std = y.groupby(x).std() + y_cis = pd.DataFrame(dict(low=y_mean - y_std, + high=y_mean + y_std), + columns=["low", "high"]) + + index, est, cis = p.aggregate(y, x, "mean", "sd") + assert np.array_equal(index.values, x.unique()) + assert est.index.equals(index) + assert est.values == pytest.approx(y_mean.values) + assert cis.values == pytest.approx(y_cis.values) + + index, est, cis = p.aggregate(y, x, "mean", None) + assert cis is None + + x, y = pd.Series([1, 2, 3]), pd.Series([4, 3, 2]) + index, est, cis = p.aggregate(y, x, "mean", 68) + assert np.array_equal(index.values, x) + assert np.array_equal(est.values, y) + assert cis is None From 67f42c0ab25a7a6e114c5612fe0273527eeedb5d Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 25 Nov 2017 11:54:24 -0500 Subject: [PATCH 0595/1738] Refactor guts out of parse_hue to generic functions --- seaborn/basic.py | 192 ++++++++++++++++++++---------------- seaborn/tests/test_basic.py | 8 +- 2 files changed, 110 insertions(+), 90 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 2520079b92..435dc0ba21 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -159,6 +159,87 @@ def establish_variables(self, x=None, y=None, self.y_label = y_label self.plot_data = plot_data + return plot_data + + def categorical_to_palette(self, data, order, palette): + + # -- Identify the order and name of the levels + + if order is None: + levels = categorical_order(data) + else: + levels = order + n_colors = len(levels) + + # -- Identify the set of colors to use + + if isinstance(palette, dict): + + missing = set(levels) - set(palette) + if any(missing): + err = "The palette dictionary is missing keys: {}" + raise ValueError(err.format(missing)) + + else: + + if palette is None: + if n_colors <= len(get_color_cycle()): + colors = color_palette(None, n_colors) + else: + colors = color_palette("husl", n_colors) + else: + colors = color_palette(palette, n_colors) + + palette = dict(zip(levels, colors)) + + return levels, palette + + def numeric_to_palette(self, data, order, palette, limits): + + levels = list(np.sort(data.unique())) + + # TODO do we want to do something complicated to ensure contrast + # at the extremes of the colormap against the background? + + # Identify the colormap to use + if palette is None: + cmap = mpl.cm.get_cmap(plt.rcParams["image.cmap"]) + elif isinstance(palette, dict): + missing = set(levels) - set(palette) + if any(missing): + err = "The palette dictionary is missing keys: {}" + raise ValueError(err.format(missing)) + cmap = None + elif isinstance(palette, list): + if len(palette) != len(levels): + err = "The palette has the wrong number of colors" + raise ValueError(err) + palette = dict(zip(levels, palette)) + cmap = None + elif isinstance(palette, mpl.colors.Colormap): + cmap = palette + else: + try: + cmap = mpl.cm.get_cmap(palette) + except (ValueError, TypeError): + err = "Palette {} not understood" + raise ValueError(err) + + if cmap is not None: + + if limits is None: + hue_min, hue_max = data.min(), data.max() + else: + hue_min, hue_max = limits + hue_min = data.min() if hue_min is None else hue_min + hue_max = data.max() if hue_max is None else hue_max + + limits = hue_min, hue_max + normalize = mpl.colors.Normalize(hue_min, hue_max, clip=True) + palette = {l: cmap(normalize(l)) for l in levels} + + return levels, palette, cmap, limits + def _empty_data(self, data): empty_data = data.isnull().all() @@ -182,15 +263,15 @@ def __init__(self, x=None, y=None, hue=None, size=None, style=None, data=None, palette=None, hue_order=None, hue_limits=None, dashes=None, markers=None, style_order=None, - size_limits=None, size_range=None, + size_limits=None, size_range=None, size_order=None, units=None, estimator=None, ci=None, n_boot=None, sort=True, errstyle=None, legend=None): - self.establish_variables(x, y, hue, size, style, data) + plot_data = self.establish_variables(x, y, hue, size, style, data) - self.parse_hue(self.plot_data["hue"], palette, hue_order, hue_limits) - self.parse_size(self.plot_data["size"], size_limits, size_range) - self.parse_style(self.plot_data["style"], markers, dashes, style_order) + self.parse_hue(plot_data["hue"], palette, hue_order, hue_limits) + self.parse_size(plot_data["size"], size_limits, size_range, size_order) + self.parse_style(plot_data["style"], markers, dashes, style_order) self.sort = sort self.estimator = estimator @@ -221,108 +302,45 @@ def subset_data(self): yield (hue, size, style), subset_data - def parse_hue(self, data, palette, hue_order, hue_limits): + def parse_hue(self, data, palette, order, limits): """Determine what colors to use given data characteristics.""" if self._empty_data(data): # Set default values when not using a hue mapping - hue_levels = [None] + levels = [None] palette = {} - hue_type = None + var_type = None cmap = None else: # Determine what kind of hue mapping we want - hue_type = self._attribute_type(data) + var_type = self._attribute_type(data) # -- Option 1: categorical color palette - if hue_type == "categorical": - - # -- Identify the order and name of the levels + if var_type == "categorical": cmap = None - if hue_order is None: - hue_levels = categorical_order(data) - else: - hue_levels = hue_order - n_colors = len(hue_levels) - - # -- Identify the set of colors to use - - if isinstance(palette, dict): - - missing = set(hue_levels) - set(palette) - if any(missing): - err = "The palette dictionary is missing keys: {}" - raise ValueError(err.format(missing)) - - else: - - if palette is None: - if n_colors <= len(get_color_cycle()): - colors = color_palette(None, n_colors) - else: - colors = color_palette("husl", n_colors) - else: - colors = color_palette(palette, n_colors) - - palette = dict(zip(hue_levels, colors)) + levels, palette = self.categorical_to_palette( + data, order, palette + ) # -- Option 2: sequential color palette - elif hue_type == "numeric": - - hue_levels = list(np.sort(data.unique())) - - # TODO do we want to do something complicated to ensure contrast - # at the extremes of the colormap against the background? - - # Identify the colormap to use - if palette is None: - cmap = mpl.cm.get_cmap(plt.rcParams["image.cmap"]) - elif isinstance(palette, dict): - missing = set(hue_levels) - set(palette) - if any(missing): - err = "The palette dictionary is missing keys: {}" - raise ValueError(err.format(missing)) - cmap = None - elif isinstance(palette, list): - if len(palette) != len(hue_levels): - err = "The palette has the wrong number of colors" - raise ValueError(err) - palette = dict(zip(hue_levels, palette)) - cmap = None - elif isinstance(palette, mpl.colors.Colormap): - cmap = palette - else: - try: - cmap = mpl.cm.get_cmap(palette) - except (ValueError, TypeError): - err = "Palette {} not understood" - raise ValueError(err) - - if cmap is not None: - - if hue_limits is None: - hue_min, hue_max = data.min(), data.max() - else: - hue_min, hue_max = hue_limits - hue_min = data.min() if hue_min is None else hue_min - hue_max = data.max() if hue_max is None else hue_max + elif var_type == "numeric": - hue_limits = hue_min, hue_max - normalize = mpl.colors.Normalize(hue_min, hue_max, clip=True) - palette = {l: cmap(normalize(l)) for l in hue_levels} + levels, palette, cmap, limits = self.numeric_to_palette( + data, order, palette, limits + ) - self.hue_levels = hue_levels - self.hue_limits = hue_limits + self.hue_levels = levels + self.hue_limits = limits + self.hue_type = var_type self.palette = palette - self.hue_type = hue_type self.cmap = cmap - def parse_size(self, data, size_limits, size_range): + def parse_size(self, data, size_limits, size_range, size_order): """Determine the widths of the lines.""" if self._empty_data(data): size_levels = [None] @@ -331,6 +349,8 @@ def parse_size(self, data, size_limits, size_range): else: if self._attribute_type(data) == "categorical": + # TODO be more flexible, it could be fine to do heavy and light + # lines for categorical variables err = "The variable that determines size must be numeric." raise ValueError(err) @@ -569,7 +589,7 @@ def plot(self, ax=None): def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, palette=None, hue_order=None, hue_limits=None, dashes=True, markers=None, style_order=None, - size_limits=None, size_range=None, + size_limits=None, size_range=None, size_order=None, units=None, estimator="mean", ci=95, n_boot=1000, sort=True, errstyle="band", legend="brief", ax=None, **kwargs): @@ -582,7 +602,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, x=x, y=y, hue=hue, size=size, style=style, data=data, palette=palette, hue_order=hue_order, hue_limits=hue_limits, dashes=dashes, markers=markers, style_order=style_order, - size_range=size_range, size_limits=size_limits, + size_range=size_range, size_limits=size_limits, size_order=size_order, units=units, estimator=estimator, ci=ci, n_boot=n_boot, sort=sort, errstyle=errstyle, ) diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 0ab7ca64af..37a9fe1385 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -377,26 +377,26 @@ def test_parse_size(self, long_df): default_linewidth = mpl.rcParams["lines.linewidth"] default_limits = p.plot_data["size"].min(), p.plot_data["size"].max() default_range = .5 * default_linewidth, 2 * default_linewidth - p.parse_size(p.plot_data["size"], None, None) + p.parse_size(p.plot_data["size"], None, None, None) assert p.size_limits == default_limits assert p.size_range == default_range # Test specified size limits size_limits = (1, 5) - p.parse_size(p.plot_data["size"], size_limits, None) + p.parse_size(p.plot_data["size"], size_limits, None, None) assert p.size_limits == size_limits assert p.size_range == default_range # Test specified size range size_range = (.1, .5) - p.parse_size(p.plot_data["size"], None, size_range) + p.parse_size(p.plot_data["size"], None, size_range, None) assert p.size_limits == default_limits assert p.size_range == size_range # Test size values size_limits = (1, 10) size_range = (1, 5) - p.parse_size(p.plot_data["size"], size_limits, size_range) + p.parse_size(p.plot_data["size"], size_limits, size_range, None) normalize = mpl.colors.Normalize(*size_limits, clip=False) for level, width in p.sizes.items(): assert width == size_range[0] + size_range[1] * normalize(level) From 32cc2eb95e214d721ea7c05de4d896e5a96a6b4e Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 25 Nov 2017 12:10:27 -0500 Subject: [PATCH 0596/1738] Refactor parse_style method in LinePlotter --- seaborn/basic.py | 74 +++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 435dc0ba21..c6aa45952b 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -22,6 +22,7 @@ class _BasicPlotter(object): # TODO use different lists for mpl 1 and 2? + # We could use "line art glyphs" (e.g. "P") on mpl 2 default_markers = ["o", "s", "D", "v", "^", "p"] marker_scales = {"o": 1, "s": .85, "D": .9, "v": 1.3, "^": 1.3, "p": 1.25} default_dashes = ["", (4, 1.5), (1, 1), @@ -30,7 +31,7 @@ class _BasicPlotter(object): def establish_variables(self, x=None, y=None, hue=None, size=None, style=None, data=None): - + """Parse the inputs to define data for plotting.""" # Initialize label variables x_label = y_label = None @@ -162,7 +163,7 @@ def establish_variables(self, x=None, y=None, return plot_data def categorical_to_palette(self, data, order, palette): - + """Determine colors when the hue variable is qualitative.""" # -- Identify the order and name of the levels if order is None: @@ -195,7 +196,7 @@ def categorical_to_palette(self, data, order, palette): return levels, palette def numeric_to_palette(self, data, order, palette, limits): - + """Determine colors when the hue variable is quantitative.""" levels = list(np.sort(data.unique())) # TODO do we want to do something complicated to ensure contrast @@ -240,6 +241,25 @@ def numeric_to_palette(self, data, order, palette, limits): return levels, palette, cmap, limits + def style_to_attributes(self, levels, style, defaults, name): + """Convert a style argument to a dict of matplotlib attributes.""" + if style is True: + attrdict = dict(zip(levels, defaults)) + elif style and isinstance(style, dict): + attrdict = style + elif style: + attrdict = dict(zip(levels, style)) + else: + attrdict = {} + + if attrdict: + missing_levels = set(levels) - set(attrdict) + if any(missing_levels): + err = "These `style` levels are missing {}: {}" + raise ValueError(err.format(name, missing_levels)) + + return attrdict + def _empty_data(self, data): empty_data = data.isnull().all() @@ -380,53 +400,31 @@ def parse_size(self, data, size_limits, size_range, size_order): self.size_range = size_range self.sizes = sizes - def parse_style(self, data, markers, dashes, style_order): + def parse_style(self, data, markers, dashes, order): """Determine the markers and line dashes.""" - def _validate_style_dicts(levels, attrdict, attr): - - missing_levels = set(levels) - set(attrdict) - if any(missing_levels): - err = "These `style` levels are missing {}: {}" - raise ValueError(err.format(attr, missing_levels)) if self._empty_data(data): - style_levels = [None] + levels = [None] dashes = {} markers = {} else: - if style_order is None: - style_levels = categorical_order(data) - else: - style_levels = style_order - - if markers is True: - markers = dict(zip(style_levels, self.default_markers)) - elif markers and isinstance(markers, dict): - pass - elif markers: - markers = dict(zip(style_levels, markers)) - - if markers: - _validate_style_dicts(style_levels, markers, "markers") + if order is None: + levels = categorical_order(data) else: - markers = {} + levels = order - if dashes is True: - dashes = dict(zip(style_levels, self.default_dashes)) - elif dashes and isinstance(dashes, dict): - pass - elif dashes: - dashes = dict(zip(style_levels, dashes)) + markers = self.style_to_attributes( + levels, markers, self.default_markers, "markers" + ) - if dashes: - _validate_style_dicts(style_levels, dashes, "dashes") - else: - dashes = {} + dashes = self.style_to_attributes( + levels, dashes, self.default_dashes, "dashes" + ) - self.style_levels = style_levels + self.style_levels = levels self.dashes = dashes self.markers = markers @@ -602,7 +600,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, x=x, y=y, hue=hue, size=size, style=style, data=data, palette=palette, hue_order=hue_order, hue_limits=hue_limits, dashes=dashes, markers=markers, style_order=style_order, - size_range=size_range, size_limits=size_limits, size_order=size_order, + size_limits=size_limits, size_range=size_range, size_order=size_order, units=units, estimator=estimator, ci=ci, n_boot=n_boot, sort=sort, errstyle=errstyle, ) From 5f0c995397cc4f8e542dc10aea72dc0bbd6802d1 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 25 Nov 2017 16:22:32 -0500 Subject: [PATCH 0597/1738] Allow non-numeric size argument in lineplot --- seaborn/basic.py | 91 +++++++++++++++++++++++-------------- seaborn/tests/test_basic.py | 55 ++++++++++++++++------ 2 files changed, 97 insertions(+), 49 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index c6aa45952b..666fdb37cf 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -282,15 +282,15 @@ class _LinePlotter(_BasicPlotter): def __init__(self, x=None, y=None, hue=None, size=None, style=None, data=None, palette=None, hue_order=None, hue_limits=None, + sizes=None, size_order=None, size_limits=None, dashes=None, markers=None, style_order=None, - size_limits=None, size_range=None, size_order=None, units=None, estimator=None, ci=None, n_boot=None, sort=True, errstyle=None, legend=None): plot_data = self.establish_variables(x, y, hue, size, style, data) self.parse_hue(plot_data["hue"], palette, hue_order, hue_limits) - self.parse_size(plot_data["size"], size_limits, size_range, size_order) + self.parse_size(plot_data["size"], sizes, size_order, size_limits) self.parse_style(plot_data["style"], markers, dashes, style_order) self.sort = sort @@ -300,7 +300,7 @@ def __init__(self, self.errstyle = errstyle def subset_data(self): - + """Return (x, y) data for each subset defined by semantics.""" data = self.plot_data all_true = pd.Series(True, data.index) @@ -360,45 +360,66 @@ def parse_hue(self, data, palette, order, limits): self.palette = palette self.cmap = cmap - def parse_size(self, data, size_limits, size_range, size_order): - """Determine the widths of the lines.""" + def parse_size(self, data, sizes, order, limits): + """Determine the linewidths given data characteristics.""" if self._empty_data(data): - size_levels = [None] + levels = [None] sizes = {} else: - if self._attribute_type(data) == "categorical": - # TODO be more flexible, it could be fine to do heavy and light - # lines for categorical variables - err = "The variable that determines size must be numeric." - raise ValueError(err) + var_type = self._attribute_type(data) + if var_type == "categorical": + levels = categorical_order(data) + numbers = np.arange(0, len(levels)) + elif var_type == "numeric": + levels = numbers = np.sort(data.unique()) - size_levels = categorical_order(data) + if isinstance(sizes, (dict, list)): - if size_range is None: - default = plt.rcParams["lines.linewidth"] - min_width, max_width = default * .5, default * 2 - else: - min_width, max_width = size_range + # Use literal size values + if isinstance(sizes, list): + if len(sizes) != len(levels): + err = "The `sizes` list has wrong number of levels" + raise ValueError(err) + sizes = dict(zip(levels, sizes)) + + missing = set(levels) - set(sizes) + if any(missing): + err = "Missing sizes for the following levels: {}" + raise ValueError(err.format(missing)) - if size_limits is None: - s_min, s_max = data.min(), data.max() else: - s_min, s_max = size_limits - s_min = data.min() if s_min is None else s_min - s_max = data.max() if s_max is None else s_max - - size_limits = s_min, s_max - size_range = min_width, max_width - normalize = mpl.colors.Normalize(s_min, s_max, clip=True) - sizes = {l: min_width + (normalize(l) * max_width) - for l in size_levels} - - self.size_levels = size_levels - self.size_limits = size_limits - self.size_range = size_range + + # Infer the range of sizes to use + if sizes is None: + default = plt.rcParams["lines.linewidth"] + min_width, max_width = default * .5, default * 2 + else: + try: + min_width, max_width = sizes + except (TypeError, ValueError): + err = "sizes argument {} not understood".format(sizes) + raise ValueError(err) + + # Infer the range of numeric values to map to sizes + if limits is None: + s_min, s_max = numbers.min(), numbers.max() + else: + s_min, s_max = limits + s_min = numbers.min() if s_min is None else s_min + s_max = numbers.max() if s_max is None else s_max + + # Map the numeric labels into the range of sizes + limits = s_min, s_max + normalize = mpl.colors.Normalize(s_min, s_max, clip=True) + width_range = max_width - min_width + sizes = {l: min_width + (normalize(n) * width_range) + for l, n in zip(levels, numbers)} + self.sizes = sizes + self.size_levels = levels + self.size_limits = limits def parse_style(self, data, markers, dashes, order): """Determine the markers and line dashes.""" @@ -586,8 +607,8 @@ def plot(self, ax=None): def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, palette=None, hue_order=None, hue_limits=None, + sizes=None, size_order=None, size_limits=None, dashes=True, markers=None, style_order=None, - size_limits=None, size_range=None, size_order=None, units=None, estimator="mean", ci=95, n_boot=1000, sort=True, errstyle="band", legend="brief", ax=None, **kwargs): @@ -599,8 +620,8 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, p = _LinePlotter( x=x, y=y, hue=hue, size=size, style=style, data=data, palette=palette, hue_order=hue_order, hue_limits=hue_limits, + sizes=sizes, size_order=size_order, size_limits=size_limits, dashes=dashes, markers=markers, style_order=style_order, - size_limits=size_limits, size_range=size_range, size_order=size_order, units=units, estimator=estimator, ci=ci, n_boot=n_boot, sort=sort, errstyle=errstyle, ) @@ -616,7 +637,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, palette=None, hue_order=None, hue_limits=None, markers=None, style_order=None, - size_range=None, size_limits=None, + sizes=None, size_order=None, size_limits=None, x_bins=None, y_bins=None, estimator=None, ci=95, n_boot=1000, units=None, errstyle="bars", alpha="auto", diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 37a9fe1385..d475b8d27d 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -379,30 +379,57 @@ def test_parse_size(self, long_df): default_range = .5 * default_linewidth, 2 * default_linewidth p.parse_size(p.plot_data["size"], None, None, None) assert p.size_limits == default_limits - assert p.size_range == default_range + size_range = min(p.sizes.values()), max(p.sizes.values()) + assert size_range == default_range # Test specified size limits size_limits = (1, 5) - p.parse_size(p.plot_data["size"], size_limits, None, None) + p.parse_size(p.plot_data["size"], None, None, size_limits) assert p.size_limits == size_limits - assert p.size_range == default_range # Test specified size range - size_range = (.1, .5) - p.parse_size(p.plot_data["size"], None, size_range, None) + sizes = (.1, .5) + p.parse_size(p.plot_data["size"], sizes, None, None) assert p.size_limits == default_limits - assert p.size_range == size_range - # Test size values + # Test size values inferred from ranges + sizes = (1, 5) size_limits = (1, 10) - size_range = (1, 5) - p.parse_size(p.plot_data["size"], size_limits, size_range, None) + p.parse_size(p.plot_data["size"], sizes, None, size_limits) normalize = mpl.colors.Normalize(*size_limits, clip=False) for level, width in p.sizes.items(): - assert width == size_range[0] + size_range[1] * normalize(level) + assert width == sizes[0] + (sizes[1] - sizes[0]) * normalize(level) + # Test list of sizes + var = "a" + levels = categorical_order(long_df[var]) + sizes = list(np.random.rand(len(levels))) + p = basic._LinePlotter(x="x", y="y", size=var, data=long_df) + p.parse_size(p.plot_data["size"], sizes, None, None) + assert p.sizes == dict(zip(levels, sizes)) + + # Test dict of sizes + var = "a" + levels = categorical_order(long_df[var]) + sizes = dict(zip(levels, np.random.rand(len(levels)))) + p = basic._LinePlotter(x="x", y="y", size=var, data=long_df) + p.parse_size(p.plot_data["size"], sizes, None, None) + assert p.sizes == sizes + + # Test sizes list with wrong length + sizes = list(np.random.rand(len(levels) + 1)) with pytest.raises(ValueError): - basic._LinePlotter(x="x", y="y", size="a", data=long_df) + p.parse_size(p.plot_data["size"], sizes, None, None) + + # Test sizes dict with missing levels + sizes = dict(zip(levels, np.random.rand(len(levels) - 1))) + with pytest.raises(ValueError): + p.parse_size(p.plot_data["size"], sizes, None, None) + + # Test bad sizes argument + sizes = "bad_size" + with pytest.raises(ValueError): + p.parse_size(p.plot_data["size"], sizes, None, None) def test_parse_style(self, long_df): @@ -468,7 +495,6 @@ def test_subset_data_quantities(self, long_df): p = basic._LinePlotter(x="x", y="y", style=var, data=long_df) assert len(list(p.subset_data())) == n_subsets - var = "s" n_subsets = len(long_df[var].unique()) p = basic._LinePlotter(x="x", y="y", size=var, data=long_df) @@ -535,8 +561,6 @@ def test_subset_data_keys(self, long_df): assert size is None assert style in long_df[var].values - var = "s" - p = basic._LinePlotter(x="x", y="y", size=var, data=long_df) for (hue, size, style), _ in p.subset_data(): assert hue is None @@ -598,12 +622,14 @@ def sem(x): assert est.index.equals(index) assert est.values == pytest.approx(y_mean.values) assert cis.values == pytest.approx(y_cis.values, 4) + assert list(cis.columns) == ["low", "high"] index, est, cis = p.aggregate(y, x, np.mean, 68) assert np.array_equal(index.values, x.unique()) assert est.index.equals(index) assert est.values == pytest.approx(y_mean.values) assert cis.values == pytest.approx(y_cis.values, 4) + assert list(cis.columns) == ["low", "high"] y_std = y.groupby(x).std() y_cis = pd.DataFrame(dict(low=y_mean - y_std, @@ -615,6 +641,7 @@ def sem(x): assert est.index.equals(index) assert est.values == pytest.approx(y_mean.values) assert cis.values == pytest.approx(y_cis.values) + assert list(cis.columns) == ["low", "high"] index, est, cis = p.aggregate(y, x, "mean", None) assert cis is None From f082088378ce7a218e3e73ef90c86564584a59ff Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 25 Nov 2017 19:02:30 -0500 Subject: [PATCH 0598/1738] Extract labels for hue/size/style --- seaborn/basic.py | 19 +++++++++++++++---- seaborn/tests/test_basic.py | 21 +++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 666fdb37cf..9e76ad8591 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -33,7 +33,7 @@ def establish_variables(self, x=None, y=None, data=None): """Parse the inputs to define data for plotting.""" # Initialize label variables - x_label = y_label = None + x_label = y_label = hue_label = size_label = style_label = None # Option 1: # We have a wide-form datast @@ -67,6 +67,8 @@ def establish_variables(self, x=None, y=None, plot_data["style"] = plot_data["hue"] x_label = getattr(data.index, "name", None) + hue_label = style_label = getattr(plot_data.columns, + "name", None) # Option 1b: # The input data is an array or list @@ -135,6 +137,9 @@ def establish_variables(self, x=None, y=None, # Extract variable names x_label = getattr(x, "name", None) y_label = getattr(y, "name", None) + hue_label = getattr(hue, "name", None) + size_label = getattr(size, "name", None) + style_label = getattr(style, "name", None) # Reassemble into a DataFrame plot_data = dict(x=x, y=y, hue=hue, style=style, size=size) @@ -158,6 +163,9 @@ def establish_variables(self, x=None, y=None, self.x_label = x_label self.y_label = y_label + self.hue_label = hue_label + self.size_label = size_label + self.style_label = style_label self.plot_data = plot_data return plot_data @@ -507,7 +515,9 @@ def plot(self, ax, legend, kws): kws.setdefault("markeredgewidth", kws.pop("mew", .75)) kws.setdefault("markeredgecolor", kws.pop("mec", "w")) - for (hue, size, style), subset_data in self.subset_data(): + for semantics, subset_data in self.subset_data(): + + hue, size, style = semantics if self.sort: subset_data = subset_data.sort_values(["x", "y"]) @@ -524,7 +534,8 @@ def plot(self, ax, legend, kws): kws["marker"] = self.markers.get(style, orig_marker) kws["linewidth"] = self.sizes.get(size, orig_linewidth) - # TODO handle marker size adjustment + label = self.semantics_to_label(semantics) + kws.setdefault("label", label) line, = ax.plot(x.values, y.values, **kws) line_color = line.get_color() @@ -631,7 +642,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, p.plot(ax, legend, kwargs) - return ax + return ax, p def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index d475b8d27d..53145278f3 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -78,6 +78,9 @@ def test_wide_df_variables(self, wide_df): assert p.x_label == wide_df.index.name assert p.y_label is None + assert p.hue_label == wide_df.columns.name + assert p.size_label is None + assert p.style_label == wide_df.columns.name def test_wide_df_variables_check(self, wide_df): @@ -116,6 +119,9 @@ def test_wide_array_variables(self, wide_array): assert p.x_label is None assert p.y_label is None + assert p.hue_label is None + assert p.size_label is None + assert p.style_label is None def test_flat_array_variables(self, flat_array): @@ -138,6 +144,9 @@ def test_flat_array_variables(self, flat_array): assert p.x_label is None assert p.y_label is None + assert p.hue_label is None + assert p.size_label is None + assert p.style_label is None def test_wide_list_variables(self, wide_list): @@ -168,6 +177,9 @@ def test_wide_list_variables(self, wide_list): assert p.x_label is None assert p.y_label is None + assert p.hue_label is None + assert p.size_label is None + assert p.style_label is None def test_long_df(self, long_df): @@ -180,6 +192,9 @@ def test_long_df(self, long_df): for col in ["hue", "style", "size"]: assert p.plot_data[col].isnull().all() assert (p.x_label, p.y_label) == ("x", "y") + assert p.hue_label is None + assert p.size_label is None + assert p.style_label is None p.establish_variables(x=long_df.x, y="y", data=long_df) assert np.array_equal(p.plot_data["x"], long_df["x"]) @@ -195,11 +210,15 @@ def test_long_df(self, long_df): assert np.array_equal(p.plot_data["hue"], long_df["a"]) for col in ["style", "size"]: assert p.plot_data[col].isnull().all() + assert p.hue_label == "a" + assert p.size_label is None and p.style_label is None p.establish_variables(x="x", y="y", hue="a", style="a", data=long_df) assert np.array_equal(p.plot_data["hue"], long_df["a"]) assert np.array_equal(p.plot_data["style"], long_df["a"]) assert p.plot_data["size"].isnull().all() + assert p.hue_label == p.style_label == "a" + assert p.size_label is None p.establish_variables(x="x", y="y", hue="a", style="b", data=long_df) assert np.array_equal(p.plot_data["hue"], long_df["a"]) @@ -208,6 +227,8 @@ def test_long_df(self, long_df): p.establish_variables(x="x", y="y", size="y", data=long_df) assert np.array_equal(p.plot_data["size"], long_df["y"]) + assert p.size_label == "y" + assert p.hue_label is None and p.style_label is None def test_bad_input(self, long_df): From dbd866159a32ed7abd25787419e46cb1636d86c1 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 25 Nov 2017 19:35:19 -0500 Subject: [PATCH 0599/1738] Change where sorting happens --- seaborn/basic.py | 6 +++--- seaborn/tests/test_basic.py | 26 ++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 9e76ad8591..3089e20e31 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -328,6 +328,9 @@ def subset_data(self): if not len(subset_data): continue + if self.sort: + subset_data = subset_data.sort_values(["x", "y"]) + yield (hue, size, style), subset_data def parse_hue(self, data, palette, order, limits): @@ -519,9 +522,6 @@ def plot(self, ax, legend, kws): hue, size, style = semantics - if self.sort: - subset_data = subset_data.sort_values(["x", "y"]) - x, y = subset_data["x"], subset_data["y"] if self.estimator is not None: diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 53145278f3..73ca0eb294 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -602,22 +602,40 @@ def test_subset_data_values(self, long_df): p = basic._LinePlotter(x="x", y="y", data=long_df) _, data = next(p.subset_data()) - assert np.array_equal(data.values, p.plot_data[["x", "y"]].values) + expected = p.plot_data.loc[:, ["x", "y"]].sort_values(["x", "y"]) + assert np.array_equal(data.values, expected) + + p = basic._LinePlotter(x="x", y="y", data=long_df, sort=False) + _, data = next(p.subset_data()) + expected = p.plot_data.loc[:, ["x", "y"]] + assert np.array_equal(data.values, expected) p = basic._LinePlotter(x="x", y="y", hue="a", data=long_df) for (hue, _, _), data in p.subset_data(): - expected = p.plot_data.loc[p.plot_data["hue"] == hue, ["x", "y"]] + rows = p.plot_data["hue"] == hue + cols = ["x", "y"] + expected = p.plot_data.loc[rows, cols].sort_values(cols) + assert np.array_equal(data.values, expected.values) + + p = basic._LinePlotter(x="x", y="y", hue="a", data=long_df, sort=False) + for (hue, _, _), data in p.subset_data(): + rows = p.plot_data["hue"] == hue + cols = ["x", "y"] + expected = p.plot_data.loc[rows, cols] assert np.array_equal(data.values, expected.values) p = basic._LinePlotter(x="x", y="y", hue="a", style="a", data=long_df) for (hue, _, _), data in p.subset_data(): - expected = p.plot_data.loc[p.plot_data["hue"] == hue, ["x", "y"]] + rows = p.plot_data["hue"] == hue + cols = ["x", "y"] + expected = p.plot_data.loc[rows, cols].sort_values(cols) assert np.array_equal(data.values, expected.values) p = basic._LinePlotter(x="x", y="y", hue="a", size="s", data=long_df) for (hue, size, _), data in p.subset_data(): rows = (p.plot_data["hue"] == hue) & (p.plot_data["size"] == size) - expected = p.plot_data.loc[rows, ["x", "y"]] + cols = ["x", "y"] + expected = p.plot_data.loc[rows, cols].sort_values(cols) assert np.array_equal(data.values, expected.values) def test_aggregate(self, long_df): From a88b8210b344d502890f9b97600cfccb2a15445f Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 25 Nov 2017 19:41:08 -0500 Subject: [PATCH 0600/1738] Improve legending to handle multiple assignment of semantics --- seaborn/basic.py | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 3089e20e31..04ea5b3e6d 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -534,9 +534,6 @@ def plot(self, ax, legend, kws): kws["marker"] = self.markers.get(style, orig_marker) kws["linewidth"] = self.sizes.get(size, orig_linewidth) - label = self.semantics_to_label(semantics) - kws.setdefault("label", label) - line, = ax.plot(x.values, y.values, **kws) line_color = line.get_color() line_alpha = line.get_alpha() @@ -582,21 +579,39 @@ def add_legend_data(self, ax, legend): err = "`legend` must be 'brief', 'full', or False" raise ValueError(err) + keys = [] + legend_data = {} + + def add_legend_data(var_name, val_name, **kws): + + key = var_name, val_name + if key in legend_data: + legend_data[key].update(**kws) + else: + keys.append(key) + legend_data[key] = dict(**kws) + for level in self.hue_levels: if level is not None: - ax.plot([], [], color=self.palette[level], label=level) + add_legend_data(self.hue_label, level, + color=self.palette[level]) for level in self.size_levels: if level is not None: - ax.plot([], [], color=".2", - linewidth=self.sizes[level], label=level) + add_legend_data(self.size_label, level, + linewidth=self.sizes[level]) for level in self.style_levels: if level is not None: - marker = self.markers.get(level, "") - dashes = self.dashes.get(level, "") - ax.plot([], [], color=".2", - marker=marker, dashes=dashes, label=level) + add_legend_data(self.style_label, level, + marker=self.markers.get(level, ""), + dashes=self.dashes.get(level, "")) + + for key in keys: + _, label = key + kws = legend_data[key] + kws.setdefault("color", ".2") + ax.plot([], [], label=label, **kws) class _ScatterPlotter(_BasicPlotter): From ec92c444b10a7fbf2559b305b0555e75690af833 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 26 Nov 2017 11:27:10 -0500 Subject: [PATCH 0601/1738] Messy approach to brief legend for numeric variables --- seaborn/basic.py | 60 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 04ea5b3e6d..ad98d17b3b 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -249,6 +249,23 @@ def numeric_to_palette(self, data, order, palette, limits): return levels, palette, cmap, limits + def color_lookup(self, key): + + if self.hue_type == "numeric": + norm = mpl.colors.Normalize(*self.hue_limits, clip=True) + return self.cmap(norm(key)) + elif self.hue_type == "categorical": + return self.palette[key] + + def size_lookup(self, key): + + if self.size_type == "numeric": + norm = mpl.colors.Normalize(*self.size_limits, clip=True) + min_size, max_size = self.size_range + return min_size + norm(key) * (max_size - min_size) + elif self.size_type == "categorical": + return self.sizes[key] + def style_to_attributes(self, levels, style, defaults, name): """Convert a style argument to a dict of matplotlib attributes.""" if style is True: @@ -376,6 +393,8 @@ def parse_size(self, data, sizes, order, limits): if self._empty_data(data): levels = [None] sizes = {} + var_type = None + width_range = None else: @@ -400,6 +419,8 @@ def parse_size(self, data, sizes, order, limits): err = "Missing sizes for the following levels: {}" raise ValueError(err.format(missing)) + width_range = min(sizes.values()), max(sizes.values()) + else: # Infer the range of sizes to use @@ -412,6 +433,7 @@ def parse_size(self, data, sizes, order, limits): except (TypeError, ValueError): err = "sizes argument {} not understood".format(sizes) raise ValueError(err) + width_range = min_width, max_width # Infer the range of numeric values to map to sizes if limits is None: @@ -424,13 +446,14 @@ def parse_size(self, data, sizes, order, limits): # Map the numeric labels into the range of sizes limits = s_min, s_max normalize = mpl.colors.Normalize(s_min, s_max, clip=True) - width_range = max_width - min_width - sizes = {l: min_width + (normalize(n) * width_range) + sizes = {l: min_width + normalize(n) * (max_width - min_width) for l, n in zip(levels, numbers)} self.sizes = sizes + self.size_type = var_type self.size_levels = levels self.size_limits = limits + self.size_range = width_range def parse_style(self, data, markers, dashes, order): """Determine the markers and line dashes.""" @@ -564,7 +587,7 @@ def plot(self, ax, legend, kws): y_visible = any(t.get_visible() for t in ax.get_yticklabels()) ax.set_ylabel(self.y_label, visible=y_visible) - # Add legend data + # Add legend if legend: self.add_legend_data(ax, legend) handles, _ = ax.get_legend_handles_labels() @@ -591,15 +614,34 @@ def add_legend_data(var_name, val_name, **kws): keys.append(key) legend_data[key] = dict(**kws) - for level in self.hue_levels: + ticker = mpl.ticker.MaxNLocator(3) + + # -- + + if legend == "brief" and self.hue_type == "numeric": + hue_levels = (ticker.tick_values(*self.hue_limits) + .astype(self.plot_data["hue"].dtype)) + else: + hue_levels = self.hue_levels + + for level in hue_levels: if level is not None: - add_legend_data(self.hue_label, level, - color=self.palette[level]) + color = self.color_lookup(level) + add_legend_data(self.hue_label, level, color=color) - for level in self.size_levels: + # -- + if legend == "brief" and self.size_type == "numeric": + size_levels = (ticker.tick_values(*self.size_limits) + .astype(self.plot_data["size"].dtype)) + else: + size_levels = self.size_levels + + for level in size_levels: if level is not None: - add_legend_data(self.size_label, level, - linewidth=self.sizes[level]) + linewidth = self.size_lookup(level) + add_legend_data(self.size_label, level, linewidth=linewidth) + + # -- for level in self.style_levels: if level is not None: From 97b4ac4e1e63f00a8d64828734748ff2a079958a Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 26 Nov 2017 11:39:29 -0500 Subject: [PATCH 0602/1738] Don't cycle line colors internally --- seaborn/basic.py | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index ad98d17b3b..82c780ec98 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -444,6 +444,7 @@ def parse_size(self, data, sizes, order, limits): s_max = numbers.max() if s_max is None else s_max # Map the numeric labels into the range of sizes + # TODO rework to use size_lookup from above limits = s_min, s_max normalize = mpl.colors.Normalize(s_min, s_max, clip=True) sizes = {l: min_width + normalize(n) * (max_width - min_width) @@ -533,14 +534,30 @@ def bootstrapped_cis(vals): def plot(self, ax, legend, kws): - orig_color = kws.pop("color", None) - orig_dashes = kws.pop("dashes", (np.inf, 1)) - orig_marker = kws.pop("marker", None) - orig_linewidth = kws.pop("linewidth", kws.pop("lw", None)) + # Draw a test line, using the passed in kwargs. The goal here is to + # honor both (a) the current state of the plot cycler and (b) the + # specified kwargs on all the lines we will draw, overriding when + # relevant with the lineplot semantics. Note that we won't cycle + # internally; in other words, if ``hue`` is not used, all lines + # will have the same color, but they will have the color that + # ax.plot() would have used, and will advance the color cycle. + + scout, = ax.plot([], [], **kws) + + orig_color = kws.pop("color", scout.get_color()) + orig_marker = kws.pop("marker", scout.get_marker()) + orig_linewidth = kws.pop("linewidth", + kws.pop("lw", scout.get_linewidth())) + + orig_dashes = kws.pop("dashes", "") kws.setdefault("markeredgewidth", kws.pop("mew", .75)) kws.setdefault("markeredgecolor", kws.pop("mec", "w")) + scout.remove() + + # Loop over the semantic subsets and draw a line for each + for semantics, subset_data in self.subset_data(): hue, size, style = semantics @@ -557,10 +574,17 @@ def plot(self, ax, legend, kws): kws["marker"] = self.markers.get(style, orig_marker) kws["linewidth"] = self.sizes.get(size, orig_linewidth) + # --- Draw the main line + + # TODO when not estimating, use units to get multiple lines + # with the same semantics? + line, = ax.plot(x.values, y.values, **kws) line_color = line.get_color() line_alpha = line.get_alpha() + # --- Draw the confidence intervals + if y_ci is not None: if self.errstyle == "band": @@ -596,8 +620,6 @@ def plot(self, ax, legend, kws): def add_legend_data(self, ax, legend): - # TODO doesn't handle overlapping keys (i.e. hue="a", style="a") well - if legend not in ["brief", "full"]: err = "`legend` must be 'brief', 'full', or False" raise ValueError(err) @@ -616,7 +638,7 @@ def add_legend_data(var_name, val_name, **kws): ticker = mpl.ticker.MaxNLocator(3) - # -- + # -- Add a legend for hue semantics if legend == "brief" and self.hue_type == "numeric": hue_levels = (ticker.tick_values(*self.hue_limits) @@ -629,7 +651,8 @@ def add_legend_data(var_name, val_name, **kws): color = self.color_lookup(level) add_legend_data(self.hue_label, level, color=color) - # -- + # -- Add a legend for size semantics + if legend == "brief" and self.size_type == "numeric": size_levels = (ticker.tick_values(*self.size_limits) .astype(self.plot_data["size"].dtype)) @@ -641,7 +664,7 @@ def add_legend_data(var_name, val_name, **kws): linewidth = self.size_lookup(level) add_legend_data(self.size_label, level, linewidth=linewidth) - # -- + # -- Add a legend for style semantics for level in self.style_levels: if level is not None: @@ -699,7 +722,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, p.plot(ax, legend, kwargs) - return ax, p + return ax def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, From 67ab2bd9e75d4e94e2d04921e7efddc47a05c97c Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 26 Nov 2017 12:27:25 -0500 Subject: [PATCH 0603/1738] Add legend tests --- seaborn/basic.py | 14 ++--- seaborn/tests/test_basic.py | 101 ++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 7 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 82c780ec98..31feb57399 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -627,7 +627,7 @@ def add_legend_data(self, ax, legend): keys = [] legend_data = {} - def add_legend_data(var_name, val_name, **kws): + def update(var_name, val_name, **kws): key = var_name, val_name if key in legend_data: @@ -636,7 +636,7 @@ def add_legend_data(var_name, val_name, **kws): keys.append(key) legend_data[key] = dict(**kws) - ticker = mpl.ticker.MaxNLocator(3) + ticker = mpl.ticker.MaxNLocator(nbins=3) # -- Add a legend for hue semantics @@ -649,7 +649,7 @@ def add_legend_data(var_name, val_name, **kws): for level in hue_levels: if level is not None: color = self.color_lookup(level) - add_legend_data(self.hue_label, level, color=color) + update(self.hue_label, level, color=color) # -- Add a legend for size semantics @@ -662,15 +662,15 @@ def add_legend_data(var_name, val_name, **kws): for level in size_levels: if level is not None: linewidth = self.size_lookup(level) - add_legend_data(self.size_label, level, linewidth=linewidth) + update(self.size_label, level, linewidth=linewidth) # -- Add a legend for style semantics for level in self.style_levels: if level is not None: - add_legend_data(self.style_label, level, - marker=self.markers.get(level, ""), - dashes=self.dashes.get(level, "")) + update(self.style_label, level, + marker=self.markers.get(level, ""), + dashes=self.dashes.get(level, "")) for key in keys: _, label = key diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 73ca0eb294..ef489ced85 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -2,6 +2,7 @@ import numpy as np import pandas as pd import matplotlib as mpl +import matplotlib.pyplot as plt import pytest from .. import basic from ..palettes import color_palette @@ -690,3 +691,103 @@ def sem(x): assert np.array_equal(index.values, x) assert np.array_equal(est.values, y) assert cis is None + + def test_legend_data(self, long_df): + + f, ax = plt.subplots() + + p = basic._LinePlotter(x="x", y="y", data=long_df) + p.add_legend_data(ax, "full") + handles, _ = ax.get_legend_handles_labels() + assert handles == [] + + # TODO test elsewhere in own function from lineplot() entrypoint + # ax.clear() + # p.plot(ax, "full", {"label": "test"}) + # _, labels = ax.get_legend_handles_labels() + # assert labels == ["test"] + + # -- + + ax.clear() + p = basic._LinePlotter(x="x", y="y", hue="a", data=long_df) + p.add_legend_data(ax, "full") + handles, labels = ax.get_legend_handles_labels() + colors = [h.get_color() for h in handles] + assert labels == p.hue_levels + assert colors == [p.palette[l] for l in labels] + + # -- + + ax.clear() + p = basic._LinePlotter(x="x", y="y", hue="a", style="a", + markers=True, data=long_df) + p.add_legend_data(ax, "full") + handles, labels = ax.get_legend_handles_labels() + colors = [h.get_color() for h in handles] + markers = [h.get_marker() for h in handles] + assert labels == p.hue_levels == p.style_levels + assert colors == [p.palette[l] for l in labels] + assert markers == [p.markers[l] for l in labels] + + # -- + + ax.clear() + p = basic._LinePlotter(x="x", y="y", hue="a", style="b", + markers=True, data=long_df) + p.add_legend_data(ax, "full") + handles, labels = ax.get_legend_handles_labels() + colors = [h.get_color() for h in handles] + markers = [h.get_marker() for h in handles] + expected_colors = ([p.palette[l] for l in p.hue_levels] + + [".2" for _ in p.style_levels]) + expected_markers = (["None" for _ in p.hue_levels] + + [p.markers[l] for l in p.style_levels]) + assert labels == p.hue_levels + p.style_levels + assert colors == expected_colors + assert markers == expected_markers + + # -- + + ax.clear() + p = basic._LinePlotter(x="x", y="y", hue="a", size="a", data=long_df) + p.add_legend_data(ax, "full") + handles, labels = ax.get_legend_handles_labels() + colors = [h.get_color() for h in handles] + widths = [h.get_linewidth() for h in handles] + assert labels == p.hue_levels == p.size_levels + assert colors == [p.palette[l] for l in labels] + assert widths == [p.sizes[l] for l in labels] + + # -- + + x, y = np.random.randn(2, 40) + z = np.tile(np.arange(20), 2) + + p = basic._LinePlotter(x=x, y=y, hue=z) + + ax.clear() + p.add_legend_data(ax, "full") + handles, labels = ax.get_legend_handles_labels() + assert labels == [str(l) for l in p.hue_levels] + + ax.clear() + p.add_legend_data(ax, "brief") + handles, labels = ax.get_legend_handles_labels() + assert len(labels) == 4 + + p = basic._LinePlotter(x=x, y=y, size=z) + + ax.clear() + p.add_legend_data(ax, "full") + handles, labels = ax.get_legend_handles_labels() + assert labels == [str(l) for l in p.size_levels] + + ax.clear() + p.add_legend_data(ax, "brief") + handles, labels = ax.get_legend_handles_labels() + assert len(labels) == 4 + + ax.clear() + with pytest.raises(ValueError): + p.add_legend_data(ax, legend="bad_value") From 9ea3be24d5d462b4096fc30517c0fbbfed2b5744 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 26 Nov 2017 12:38:07 -0500 Subject: [PATCH 0604/1738] Change how legend verbosity gets controlled --- seaborn/basic.py | 20 ++++++++++++-------- seaborn/tests/test_basic.py | 37 ++++++++++++++++++++++--------------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 31feb57399..3e64e11b5d 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -324,6 +324,8 @@ def __init__(self, self.n_boot = n_boot self.errstyle = errstyle + self.legend = legend + def subset_data(self): """Return (x, y) data for each subset defined by semantics.""" data = self.plot_data @@ -540,7 +542,8 @@ def plot(self, ax, legend, kws): # relevant with the lineplot semantics. Note that we won't cycle # internally; in other words, if ``hue`` is not used, all lines # will have the same color, but they will have the color that - # ax.plot() would have used, and will advance the color cycle. + # ax.plot() would have used for a single line, and calling lineplot + # will advance the axes property cycle. scout, = ax.plot([], [], **kws) @@ -612,15 +615,16 @@ def plot(self, ax, legend, kws): ax.set_ylabel(self.y_label, visible=y_visible) # Add legend - if legend: - self.add_legend_data(ax, legend) + if self.legend: + self.add_legend_data(ax) handles, _ = ax.get_legend_handles_labels() if handles: ax.legend() - def add_legend_data(self, ax, legend): + def add_legend_data(self, ax): - if legend not in ["brief", "full"]: + verbosity = self.legend + if verbosity not in ["brief", "full"]: err = "`legend` must be 'brief', 'full', or False" raise ValueError(err) @@ -640,7 +644,7 @@ def update(var_name, val_name, **kws): # -- Add a legend for hue semantics - if legend == "brief" and self.hue_type == "numeric": + if verbosity == "brief" and self.hue_type == "numeric": hue_levels = (ticker.tick_values(*self.hue_limits) .astype(self.plot_data["hue"].dtype)) else: @@ -653,7 +657,7 @@ def update(var_name, val_name, **kws): # -- Add a legend for size semantics - if legend == "brief" and self.size_type == "numeric": + if verbosity == "brief" and self.size_type == "numeric": size_levels = (ticker.tick_values(*self.size_limits) .astype(self.plot_data["size"].dtype)) else: @@ -720,7 +724,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, if ax is None: ax = plt.gca() - p.plot(ax, legend, kwargs) + p.plot(ax, kwargs) return ax diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index ef489ced85..8c5d5e10d1 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -696,8 +696,8 @@ def test_legend_data(self, long_df): f, ax = plt.subplots() - p = basic._LinePlotter(x="x", y="y", data=long_df) - p.add_legend_data(ax, "full") + p = basic._LinePlotter(x="x", y="y", data=long_df, legend="full") + p.add_legend_data(ax) handles, _ = ax.get_legend_handles_labels() assert handles == [] @@ -710,8 +710,9 @@ def test_legend_data(self, long_df): # -- ax.clear() - p = basic._LinePlotter(x="x", y="y", hue="a", data=long_df) - p.add_legend_data(ax, "full") + p = basic._LinePlotter(x="x", y="y", hue="a", data=long_df, + legend="full") + p.add_legend_data(ax) handles, labels = ax.get_legend_handles_labels() colors = [h.get_color() for h in handles] assert labels == p.hue_levels @@ -721,8 +722,8 @@ def test_legend_data(self, long_df): ax.clear() p = basic._LinePlotter(x="x", y="y", hue="a", style="a", - markers=True, data=long_df) - p.add_legend_data(ax, "full") + markers=True, legend="full", data=long_df) + p.add_legend_data(ax) handles, labels = ax.get_legend_handles_labels() colors = [h.get_color() for h in handles] markers = [h.get_marker() for h in handles] @@ -734,8 +735,8 @@ def test_legend_data(self, long_df): ax.clear() p = basic._LinePlotter(x="x", y="y", hue="a", style="b", - markers=True, data=long_df) - p.add_legend_data(ax, "full") + markers=True, legend="full", data=long_df) + p.add_legend_data(ax) handles, labels = ax.get_legend_handles_labels() colors = [h.get_color() for h in handles] markers = [h.get_marker() for h in handles] @@ -750,8 +751,9 @@ def test_legend_data(self, long_df): # -- ax.clear() - p = basic._LinePlotter(x="x", y="y", hue="a", size="a", data=long_df) - p.add_legend_data(ax, "full") + p = basic._LinePlotter(x="x", y="y", hue="a", size="a", data=long_df, + legend="full") + p.add_legend_data(ax) handles, labels = ax.get_legend_handles_labels() colors = [h.get_color() for h in handles] widths = [h.get_linewidth() for h in handles] @@ -767,27 +769,32 @@ def test_legend_data(self, long_df): p = basic._LinePlotter(x=x, y=y, hue=z) ax.clear() - p.add_legend_data(ax, "full") + p.legend = "full" + p.add_legend_data(ax) handles, labels = ax.get_legend_handles_labels() assert labels == [str(l) for l in p.hue_levels] ax.clear() - p.add_legend_data(ax, "brief") + p.legend = "brief" + p.add_legend_data(ax) handles, labels = ax.get_legend_handles_labels() assert len(labels) == 4 p = basic._LinePlotter(x=x, y=y, size=z) ax.clear() - p.add_legend_data(ax, "full") + p.legend = "full" + p.add_legend_data(ax) handles, labels = ax.get_legend_handles_labels() assert labels == [str(l) for l in p.size_levels] ax.clear() - p.add_legend_data(ax, "brief") + p.legend = "brief" + p.add_legend_data(ax) handles, labels = ax.get_legend_handles_labels() assert len(labels) == 4 ax.clear() + p.legend = "bad_value" with pytest.raises(ValueError): - p.add_legend_data(ax, legend="bad_value") + p.add_legend_data(ax) From 525770b79bc1e25177851c95747a820da24e3fb7 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 26 Nov 2017 13:14:10 -0500 Subject: [PATCH 0605/1738] Add plotting tests --- seaborn/basic.py | 8 +- seaborn/tests/test_basic.py | 160 ++++++++++++++++++++++++++++++++++-- 2 files changed, 160 insertions(+), 8 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 3e64e11b5d..b2861d79b0 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -528,13 +528,13 @@ def bootstrapped_cis(vals): cis = grouped.apply(bootstrapped_cis) if cis.notnull().any(): - cis = cis.unstack() + cis = cis.unstack().reindex(est.index) else: cis = None return est.index, est, cis - def plot(self, ax, legend, kws): + def plot(self, ax, kws): # Draw a test line, using the passed in kwargs. The goal here is to # honor both (a) the current state of the plot cycler and (b) the @@ -606,6 +606,10 @@ def plot(self, ax, legend, kws): ax.add_collection(lines) ax.autoscale_view() + else: + err = "`errstyle` must by 'band' or 'bars', not {}" + raise ValueError(err.format(self.errstyle)) + # TODO this should go in its own method? if self.x_label is not None: x_visible = any(t.get_visible() for t in ax.get_xticklabels()) diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 8c5d5e10d1..716e2dcb7e 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -1,4 +1,5 @@ from __future__ import division +from itertools import product import numpy as np import pandas as pd import matplotlib as mpl @@ -692,6 +693,10 @@ def sem(x): assert np.array_equal(est.values, y) assert cis is None + x, y = pd.Series([1, 1, 2]), pd.Series([2, 3, 4]) + index, est, cis = p.aggregate(y, x, "mean", 68) + assert cis.loc[2].isnull().all() + def test_legend_data(self, long_df): f, ax = plt.subplots() @@ -701,12 +706,6 @@ def test_legend_data(self, long_df): handles, _ = ax.get_legend_handles_labels() assert handles == [] - # TODO test elsewhere in own function from lineplot() entrypoint - # ax.clear() - # p.plot(ax, "full", {"label": "test"}) - # _, labels = ax.get_legend_handles_labels() - # assert labels == ["test"] - # -- ax.clear() @@ -798,3 +797,152 @@ def test_legend_data(self, long_df): p.legend = "bad_value" with pytest.raises(ValueError): p.add_legend_data(ax) + + def test_plot(self, long_df): + + f, ax = plt.subplots() + + p = basic._LinePlotter(x="x", y="y", data=long_df, + sort=False, estimator=None) + p.plot(ax, {}) + line, = ax.lines + assert np.array_equal(line.get_xdata(), long_df.x.values) + assert np.array_equal(line.get_ydata(), long_df.y.values) + + ax.clear() + p.plot(ax, {"color": "k", "label": "test"}) + line, = ax.lines + assert line.get_color() == "k" + assert line.get_label() == "test" + + p = basic._LinePlotter(x="x", y="y", data=long_df, + sort=True, estimator=None) + + ax.clear() + p.plot(ax, {}) + line, = ax.lines + sorted_data = long_df.sort_values(["x", "y"]) + assert np.array_equal(line.get_xdata(), sorted_data.x.values) + assert np.array_equal(line.get_ydata(), sorted_data.y.values) + + p = basic._LinePlotter(x="x", y="y", hue="a", data=long_df) + + ax.clear() + p.plot(ax, {}) + assert len(ax.lines) == len(p.hue_levels) + for line, level in zip(ax.lines, p.hue_levels): + assert line.get_color() == p.palette[level] + + p = basic._LinePlotter(x="x", y="y", size="a", data=long_df) + + ax.clear() + p.plot(ax, {}) + assert len(ax.lines) == len(p.size_levels) + for line, level in zip(ax.lines, p.size_levels): + assert line.get_linewidth() == p.sizes[level] + + p = basic._LinePlotter(x="x", y="y", hue="a", style="a", + markers=True, data=long_df) + + ax.clear() + p.plot(ax, {}) + assert len(ax.lines) == len(p.hue_levels) == len(p.style_levels) + for line, level in zip(ax.lines, p.hue_levels): + assert line.get_color() == p.palette[level] + assert line.get_marker() == p.markers[level] + + p = basic._LinePlotter(x="x", y="y", hue="a", style="b", + markers=True, data=long_df) + + ax.clear() + p.plot(ax, {}) + levels = product(p.hue_levels, p.style_levels) + assert len(ax.lines) == (len(p.hue_levels) * len(p.style_levels)) + for line, (hue, style) in zip(ax.lines, levels): + assert line.get_color() == p.palette[hue] + assert line.get_marker() == p.markers[style] + + p = basic._LinePlotter(x="x", y="y", data=long_df, + estimator="mean", errstyle="band", ci="sd", + sort=True) + + ax.clear() + p.plot(ax, {}) + line, = ax.lines + expected_data = long_df.groupby("x").y.mean() + assert np.array_equal(line.get_xdata(), expected_data.index.values) + assert np.allclose(line.get_ydata(), expected_data.values) + assert len(ax.collections) == 1 + + p = basic._LinePlotter(x="x", y="y", hue="a", data=long_df, + estimator="mean", errstyle="band", ci="sd") + + ax.clear() + p.plot(ax, {}) + assert len(ax.lines) == len(ax.collections) == len(p.hue_levels) + for c in ax.collections: + assert isinstance(c, mpl.collections.PolyCollection) + + p = basic._LinePlotter(x="x", y="y", hue="a", data=long_df, + estimator="mean", errstyle="bars", ci="sd") + + ax.clear() + p.plot(ax, {}) + assert len(ax.lines) == len(ax.collections) == len(p.hue_levels) + for c in ax.collections: + assert isinstance(c, mpl.collections.LineCollection) + + def test_lineplot_axes(self, wide_df): + + f1, ax1 = plt.subplots() + f2, ax2 = plt.subplots() + + ax = basic.lineplot(data=wide_df) + assert ax is ax2 + + ax = basic.lineplot(data=wide_df, ax=ax1) + assert ax is ax1 + + def test_lineplot_smoke(self, wide_list, flat_array, wide_array, + wide_df, long_df): + + f, ax = plt.subplots() + + basic.lineplot(data=wide_list) + ax.clear() + + basic.lineplot(data=flat_array) + ax.clear() + + basic.lineplot(data=wide_array) + ax.clear() + + basic.lineplot(data=wide_df) + ax.clear() + + basic.lineplot(x="x", y="y", data=long_df) + ax.clear() + + basic.lineplot(x=long_df.x, y=long_df.y) + ax.clear() + + basic.lineplot(x=long_df.x, y="y", data=long_df) + ax.clear() + + basic.lineplot(x="x", y=long_df.y.values, data=long_df) + ax.clear() + + basic.lineplot(x="x", y="y", hue="a", data=long_df) + ax.clear() + + basic.lineplot(x="x", y="y", hue="a", style="a", data=long_df) + ax.clear() + + basic.lineplot(x="x", y="y", hue="a", style="b", data=long_df) + ax.clear() + + basic.lineplot(x="x", y="y", hue="a", size="a", data=long_df) + ax.clear() + + basic.lineplot(x="x", y="y", hue="a", size="s", data=long_df) + ax.clear() From 3cbedb35a359eef922100ab6f4fbcc311d0aeb08 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 26 Nov 2017 15:37:50 -0500 Subject: [PATCH 0606/1738] Add wrapper layer for df.sort/df.sort_values --- seaborn/basic.py | 4 ++-- seaborn/tests/test_basic.py | 10 +++++----- seaborn/utils.py | 8 ++++++++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index b2861d79b0..a25a10e789 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -11,7 +11,7 @@ from .external.six import string_types from . import utils -from .utils import categorical_order, get_color_cycle +from .utils import categorical_order, get_color_cycle, sort_df from .algorithms import bootstrap from .palettes import color_palette @@ -348,7 +348,7 @@ def subset_data(self): continue if self.sort: - subset_data = subset_data.sort_values(["x", "y"]) + subset_data = sort_df(subset_data, ["x", "y"]) yield (hue, size, style), subset_data diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 716e2dcb7e..29d40afe55 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -604,7 +604,7 @@ def test_subset_data_values(self, long_df): p = basic._LinePlotter(x="x", y="y", data=long_df) _, data = next(p.subset_data()) - expected = p.plot_data.loc[:, ["x", "y"]].sort_values(["x", "y"]) + expected = basic.sort_df(p.plot_data.loc[:, ["x", "y"]], ["x", "y"]) assert np.array_equal(data.values, expected) p = basic._LinePlotter(x="x", y="y", data=long_df, sort=False) @@ -616,7 +616,7 @@ def test_subset_data_values(self, long_df): for (hue, _, _), data in p.subset_data(): rows = p.plot_data["hue"] == hue cols = ["x", "y"] - expected = p.plot_data.loc[rows, cols].sort_values(cols) + expected = basic.sort_df(p.plot_data.loc[rows, cols], cols) assert np.array_equal(data.values, expected.values) p = basic._LinePlotter(x="x", y="y", hue="a", data=long_df, sort=False) @@ -630,14 +630,14 @@ def test_subset_data_values(self, long_df): for (hue, _, _), data in p.subset_data(): rows = p.plot_data["hue"] == hue cols = ["x", "y"] - expected = p.plot_data.loc[rows, cols].sort_values(cols) + expected = basic.sort_df(p.plot_data.loc[rows, cols], cols) assert np.array_equal(data.values, expected.values) p = basic._LinePlotter(x="x", y="y", hue="a", size="s", data=long_df) for (hue, size, _), data in p.subset_data(): rows = (p.plot_data["hue"] == hue) & (p.plot_data["size"] == size) cols = ["x", "y"] - expected = p.plot_data.loc[rows, cols].sort_values(cols) + expected = basic.sort_df(p.plot_data.loc[rows, cols], cols) assert np.array_equal(data.values, expected.values) def test_aggregate(self, long_df): @@ -821,7 +821,7 @@ def test_plot(self, long_df): ax.clear() p.plot(ax, {}) line, = ax.lines - sorted_data = long_df.sort_values(["x", "y"]) + sorted_data = basic.sort_df(long_df, ["x", "y"]) assert np.array_equal(line.get_xdata(), sorted_data.x.values) assert np.array_equal(line.get_ydata(), sorted_data.y.values) diff --git a/seaborn/utils.py b/seaborn/utils.py index 89cb9baa47..f40425d48c 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -39,6 +39,14 @@ def remove_na(arr): return arr[pd.notnull(arr)] +def sort_df(df, *args, **kwargs): + """Wrapper to handle different pandas sorting API pre/post 0.17.""" + try: + return df.sort_values(*args, **kwargs) + except AttributeError: + return df.sort(*args, **kwargs) + + def ci_to_errsize(cis, heights): """Convert intervals to error arguments relative to plot heights. From 68527151a376d80788b439ab3f535729a08eb4c2 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 26 Nov 2017 15:37:59 -0500 Subject: [PATCH 0607/1738] Add axes label tests --- seaborn/tests/test_basic.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 29d40afe55..574889a044 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -892,6 +892,21 @@ def test_plot(self, long_df): for c in ax.collections: assert isinstance(c, mpl.collections.LineCollection) + def test_axis_labels(self, long_df): + + f, (ax1, ax2) = plt.subplots(1, 2, sharey=True) + + p = basic._LinePlotter(x="x", y="y", data=long_df) + + p.plot(ax1, {}) + assert ax1.get_xlabel() == "x" + assert ax1.get_ylabel() == "y" + + p.plot(ax2, {}) + assert ax2.get_xlabel() == "x" + assert ax2.get_ylabel() == "y" + assert not ax2.yaxis.label.get_visible() + def test_lineplot_axes(self, wide_df): f1, ax1 = plt.subplots() From 67c4ab6073ef08521b682cfee4d40bb9edbedc40 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 26 Nov 2017 15:38:23 -0500 Subject: [PATCH 0608/1738] Don't test on Python 3.4 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b0c5faac7e..97bbcee21d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ env: - PYTHON=2.7 DEPS=latest BACKEND=agg DOCTESTS=true - PYTHON=2.7 DEPS=pinned BACKEND=agg DOCTESTS=false - PYTHON=2.7 DEPS=latest BACKEND=qtagg DOCTESTS=true - - PYTHON=3.4 DEPS=latest BACKEND=agg DOCTESTS=true - PYTHON=3.5 DEPS=latest BACKEND=agg DOCTESTS=true - PYTHON=3.6 DEPS=latest BACKEND=agg DOCTESTS=true - PYTHON=3.6 DEPS=latest BACKEND=qtagg DOCTESTS=true From e6e53ea2e865bec12db98055753634d4842a28cf Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 26 Nov 2017 15:58:10 -0500 Subject: [PATCH 0609/1738] Have categorical size mapping go from thick to thin --- seaborn/basic.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index a25a10e789..ac8d9851e5 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -403,7 +403,7 @@ def parse_size(self, data, sizes, order, limits): var_type = self._attribute_type(data) if var_type == "categorical": levels = categorical_order(data) - numbers = np.arange(0, len(levels)) + numbers = np.arange(0, len(levels))[::-1] elif var_type == "numeric": levels = numbers = np.sort(data.unique()) @@ -712,10 +712,6 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, sort=True, errstyle="band", legend="brief", ax=None, **kwargs): - # TODO add a "brief_legend" or similar that handles many legend entries - # using a matplotlib ticker ... maybe have it take threshold where you - # flip from itemizing levels to showing level ticks - p = _LinePlotter( x=x, y=y, hue=hue, size=size, style=style, data=data, palette=palette, hue_order=hue_order, hue_limits=hue_limits, From 96a11faec07a8e2c764683b3095f384b279e0008 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 26 Nov 2017 15:59:46 -0500 Subject: [PATCH 0610/1738] Fix legendparameter not getting passed into plotter --- seaborn/basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index ac8d9851e5..f66df61e6d 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -718,7 +718,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, sizes=sizes, size_order=size_order, size_limits=size_limits, dashes=dashes, markers=markers, style_order=style_order, units=units, estimator=estimator, ci=ci, n_boot=n_boot, - sort=sort, errstyle=errstyle, + sort=sort, errstyle=errstyle, legend=legend, ) if ax is None: From 8a4b9f21274d0feac6d9ffc9dc19bb1a946ac69d Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 26 Nov 2017 17:03:46 -0500 Subject: [PATCH 0611/1738] Ensure that brief legend can be drawn for numeric size with specified values --- seaborn/basic.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/seaborn/basic.py b/seaborn/basic.py index f66df61e6d..7c25867c08 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -422,6 +422,10 @@ def parse_size(self, data, sizes, order, limits): raise ValueError(err.format(missing)) width_range = min(sizes.values()), max(sizes.values()) + try: + limits = min(sizes.keys()), max(sizes.keys()) + except TypeError: + pass else: From 6c0e3232e4fe6eee38658f108cce5edb76390613 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 26 Nov 2017 17:03:58 -0500 Subject: [PATCH 0612/1738] Add lineplot parameter documentation --- seaborn/basic.py | 168 +++++++++++++++++++++++++++++++++++++++++ seaborn/categorical.py | 2 +- 2 files changed, 169 insertions(+), 1 deletion(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 7c25867c08..f53cf528de 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -702,9 +702,110 @@ def plot(self, ax=None): _basic_docs = dict( + # --- Introductory prose main_api_narrative=dedent("""\ + + ### TODO ### + """), + # --- Shared function parameters + data_vars=dedent("""\ + x, y : names of variables in ``data`` or vector data, optional + Input data variables; must be numeric. Can pass data directly or + reference columns in ``data``.\ + """), + data=dedent("""\ + data : DataFrame, array, or list of arrays, optional + Input data structure. If ``x`` and ``y`` are specified as names, this + should be a "long-form" DataFrame containing those columns. Otherwise + it is treated as "wide-form" data and grouping variables are ignored. + See the examples for the various ways this parameter can be specified + and the different effects of each.\ + """), + palette=dedent("""\ + palette : string, list, dict, or matplotlib colormap + An object that determines how colors are chosen when ``hue`` is used. + It can be the name of a seaborn palette or matplotlib colormap, a list + of colors (anything matplotlib understands), a dict mapping levels + of the ``hue`` variable to colors, or a matplotlib colormap object.\ + """), + hue_order=dedent("""\ + hue_order : list, optional + Specified order for the appearance of the ``hue`` variable levels, + otherwise they are determined from the data. Not relevant when the + ``hue`` variable is numeric.\ + """), + hue_limits=dedent("""\ + hue_limits : tuple, optional + Limits in data units to use for the colormap applied to the ``hue`` + variable when it is numeric. Not relevant if it is categorical.\ + """), + sizes=dedent(""" + sizes : list, dict, or tuple, optional + An object that determines how sizes are chosen when ``size`` is used. + It can always be a list of size values or a dict mapping levels of the + ``size`` variable to sizes. When ``size`` is numeric, it can also be + a tuple specifying the minimum and maximum size to use such that other + values are normalized within this range.\ + """), + size_order=dedent("""\ + size_order : list, optional + Specified order for appearance of the ``size`` variable levels, + otherwise they are determined from the data. Not relevant when the + ``size`` variable is numeric.\ + """), + size_limits=dedent("""\ + size_limits : tuple, optional + Limits in data units to use for the size normalization when the + ``size`` variable is numeric.\ + """), + style_order=dedent("""\ + style_order : list, optional + Specified order for appearance of the ``style`` variable levels + otherwise they are determined from the data. Not relevant when the + ``style`` variable is numeric.\ + """), + units=dedent("""\ + units : {long_form_var} + Grouping variable identifying sampling units. Currently has no effect.\ + """), + estimator=dedent("""\ + estimator : name of pandas method or callable or None, optional + Method for aggregating across multiple observations of the ``y`` + variable at the same ``x`` level. If ``None``, all observations will + be drawn.\ + """), + ci=dedent("""\ + ci : int or "sd" or None, optional + Size of the confidence interval to draw when aggregating with an + estimator. "sd" means to draw the standard deviation of the data. + Setting to ``None`` will skip bootstrapping.\ + """), + n_boot=dedent("""\ + n_boot : int, optional + Number of bootstraps to use for computing the confidence interval.\ + """), + legend=dedent("""\ + legend : "brief", "full", or False, optional + How to draw the legend. If "brief", numeric ``hue`` and ``size`` + variables will be represented with a sample of evenly spaced values. + If "full", every group will get an entry in the legend. If ``False``, + no legend data is added and no legend is drawn.\ + """), + ax_in=dedent("""\ + ax : matplotlib Axes, optional + Axes object to draw the plot onto, otherwise uses the current Axes.\ + """), + ax_out=dedent("""\ + ax : matplotlib Axes + Returns the Axes object with the plot drawn onto it.\ + """), + + # --- Repeated phrases + long_form_var="name of variables in ``data`` or vector data, optional", + + ) @@ -733,6 +834,73 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, return ax +lineplot.__doc__ = dedent("""\ + Draw a line plot with numeric data. + + {main_api_narrative} + + Parameters + ---------- + {data_vars} + hue : {long_form_var} + Grouping variable that will produce lines with different colors. + Can be either categorical or numeric, although color mapping will + behave differently in latter case. + size : {long_form_var} + Gropuing variable that will produce lines with different widths. + Can be either categorical or numeric, although size mapping will + behave differently in latter case. + style : {long_form_var} + Grouping variable that will produce lines with different dashes + and/or markers. Can have a numeric dtype but will always be treated + as categorical. + {data} + {palette} + {hue_order} + {hue_limits} + {sizes} + {size_order} + {size_limits} + dashes : boolean, list, or dictionary, optional + Object determining how to draw the lines for different levels of the + ``style`` variable. Dashes are specified as in matplotlib: a tuple of + ``(segment, gap)`` lengths, or an empty string to draw a solid line. + markers : boolean, list, or dictionary, optional + Object determining how to draw the markers for different levels of the + ``style`` variable. Markers are specified as in matplotlib. + {style_order} + {units} + {estimator} + {ci} + {n_boot} + sort : boolean, optional + If True, the data will be sorted by the x and y variables, otherwise + lines will connect points in the order they appear in the dataset. + errstyle : "band" or "bars", optional + Whether to draw the confidence intervals with translucent error bands + or discrete error bars. + {legend} + {ax_in} + kwargs : key, value mappings + Other keyword arguments are passed down to ``plt.plot`` at draw time. + + Returns + ------- + {ax_out} + + See Also + -------- + + ### TODO ### + + Examples + -------- + + ### TODO ### + + """).format(**_basic_docs) + + def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, palette=None, hue_order=None, hue_limits=None, markers=None, style_order=None, diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 6bbcfc7aae..1286288dcb 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2168,7 +2168,7 @@ def plot(self, ax, boxplot_kws): """), ax_out=dedent("""\ ax : matplotlib Axes - Returns the Axes object with the boxplot drawn onto it.\ + Returns the Axes object with the plot drawn onto it.\ """), # Shared see also From ee63fea804399a9f3570864ae7d98350d30301f8 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 26 Nov 2017 20:09:59 -0500 Subject: [PATCH 0613/1738] Add API examples for lineplot --- doc/api.rst | 11 ++++ seaborn/basic.py | 123 ++++++++++++++++++++++++++++++++++++++++- seaborn/categorical.py | 2 +- 3 files changed, 132 insertions(+), 4 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 7745bd63b8..6dde978a3d 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -21,6 +21,17 @@ Axis grids JointGrid jointplot + +.. _basic_api: + +Basic plots +----------- + +.. autosummary:: + :toctree: generated + + lineplot + .. _categorical_api: Categorical plots diff --git a/seaborn/basic.py b/seaborn/basic.py index f53cf528de..a5ba7fbebd 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -705,7 +705,7 @@ def plot(self, ax=None): # --- Introductory prose main_api_narrative=dedent("""\ - ### TODO ### + TODO """), @@ -891,12 +891,129 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, See Also -------- - ### TODO ### + TODO Examples -------- - ### TODO ### + Draw a single line plot with error bands showing a confidence interval: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns + >>> sns.set() + >>> fmri = sns.load_dataset("fmri") + >>> ax = sns.lineplot(x="timepoint", y="signal", data=fmri) + + Group by another variable and show the groups with different colors: + + + .. plot:: + :context: close-figs + + >>> ax = sns.lineplot(x="timepoint", y="signal", hue="event", + ... data=fmri) + + Show the grouping variable with both color and line dashing: + + .. plot:: + :context: close-figs + + >>> ax = sns.lineplot(x="timepoint", y="signal", + ... hue="event", style="event", data=fmri) + + Use color and line dashing to represent two different grouping variables: + + .. plot:: + :context: close-figs + + >>> ax = sns.lineplot(x="timepoint", y="signal", + ... hue="region", style="event", data=fmri) + + Change the markers used instead of the dashes to identify groups: + + .. plot:: + :context: close-figs + + >>> ax = sns.lineplot(x="timepoint", y="signal", + ... hue="event", style="event", + ... markers=True, dashes=False, data=fmri) + + Show error bars instead of error bands and plot the standard error: + + .. plot:: + :context: close-figs + + >>> ax = sns.lineplot(x="timepoint", y="signal", hue="event", + ... errstyle="bars", ci=68, data=fmri) + + Use a quantitative color mapping: + + .. plot:: + :context: close-figs + + >>> dots = sns.load_dataset("dots").query("align == 'dots'") + >>> ax = sns.lineplot(x="time", y="firing_rate", + ... hue="coherence", style="choice", + ... data=dots) + + Change the data limits over which the colormap is normalized: + + .. plot:: + :context: close-figs + + >>> ax = sns.lineplot(x="time", y="firing_rate", + ... hue="coherence", style="choice", + ... hue_limits=(0, 100), data=dots) + + Show an entry for every line in the legend and use a different palette: + + .. plot:: + :context: close-figs + + >>> ax = sns.lineplot(x="time", y="firing_rate", + ... hue="coherence", style="choice", + ... palette="BuGn", legend="full", data=dots) + + Change the width of the lines with a quantitative variable: + + .. plot:: + :context: close-figs + + >>> ax = sns.lineplot(x="time", y="firing_rate", + ... size="coherence", style="choice", + ... legend="full", data=dots) + + Change the range of line widths used to normalize the size variable: + + .. plot:: + :context: close-figs + + >>> ax = sns.lineplot(x="time", y="firing_rate", + ... size="coherence", style="choice", + ... sizes=(.2, 1), data=dots) + + Plot from a wide-form DataFrame: + + .. plot:: + :context: close-figs + + >>> import pandas as pd + >>> index = pd.date_range("1 1 2000", periods=100, + ... freq="m", name="date") + >>> data = np.random.randn(100, 4).cumsum(axis=0) + >>> wide_df = pd.DataFrame(data, index, ["a", "b", "c", "d"]) + >>> ax = sns.lineplot(data=wide_df) + + Draw lines at points as they appear in the dataset: + + .. plot:: + :context: close-figs + + >>> x, y = np.random.randn(2, 5000).cumsum(axis=1) + >>> sns.lineplot(x=x, y=y, sort=False, lw=1) + """).format(**_basic_docs) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 1286288dcb..6fad8b339e 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -3775,7 +3775,7 @@ def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, :context: close-figs >>> import seaborn as sns - >>> sns.set_style("whitegrid") + >>> sns.set(style="whitegrid") >>> tips = sns.load_dataset("tips") >>> ax = sns.lvplot(x=tips["total_bill"]) From fa507e16a683e3ce86f6ac17f1537dfffc966e15 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 27 Nov 2017 09:06:10 -0500 Subject: [PATCH 0614/1738] Attempt to workarond pandas 0.21 datetime converter issue for doctests --- seaborn/conftest.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/seaborn/conftest.py b/seaborn/conftest.py index 8a0425d678..686400a2cd 100644 --- a/seaborn/conftest.py +++ b/seaborn/conftest.py @@ -2,6 +2,11 @@ import matplotlib.pyplot as plt import pytest +# Attempted workaround for change in pandas 0.21 +# Can be removed when matplotlib 2.2 is released +from pandas.tseries import converter +converter.register() + @pytest.fixture(autouse=True) def close_figs(): From dd138c4a4e5b952e05fa389b943d06632d3c2f1d Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 27 Nov 2017 11:17:31 -0500 Subject: [PATCH 0615/1738] Close figure before plotting with date data Temporary (hopefully) workaround for https://stackoverflow.com/questions/47514043/ --- seaborn/basic.py | 6 +++--- seaborn/conftest.py | 5 ----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index a5ba7fbebd..90d20382d2 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -901,8 +901,8 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, .. plot:: :context: close-figs - >>> import seaborn as sns - >>> sns.set() + >>> import seaborn as sns; sns.set() + >>> import matplotlib.pyplot as plt >>> fmri = sns.load_dataset("fmri") >>> ax = sns.lineplot(x="timepoint", y="signal", data=fmri) @@ -999,7 +999,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, .. plot:: :context: close-figs - >>> import pandas as pd + >>> import numpy as np, pandas as pd; plt.close("all") >>> index = pd.date_range("1 1 2000", periods=100, ... freq="m", name="date") >>> data = np.random.randn(100, 4).cumsum(axis=0) diff --git a/seaborn/conftest.py b/seaborn/conftest.py index 686400a2cd..8a0425d678 100644 --- a/seaborn/conftest.py +++ b/seaborn/conftest.py @@ -2,11 +2,6 @@ import matplotlib.pyplot as plt import pytest -# Attempted workaround for change in pandas 0.21 -# Can be removed when matplotlib 2.2 is released -from pandas.tseries import converter -converter.register() - @pytest.fixture(autouse=True) def close_figs(): From a7a2c6be16c87f35a2b54ee6e3ae42b68850011e Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 27 Nov 2017 11:18:31 -0500 Subject: [PATCH 0616/1738] Improved usage of pandas semantic info with wide-form data --- seaborn/basic.py | 48 +++++++++++++++------- seaborn/tests/test_basic.py | 79 +++++++++++++++++++++++++++++++++++-- 2 files changed, 109 insertions(+), 18 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 90d20382d2..137cbd4fc7 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -80,37 +80,40 @@ def establish_variables(self, x=None, y=None, plot_data = pd.DataFrame(columns=["x", "y"]) - elif np.isscalar(data[0]): + elif np.isscalar(np.asarray(data)[0]): # The input data is a flat list(like): # We assign a numeric index for x and use the values for y - plot_data = pd.DataFrame(dict(x=np.arange(len(data)), - y=data)) + x = getattr(data, "index", np.arange(len(data))) + plot_data = pd.DataFrame(dict(x=x, y=data)) elif hasattr(data, "shape"): # The input data is an array(like): - # We assign a numeric index to x, the values to y, and - # numeric values to both hue and style + # We either use the index or assign a numeric index to x, + # the values to y, and id keys to both hue and style plot_data = pd.DataFrame(data) plot_data.loc[:, "x"] = plot_data.index plot_data = pd.melt(plot_data, "x", - var_name="hue", value_name="y") + var_name="hue", + value_name="y") plot_data["style"] = plot_data["hue"] else: - # The input data is a nested list: We will assign a numeric - # index for x, use the values for, y and use numeric - # hue/style identifiers for each entry. + # The input data is a nested list: We will either use the + # index or assign a numeric index for x, use the values + # for y, and use numeric hue/style identifiers. - plot_data = pd.concat([ - pd.DataFrame(dict(x=np.arange(len(data_i)), - y=data_i, hue=i, style=i, size=None)) - for i, data_i in enumerate(data) - ]) + plot_data = [] + for i, data_i in enumerate(data): + x = getattr(data_i, "index", np.arange(len(data_i))) + n = getattr(data_i, "name", i) + data_i = dict(x=x, y=data_i, hue=n, style=n, size=None) + plot_data.append(pd.DataFrame(data_i)) + plot_data = pd.concat(plot_data) # Option 2: # We have long-form data @@ -146,7 +149,7 @@ def establish_variables(self, x=None, y=None, plot_data = pd.DataFrame(plot_data) # Option 3: - # Only one variable arugment + # Only one variable argument # -------------------------- else: @@ -1006,6 +1009,21 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, >>> wide_df = pd.DataFrame(data, index, ["a", "b", "c", "d"]) >>> ax = sns.lineplot(data=wide_df) + Plot from a list of Series: + + .. plot:: + :context: close-figs + + >>> list_data = [wide_df.loc[:"2005", "a"], wide_df.loc["2003":, "b"]] + >>> ax = sns.lineplot(data=list_data) + + Plot a single Series, pass kwargs to ``plt.plot``: + + .. plot:: + :context: close-figs + + >>> ax = sns.lineplot(data=wide_df["a"], color="coral", label="line") + Draw lines at points as they appear in the dataset: .. plot:: diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 574889a044..45152898e8 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -30,11 +30,23 @@ def flat_array(self): return np.random.randn(20) + @pytest.fixture + def flat_series(self): + + index = pd.Int64Index(np.arange(10, 30), name="t") + return pd.Series(np.random.randn(20), index, name="s") + @pytest.fixture def wide_list(self): return [np.random.randn(20), np.random.randn(10)] + @pytest.fixture + def wide_list_of_series(self): + + return [pd.Series(np.random.randn(20), np.arange(20), name="a"), + pd.Series(np.random.randn(10), np.arange(5, 15), name="b")] + @pytest.fixture def long_df(self): @@ -150,6 +162,27 @@ def test_flat_array_variables(self, flat_array): assert p.size_label is None assert p.style_label is None + def test_flat_series_variables(self, flat_series): + + p = basic._BasicPlotter() + p.establish_variables(data=flat_series) + assert p.input_format == "wide" + assert len(p.plot_data) == len(flat_series) + + x = p.plot_data["x"] + expected_x = flat_series.index + assert np.array_equal(x, expected_x) + + y = p.plot_data["y"] + expected_y = flat_series + assert np.array_equal(y, expected_y) + + assert p.x_label is None + assert p.y_label is None + assert p.hue_label is None + assert p.size_label is None + assert p.style_label is None + def test_wide_list_variables(self, wide_list): p = basic._BasicPlotter() @@ -183,6 +216,39 @@ def test_wide_list_variables(self, wide_list): assert p.size_label is None assert p.style_label is None + def test_wide_list_of_series_variables(self, wide_list_of_series): + + p = basic._BasicPlotter() + p.establish_variables(data=wide_list_of_series) + assert p.input_format == "wide" + assert len(p.plot_data) == sum(len(l) for l in wide_list_of_series) + + x = p.plot_data["x"] + expected_x = np.concatenate([s.index for s in wide_list_of_series]) + assert np.array_equal(x, expected_x) + + y = p.plot_data["y"] + expected_y = np.concatenate(wide_list_of_series) + assert np.array_equal(y, expected_y) + + hue = p.plot_data["hue"] + expected_hue = np.concatenate([ + np.full(len(s), s.name) for s in wide_list_of_series + ]) + assert np.array_equal(hue, expected_hue) + + style = p.plot_data["style"] + expected_style = expected_hue + assert np.array_equal(style, expected_style) + + assert p.plot_data["size"].isnull().all() + + assert p.x_label is None + assert p.y_label is None + assert p.hue_label is None + assert p.size_label is None + assert p.style_label is None + def test_long_df(self, long_df): p = basic._BasicPlotter() @@ -918,20 +984,27 @@ def test_lineplot_axes(self, wide_df): ax = basic.lineplot(data=wide_df, ax=ax1) assert ax is ax1 - def test_lineplot_smoke(self, wide_list, flat_array, wide_array, + def test_lineplot_smoke(self, flat_array, flat_series, + wide_array, wide_list, wide_list_of_series, wide_df, long_df): f, ax = plt.subplots() - basic.lineplot(data=wide_list) + basic.lineplot(data=flat_array) ax.clear() - basic.lineplot(data=flat_array) + basic.lineplot(data=flat_series) ax.clear() basic.lineplot(data=wide_array) ax.clear() + basic.lineplot(data=wide_list) + ax.clear() + + basic.lineplot(data=wide_list_of_series) + ax.clear() + basic.lineplot(data=wide_df) ax.clear() From d1f60a873e89c2ccb8fc8241bfa791597224d8d4 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 27 Nov 2017 11:36:48 -0500 Subject: [PATCH 0617/1738] Fix doctest --- seaborn/basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 137cbd4fc7..fb73cc2aa3 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -1030,7 +1030,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, :context: close-figs >>> x, y = np.random.randn(2, 5000).cumsum(axis=1) - >>> sns.lineplot(x=x, y=y, sort=False, lw=1) + >>> ax = sns.lineplot(x=x, y=y, sort=False, lw=1) """).format(**_basic_docs) From 015bcd20bc94f959959955401757ddba0cd1fc4b Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 27 Nov 2017 11:55:55 -0500 Subject: [PATCH 0618/1738] Fix for older numpy --- seaborn/tests/test_basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 45152898e8..856a99918d 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -233,7 +233,7 @@ def test_wide_list_of_series_variables(self, wide_list_of_series): hue = p.plot_data["hue"] expected_hue = np.concatenate([ - np.full(len(s), s.name) for s in wide_list_of_series + np.full(len(s), s.name, object) for s in wide_list_of_series ]) assert np.array_equal(hue, expected_hue) From 2a967ff8f3599353f8f9208e2e3d3429b256a57c Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 27 Nov 2017 16:03:30 -0500 Subject: [PATCH 0619/1738] Treat hue variable as categorical when palette is a list or dict --- seaborn/basic.py | 43 ++++++++++++++++--------------------- seaborn/tests/test_basic.py | 7 +++++- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index fb73cc2aa3..d35f0f923a 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -199,6 +199,11 @@ def categorical_to_palette(self, data, order, palette): colors = color_palette(None, n_colors) else: colors = color_palette("husl", n_colors) + elif isinstance(palette, list): + if len(palette) != n_colors: + err = "The palette list has the wrong number of colors." + raise ValueError(err) + colors = palette else: colors = color_palette(palette, n_colors) @@ -216,18 +221,6 @@ def numeric_to_palette(self, data, order, palette, limits): # Identify the colormap to use if palette is None: cmap = mpl.cm.get_cmap(plt.rcParams["image.cmap"]) - elif isinstance(palette, dict): - missing = set(levels) - set(palette) - if any(missing): - err = "The palette dictionary is missing keys: {}" - raise ValueError(err.format(missing)) - cmap = None - elif isinstance(palette, list): - if len(palette) != len(levels): - err = "The palette has the wrong number of colors" - raise ValueError(err) - palette = dict(zip(levels, palette)) - cmap = None elif isinstance(palette, mpl.colors.Colormap): cmap = palette else: @@ -237,18 +230,16 @@ def numeric_to_palette(self, data, order, palette, limits): err = "Palette {} not understood" raise ValueError(err) - if cmap is not None: + if limits is None: + limits = data.min(), data.max() - if limits is None: - hue_min, hue_max = data.min(), data.max() - else: - hue_min, hue_max = limits - hue_min = data.min() if hue_min is None else hue_min - hue_max = data.max() if hue_max is None else hue_max + hue_min, hue_max = limits + hue_min = data.min() if hue_min is None else hue_min + hue_max = data.max() if hue_max is None else hue_max - limits = hue_min, hue_max - normalize = mpl.colors.Normalize(hue_min, hue_max, clip=True) - palette = {l: cmap(normalize(l)) for l in levels} + limits = hue_min, hue_max + normalize = mpl.colors.Normalize(hue_min, hue_max, clip=True) + palette = {l: cmap(normalize(l)) for l in levels} return levels, palette, cmap, limits @@ -368,7 +359,11 @@ def parse_hue(self, data, palette, order, limits): else: # Determine what kind of hue mapping we want - var_type = self._attribute_type(data) + var_type = self._semantic_type(data) + + # Override depending on the type of the palette argument + if isinstance(palette, (dict, list)): + var_type = "categorical" # -- Option 1: categorical color palette @@ -403,7 +398,7 @@ def parse_size(self, data, sizes, order, limits): else: - var_type = self._attribute_type(data) + var_type = self._semantic_type(data) if var_type == "categorical": levels = categorical_order(data) numbers = np.arange(0, len(levels))[::-1] diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 856a99918d..0ea0fc1ab6 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -353,7 +353,7 @@ def test_parse_hue_categorical(self, wide_df, long_df): assert p.palette == expected_palette # Test list palette - palette = color_palette("Reds", wide_df.shape[1] + 2) + palette = color_palette("Reds", wide_df.shape[1]) p.parse_hue(p.plot_data.hue, palette, None, None) expected_palette = dict(zip(wide_df.columns, palette)) assert p.palette == expected_palette @@ -369,6 +369,11 @@ def test_parse_hue_categorical(self, wide_df, long_df): with pytest.raises(ValueError): p.parse_hue(p.plot_data.hue, palette, None, None) + # Test list with wrong number of colors + palette = colors[:-1] + with pytest.raises(ValueError): + p.parse_hue(p.plot_data.hue, palette, None, None) + # Test hue order hue_order = ["a", "c", "d"] p.parse_hue(p.plot_data.hue, None, hue_order, None) From 63346b65de34bed0632b017e1846987e070d7d51 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 27 Nov 2017 17:24:29 -0500 Subject: [PATCH 0620/1738] Don't sort keys in FacetGrid legend --- seaborn/axisgrid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 1d84ee271f..0838d68273 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -50,7 +50,7 @@ def add_legend(self, legend_data=None, title=None, label_order=None, Title for the legend. The default reads from ``self._hue_var``. label_order : list of labels, optional The order that the legend entries should appear in. The default - reads from ``self.hue_names`` or sorts the keys in ``legend_data``. + reads from ``self.hue_names``. kwargs : key, value pairings Other keyword arguments are passed to the underlying legend methods on the Figure or Axes object. @@ -65,7 +65,7 @@ def add_legend(self, legend_data=None, title=None, label_order=None, legend_data = self._legend_data if legend_data is None else legend_data if label_order is None: if self.hue_names is None: - label_order = np.sort(list(legend_data.keys())) + label_order = list(legend_data.keys()) else: label_order = list(map(utils.to_utf8, self.hue_names)) From 0a225dc17bc9c64f5ebb9baf294a248a4186ee42 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 27 Nov 2017 17:25:09 -0500 Subject: [PATCH 0621/1738] Round out lineplot docstring and add some code comments --- seaborn/basic.py | 80 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 23 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index d35f0f923a..34eb555070 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -244,7 +244,7 @@ def numeric_to_palette(self, data, order, palette, limits): return levels, palette, cmap, limits def color_lookup(self, key): - + """Return the color corresponding to the hue level.""" if self.hue_type == "numeric": norm = mpl.colors.Normalize(*self.hue_limits, clip=True) return self.cmap(norm(key)) @@ -252,7 +252,7 @@ def color_lookup(self, key): return self.palette[key] def size_lookup(self, key): - + """Return the size corresponding to the size level.""" if self.size_type == "numeric": norm = mpl.colors.Normalize(*self.size_limits, clip=True) min_size, max_size = self.size_range @@ -280,12 +280,11 @@ def style_to_attributes(self, levels, style, defaults, name): return attrdict def _empty_data(self, data): + """Test if a series is completely missing.""" + return data.isnull().all() - empty_data = data.isnull().all() - return empty_data - - def _attribute_type(self, data): - + def _semantic_type(self, data): + """Determine if data should considered numeric or categorical.""" if self.input_format == "wide": return "categorical" else: @@ -489,9 +488,10 @@ def parse_style(self, data, markers, dashes, order): self.markers = markers def aggregate(self, vals, grouper, func, ci): - + """Compute an estimate and confidence interval using grouper.""" n_boot = self.n_boot + # Define a function that might call an object method for aggregation # TODO rework this logic, which is a mess if callable(func): def f(x): @@ -504,8 +504,10 @@ def f(x): def f(x): return getattr(x, func)() + # Define a "null" CI for when we only have one value null_ci = pd.Series(index=["low", "high"], dtype=np.float) + # Function to bootstrap in the context of a pandas group by def bootstrapped_cis(vals): if len(vals) == 1: @@ -515,13 +517,16 @@ def bootstrapped_cis(vals): cis = utils.ci(boots, ci) return pd.Series(cis, ["low", "high"]) + # Group and get the aggregation estimate grouped = vals.groupby(grouper, sort=self.sort) - est = f(grouped) + # Exit early if we don't want a confidence interval if ci is None: return est.index, est, None - elif ci == "sd": + + # Compute the error bar extents + if ci == "sd": sd = grouped.std() cis = pd.DataFrame(np.c_[est - sd, est + sd], index=est.index, @@ -529,6 +534,7 @@ def bootstrapped_cis(vals): else: cis = grouped.apply(bootstrapped_cis) + # Unpack the CIs into "wide" format for plotting if cis.notnull().any(): cis = cis.unstack().reindex(est.index) else: @@ -537,6 +543,7 @@ def bootstrapped_cis(vals): return est.index, est, cis def plot(self, ax, kws): + """Draw the plot onto an axes, passing matplotlib kwargs.""" # Draw a test line, using the passed in kwargs. The goal here is to # honor both (a) the current state of the plot cycler and (b) the @@ -628,7 +635,7 @@ def plot(self, ax, kws): ax.legend() def add_legend_data(self, ax): - + """Add labeled artists to represent the different plot semantics.""" verbosity = self.legend if verbosity not in ["brief", "full"]: err = "`legend` must be 'brief', 'full', or False" @@ -702,9 +709,14 @@ def plot(self, ax=None): # --- Introductory prose main_api_narrative=dedent("""\ - - TODO - + The relationship between ``x`` and ``y`` can be shown for different subsets + of the data using the ``hue``, ``size``, and ``style`` parameters. These + parameters control what visual semantics are used to identify the different + subsets. It is possible to show up to three dimensions independently by + using all three semantic types, but this style of plot can be hard to + interpret and is often ineffective. Using redundant semantics (i.e. both + ``hue`` and ``style`` for the same variable) can be helpful for making + graphics more accessible.\ """), # --- Shared function parameters @@ -833,10 +845,14 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, lineplot.__doc__ = dedent("""\ - Draw a line plot with numeric data. + Draw a plot with numeric x and y values where the points are connected. {main_api_narrative} + By default, the plot aggregates over multiple ``y`` values at each value of + ``x`` and shows an estimate of the central tendency and a confidence + interval for that estimate. + Parameters ---------- {data_vars} @@ -861,11 +877,17 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, {size_limits} dashes : boolean, list, or dictionary, optional Object determining how to draw the lines for different levels of the - ``style`` variable. Dashes are specified as in matplotlib: a tuple of - ``(segment, gap)`` lengths, or an empty string to draw a solid line. + ``style`` variable. Setting to ``True`` will use default dash codes, or + you can pass a list of dash codes or a dictionary mapping levels of the + ``style`` variable to dash codes. Setting to ``False`` will use solid + lines for all subsets. Dashes are specified as in matplotlib: a tuple + of ``(segment, gap)`` lengths, or an empty string to draw a solid line. markers : boolean, list, or dictionary, optional Object determining how to draw the markers for different levels of the - ``style`` variable. Markers are specified as in matplotlib. + ``style`` variable. Setting to ``True`` will use default markers, or + you can pass a list of markers or a dictionary mapping levels of the + ``style`` variable to markers. Setting to ``False`` will draw + marker-less lines. Markers are specified as in matplotlib. {style_order} {units} {estimator} @@ -888,8 +910,10 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, See Also -------- - - TODO + scatterplot : Show the relationship between two variables without + emphasizing continuity of the ``x`` variable. + pointplot : Show the relationship between two variables when one is + categorical. Examples -------- @@ -929,7 +953,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, >>> ax = sns.lineplot(x="timepoint", y="signal", ... hue="region", style="event", data=fmri) - Change the markers used instead of the dashes to identify groups: + Use markers instead of the dashes to identify groups: .. plot:: :context: close-figs @@ -965,14 +989,24 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, ... hue="coherence", style="choice", ... hue_limits=(0, 100), data=dots) - Show an entry for every line in the legend and use a different palette: + Use a different color palette: + + .. plot:: + :context: close-figs + + >>> ax = sns.lineplot(x="time", y="firing_rate", + ... hue="coherence", style="choice", + ... palette="viridis_r", data=dots) + + Use specific color values, treating the hue variable as categorical: .. plot:: :context: close-figs + >>> palette = sns.color_palette("mako_r", 6) >>> ax = sns.lineplot(x="time", y="firing_rate", ... hue="coherence", style="choice", - ... palette="BuGn", legend="full", data=dots) + ... palette=palette, data=dots) Change the width of the lines with a quantitative variable: From 7ea7bada1a04dc825cdb2f405e262b610426a944 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 27 Nov 2017 17:25:38 -0500 Subject: [PATCH 0622/1738] Add new lineplot examples and remove old tsplot ones --- examples/errorband_lineplots.py | 17 +++++++++++++++++ examples/faceted_lineplot.py | 27 +++++++++++++++++++++++++++ examples/timeseries_from_dataframe.py | 15 --------------- examples/timeseries_of_barplots.py | 20 -------------------- examples/wide_data_lineplot.py | 19 +++++++++++++++++++ 5 files changed, 63 insertions(+), 35 deletions(-) create mode 100644 examples/errorband_lineplots.py create mode 100644 examples/faceted_lineplot.py delete mode 100644 examples/timeseries_from_dataframe.py delete mode 100644 examples/timeseries_of_barplots.py create mode 100644 examples/wide_data_lineplot.py diff --git a/examples/errorband_lineplots.py b/examples/errorband_lineplots.py new file mode 100644 index 0000000000..6ecda53427 --- /dev/null +++ b/examples/errorband_lineplots.py @@ -0,0 +1,17 @@ +""" +Timeseries plots with error bands +================================= + +_thumb: .5, .45 + +""" +import seaborn as sns +sns.set(style="ticks") + +# Load an example dataset with long-form data +fmri = sns.load_dataset("fmri") + +# Plot the responses for different events and regions +sns.lineplot(x="timepoint", y="signal", + hue="region", style="event", + data=fmri) diff --git a/examples/faceted_lineplot.py b/examples/faceted_lineplot.py new file mode 100644 index 0000000000..bc452deed3 --- /dev/null +++ b/examples/faceted_lineplot.py @@ -0,0 +1,27 @@ +""" +Line plots on multiple facets +============================= + +_thumb: .45, .42 + +""" +import seaborn as sns +sns.set(style="ticks") + +dots = sns.load_dataset("dots") + +# Define a palette to ensure that colors will be +# shared across the facets +palette = dict(zip(dots.coherence.unique(), + sns.color_palette("rocket_r", 6))) + +# Set up the FacetGrid with independent x axes +g = sns.FacetGrid(dots, col="align", + sharex=False, size=5, aspect=.75) + +# Draw the lineplot on each facet +g.map_dataframe(sns.lineplot, "time", "firing_rate", + hue="coherence", size="choice", + size_order=["T1", "T2"], + palette=palette) +g.add_legend() diff --git a/examples/timeseries_from_dataframe.py b/examples/timeseries_from_dataframe.py deleted file mode 100644 index 554ce98b24..0000000000 --- a/examples/timeseries_from_dataframe.py +++ /dev/null @@ -1,15 +0,0 @@ -""" -Timeseries from DataFrame -========================= - -""" - -import seaborn as sns -sns.set(style="darkgrid") - -# Load the long-form example gammas dataset -gammas = sns.load_dataset("gammas") - -# Plot the response with standard error -sns.tsplot(data=gammas, time="timepoint", unit="subject", - condition="ROI", value="BOLD signal") diff --git a/examples/timeseries_of_barplots.py b/examples/timeseries_of_barplots.py deleted file mode 100644 index 528b9f19ad..0000000000 --- a/examples/timeseries_of_barplots.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -Barplot timeseries -================== - -_thumb: .6, .4 -""" -import numpy as np -import seaborn as sns -sns.set(style="white") - -# Load the example planets dataset -planets = sns.load_dataset("planets") - -# Make a range of years to show categories with no observations -years = np.arange(2000, 2015) - -# Draw a count plot to show the number of planets discovered each year -g = sns.factorplot(x="year", data=planets, kind="count", - palette="BuPu", size=6, aspect=1.5, order=years) -g.set_xticklabels(step=2) diff --git a/examples/wide_data_lineplot.py b/examples/wide_data_lineplot.py new file mode 100644 index 0000000000..580217eba0 --- /dev/null +++ b/examples/wide_data_lineplot.py @@ -0,0 +1,19 @@ +""" +Lineplot from a wide-form dataset +================================= + +_thumb: .52, .5 + +""" +import numpy as np +import pandas as pd +import seaborn as sns +sns.set(style="whitegrid") + +rs = np.random.RandomState(365) +values = rs.randn(365, 4).cumsum(axis=0) +dates = pd.date_range("1 1 2016", periods=365, freq="D") +data = pd.DataFrame(values, dates, columns=["A", "B", "C", "D"]) +data = data.rolling(7).mean() + +sns.lineplot(data=data, palette="tab10", linewidth=2.5) From b9bb5fb08d0dcad2aa198ea8cb7a869ecfcd4d70 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 27 Nov 2017 17:26:35 -0500 Subject: [PATCH 0623/1738] Update tsplot warning message --- seaborn/timeseries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/timeseries.py b/seaborn/timeseries.py index 6278838107..a3b25bf457 100644 --- a/seaborn/timeseries.py +++ b/seaborn/timeseries.py @@ -177,8 +177,8 @@ def tsplot(data, time=None, unit=None, condition=None, value=None, """ msg = ( - "The tsplot function is deprecated and will be removed or replaced " - "(in a substantially altered version) in a future release." + "The `tsplot` function is deprecated and will be removed in a future " + "release. Please update your code to use the new `lineplot` function." ) warnings.warn(msg, UserWarning) From 5516ae559d3ac2d2b43fc0d0cda56b9da8791e15 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 29 Nov 2017 09:39:20 -0500 Subject: [PATCH 0624/1738] Tweaks to a few examples --- examples/color_palettes.py | 17 ++++++++--------- examples/errorband_lineplots.py | 2 +- ...te_violinplot.py => wide_form_violinplot.py} | 0 3 files changed, 9 insertions(+), 10 deletions(-) rename examples/{elaborate_violinplot.py => wide_form_violinplot.py} (100%) diff --git a/examples/color_palettes.py b/examples/color_palettes.py index 21d8edbee9..9daa6a260f 100644 --- a/examples/color_palettes.py +++ b/examples/color_palettes.py @@ -7,26 +7,25 @@ import seaborn as sns import matplotlib.pyplot as plt sns.set(style="white", context="talk") -rs = np.random.RandomState(7) - +rs = np.random.RandomState(8) # Set up the matplotlib figure f, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(8, 6), sharex=True) # Generate some sequential data -x = np.array(list("ABCDEFGHI")) -y1 = np.arange(1, 10) -sns.barplot(x, y1, palette="BuGn_d", ax=ax1) +x = np.array(list("ABCDEFGHIJ")) +y1 = np.arange(1, 11) +sns.barplot(x=x, y=y1, palette="mako", ax=ax1) ax1.set_ylabel("Sequential") # Center the data to make it diverging -y2 = y1 - 5 -sns.barplot(x, y2, palette="RdBu_r", ax=ax2) +y2 = y1 - 5.5 +sns.barplot(x=x, y=y2, palette="vlag", ax=ax2) ax2.set_ylabel("Diverging") # Randomly reorder the data to make it qualitative -y3 = rs.choice(y1, 9, replace=False) -sns.barplot(x, y3, palette="Set3", ax=ax3) +y3 = rs.choice(y1, len(y1), replace=False) +sns.barplot(x=x, y=y3, palette="Set3", ax=ax3) ax3.set_ylabel("Qualitative") # Finalize the plot diff --git a/examples/errorband_lineplots.py b/examples/errorband_lineplots.py index 6ecda53427..9c1ba16abe 100644 --- a/examples/errorband_lineplots.py +++ b/examples/errorband_lineplots.py @@ -6,7 +6,7 @@ """ import seaborn as sns -sns.set(style="ticks") +sns.set(style="darkgrid") # Load an example dataset with long-form data fmri = sns.load_dataset("fmri") diff --git a/examples/elaborate_violinplot.py b/examples/wide_form_violinplot.py similarity index 100% rename from examples/elaborate_violinplot.py rename to examples/wide_form_violinplot.py From 9420611955ff85210121cc3a94c5ca5ac7e4f780 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 29 Nov 2017 22:24:57 -0500 Subject: [PATCH 0625/1738] Update bootstrap to use named numpy array method --- seaborn/algorithms.py | 22 +++++-- seaborn/tests/test_algorithms.py | 107 ++++++++++++++++++------------- 2 files changed, 79 insertions(+), 50 deletions(-) diff --git a/seaborn/algorithms.py b/seaborn/algorithms.py index 9f850a607e..8a7a590ab9 100644 --- a/seaborn/algorithms.py +++ b/seaborn/algorithms.py @@ -2,6 +2,8 @@ from __future__ import division import numpy as np from scipy import stats +import warnings +from .external.six import string_types from .external.six.moves import range @@ -24,8 +26,9 @@ def bootstrap(*args, **kwargs): If True, performs a smoothed bootstrap (draws samples from a kernel destiny estimate); only works for one-dimensional inputs and cannot be used `units` is present. - func : callable, default np.mean - Function to call on the args that are passed in. + func : string or callable, default np.mean + Function to call on the args that are passed in. If string, tries + to use as named method on numpy array. random_seed : int | None, default None Seed for the random number generator; useful if you want reproducible resamples. @@ -61,19 +64,28 @@ def bootstrap(*args, **kwargs): if units is not None: units = np.asarray(units) + # Allow for a function that is the name of a method on an array + if isinstance(func, string_types): + def f(x): + return getattr(x, func)() + else: + f = func + # Do the bootstrap if smooth: - return _smooth_bootstrap(args, n_boot, func, func_kwargs) + msg = "Smooth bootstraps are deprecated and will be removed." + warnings.warn(msg) + return _smooth_bootstrap(args, n_boot, f, func_kwargs) if units is not None: - return _structured_bootstrap(args, n_boot, units, func, + return _structured_bootstrap(args, n_boot, units, f, func_kwargs, rs) boot_dist = [] for i in range(int(n_boot)): resampler = rs.randint(0, n, n) sample = [a.take(resampler, axis=0) for a in args] - boot_dist.append(func(*sample, **func_kwargs)) + boot_dist.append(f(*sample, **func_kwargs)) return np.array(boot_dist) diff --git a/seaborn/tests/test_algorithms.py b/seaborn/tests/test_algorithms.py index dfd31cac8f..1beede992c 100644 --- a/seaborn/tests/test_algorithms.py +++ b/seaborn/tests/test_algorithms.py @@ -2,16 +2,17 @@ from ..external.six.moves import range from numpy.testing import assert_array_equal -import nose.tools -from nose.tools import assert_equal, raises +import pytest from .. import algorithms as algo -rs = np.random.RandomState(sum(map(ord, "test_algorithms"))) -a_norm = rs.randn(100) +@pytest.fixture +def random(): + np.random.seed(sum(map(ord, "test_algorithms"))) -def test_bootstrap(): + +def test_bootstrap(random): """Test that bootstrapping gives the right answer in dumb cases.""" a_ones = np.ones(10) n_boot = 5 @@ -21,71 +22,79 @@ def test_bootstrap(): assert_array_equal(out2, np.ones(n_boot)) -def test_bootstrap_length(): +def test_bootstrap_length(random): """Test that we get a bootstrap array of the right shape.""" + a_norm = np.random.randn(1000) out = algo.bootstrap(a_norm) - assert_equal(len(out), 10000) + assert len(out) == 10000 n_boot = 100 out = algo.bootstrap(a_norm, n_boot=n_boot) - assert_equal(len(out), n_boot) + assert len(out) == n_boot -def test_bootstrap_range(): +def test_bootstrap_range(random): """Test that boostrapping a random array stays within the right range.""" - min, max = a_norm.min(), a_norm.max() + a_norm = np.random.randn(1000) + amin, amax = a_norm.min(), a_norm.max() out = algo.bootstrap(a_norm) - nose.tools.assert_less(min, out.min()) - nose.tools.assert_greater_equal(max, out.max()) + assert amin <= out.min() + assert amax >= out.max() -def test_bootstrap_multiarg(): +def test_bootstrap_multiarg(random): """Test that bootstrap works with multiple input arrays.""" x = np.vstack([[1, 10] for i in range(10)]) y = np.vstack([[5, 5] for i in range(10)]) - test_func = lambda x, y: np.vstack((x, y)).max(axis=0) - out_actual = algo.bootstrap(x, y, n_boot=2, func=test_func) + def f(x, y): + return np.vstack((x, y)).max(axis=0) + + out_actual = algo.bootstrap(x, y, n_boot=2, func=f) out_wanted = np.array([[5, 10], [5, 10]]) assert_array_equal(out_actual, out_wanted) -def test_bootstrap_axis(): +def test_bootstrap_axis(random): """Test axis kwarg to bootstrap function.""" - x = rs.randn(10, 20) + x = np.random.randn(10, 20) n_boot = 100 + out_default = algo.bootstrap(x, n_boot=n_boot) - assert_equal(out_default.shape, (n_boot,)) + assert out_default.shape == (n_boot,) + out_axis = algo.bootstrap(x, n_boot=n_boot, axis=0) - assert_equal(out_axis.shape, (n_boot, 20)) + assert out_axis.shape, (n_boot, x.shape[1]) -def test_bootstrap_random_seed(): +def test_bootstrap_random_seed(random): """Test that we can get reproducible resamples by seeding the RNG.""" - data = rs.randn(50) + data = np.random.randn(50) seed = 42 boots1 = algo.bootstrap(data, random_seed=seed) boots2 = algo.bootstrap(data, random_seed=seed) assert_array_equal(boots1, boots2) -def test_smooth_bootstrap(): +def test_smooth_bootstrap(random): """Test smooth bootstrap.""" - x = rs.randn(15) + x = np.random.randn(15) n_boot = 100 out_smooth = algo.bootstrap(x, n_boot=n_boot, smooth=True, func=np.median) - assert(not np.median(out_smooth) in x) + assert not np.median(out_smooth) in x -def test_bootstrap_ols(): +def test_bootstrap_ols(random): """Test bootstrap of OLS model fit.""" - ols_fit = lambda X, y: np.dot(np.dot(np.linalg.inv( - np.dot(X.T, X)), X.T), y) - X = np.column_stack((rs.randn(50, 4), np.ones(50))) + def ols_fit(X, y): + XtXinv = np.linalg.inv(np.dot(X.T, X)) + return XtXinv.dot(X.T).dot(y) + + X = np.column_stack((np.random.randn(50, 4), np.ones(50))) w = [2, 4, 0, 3, 5] - y_noisy = np.dot(X, w) + rs.randn(50) * 20 - y_lownoise = np.dot(X, w) + rs.randn(50) + y_noisy = np.dot(X, w) + np.random.randn(50) * 20 + y_lownoise = np.dot(X, w) + np.random.randn(50) n_boot = 500 w_boot_noisy = algo.bootstrap(X, y_noisy, @@ -95,34 +104,42 @@ def test_bootstrap_ols(): n_boot=n_boot, func=ols_fit) - assert_equal(w_boot_noisy.shape, (n_boot, 5)) - assert_equal(w_boot_lownoise.shape, (n_boot, 5)) - nose.tools.assert_greater(w_boot_noisy.std(), - w_boot_lownoise.std()) + assert w_boot_noisy.shape == (n_boot, 5) + assert w_boot_lownoise.shape == (n_boot, 5) + assert w_boot_noisy.std() > w_boot_lownoise.std() -def test_bootstrap_units(): +def test_bootstrap_units(random): """Test that results make sense when passing unit IDs to bootstrap.""" - data = rs.randn(50) + data = np.random.randn(50) ids = np.repeat(range(10), 5) - bwerr = rs.normal(0, 2, 10) + bwerr = np.random.normal(0, 2, 10) bwerr = bwerr[ids] data_rm = data + bwerr seed = 77 boots_orig = algo.bootstrap(data_rm, random_seed=seed) boots_rm = algo.bootstrap(data_rm, units=ids, random_seed=seed) - nose.tools.assert_greater(boots_rm.std(), boots_orig.std()) + assert boots_rm.std() > boots_orig.std() -@raises(ValueError) def test_bootstrap_arglength(): """Test that different length args raise ValueError.""" - algo.bootstrap(np.arange(5), np.arange(10)) + with pytest.raises(ValueError): + algo.bootstrap(np.arange(5), np.arange(10)) + + +def test_bootstrap_string_func(): + """Test that named numpy methods are the same as the numpy function.""" + x = np.random.randn(100) + + res_a = algo.bootstrap(x, func="mean", random_seed=0) + res_b = algo.bootstrap(x, func=np.mean, random_seed=0) + assert np.array_equal(res_a, res_b) + res_a = algo.bootstrap(x, func="std", random_seed=0) + res_b = algo.bootstrap(x, func=np.std, random_seed=0) + assert np.array_equal(res_a, res_b) -@raises(TypeError) -def test_bootstrap_noncallable(): - """Test that we get a TypeError with noncallable algo.unc.""" - non_func = "mean" - algo.bootstrap(a_norm, 100, non_func) + with pytest.raises(AttributeError): + algo.bootstrap(x, func="not_a_method_name") From 5574f5174a5b1026b5dc3aa69f8dcd50979f3137 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 29 Nov 2017 22:25:21 -0500 Subject: [PATCH 0626/1738] Simplify lineplot bootstrap logic --- seaborn/basic.py | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 34eb555070..dfa571de11 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -491,19 +491,6 @@ def aggregate(self, vals, grouper, func, ci): """Compute an estimate and confidence interval using grouper.""" n_boot = self.n_boot - # Define a function that might call an object method for aggregation - # TODO rework this logic, which is a mess - if callable(func): - def f(x): - try: - return x.apply(func) - except AttributeError: - return func(x) - - else: - def f(x): - return getattr(x, func)() - # Define a "null" CI for when we only have one value null_ci = pd.Series(index=["low", "high"], dtype=np.float) @@ -513,13 +500,13 @@ def bootstrapped_cis(vals): if len(vals) == 1: return null_ci - boots = bootstrap(vals, func=f, n_boot=n_boot) + boots = bootstrap(vals, func=func, n_boot=n_boot) cis = utils.ci(boots, ci) return pd.Series(cis, ["low", "high"]) # Group and get the aggregation estimate grouped = vals.groupby(grouper, sort=self.sort) - est = f(grouped) + est = grouped.agg(func) # Exit early if we don't want a confidence interval if ci is None: From 596d4ca14d6b8193f239966b497e9dfa96603aec Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 29 Nov 2017 22:32:49 -0500 Subject: [PATCH 0627/1738] Add lineplot release notes --- doc/releases/v0.9.0.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index 83d9c33ecf..43145e3451 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -2,6 +2,8 @@ v0.9.0 (Unreleased) ------------------- +- Added the :func:`lineplot` function for representing relationships between numeric ``x`` and ``y`` variables with lines, potentially subsetting by up to three other variables and semantically mapping those subsets with the color, size, or style of the lines. This function replaces :func:`tsplot`, but with an API that is more consistent with other modern seaborn functions and has both more flexibility (more dimensions of semantic mapping, better handling of dates) and less flexibility (fewer options for visual representing uncertainty). There is considerable new API documentation and a new tutorial TODO. + - Final removal of the previously-deprecated ``coefplot`` and ``interactplot`` functions. - Updated the testing infrastructure to execute tests with `pytest ` (although many individual tests still use nose assertion). From 685ca8b14ca05c8b5f7cc88fc82708a8f0a492fb Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 30 Nov 2017 15:14:24 -0500 Subject: [PATCH 0628/1738] Add tutorial notebook for new basic plots --- doc/.gitignore | 1 + doc/tutorial.rst | 1 + doc/tutorial/Makefile | 1 + doc/tutorial/aesthetics.ipynb | 102 ++----- doc/tutorial/axis_grids.ipynb | 138 +++------ doc/tutorial/basic.ipynb | 466 ++++++++++++++++++++++++++++++ doc/tutorial/categorical.ipynb | 146 +++------- doc/tutorial/color_palettes.ipynb | 162 +++-------- doc/tutorial/distributions.ipynb | 110 ++----- doc/tutorial/regression.ipynb | 139 +++------ 10 files changed, 689 insertions(+), 577 deletions(-) create mode 100644 doc/tutorial/basic.ipynb diff --git a/doc/.gitignore b/doc/.gitignore index 30dbc876a8..efe960d466 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -4,6 +4,7 @@ generated/ examples/ example_thumbs/ aesthetics.rst +basic.rst color_palettes.rst distributions.rst regression.rst diff --git a/doc/tutorial.rst b/doc/tutorial.rst index 03c2a39567..c6ab1f6be9 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -18,6 +18,7 @@ Plotting functions .. toctree:: :maxdepth: 2 + tutorial/basic tutorial/distributions tutorial/categorical tutorial/regression diff --git a/doc/tutorial/Makefile b/doc/tutorial/Makefile index 1dd93da344..d2edde1aa0 100644 --- a/doc/tutorial/Makefile +++ b/doc/tutorial/Makefile @@ -2,6 +2,7 @@ notebooks: tools/nb_to_doc.py aesthetics tools/nb_to_doc.py color_palettes + tools/nb_to_doc.py basic tools/nb_to_doc.py distributions tools/nb_to_doc.py regression tools/nb_to_doc.py categorical diff --git a/doc/tutorial/aesthetics.ipynb b/doc/tutorial/aesthetics.ipynb index dd65a764ec..9a830915df 100644 --- a/doc/tutorial/aesthetics.ipynb +++ b/doc/tutorial/aesthetics.ipynb @@ -28,9 +28,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline" @@ -39,9 +37,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", @@ -61,9 +57,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "def sinplot(flip=1):\n", @@ -82,9 +76,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sinplot()" @@ -100,9 +92,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.set()\n", @@ -130,9 +120,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.set_style(\"whitegrid\")\n", @@ -150,9 +138,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.set_style(\"dark\")" @@ -161,9 +147,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sinplot()" @@ -172,9 +156,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.set_style(\"white\")" @@ -183,9 +165,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sinplot()" @@ -201,9 +181,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.set_style(\"ticks\")\n", @@ -225,9 +203,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sinplot()\n", @@ -244,9 +220,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "f, ax = plt.subplots()\n", @@ -264,9 +238,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.set_style(\"whitegrid\")\n", @@ -287,9 +259,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "with sns.axes_style(\"darkgrid\"):\n", @@ -314,9 +284,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.axes_style()" @@ -332,9 +300,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.set_style(\"darkgrid\", {\"axes.facecolor\": \".9\"})\n", @@ -358,9 +324,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.set()" @@ -376,9 +340,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.set_context(\"paper\")\n", @@ -388,9 +350,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.set_context(\"talk\")\n", @@ -400,9 +360,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.set_context(\"poster\")\n", @@ -423,9 +381,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.set_context(\"notebook\", font_scale=1.5, rc={\"lines.linewidth\": 2.5})\n", @@ -444,23 +400,23 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 2", + "display_name": "Python 3.6 (seaborn-dev)", "language": "python", - "name": "python2" + "name": "seaborn-dev" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.12" + "pygments_lexer": "ipython3", + "version": "3.6.3" } }, "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file + "nbformat_minor": 1 +} diff --git a/doc/tutorial/axis_grids.ipynb b/doc/tutorial/axis_grids.ipynb index 779e0c1623..3d45ca4502 100644 --- a/doc/tutorial/axis_grids.ipynb +++ b/doc/tutorial/axis_grids.ipynb @@ -30,9 +30,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline" @@ -41,9 +39,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", @@ -57,9 +53,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.set(style=\"ticks\")\n", @@ -85,9 +79,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "tips = sns.load_dataset(\"tips\")" @@ -96,9 +88,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.FacetGrid(tips, col=\"time\")" @@ -116,9 +106,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.FacetGrid(tips, col=\"time\")\n", @@ -135,9 +123,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.FacetGrid(tips, col=\"sex\", hue=\"smoker\")\n", @@ -155,9 +141,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.FacetGrid(tips, row=\"smoker\", col=\"time\", margin_titles=True)\n", @@ -176,9 +160,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.FacetGrid(tips, col=\"day\", size=4, aspect=.5)\n", @@ -195,9 +177,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "titanic = sns.load_dataset(\"titanic\")\n", @@ -217,9 +197,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "ordered_days = tips.day.value_counts().index\n", @@ -238,9 +216,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "pal = dict(Lunch=\"seagreen\", Dinner=\"gray\")\n", @@ -259,9 +235,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.FacetGrid(tips, hue=\"sex\", palette=\"Set1\", size=5, hue_kws={\"marker\": [\"^\", \"v\"]})\n", @@ -279,9 +253,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "attend = sns.load_dataset(\"attention\").query(\"subject <= 12\")\n", @@ -299,9 +271,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "with sns.axes_style(\"white\"):\n", @@ -322,9 +292,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.FacetGrid(tips, col=\"smoker\", margin_titles=True, size=4)\n", @@ -355,9 +323,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "def quantile_plot(x, **kwargs):\n", @@ -378,9 +344,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "def qqplot(x, y, **kwargs):\n", @@ -402,9 +366,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.FacetGrid(tips, hue=\"time\", col=\"sex\", size=4)\n", @@ -422,9 +384,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.FacetGrid(tips, hue=\"time\", col=\"sex\", size=4,\n", @@ -443,9 +403,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "def hexbin(x, y, color, **kwargs):\n", @@ -476,9 +434,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "iris = sns.load_dataset(\"iris\")\n", @@ -496,9 +452,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.PairGrid(iris)\n", @@ -516,9 +470,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.PairGrid(iris, hue=\"species\")\n", @@ -537,9 +489,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.PairGrid(iris, vars=[\"sepal_length\", \"sepal_width\"], hue=\"species\")\n", @@ -556,9 +506,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.PairGrid(iris)\n", @@ -577,9 +525,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.PairGrid(tips, y_vars=[\"tip\"], x_vars=[\"total_bill\", \"size\"], size=4)\n", @@ -597,9 +543,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.PairGrid(tips, hue=\"size\", palette=\"GnBu_d\")\n", @@ -617,9 +561,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.pairplot(iris, hue=\"species\", size=2.5);" @@ -635,9 +577,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.pairplot(iris, hue=\"species\", palette=\"Set2\", diag_kind=\"kde\", size=2.5)" @@ -646,32 +586,30 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 2", + "display_name": "Python 3.6 (seaborn-dev)", "language": "python", - "name": "python2" + "name": "seaborn-dev" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.12" + "pygments_lexer": "ipython3", + "version": "3.6.3" } }, "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file + "nbformat_minor": 1 +} diff --git a/doc/tutorial/basic.ipynb b/doc/tutorial/basic.ipynb new file mode 100644 index 0000000000..6a2f3c6d35 --- /dev/null +++ b/doc/tutorial/basic.ipynb @@ -0,0 +1,466 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": {}, + "source": [ + ".. _basic_tutorial:\n", + "\n", + ".. currentmodule:: seaborn" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Basic plots to show numeric relationships" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib as mpl\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import seaborn as sns\n", + "sns.set(style=\"darkgrid\", color_codes=True)\n", + "np.random.seed(sum(map(ord, \"basic\")))" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Emphasizing continuity with line plots\n", + "--------------------------------------\n", + "\n", + "With some datasets, you may want to understand changes in one variable as a function of another variable that has some sense of continuity. This is most common when one of the variables represents time, either abstractly or with a datetime object. In this situation, a good choice is to draw a line plot. In seaborn, this can be accomplished with the :func:`lineplot` function.\n", + "\n", + "The simplest case is when you have a vector of timepoints and a vector of values. To draw the function relating these variables, pass each to the ``x`` and ``y`` parameters of the :func:`lineplot` function, respectively:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "time = pd.date_range(\"2017-01-01\", periods=24 * 31, freq=\"h\")\n", + "value = np.random.randn(len(time)).cumsum()\n", + "sns.lineplot(x=time, y=value);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Because :func:`lineplot` assumes that you are most often trying to draw ``y`` as a function of ``x``, the default behavior is to sort the data by the ``x`` values before plotting. However, this can be disabled:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x, y = np.random.randn(2, 1000).cumsum(axis=1)\n", + "sns.lineplot(x=x, y=y, sort=False);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Aggregation and representing uncertainty\n", + "----------------------------------------\n", + "\n", + "More complex datasets will have multiple measurements for the same value of the ``x`` variable. The default behavior in seaborn is to aggregate the multiple measurements at each ``x`` value by plotting the mean and the 95% confidence interval around the mean:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fmri = sns.load_dataset(\"fmri\")\n", + "sns.lineplot(x=\"timepoint\", y=\"signal\", data=fmri);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "The confidence intervals are computed using bootstrapping, which can be time-intensive for larger datasets. It's therefore possible to disable them:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sns.lineplot(x=\"timepoint\", y=\"signal\", data=fmri, ci=None);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Another good option, especially with larger data, is to represent the spread of the distribution at each timepoint by plotting the standard deviation instead of a confidence interval:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sns.lineplot(x=\"timepoint\", y=\"signal\", data=fmri, ci=\"sd\");" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "To turn off aggregation altogether, set the ``estimator`` parameter to ``None``." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sns.lineplot(x=\"timepoint\", y=\"signal\", data=fmri, estimator=None);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "The ``estimator`` parameter can also be used to control what method is used to aggregate the data." + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Plotting subsets of data with semantic mappings\n", + "-----------------------------------------------\n", + "\n", + "Often there will be multiple measurements at each value of ``x`` because we want to know how the relationship between ``x`` and ``y`` changes as a function of other variables. The :func:`lineplot` function allows you to define up to three additional variables that will be used to subset the data. These variables are then semantically mapped by the color (``hue``), width (``size``) and dashes/markers (``style``) used to draw the lines. For example, we can draw two line plots with different colors simply by defining a variable to be used for ``hue`` subsets:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sns.lineplot(x=\"timepoint\", y=\"signal\", hue=\"event\", data=fmri);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "By adding a separate ``style`` variable, we can explore more complex relationships:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sns.lineplot(x=\"timepoint\", y=\"signal\", hue=\"region\", style=\"event\", data=fmri);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Be cautious about making plots with multiple subset variables. While sometimes informative, they can also be very difficult to parse and interperet. However, even when you are only examining changes across one subset variable, it can be useful to alter both the color and style of the lines, which can make the plot more accessible when printed to black-and-white or viewed by someone with colorblindness:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sns.lineplot(x=\"timepoint\", y=\"signal\", hue=\"event\", style=\"event\", data=fmri);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Some effort has been put into choosing good defaults so that you do not need to spend time specifying plot attributes for quick exploration, but the way the ``hue`` and ``style`` variables are mapped can be controlled through various parameters to the :func:`lineplot` function:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sns.lineplot(x=\"timepoint\", y=\"signal\", hue=\"region\", style=\"event\",\n", + " palette=\"Set2\", hue_order=[\"frontal\", \"parietal\"],\n", + " dashes=[\"\", (1, 1)],\n", + " data=fmri);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "By default, the ``style`` variable is represented by drawing lines with different dash patterns, but you can also draw markers with different shapes at the exact position of each observation:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sns.lineplot(x=\"timepoint\", y=\"signal\", hue=\"region\", style=\"event\",\n", + " palette=\"Set2\", hue_order=[\"frontal\", \"parietal\"],\n", + " dashes=False, markers=True,\n", + " data=fmri);" + ] + }, + { + "cell_type": "raw", + "metadata": { + "collapsed": true + }, + "source": [ + "In the above examples, the ``hue`` variable takes different categorical values, and the colors of the lines are chosen with an appropriate qualitative colormap. When the ``hue`` variable is instead numeric (specifcally, if it can be cast to float), the default behavior is to use a sequential colormap and to make a legend with \"ticks\" instead of an entry for each line (allowing it to scale to showing many lines):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dots = sns.load_dataset(\"dots\").query(\"align == 'dots'\")\n", + "sns.lineplot(x=\"time\", y=\"firing_rate\",\n", + " hue=\"coherence\", style=\"choice\",\n", + " data=dots);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "A non-default colormap can be selected in this case by passing a colormap name or object, and you can ask for a ``\"full\"`` legend:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cmap = sns.cubehelix_palette(light=.7, as_cmap=True)\n", + "sns.lineplot(x=\"time\", y=\"firing_rate\",\n", + " hue=\"coherence\", style=\"choice\",\n", + " palette=cmap,\n", + " legend=\"full\", data=dots);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "It may happen that, even though the ``hue`` variable is numeric, it is poorly represented by a linear color scale. That's the case here, where the levels of the ``hue`` variable are logarithmically scaled. You can provide specific color values for each line by passing a list or dictionary:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "palette = sns.cubehelix_palette(light=.7, n_colors=6)\n", + "sns.lineplot(x=\"time\", y=\"firing_rate\",\n", + " hue=\"coherence\", style=\"choice\",\n", + " palette=palette,\n", + " data=dots);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Another option for semantically mapping a subset is to change the width of its lines, which is accomplished by defining the ``size`` parameter:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sns.lineplot(x=\"time\", y=\"firing_rate\",\n", + " size=\"coherence\", style=\"choice\",\n", + " data=dots);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "It's possible to control the range of line widths that are spanned by the data using the ``sizes`` parameter. Here we pass a ``(min, max)`` tuple, it it's also possible to pass a list of dictionary to precisely specify the width of each line:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sns.lineplot(x=\"time\", y=\"firing_rate\",\n", + " size=\"coherence\", style=\"choice\",\n", + " sizes=(1, 2),\n", + " data=dots);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "While the ``size`` variable will typically be numeric, it's also possible to map a categorical variable with the width of the lines. Be cautious when doing so, because it will be very difficult to distinguish much more than \"thick\" vs \"thin\" lines. However, dashes can be hard to perceive when lines have considerable high-frequency variability, so using different widths may be helpful:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "palette = sns.cubehelix_palette(light=.7, n_colors=6)\n", + "sns.lineplot(x=\"time\", y=\"firing_rate\",\n", + " hue=\"coherence\", size=\"choice\",\n", + " palette=palette,\n", + " data=dots);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Plotting with \"long\" versus \"wide\" data\n", + "---------------------------------------\n", + "\n", + "Like many other functions, :func:`lineplot` is most flexible when it is provided with \"long-\n", + "form\" (or \"tidy\") data, typically in the form of a DataFrame where each column represents a variable and each row represents an observation. However, data are not always naturally generated or stored in long-form format, and it can be helpful to be able to take a quick look without reformatting. To support this, :func:`lineplot` can visualize a number of different \"wide-form\" representations if they are passed to ``data``. For instance, you can pass a wide DataFrame, which will draw a line for each column using the index for the ``x`` values:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "date = pd.date_range(\"2017-01-01\", periods=365)\n", + "vals = np.random.randn(365, 4).cumsum(axis=0)\n", + "wide_df = pd.DataFrame(vals, date, list(\"ABCD\"))\n", + "sns.lineplot(data=wide_df);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "Numpy arrays are handled similarly, except you lose the label information that you get from pandas:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "wide_array = np.asarray(wide_df)\n", + "sns.lineplot(data=wide_array);" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "You can even pass in a list of objects with heterogreneous indices:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "wide_list = [wide_df.loc[:\"2017-09-01\", \"A\"], wide_df.loc[\"2017-05-1\":, \"B\"]]\n", + "sns.lineplot(data=wide_list);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.6 (seaborn-dev)", + "language": "python", + "name": "seaborn-dev" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/doc/tutorial/categorical.ipynb b/doc/tutorial/categorical.ipynb index 0f8661cd0b..bbf9bb5328 100644 --- a/doc/tutorial/categorical.ipynb +++ b/doc/tutorial/categorical.ipynb @@ -32,9 +32,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline" @@ -43,9 +41,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", @@ -57,9 +53,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "import seaborn as sns\n", @@ -69,9 +63,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "np.random.seed(sum(map(ord, \"categorical\")))" @@ -80,9 +72,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "titanic = sns.load_dataset(\"titanic\")\n", @@ -103,9 +93,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.stripplot(x=\"day\", y=\"total_bill\", data=tips);" @@ -121,9 +109,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.stripplot(x=\"day\", y=\"total_bill\", data=tips, jitter=True);" @@ -139,9 +125,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.swarmplot(x=\"day\", y=\"total_bill\", data=tips);" @@ -157,9 +141,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.swarmplot(x=\"day\", y=\"total_bill\", hue=\"sex\", data=tips);" @@ -175,9 +157,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.swarmplot(x=\"size\", y=\"total_bill\", data=tips);" @@ -193,9 +173,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.swarmplot(x=\"total_bill\", y=\"day\", hue=\"time\", data=tips);" @@ -219,9 +197,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.boxplot(x=\"day\", y=\"total_bill\", hue=\"time\", data=tips);" @@ -237,9 +213,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "tips[\"weekend\"] = tips[\"day\"].isin([\"Sat\", \"Sun\"])\n", @@ -259,9 +233,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.violinplot(x=\"total_bill\", y=\"day\", hue=\"time\", data=tips);" @@ -277,9 +249,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.violinplot(x=\"total_bill\", y=\"day\", hue=\"time\", data=tips,\n", @@ -296,9 +266,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.violinplot(x=\"day\", y=\"total_bill\", hue=\"sex\", data=tips, split=True);" @@ -314,9 +282,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.violinplot(x=\"day\", y=\"total_bill\", hue=\"sex\", data=tips,\n", @@ -333,9 +299,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.violinplot(x=\"day\", y=\"total_bill\", data=tips, inner=None)\n", @@ -360,9 +324,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.barplot(x=\"sex\", y=\"survived\", hue=\"class\", data=titanic);" @@ -378,9 +340,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.countplot(x=\"deck\", data=titanic, palette=\"Greens_d\");" @@ -388,9 +348,7 @@ }, { "cell_type": "raw", - "metadata": { - "collapsed": false - }, + "metadata": {}, "source": [ "Both :func:`barplot` and :func:`countplot` can be invoked with all of the options discussed above, along with others that are demonstrated in the detailed documentation for each function:" ] @@ -398,9 +356,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.countplot(y=\"deck\", hue=\"class\", data=titanic, palette=\"Greens_d\");" @@ -419,9 +375,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.pointplot(x=\"sex\", y=\"survived\", hue=\"class\", data=titanic);" @@ -437,9 +391,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.pointplot(x=\"class\", y=\"survived\", hue=\"sex\", data=titanic,\n", @@ -460,9 +412,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.boxplot(data=iris, orient=\"h\");" @@ -478,9 +428,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.violinplot(x=iris.species, y=iris.sepal_length);" @@ -496,9 +444,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "f, ax = plt.subplots(figsize=(7, 3))\n", @@ -518,9 +464,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.factorplot(x=\"day\", y=\"total_bill\", hue=\"smoker\", data=tips);" @@ -536,9 +480,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.factorplot(x=\"day\", y=\"total_bill\", hue=\"smoker\", data=tips, kind=\"bar\");" @@ -554,9 +496,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.factorplot(x=\"day\", y=\"total_bill\", hue=\"smoker\",\n", @@ -573,9 +513,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.factorplot(x=\"time\", y=\"total_bill\", hue=\"smoker\",\n", @@ -594,9 +532,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.PairGrid(tips,\n", @@ -609,32 +545,30 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 2", + "display_name": "Python 3.6 (seaborn-dev)", "language": "python", - "name": "python2" + "name": "seaborn-dev" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.12" + "pygments_lexer": "ipython3", + "version": "3.6.3" } }, "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file + "nbformat_minor": 1 +} diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index a07efea16f..2609d5eab5 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -32,9 +32,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline" @@ -43,9 +41,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", @@ -56,9 +52,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.set(rc={\"figure.figsize\": (6, 6)})\n", @@ -102,9 +96,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "current_palette = sns.color_palette()\n", @@ -128,9 +120,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.color_palette(\"hls\", 8))" @@ -146,9 +136,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.hls_palette(8, l=.3, s=.8))" @@ -168,9 +156,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.color_palette(\"husl\", 8))" @@ -193,9 +179,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.color_palette(\"Paired\"))" @@ -204,9 +188,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.color_palette(\"Set2\", 10))" @@ -224,9 +206,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "flatui = [\"#9b59b6\", \"#3498db\", \"#95a5a6\", \"#e74c3c\", \"#34495e\", \"#2ecc71\"]\n", @@ -248,9 +228,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.plot([0, 1], [0, 1], sns.xkcd_rgb[\"pale red\"], lw=3)\n", @@ -268,9 +246,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "colors = [\"windows blue\", \"amber\", \"greyish\", \"faded green\", \"dusty purple\"]\n", @@ -298,9 +274,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.color_palette(\"Blues\"))" @@ -316,9 +290,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.color_palette(\"BuGn_r\"))" @@ -334,9 +306,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.color_palette(\"GnBu_d\"))" @@ -368,9 +338,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.color_palette(\"cubehelix\", 8))" @@ -388,9 +356,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.cubehelix_palette(8))" @@ -406,9 +372,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.cubehelix_palette(8, start=.5, rot=-.75))" @@ -424,9 +388,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.cubehelix_palette(8, start=2, rot=0, dark=0, light=.95, reverse=True))" @@ -442,9 +404,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "x, y = np.random.multivariate_normal([0, 0], [[1, -.5], [-.5, 1]], size=300).T\n", @@ -472,9 +432,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.light_palette(\"green\"))" @@ -483,9 +441,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.dark_palette(\"purple\"))" @@ -501,9 +457,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.light_palette(\"navy\", reverse=True))" @@ -519,9 +473,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "pal = sns.dark_palette(\"palegreen\", as_cmap=True)\n", @@ -538,9 +490,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.light_palette((210, 90, 60), input=\"husl\"))" @@ -549,9 +499,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.dark_palette(\"muted purple\", input=\"xkcd\"))" @@ -585,9 +533,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.color_palette(\"BrBG\", 7))" @@ -596,9 +542,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.color_palette(\"RdBu_r\", 7))" @@ -614,9 +558,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.color_palette(\"coolwarm\", 7))" @@ -635,9 +577,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.diverging_palette(220, 20, n=7))" @@ -646,9 +586,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.diverging_palette(145, 280, s=85, l=25, n=7))" @@ -664,9 +602,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.diverging_palette(10, 220, sep=80, n=7))" @@ -682,9 +618,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.palplot(sns.diverging_palette(255, 133, l=60, n=7, center=\"dark\"))" @@ -705,9 +639,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "def sinplot(flip=1):\n", @@ -719,9 +651,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.set_palette(\"husl\")\n", @@ -738,9 +668,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "with sns.color_palette(\"PuBuGn_d\"):\n", @@ -750,32 +678,30 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 2", + "display_name": "Python 3.6 (seaborn-dev)", "language": "python", - "name": "python2" + "name": "seaborn-dev" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.12" + "pygments_lexer": "ipython3", + "version": "3.6.3" } }, "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file + "nbformat_minor": 1 +} diff --git a/doc/tutorial/distributions.ipynb b/doc/tutorial/distributions.ipynb index ae0eb94e8b..fc69327ffb 100644 --- a/doc/tutorial/distributions.ipynb +++ b/doc/tutorial/distributions.ipynb @@ -26,9 +26,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline" @@ -37,9 +35,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", @@ -51,9 +47,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "import seaborn as sns\n", @@ -63,9 +57,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "np.random.seed(sum(map(ord, \"distributions\")))" @@ -84,9 +76,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "x = np.random.normal(size=100)\n", @@ -108,9 +98,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.distplot(x, kde=False, rug=True);" @@ -126,9 +114,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.distplot(x, bins=20, kde=False, rug=True);" @@ -147,9 +133,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.distplot(x, hist=False, rug=True);" @@ -165,9 +149,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "x = np.random.normal(0, 1, size=30)\n", @@ -194,9 +176,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "density = np.sum(kernels, axis=0)\n", @@ -214,9 +194,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.kdeplot(x, shade=True);" @@ -232,9 +210,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.kdeplot(x)\n", @@ -253,9 +229,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.kdeplot(x, shade=True, cut=0)\n", @@ -275,9 +249,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "x = np.random.gamma(6, size=200)\n", @@ -297,9 +269,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "mean, cov = [0, 1], [(1, .5), (.5, 1)]\n", @@ -320,9 +290,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.jointplot(x=\"x\", y=\"y\", data=df);" @@ -341,9 +309,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "x, y = np.random.multivariate_normal(mean, cov, 1000).T\n", @@ -364,9 +330,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.jointplot(x=\"x\", y=\"y\", data=df, kind=\"kde\");" @@ -382,9 +346,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "f, ax = plt.subplots(figsize=(6, 6))\n", @@ -403,9 +365,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "f, ax = plt.subplots(figsize=(6, 6))\n", @@ -423,9 +383,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.jointplot(x=\"x\", y=\"y\", data=df, kind=\"kde\", color=\"m\")\n", @@ -447,9 +405,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "iris = sns.load_dataset(\"iris\")\n", @@ -466,9 +422,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "g = sns.PairGrid(iris)\n", @@ -479,32 +433,30 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 2", + "display_name": "Python 3.6 (seaborn-dev)", "language": "python", - "name": "python2" + "name": "seaborn-dev" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.10" + "pygments_lexer": "ipython3", + "version": "3.6.3" } }, "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file + "nbformat_minor": 1 +} diff --git a/doc/tutorial/regression.ipynb b/doc/tutorial/regression.ipynb index 8763255fea..75547ded12 100644 --- a/doc/tutorial/regression.ipynb +++ b/doc/tutorial/regression.ipynb @@ -28,9 +28,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline" @@ -39,9 +37,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", @@ -53,9 +49,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "import seaborn as sns\n", @@ -65,9 +59,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "np.random.seed(sum(map(ord, \"regression\")))" @@ -76,9 +68,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "tips = sns.load_dataset(\"tips\")" @@ -99,9 +89,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.regplot(x=\"total_bill\", y=\"tip\", data=tips);" @@ -110,9 +98,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"total_bill\", y=\"tip\", data=tips);" @@ -130,9 +116,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"size\", y=\"tip\", data=tips);" @@ -148,9 +132,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"size\", y=\"tip\", data=tips, x_jitter=.05);" @@ -166,9 +148,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"size\", y=\"tip\", data=tips, x_estimator=np.mean);" @@ -187,9 +167,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "anscombe = sns.load_dataset(\"anscombe\")" @@ -198,9 +176,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"x\", y=\"y\", data=anscombe.query(\"dataset == 'I'\"),\n", @@ -217,9 +193,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"x\", y=\"y\", data=anscombe.query(\"dataset == 'II'\"),\n", @@ -238,9 +212,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"x\", y=\"y\", data=anscombe.query(\"dataset == 'II'\"),\n", @@ -257,9 +229,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"x\", y=\"y\", data=anscombe.query(\"dataset == 'III'\"),\n", @@ -276,9 +246,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"x\", y=\"y\", data=anscombe.query(\"dataset == 'III'\"),\n", @@ -295,9 +263,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "tips[\"big_tip\"] = (tips.tip / tips.total_bill) > .15\n", @@ -315,9 +281,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"total_bill\", y=\"big_tip\", data=tips,\n", @@ -336,9 +300,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"total_bill\", y=\"tip\", data=tips,\n", @@ -355,9 +317,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.residplot(x=\"x\", y=\"y\", data=anscombe.query(\"dataset == 'I'\"),\n", @@ -374,9 +334,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.residplot(x=\"x\", y=\"y\", data=anscombe.query(\"dataset == 'II'\"),\n", @@ -398,9 +356,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"total_bill\", y=\"tip\", hue=\"smoker\", data=tips);" @@ -416,9 +372,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"total_bill\", y=\"tip\", hue=\"smoker\", data=tips,\n", @@ -435,9 +389,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"total_bill\", y=\"tip\", hue=\"smoker\", col=\"time\", data=tips);" @@ -446,9 +398,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"total_bill\", y=\"tip\", hue=\"smoker\",\n", @@ -468,9 +418,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "f, ax = plt.subplots(figsize=(5, 6))\n", @@ -487,9 +435,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"total_bill\", y=\"tip\", col=\"day\", data=tips,\n", @@ -499,10 +445,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.lmplot(x=\"total_bill\", y=\"tip\", col=\"day\", data=tips,\n", @@ -522,9 +465,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.jointplot(x=\"total_bill\", y=\"tip\", data=tips, kind=\"reg\");" @@ -540,9 +481,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.pairplot(tips, x_vars=[\"total_bill\", \"size\"], y_vars=[\"tip\"],\n", @@ -559,9 +498,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "sns.pairplot(tips, x_vars=[\"total_bill\", \"size\"], y_vars=[\"tip\"],\n", @@ -571,23 +508,23 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 2", + "display_name": "Python 3.6 (seaborn-dev)", "language": "python", - "name": "python2" + "name": "seaborn-dev" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.10" + "pygments_lexer": "ipython3", + "version": "3.6.3" } }, "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file + "nbformat_minor": 1 +} From b882bf121932739a9fe237d3fdab9f5cee975a88 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 30 Nov 2017 16:15:36 -0500 Subject: [PATCH 0629/1738] Improve lineplot example --- seaborn/basic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index dfa571de11..e7d63cb00a 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -1001,7 +1001,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, :context: close-figs >>> ax = sns.lineplot(x="time", y="firing_rate", - ... size="coherence", style="choice", + ... size="coherence", hue="choice", ... legend="full", data=dots) Change the range of line widths used to normalize the size variable: @@ -1010,7 +1010,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, :context: close-figs >>> ax = sns.lineplot(x="time", y="firing_rate", - ... size="coherence", style="choice", + ... size="coherence", hue="choice", ... sizes=(.2, 1), data=dots) Plot from a wide-form DataFrame: From 6a8f9fd91784424ed7ee31dd4b547ea782bc8b78 Mon Sep 17 00:00:00 2001 From: Fabian Rost Date: Wed, 7 Feb 2018 10:22:17 +0100 Subject: [PATCH 0630/1738] Fix typo in distplot docstring --- seaborn/distributions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index dde8fea6f4..71c87035ec 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -117,7 +117,7 @@ def distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, >>> x = pd.Series(x, name="x variable") >>> ax = sns.distplot(x) - Plot the distribution with a kenel density estimate and rug plot: + Plot the distribution with a kernel density estimate and rug plot: .. plot:: :context: close-figs From a82953529647cdda9ee20addee486b17a18fb8bf Mon Sep 17 00:00:00 2001 From: Chris Catalfo Date: Wed, 21 Feb 2018 21:51:41 -0500 Subject: [PATCH 0631/1738] Switch docstring parameters from split to dodge for swarmplot and stripplot. --- seaborn/categorical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 6fad8b339e..30620b0d43 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2633,7 +2633,7 @@ def stripplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, it is easier to see the distribution. You can specify the amount of jitter (half the width of the uniform random variable support), or just use ``True`` for a good default. - split : bool, optional + dodge : bool, optional When using ``hue`` nesting, setting this to ``True`` will separate the strips for different hue levels along the categorical axis. Otherwise, the points for each level will be plotted on top of @@ -2837,7 +2837,7 @@ def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, {input_params} {categorical_data} {order_vars} - split : bool, optional + dodge : bool, optional When using ``hue`` nesting, setting this to ``True`` will separate the strips for different hue levels along the categorical axis. Otherwise, the points for each level will be plotted in one swarm. From 0dc3dd2b1355e83ab3241781ab968489e6c19a3d Mon Sep 17 00:00:00 2001 From: stonebig Date: Sat, 24 Feb 2018 18:33:41 +0100 Subject: [PATCH 0632/1738] patching for matplotlib-2.2 --- seaborn/distributions.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index dde8fea6f4..7bda92fe80 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -6,6 +6,7 @@ import matplotlib as mpl import matplotlib.pyplot as plt import warnings +from distutils.version import LooseVersion from six import string_types @@ -211,7 +212,11 @@ def distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, if bins is None: bins = min(_freedman_diaconis_bins(a), 50) hist_kws.setdefault("alpha", 0.4) - hist_kws.setdefault("normed", norm_hist) + if LooseVersion(mpl.__version__) < LooseVersion("2.2"): + hist_kws.setdefault("normed", norm_hist) + else: + hist_kws.setdefault("density", norm_hist) + orientation = "horizontal" if vertical else "vertical" hist_color = hist_kws.pop("color", color) ax.hist(a, bins, orientation=orientation, From 44436871d709c6f8003f432c18c893f8e3043da3 Mon Sep 17 00:00:00 2001 From: Ravi Makhija Date: Wed, 10 Jan 2018 22:23:32 -0500 Subject: [PATCH 0633/1738] Fix for list inputs to JointGrid --- seaborn/axisgrid.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 0838d68273..46a2473cbc 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1682,12 +1682,6 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, err = "Could not interpret input '{}'".format(var) raise ValueError(err) - # Possibly drop NA - if dropna: - not_na = pd.notnull(x) & pd.notnull(y) - x = x[not_na] - y = y[not_na] - # Find the names of the variables if hasattr(x, "name"): xlabel = x.name @@ -1696,9 +1690,18 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, ylabel = y.name ax_joint.set_ylabel(ylabel) - # Convert the x and y data to arrays for plotting - self.x = np.asarray(x) - self.y = np.asarray(y) + # Convert the x and y data to arrays for indexing and plotting + x_array = np.asarray(x) + y_array = np.asarray(y) + + # Possibly drop NA + if dropna: + not_na = pd.notnull(x_array) & pd.notnull(y_array) + x_array = x_array[not_na] + y_array = y_array[not_na] + + self.x = x_array + self.y = y_array if xlim is not None: ax_joint.set_xlim(xlim) From 35c4cfbfffa429ac9dc47c2f1e9a2d5839d7ada2 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 5 Mar 2018 14:55:16 -0500 Subject: [PATCH 0634/1738] Remove troublesome and superfluous doctest --- seaborn/matrix.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index f64170bd08..1c2da1e71a 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -912,10 +912,6 @@ def standard_scale(data2d, axis=1): Noramlized data with a mean of 0 and variance of 1 across the specified axis. - >>> import numpy as np - >>> d = np.arange(5, 8, 0.5) - >>> ClusterGrid.standard_scale(d) - array([ 0. , 0.2, 0.4, 0.6, 0.8, 1. ]) """ # Normalize these values to range from 0 to 1 if axis == 1: From 526795d7c06861aa4a1762f9f10d74a5d95dfa7c Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 5 Mar 2018 14:55:29 -0500 Subject: [PATCH 0635/1738] Test that JointGrid accepts lists --- seaborn/tests/test_axisgrid.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 2a4825ccfe..89f4eea6ae 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -1276,6 +1276,12 @@ class TestJointGrid(object): x_na[20] = np.nan data = pd.DataFrame(dict(x=x, y=y, x_na=x_na)) + def test_margin_grid_from_lists(self): + + g = ag.JointGrid(self.x.tolist(), self.y.tolist()) + npt.assert_array_equal(g.x, self.x) + npt.assert_array_equal(g.y, self.y) + def test_margin_grid_from_arrays(self): g = ag.JointGrid(self.x, self.y) @@ -1297,7 +1303,7 @@ def test_margin_grid_from_dataframe(self): def test_margin_grid_from_dataframe_bad_variable(self): with nt.assert_raises(ValueError): - g = ag.JointGrid("x", "bad_column", self.data) + ag.JointGrid("x", "bad_column", self.data) def test_margin_grid_axis_labels(self): From 32a5dba311d32279e170dfbafd46536a3a0e7578 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 5 Mar 2018 14:57:23 -0500 Subject: [PATCH 0636/1738] Update release notes --- doc/releases/v0.9.0.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index 43145e3451..ebd5265909 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -2,8 +2,11 @@ v0.9.0 (Unreleased) ------------------- -- Added the :func:`lineplot` function for representing relationships between numeric ``x`` and ``y`` variables with lines, potentially subsetting by up to three other variables and semantically mapping those subsets with the color, size, or style of the lines. This function replaces :func:`tsplot`, but with an API that is more consistent with other modern seaborn functions and has both more flexibility (more dimensions of semantic mapping, better handling of dates) and less flexibility (fewer options for visual representing uncertainty). There is considerable new API documentation and a new tutorial TODO. +- Added the :func:`lineplot` function for representing relationships between numeric ``x`` and ``y`` variables with lines, potentially after conditioning on up to three other variables and semantically mapping those conditions with the color, size, or style of the lines. This function replaces :func:`tsplot`, but with an API that is more consistent with other modern seaborn functions and has both more flexibility (more dimensions of semantic mapping, better handling of dates) and less flexibility (fewer options for visual representing uncertainty). There is considerable new API documentation and there should also be a new tutorial. - Final removal of the previously-deprecated ``coefplot`` and ``interactplot`` functions. +- Fixed :func:`jointplot`/:class:`JointGrid` so that they now accept list inputs. + - Updated the testing infrastructure to execute tests with `pytest ` (although many individual tests still use nose assertion). + From 58868a33db82bb16f9040a0b0474ebe9e3b72c21 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 5 Mar 2018 15:50:35 -0500 Subject: [PATCH 0637/1738] Remove blank whitespace --- seaborn/distributions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 8e35ade44a..e342d86a71 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -216,7 +216,7 @@ def distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, hist_kws.setdefault("normed", norm_hist) else: hist_kws.setdefault("density", norm_hist) - + orientation = "horizontal" if vertical else "vertical" hist_color = hist_kws.pop("color", color) ax.hist(a, bins, orientation=orientation, From 24882b07c1bdefc836f4d126d1c21407c63e1a80 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Thu, 26 Oct 2017 22:21:28 -0400 Subject: [PATCH 0638/1738] Rotate all axis labels g.set_xticklabels and g.set_yticklabels does not only apply to bottom and left axes --- seaborn/axisgrid.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 46a2473cbc..c6b6647c93 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -882,7 +882,7 @@ def set_ylabels(self, label=None, **kwargs): def set_xticklabels(self, labels=None, step=None, **kwargs): """Set x axis tick labels on the bottom row of the grid.""" - for ax in self._bottom_axes: + for ax in self.axes.flat: if labels is None: labels = [l.get_text() for l in ax.get_xticklabels()] if step is not None: @@ -890,14 +890,16 @@ def set_xticklabels(self, labels=None, step=None, **kwargs): labels = labels[::step] ax.set_xticks(xticks) ax.set_xticklabels(labels, **kwargs) + self.fig.tight_layout() return self def set_yticklabels(self, labels=None, **kwargs): """Set y axis tick labels on the left column of the grid.""" - for ax in self._left_axes: + for ax in self.axes.flat: if labels is None: labels = [l.get_text() for l in ax.get_yticklabels()] ax.set_yticklabels(labels, **kwargs) + self.fig.tight_layout() return self def set_titles(self, template=None, row_template=None, col_template=None, From 934341cfb9243d5b6f9e32a3a2e844207671a8ff Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 5 Mar 2018 16:43:44 -0500 Subject: [PATCH 0639/1738] Fix test and remove calls to tight_layout --- seaborn/axisgrid.py | 2 -- seaborn/tests/test_axisgrid.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index c6b6647c93..0517817788 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -890,7 +890,6 @@ def set_xticklabels(self, labels=None, step=None, **kwargs): labels = labels[::step] ax.set_xticks(xticks) ax.set_xticklabels(labels, **kwargs) - self.fig.tight_layout() return self def set_yticklabels(self, labels=None, **kwargs): @@ -899,7 +898,6 @@ def set_yticklabels(self, labels=None, **kwargs): if labels is None: labels = [l.get_text() for l in ax.get_yticklabels()] ax.set_yticklabels(labels, **kwargs) - self.fig.tight_layout() return self def set_titles(self, template=None, row_template=None, col_template=None, diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 89f4eea6ae..eb1e90877d 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -467,7 +467,7 @@ def test_set_ticklabels(self): g.set_xticklabels(xlab) g.set_yticklabels(rotation=90) - got_x = [l.get_text() + "h" for l in g.axes[1, 1].get_xticklabels()] + got_x = [l.get_text() for l in g.axes[1, 1].get_xticklabels()] got_y = [l.get_text() for l in g.axes[0, 0].get_yticklabels()] npt.assert_array_equal(got_x, xlab) npt.assert_array_equal(got_y, ylab) From 0a0d0ec4ca4532eb7a08434abe6e701995731a71 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 11 Apr 2018 14:47:51 -0400 Subject: [PATCH 0640/1738] Fix color_cycle test --- seaborn/tests/test_palettes.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/seaborn/tests/test_palettes.py b/seaborn/tests/test_palettes.py index 098a946c9a..10c52595d1 100644 --- a/seaborn/tests/test_palettes.py +++ b/seaborn/tests/test_palettes.py @@ -1,15 +1,18 @@ -import warnings import colorsys import numpy as np import matplotlib as mpl import nose.tools as nt import numpy.testing as npt +import matplotlib.pyplot as plt from .. import palettes, utils, rcmod from ..external import husl from ..colors import xkcd_rgb, crayons +from distutils.version import LooseVersion +mpl_ge_150 = LooseVersion(mpl.__version__) >= '1.5.0' + class TestColorPalettes(object): @@ -290,8 +293,10 @@ def test_preserved_palette_length(self): nt.assert_equal(pal_in, pal_out) def test_get_color_cycle(self): - with warnings.catch_warnings(): - warnings.simplefilter('ignore') - result = utils.get_color_cycle() - expected = mpl.rcParams['axes.color_cycle'] - nt.assert_equal(result, expected) + + if mpl_ge_150: + colors = [(1., 0., 0.), (0, 1., 0.)] + prop_cycle = plt.cycler(color=colors) + with plt.rc_context({"axes.prop_cycle": prop_cycle}): + result = utils.get_color_cycle() + assert result == colors From 12197148eb2d368ae74d31bbd53a45f05243e1ad Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 11 Apr 2018 15:12:28 -0400 Subject: [PATCH 0641/1738] Don't fail when function does not have __module__ attribute --- seaborn/axisgrid.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 0517817788..ada3a5330a 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -694,8 +694,13 @@ def map(self, func, *args, **kwargs): # If color was a keyword argument, grab it here kw_color = kwargs.pop("color", None) + if hasattr(func, "__module__"): + func_module = str(func.__module__) + else: + func_module = "" + # Check for categorical plots without order information - if func.__module__ == "seaborn.categorical": + if func_module == "seaborn.categorical": if "order" not in kwargs: warning = ("Using the {} function without specifying " "`order` is likely to produce an incorrect " @@ -735,9 +740,8 @@ def map(self, func, *args, **kwargs): plot_args = [v for k, v in plot_data.iteritems()] # Some matplotlib functions don't handle pandas objects correctly - if func.__module__ is not None: - if func.__module__.startswith("matplotlib"): - plot_args = [v.values for v in plot_args] + if func_module.startswith("matplotlib"): + plot_args = [v.values for v in plot_args] # Draw the plot self._facet_plot(func, ax, plot_args, kwargs) From 102cf08f9e40ef7037daf4b3b60b94ee144dc5cb Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 11 Apr 2018 15:59:32 -0400 Subject: [PATCH 0642/1738] Fix/update some links in the docs Closes #1361 --- .github/CONTRIBUTING.md | 2 +- README.md | 28 ++++++++++++++-------------- doc/index.rst | 2 +- doc/installing.rst | 8 ++++---- doc/introduction.rst | 10 +++++----- doc/releases/v0.4.0.txt | 2 +- doc/releases/v0.5.0.txt | 4 ++-- doc/releases/v0.6.0.txt | 2 +- doc/sphinxext/ipython_directive.py | 6 +++--- doc/tutorial/axis_grids.ipynb | 4 ++-- doc/tutorial/categorical.ipynb | 2 +- doc/tutorial/color_palettes.ipynb | 16 ++++++++-------- doc/tutorial/regression.ipynb | 4 ++-- seaborn/categorical.py | 2 +- seaborn/distributions.py | 2 +- seaborn/matrix.py | 4 ++-- seaborn/palettes.py | 4 ++-- seaborn/utils.py | 2 +- setup.py | 2 +- 19 files changed, 53 insertions(+), 53 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index dd8fc4a24c..3b26a16e3e 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -4,7 +4,7 @@ Contributing to seaborn General support --------------- -General support questions ("how do I do ?") are most at home on [StackOverflow](http://stackoverflow.com/), where they will be seen by more people and are more easily searchable. StackOverflow has a `[seaborn]` tag, which will bring the question to the attention of people who might be able to answer. +General support questions ("how do I do ?") are most at home on [StackOverflow](https://stackoverflow.com/), where they will be seen by more people and are more easily searchable. StackOverflow has a `[seaborn]` tag, which will bring the question to the attention of people who might be able to answer. Reporting bugs -------------- diff --git a/README.md b/README.md index 877be38eaa..dfe3240246 100644 --- a/README.md +++ b/README.md @@ -2,28 +2,28 @@ seaborn: statistical data visualization ======================================= @@ -44,7 +44,7 @@ Documentation Online documentation is available at [seaborn.pydata.org](https://seaborn.pydata.org). -The docs include a [tutorial](http://seaborn.pydata.org/tutorial.html), [example gallery](http://seaborn.pydata.org/examples/index.html), [API reference](http://seaborn.pydata.org/api.html), and other useful information. +The docs include a [tutorial](https://seaborn.pydata.org/tutorial.html), [example gallery](https://seaborn.pydata.org/examples/index.html), [API reference](https://seaborn.pydata.org/api.html), and other useful information. Dependencies @@ -52,7 +52,7 @@ Dependencies Seaborn supports Python 2.7 and 3.4+. -Installation requires [numpy](http://www.numpy.org/), [scipy](http://www.scipy.org/), [pandas](http://pandas.pydata.org/), and [matplotlib](http://matplotlib.org/). Some functions will optionally use [statsmodels](http://statsmodels.sourceforge.net/) if it is installed. +Installation requires [numpy](http://www.numpy.org/), [scipy](https://www.scipy.org/), [pandas](https://pandas.pydata.org/), and [matplotlib](https://matplotlib.org/). Some functions will optionally use [statsmodels](https://www.statsmodels.org/) if it is installed. Installation diff --git a/doc/index.rst b/doc/index.rst index a8437ac23e..f230b1609d 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -76,7 +76,7 @@ For a brief introduction to the ideas behind the package, you can read the :ref:`example gallery ` to get a sense for what you can do with seaborn and then check out the :ref:`tutorial ` and :ref:`API reference ` to find out how. To see the code or report a bug, please visit the `github repository -`_. General support issues are most at home on `stackoverflow `_, where there is a seaborn tag. +`_. General support issues are most at home on `stackoverflow `_, where there is a seaborn tag. .. raw:: html diff --git a/doc/installing.rst b/doc/installing.rst index 21541507e2..70470bda81 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -31,16 +31,16 @@ Mandatory dependencies - `numpy `__ -- `scipy `__ +- `scipy `__ -- `matplotlib `__ +- `matplotlib `__ -- `pandas `__ +- `pandas `__ Recommended dependencies ^^^^^^^^^^^^^^^^^^^^^^^^ -- `statsmodels `__ +- `statsmodels `__ The ``pip`` installation script will attempt to download the mandatory dependencies only if they do not exist at install-time. diff --git a/doc/introduction.rst b/doc/introduction.rst index 5ea8920c09..7c7494440a 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -4,12 +4,12 @@ An introduction to seaborn ========================== Seaborn is a library for making attractive and informative statistical graphics -in Python. It is built on top of `matplotlib `_ and -tightly integrated with the `PyData `_ stack, including +in Python. It is built on top of `matplotlib `_ and +tightly integrated with the `PyData `_ stack, including support for `numpy `_ and `pandas -`_ data structures and statistical routines from -`scipy `_ and `statsmodels -`_. +`_ data structures and statistical routines from +`scipy `_ and `statsmodels +`_. Some of the features that seaborn offers are diff --git a/doc/releases/v0.4.0.txt b/doc/releases/v0.4.0.txt index 2d4df83ad7..52a95dc95d 100644 --- a/doc/releases/v0.4.0.txt +++ b/doc/releases/v0.4.0.txt @@ -26,7 +26,7 @@ Style and color palettes - Added the :func:`cubehelix_palette` function for generating sequential palettes from the cubehelix system. See the :ref:`palette docs ` for more information on how these palettes can be used. There is also the :func:`choose_cubehelix` which will launch an interactive app to select cubehelix parameters in the notebook. -- Added the :func:`xkcd_palette` and the ``xkcd_rgb`` dictionary so that colors :ref:`can be specified ` with names from the `xkcd color survey `_. +- Added the :func:`xkcd_palette` and the ``xkcd_rgb`` dictionary so that colors :ref:`can be specified ` with names from the `xkcd color survey `_. - Added the ``font_scale`` option to :func:`plotting_context`, :func:`set_context`, and :func:`set`. ``font_scale`` can independently increase or decrease the size of the font elements in the plot. diff --git a/doc/releases/v0.5.0.txt b/doc/releases/v0.5.0.txt index 278bd15814..49d280aa50 100644 --- a/doc/releases/v0.5.0.txt +++ b/doc/releases/v0.5.0.txt @@ -15,7 +15,7 @@ Plotting functions - More generally, there is a new keyword argument in :class:`FacetGrid` and :class:`PairGrid`, ``hue_kws``. This similarly lets plot aesthetics vary across the levels of the hue variable, but more flexibily. ``hue_kws`` should be a dictionary that maps the name of keyword arguments to lists of values that are as long as the number of levels of the hue variable. -- The argument ``subplot_kws`` has been added to ``FacetGrid``. This allows for faceted plots with custom projections, including `maps with Cartopy `_. +- The argument ``subplot_kws`` has been added to ``FacetGrid``. This allows for faceted plots with custom projections, including `maps with Cartopy `_. Color palettes ~~~~~~~~~~~~~~ @@ -24,7 +24,7 @@ Color palettes - Added the ability to specify the seed color for :func:`light_palette` and :func:`dark_palette` as a tuple of ``husl`` or ``hls`` space values or as a named ``xkcd`` color. The interpretation of the seed color is now provided by the new ``input`` parameter to these functions. -- Added several new interactive palette widgets: :func:`choose_colorbrewer_palette`, :func:`choose_light_palette`, :func:`choose_dark_palette`, and :func:`choose_diverging_palette`. For consistency, renamed the cubehelix widget to :func:`choose_cubehelix_palette` (and fixed a bug where the cubehelix palette was reversed). These functions also now return either a color palette list or a matplotlib colormap when called, and that object will be live-updated as you play with the widget. This should make it easy to iterate over a plot until you find a good representation for the data. See the `Github pull request `_ or `this notebook (download it to use the widgets) `_ for more information. +- Added several new interactive palette widgets: :func:`choose_colorbrewer_palette`, :func:`choose_light_palette`, :func:`choose_dark_palette`, and :func:`choose_diverging_palette`. For consistency, renamed the cubehelix widget to :func:`choose_cubehelix_palette` (and fixed a bug where the cubehelix palette was reversed). These functions also now return either a color palette list or a matplotlib colormap when called, and that object will be live-updated as you play with the widget. This should make it easy to iterate over a plot until you find a good representation for the data. See the `Github pull request `_ or `this notebook (download it to use the widgets) `_ for more information. - Overhauled the color :ref:`palette tutorial ` to organize the discussion by class of color palette and provide more motivation behind the various choices one might make when choosing colors for their data. diff --git a/doc/releases/v0.6.0.txt b/doc/releases/v0.6.0.txt index 7bbd7ae52c..081d85946e 100644 --- a/doc/releases/v0.6.0.txt +++ b/doc/releases/v0.6.0.txt @@ -60,7 +60,7 @@ Other additions and changes - :func:`heatmap` and :func:`clustermap` now automatically use a mask for missing values, which previously were shown with the "under" value of the colormap per default `plt.pcolormesh` behavior. -- Added the ``seaborn.crayons`` dictionary and the :func:`crayon_palette` function to define colors from the 120 box (!) of `Crayola crayons `_. +- Added the ``seaborn.crayons`` dictionary and the :func:`crayon_palette` function to define colors from the 120 box (!) of `Crayola crayons `_. - Added the ``line_kws`` parameter to :func:`residplot` to change the style of the lowess line, when used. diff --git a/doc/sphinxext/ipython_directive.py b/doc/sphinxext/ipython_directive.py index a79d2f45e9..5a0a8d5e7b 100644 --- a/doc/sphinxext/ipython_directive.py +++ b/doc/sphinxext/ipython_directive.py @@ -80,7 +80,7 @@ In [3]: print(y) -See http://matplotlib.org/sampledoc/ipython_directive.html for additional +See https://matplotlib.org/sampledoc/ipython_directive.html for additional documentation. ToDo @@ -947,11 +947,11 @@ def test(): """, r""" -In [130]: url = 'http://ichart.finance.yahoo.com/table.csv?s=CROX\ +In [130]: url = 'https://ichart.finance.yahoo.com/table.csv?s=CROX\ .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv' In [131]: print url.split('&') -['http://ichart.finance.yahoo.com/table.csv?s=CROX', 'd=9', 'e=22', 'f=2009', 'g=d', 'a=1', 'b=8', 'c=2006', 'ignore=.csv'] +['https://ichart.finance.yahoo.com/table.csv?s=CROX', 'd=9', 'e=22', 'f=2009', 'g=d', 'a=1', 'b=8', 'c=2006', 'ignore=.csv'] In [60]: import urllib diff --git a/doc/tutorial/axis_grids.ipynb b/doc/tutorial/axis_grids.ipynb index 3d45ca4502..07305a112a 100644 --- a/doc/tutorial/axis_grids.ipynb +++ b/doc/tutorial/axis_grids.ipynb @@ -20,9 +20,9 @@ "cell_type": "raw", "metadata": {}, "source": [ - "When exploring medium-dimensional data, a useful approach is to draw multiple instances of the same plot on different subsets of your dataset. This technique is sometimes called either \"lattice\", or \"trellis\" plotting, and it is related to the idea of `\"small multiples\" `_. It allows a viewer to quickly extract a large amount of information about complex data. Matplotlib offers good support for making figures with multiple axes; seaborn builds on top of this to directly link the structure of the plot to the structure of your dataset.\n", + "When exploring medium-dimensional data, a useful approach is to draw multiple instances of the same plot on different subsets of your dataset. This technique is sometimes called either \"lattice\", or \"trellis\" plotting, and it is related to the idea of `\"small multiples\" `_. It allows a viewer to quickly extract a large amount of information about complex data. Matplotlib offers good support for making figures with multiple axes; seaborn builds on top of this to directly link the structure of the plot to the structure of your dataset.\n", "\n", - "To use these features, your data has to be in a Pandas DataFrame and it must take the form of what Hadley Whickam calls `\"tidy\" data `_. In brief, that means your dataframe should be structured such that each column is a variable and each row is an observation.\n", + "To use these features, your data has to be in a Pandas DataFrame and it must take the form of what Hadley Whickam calls `\"tidy\" data `_. In brief, that means your dataframe should be structured such that each column is a variable and each row is an observation.\n", "\n", "For advanced use, you can use the objects discussed in this part of the tutorial directly, which will provide maximum flexibility. Some seaborn functions (such as :func:`lmplot`, :func:`factorplot`, and :func:`pairplot`) also use them behind the scenes. Unlike other seaborn functions that are \"Axes-level\" and draw onto specific (possibly already-existing) matplotlib ``Axes`` without otherwise manipulating the figure, these higher-level functions create a figure when called and are generally more strict about how it gets set up. In some cases, arguments either to those functions or to the constructor of the class they rely on will provide a different interface attributes like the figure size, as in the case of :func:`lmplot` where you can set the height and aspect ratio for each facet rather than the overall size of the figure. Any function that uses one of these objects will always return it after plotting, though, and most of these objects have convenience methods for changing how the plot is drawn, often in a more abstract and easy way." ] diff --git a/doc/tutorial/categorical.ipynb b/doc/tutorial/categorical.ipynb index bbf9bb5328..10ea0f1195 100644 --- a/doc/tutorial/categorical.ipynb +++ b/doc/tutorial/categorical.ipynb @@ -26,7 +26,7 @@ "\n", "Much like the relationship between :func:`regplot` and :func:`lmplot`, in seaborn there are both relatively low-level and relatively high-level approaches for making categorical plots. The functions named above are all low-level in that they plot onto a specific matplotlib axes. There is also the higher-level :func:`factorplot`, which combines these functions with a :class:`FacetGrid` to apply a categorical plot across a grid of figure panels.\n", "\n", - "It is easiest and best to invoke these functions with a DataFrame that is in `\"tidy\" `_ format, although the lower-level functions also accept wide-form DataFrames or simple vectors of observations. See below for examples." + "It is easiest and best to invoke these functions with a DataFrame that is in `\"tidy\" `_ format, although the lower-level functions also accept wide-form DataFrames or simple vectors of observations. See below for examples." ] }, { diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index 2609d5eab5..e3b8254674 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -24,7 +24,7 @@ "raw_mimetype": "text/restructuredtext" }, "source": [ - "Color is more important than other aspects of figure style because color can reveal patterns in the data if used effectively or hide those patterns if used poorly. There are a number of great resources to learn about good techniques for using color in visualizations, I am partial to this `series of blog posts `_ from Rob Simmon and this `more technical paper `_. The matplotlib docs also now have a `nice tutorial `_ that illustrates some of the perceptual properties of the built in colormaps.\n", + "Color is more important than other aspects of figure style because color can reveal patterns in the data if used effectively or hide those patterns if used poorly. There are a number of great resources to learn about good techniques for using color in visualizations, I am partial to this `series of blog posts `_ from Rob Simmon and this `more technical paper `_. The matplotlib docs also now have a `nice tutorial `_ that illustrates some of the perceptual properties of the built in colormaps.\n", "\n", "Seaborn makes it easy to select and use color palettes that are suited to the kind of data you are working with and the goals you have in visualizing it." ] @@ -148,9 +148,9 @@ "raw_mimetype": "text/restructuredtext" }, "source": [ - "However, because of the way the human visual system works, colors that are even \"intensity\" in terms of their RGB levels won't necessarily look equally intense. `We perceive `_ yellows and greens as relatively brighter and blues as relatively darker, which can be a problem when aiming for uniformity with the ``hls`` system.\n", + "However, because of the way the human visual system works, colors that are even \"intensity\" in terms of their RGB levels won't necessarily look equally intense. `We perceive `_ yellows and greens as relatively brighter and blues as relatively darker, which can be a problem when aiming for uniformity with the ``hls`` system.\n", "\n", - "To remedy this, seaborn provides an interface to the `husl `_ system, which also makes it easy to select evenly spaced hues while keeping the apparent brightness and saturation much more uniform." + "To remedy this, seaborn provides an interface to the `husl `_ system (since renamed to HSLuv), which also makes it easy to select evenly spaced hues while keeping the apparent brightness and saturation much more uniform." ] }, { @@ -173,7 +173,7 @@ "\n", "Another source of visually pleasing categorical palettes comes from the `Color Brewer `_ tool (which also has sequential and diverging palettes, as we'll see below). These also exist as matplotlib colormaps, but they are not handled properly. In seaborn, when you ask for a qualitative Color Brewer palette, you'll always get the discrete colors, but this means that at a certain point they will begin to cycle.\n", "\n", - "A nice feature of the Color Brewer website is that it provides some guidance on which palettes are color blind safe. There are a variety of [kinds](http://en.wikipedia.org/wiki/Color_blindness) of color blindness, but the most common variant leads to difficulty distinguishing reds and greens. It's generally a good idea to avoid using red and green for plot elements that need to be discriminated based on color." + "A nice feature of the Color Brewer website is that it provides some guidance on which palettes are color blind safe. There are a variety of [kinds](https://en.wikipedia.org/wiki/Color_blindness) of color blindness, but the most common variant leads to difficulty distinguishing reds and greens. It's generally a good idea to avoid using red and green for plot elements that need to be discriminated based on color." ] }, { @@ -222,7 +222,7 @@ "Using named colors from the xkcd color survey\n", "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", "\n", - "A while back, `xkcd `_ ran a `crowdsourced effort `_ to name random RGB colors. This produced a set of `954 named colors `_, which you can now reference in seaborn using the ``xkcd_rgb`` dictionary:" + "A while back, `xkcd `_ ran a `crowdsourced effort `_ to name random RGB colors. This produced a set of `954 named colors `_, which you can now reference in seaborn using the ``xkcd_rgb`` dictionary:" ] }, { @@ -264,7 +264,7 @@ "\n", "The second major class of color palettes is called \"sequential\". This kind of color mapping is appropriate when data range from relatively low or unintersting values to relatively high or interesting values. Although there are cases where you will want discrete colors in a sequential palette, it's more common to use them as a colormap in functions like :func:`kdeplot` or :func:`corrplot` (along with similar matplotlib functions).\n", "\n", - "It's common to see colormaps like ``jet`` (or other rainbow palettes) used in this case, becuase the range of hues gives the impression of providing additional information about the data. However, colormaps with large hue shifts tend to introduce discontinuities that don't exist in the data, and our visual system isn't able to naturally map the rainbow to quantitative distinctions like \"high\" or \"low\". The result is that these visualizations end up being more like a puzzle, and they obscure patterns in the data rather than revealing them. The jet palette is `particularly bad `_ because the brightest colors, yellow and cyan, are used for intermediate data values. This has the effect of emphasizing uninteresting (and arbitrary) values while demphasizing the extremes.\n", + "It's common to see colormaps like ``jet`` (or other rainbow palettes) used in this case, becuase the range of hues gives the impression of providing additional information about the data. However, colormaps with large hue shifts tend to introduce discontinuities that don't exist in the data, and our visual system isn't able to naturally map the rainbow to quantitative distinctions like \"high\" or \"low\". The result is that these visualizations end up being more like a puzzle, and they obscure patterns in the data rather than revealing them. The jet palette is because the brightest colors, yellow and cyan, are used for intermediate data values. This has the effect of emphasizing uninteresting (and arbitrary) values while demphasizing the extremes.\n", "\n", "For sequential data, it's better to use palettes that have at most a relatively subtle shift in hue accompanied by a large shift in brightness and saturation. This approach will naturally draw the eye to the relatively important parts of the data.\n", "\n", @@ -330,7 +330,7 @@ "Sequential \"cubehelix\" palettes\n", "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", "\n", - "The `cubehelix `_ color palette system makes sequential palettes with a linear increase or decrease in brightness and some variation in hue. This means that the information in your colormap will be preserved when converted to black and white (for printing) or when viewed by a colorblind individual.\n", + "The `cubehelix `_ color palette system makes sequential palettes with a linear increase or decrease in brightness and some variation in hue. This means that the information in your colormap will be preserved when converted to black and white (for printing) or when viewed by a colorblind individual.\n", "\n", "Matplotlib has the default cubehelix version built into it:" ] @@ -525,7 +525,7 @@ "\n", "The rules for choosing good diverging palettes are similar to good sequential palettes, except now you want to have two relatively subtle hue shifts from distinct starting hues that meet in an under-emphasized color at the midpoint. It's also important that the starting values are of similar brightness and saturation.\n", "\n", - "It's also important to emphasize here that using red and green should be avoided, as a substantial population of potential viewers will be `unable to distinguish them `_.\n", + "It's also important to emphasize here that using red and green should be avoided, as a substantial population of potential viewers will be `unable to distinguish them `_.\n", "\n", "It should not surprise you that the Color Brewer library comes with a set of well-choosen diverging colormaps." ] diff --git a/doc/tutorial/regression.ipynb b/doc/tutorial/regression.ipynb index 75547ded12..b429bdfd6a 100644 --- a/doc/tutorial/regression.ipynb +++ b/doc/tutorial/regression.ipynb @@ -22,7 +22,7 @@ "source": [ "Many datasets contain multiple quantitative variables, and the goal of an analysis is often to relate those variables to each other. We :ref:`previously discussed ` functions that can accomplish this by showing the joint distribution of two variables. It can be very helpful, though, to use statistical models to estimate a simple relationship between two noisy sets of observations. The functions discussed in this chapter will do so through the common framework of linear regression.\n", "\n", - "In the spirit of Tukey, the regression plots in seaborn are primarily intended to add a visual guide that helps to emphasize patterns in a dataset during exploratory data analyses. That is to say that seaborn is not itself a package for statistical analysis. To obtain quantitative measures related to the fit of regression models, you should use `statsmodels `_. The goal of seaborn, however, is to make exploring a dataset through visualization quick and easy, as doing so is just as (if not more) important than exploring a dataset through tables of statistics." + "In the spirit of Tukey, the regression plots in seaborn are primarily intended to add a visual guide that helps to emphasize patterns in a dataset during exploratory data analyses. That is to say that seaborn is not itself a package for statistical analysis. To obtain quantitative measures related to the fit of regression models, you should use `statsmodels `_. The goal of seaborn, however, is to make exploring a dataset through visualization quick and easy, as doing so is just as (if not more) important than exploring a dataset through tables of statistics." ] }, { @@ -108,7 +108,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "You should note that the resulting plots are identical, except that the figure shapes are different. We will explain why this is shortly. For now, the other main difference to know about is that :func:`regplot` accepts the ``x`` and ``y`` variables in a variety of formats including simple numpy arrays, pandas ``Series`` objects, or as references to variables in a pandas ``DataFrame`` object passed to ``data``. In contrast, :func:`lmplot` has ``data`` as a required parameter and the ``x`` and ``y`` variables must be specified as strings. This data format is called \"long-form\" or `\"tidy\" `_ data. Other than this input flexibility, :func:`regplot` possesses a subset of :func:`lmplot`'s features, so we will demonstrate them using the latter.\n", + "You should note that the resulting plots are identical, except that the figure shapes are different. We will explain why this is shortly. For now, the other main difference to know about is that :func:`regplot` accepts the ``x`` and ``y`` variables in a variety of formats including simple numpy arrays, pandas ``Series`` objects, or as references to variables in a pandas ``DataFrame`` object passed to ``data``. In contrast, :func:`lmplot` has ``data`` as a required parameter and the ``x`` and ``y`` variables must be specified as strings. This data format is called \"long-form\" or `\"tidy\" `_ data. Other than this input flexibility, :func:`regplot` possesses a subset of :func:`lmplot`'s features, so we will demonstrate them using the latter.\n", "\n", "It's possible to fit a linear regression when one of the variables takes discrete values, however, the simple scatterplot produced by this kind of dataset is often not optimal:" ] diff --git a/seaborn/categorical.py b/seaborn/categorical.py index c02ab0b184..83593fde8f 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -3722,7 +3722,7 @@ def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, value plots and their properties, see Hadley Wickham's excellent paper on the topic: - http://vita.had.co.nz/papers/letter-value-plot.html + https://vita.had.co.nz/papers/letter-value-plot.html {main_api_narrative} diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 02e19786c4..29eb10e359 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -25,7 +25,7 @@ def _freedman_diaconis_bins(a): """Calculate number of hist bins using Freedman-Diaconis rule.""" - # From http://stats.stackexchange.com/questions/798/ + # From https://stats.stackexchange.com/questions/798/ a = np.asarray(a) if len(a) < 2: return 1 diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 4ee456b38e..0dd45663aa 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -1155,11 +1155,11 @@ def clustermap(data, pivot_kws=None, method='average', metric='euclidean', method : str, optional Linkage method to use for calculating clusters. See scipy.cluster.hierarchy.linkage documentation for more information: - http://docs.scipy.org/doc/scipy/reference/generated/scipy.cluster.hierarchy.linkage.html + https://docs.scipy.org/doc/scipy/reference/generated/scipy.cluster.hierarchy.linkage.html metric : str, optional Distance metric to use for the data. See scipy.spatial.distance.pdist documentation for more options - http://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.pdist.html + https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.pdist.html To use different metrics (or methods) for rows and columns, you may construct each linkage matrix yourself and provide them as {row,col}_linkage. diff --git a/seaborn/palettes.py b/seaborn/palettes.py index 060e43c9b4..86ff656dcf 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -730,7 +730,7 @@ def blend_palette(colors, n_colors=6, as_cmap=False, input="rgb"): def xkcd_palette(colors): """Make a palette with color names from the xkcd color survey. - See xkcd for the full list of colors: http://xkcd.com/color/rgb/ + See xkcd for the full list of colors: https://xkcd.com/color/rgb/ This is just a simple wrapper around the ``seaborn.xkcd_rgb`` dictionary. @@ -758,7 +758,7 @@ def crayon_palette(colors): """Make a palette with color names from Crayola crayons. Colors are taken from here: - http://en.wikipedia.org/wiki/List_of_Crayola_crayon_colors + https://en.wikipedia.org/wiki/List_of_Crayola_crayon_colors This is just a simple wrapper around the ``seaborn.crayons`` dictionary. diff --git a/seaborn/utils.py b/seaborn/utils.py index f40425d48c..38544a27df 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -616,7 +616,7 @@ def to_utf8(obj): return obj.__str__() -def _network(t=None, url='http://google.com'): +def _network(t=None, url='https://google.com'): """ Decorator that will skip a test if `url` is unreachable. diff --git a/setup.py b/setup.py index 23910e1e99..1940d6e4f8 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ DISTNAME = 'seaborn' MAINTAINER = 'Michael Waskom' MAINTAINER_EMAIL = 'mwaskom@nyu.edu' -URL = 'http://seaborn.pydata.org' +URL = 'https://seaborn.pydata.org' LICENSE = 'BSD (3-clause)' DOWNLOAD_URL = 'https://github.com/mwaskom/seaborn/' VERSION = '0.9.dev' From 1babb9b3afeb00db80ee1919226f09bf54bcc5dc Mon Sep 17 00:00:00 2001 From: Benjamin Rose Date: Fri, 13 Apr 2018 10:43:34 -0400 Subject: [PATCH 0643/1738] Fix typo in regplot x_ci parameter docs --- seaborn/regression.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/regression.py b/seaborn/regression.py index 99deb70cf3..14bb411819 100644 --- a/seaborn/regression.py +++ b/seaborn/regression.py @@ -441,8 +441,8 @@ def lineplot(self, ax, kws): x_ci : "ci", "sd", int in [0, 100] or None, optional Size of the confidence interval used when plotting a central tendency for discrete values of ``x``. If ``"ci"``, defer to the value of the - ``ci`` parameter. If ``"sd"``, skip bootstrappig and show the standard - deviation of the observations in each bin.\ + ``ci`` parameter. If ``"sd"``, skip bootstrapping and show the + standard deviation of the observations in each bin.\ """), scatter=dedent("""\ scatter : bool, optional From 95e47ff20622b95c64f92bb109f065d2aa4ec31e Mon Sep 17 00:00:00 2001 From: CorbanSwain Date: Thu, 10 May 2018 22:50:29 -0400 Subject: [PATCH 0644/1738] Adding functionality to control dendrogram linewidth and other LineCollection props from clustergram call --- seaborn/matrix.py | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 0dd45663aa..2f1f846423 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -531,7 +531,8 @@ def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, class _DendrogramPlotter(object): """Object for drawing tree of similarities between data rows/columns""" - def __init__(self, data, linkage, metric, method, axis, label, rotate): + def __init__(self, data, linkage, metric, method, axis, label, rotate, + line_kws): """Plot a dendrogram of the relationships between the columns of data Parameters @@ -558,6 +559,7 @@ def __init__(self, data, linkage, metric, method, axis, label, rotate): self.axis = axis self.label = label self.rotate = rotate + self.line_kws = line_kws if linkage is None: self.linkage = self.calculated_linkage @@ -647,6 +649,18 @@ def reordered_ind(self): """Indices of the matrix, reordered by the dendrogram""" return self.dendrogram['leaves'] + @property + def line_kws(self): + return self._line_kws + + @line_kws.setter + def line_kws(self, line_kws): + if line_kws is None: + line_kws = {} + defaults = dict(linewidths=.5, colors='k') + [line_kws.setdefault(*kv) for kv in defaults.items()] + self._line_kws = line_kws + def plot(self, ax): """Plots a dendrogram of the similarities between data on the axes @@ -656,17 +670,16 @@ def plot(self, ax): Axes object upon which the dendrogram is plotted """ - line_kwargs = dict(linewidths=.5, colors='k') if self.rotate and self.axis == 0: lines = LineCollection([list(zip(x, y)) for x, y in zip(self.dependent_coord, self.independent_coord)], - **line_kwargs) + **self.line_kws) else: lines = LineCollection([list(zip(x, y)) for x, y in zip(self.independent_coord, self.dependent_coord)], - **line_kwargs) + **self.line_kws) ax.add_collection(lines) number_of_leaves = len(self.reordered_ind) @@ -705,7 +718,7 @@ def plot(self, ax): def dendrogram(data, linkage=None, axis=1, label=True, metric='euclidean', - method='average', rotate=False, ax=None): + method='average', rotate=False, ax=None, line_kws=None): """Draw a tree diagram of relationships within a matrix Parameters @@ -742,7 +755,7 @@ def dendrogram(data, linkage=None, axis=1, label=True, metric='euclidean', """ plotter = _DendrogramPlotter(data, linkage=linkage, axis=axis, metric=metric, method=method, - label=label, rotate=rotate) + label=label, rotate=rotate, line_kws=line_kws) if ax is None: ax = plt.gca() return plotter.plot(ax=ax) @@ -1011,12 +1024,13 @@ def savefig(self, *args, **kwargs): self.fig.savefig(*args, **kwargs) def plot_dendrograms(self, row_cluster, col_cluster, metric, method, - row_linkage, col_linkage): + row_linkage, col_linkage, line_kws): # Plot the row dendrogram if row_cluster: self.dendrogram_row = dendrogram( self.data2d, metric=metric, method=method, label=False, axis=0, - ax=self.ax_row_dendrogram, rotate=True, linkage=row_linkage) + ax=self.ax_row_dendrogram, rotate=True, linkage=row_linkage, + line_kws=line_kws) else: self.ax_row_dendrogram.set_xticks([]) self.ax_row_dendrogram.set_yticks([]) @@ -1024,7 +1038,8 @@ def plot_dendrograms(self, row_cluster, col_cluster, metric, method, if col_cluster: self.dendrogram_col = dendrogram( self.data2d, metric=metric, method=method, label=False, - axis=1, ax=self.ax_col_dendrogram, linkage=col_linkage) + axis=1, ax=self.ax_col_dendrogram, linkage=col_linkage, + line_kws=line_kws) else: self.ax_col_dendrogram.set_xticks([]) self.ax_col_dendrogram.set_yticks([]) @@ -1120,10 +1135,11 @@ def plot_matrix(self, colorbar_kws, xind, yind, **kws): plt.setp(ytl, rotation=ytl_rot) def plot(self, metric, method, colorbar_kws, row_cluster, col_cluster, - row_linkage, col_linkage, **kws): + row_linkage, col_linkage, line_kws, **kws): colorbar_kws = {} if colorbar_kws is None else colorbar_kws self.plot_dendrograms(row_cluster, col_cluster, metric, method, - row_linkage=row_linkage, col_linkage=col_linkage) + row_linkage=row_linkage, col_linkage=col_linkage, + line_kws=line_kws) try: xind = self.dendrogram_col.reordered_ind except AttributeError: @@ -1142,7 +1158,8 @@ def clustermap(data, pivot_kws=None, method='average', metric='euclidean', z_score=None, standard_scale=None, figsize=None, cbar_kws=None, row_cluster=True, col_cluster=True, row_linkage=None, col_linkage=None, - row_colors=None, col_colors=None, mask=None, **kwargs): + row_colors=None, col_colors=None, mask=None, + dgline_kws=None, **kwargs): """Plot a matrix dataset as a hierarchically-clustered heatmap. Parameters @@ -1195,6 +1212,9 @@ def clustermap(data, pivot_kws=None, method='average', metric='euclidean', If passed, data will not be shown in cells where ``mask`` is True. Cells with missing values are automatically masked. Only used for visualizing, not for calculating. + dgline_kws : dict, optional + Keyword arguments to pass to ''matplotlib.collections.LineCollection'' + which is used for plotting the lines of the dendrogram tree. kwargs : other keyword arguments All other keyword arguments are passed to ``sns.heatmap`` @@ -1296,4 +1316,4 @@ def clustermap(data, pivot_kws=None, method='average', metric='euclidean', colorbar_kws=cbar_kws, row_cluster=row_cluster, col_cluster=col_cluster, row_linkage=row_linkage, col_linkage=col_linkage, - **kwargs) + line_kws=dgline_kws, **kwargs) From c2d101ff6fc275b35f4ac0eb124aa7677a39f768 Mon Sep 17 00:00:00 2001 From: CorbanSwain Date: Fri, 11 May 2018 00:16:59 -0400 Subject: [PATCH 0645/1738] Adding default value for `line_kws` int the `DendrogramPlotter` `__init__` method. Should correct compliance with build checks. --- seaborn/matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 2f1f846423..2aec629340 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -532,7 +532,7 @@ class _DendrogramPlotter(object): """Object for drawing tree of similarities between data rows/columns""" def __init__(self, data, linkage, metric, method, axis, label, rotate, - line_kws): + line_kws=None): """Plot a dendrogram of the relationships between the columns of data Parameters From 3f32662b2fa7ef16f64160f177df5bf41a99e548 Mon Sep 17 00:00:00 2001 From: CorbanSwain Date: Fri, 11 May 2018 00:42:28 -0400 Subject: [PATCH 0646/1738] Simplifying implementation, adding default arguments for `line_kws`. --- seaborn/matrix.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 2aec629340..04c171e51d 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -559,7 +559,13 @@ def __init__(self, data, linkage, metric, method, axis, label, rotate, self.axis = axis self.label = label self.rotate = rotate - self.line_kws = line_kws + + if line_kws is None: + self.line_kws = {} + else: + self.line_kws = line_kws + defaults = dict(linewidths=.5, colors='k') + [self.line_kws.setdefault(k, v) for k, v in defaults.items()] if linkage is None: self.linkage = self.calculated_linkage @@ -649,18 +655,6 @@ def reordered_ind(self): """Indices of the matrix, reordered by the dendrogram""" return self.dendrogram['leaves'] - @property - def line_kws(self): - return self._line_kws - - @line_kws.setter - def line_kws(self, line_kws): - if line_kws is None: - line_kws = {} - defaults = dict(linewidths=.5, colors='k') - [line_kws.setdefault(*kv) for kv in defaults.items()] - self._line_kws = line_kws - def plot(self, ax): """Plots a dendrogram of the similarities between data on the axes @@ -1024,7 +1018,7 @@ def savefig(self, *args, **kwargs): self.fig.savefig(*args, **kwargs) def plot_dendrograms(self, row_cluster, col_cluster, metric, method, - row_linkage, col_linkage, line_kws): + row_linkage, col_linkage, line_kws=None): # Plot the row dendrogram if row_cluster: self.dendrogram_row = dendrogram( @@ -1135,7 +1129,7 @@ def plot_matrix(self, colorbar_kws, xind, yind, **kws): plt.setp(ytl, rotation=ytl_rot) def plot(self, metric, method, colorbar_kws, row_cluster, col_cluster, - row_linkage, col_linkage, line_kws, **kws): + row_linkage, col_linkage, line_kws=None, **kws): colorbar_kws = {} if colorbar_kws is None else colorbar_kws self.plot_dendrograms(row_cluster, col_cluster, metric, method, row_linkage=row_linkage, col_linkage=col_linkage, From 9367db50491a9dd90e30cd2ddd1cec0d92307884 Mon Sep 17 00:00:00 2001 From: CorbanSwain Date: Fri, 11 May 2018 00:49:38 -0400 Subject: [PATCH 0647/1738] Further simplifying implementation with 1 line if. --- seaborn/matrix.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 04c171e51d..7e7622cf5a 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -560,10 +560,7 @@ def __init__(self, data, linkage, metric, method, axis, label, rotate, self.label = label self.rotate = rotate - if line_kws is None: - self.line_kws = {} - else: - self.line_kws = line_kws + self.line_kws = {} if line_kws is None else line_kws defaults = dict(linewidths=.5, colors='k') [self.line_kws.setdefault(k, v) for k, v in defaults.items()] From 6f6cb5e5acd94ca5376147f95110973fbcd89ea2 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 12 May 2018 19:17:22 -0400 Subject: [PATCH 0648/1738] Update docs infrastructue; remove unused sphinx extensions --- devel_requirements_py2.txt | 6 - devel_requirements_py3.txt | 6 - doc/conf.py | 23 +- ...plot_generator.py => gallery_generator.py} | 0 doc/sphinxext/ipython_console_highlighting.py | 116 -- doc/sphinxext/ipython_directive.py | 1086 ----------------- doc/sphinxext/plot_directive.py | 837 ------------- examples/{kde_joyplot.py => kde_ridgeplot.py} | 0 8 files changed, 11 insertions(+), 2063 deletions(-) delete mode 100644 devel_requirements_py2.txt delete mode 100644 devel_requirements_py3.txt rename doc/sphinxext/{plot_generator.py => gallery_generator.py} (100%) delete mode 100644 doc/sphinxext/ipython_console_highlighting.py delete mode 100644 doc/sphinxext/ipython_directive.py delete mode 100644 doc/sphinxext/plot_directive.py rename examples/{kde_joyplot.py => kde_ridgeplot.py} (100%) diff --git a/devel_requirements_py2.txt b/devel_requirements_py2.txt deleted file mode 100644 index c86433fdce..0000000000 --- a/devel_requirements_py2.txt +++ /dev/null @@ -1,6 +0,0 @@ -ipython>=1.0 -sphinx>=1.2 -sphinx_bootstrap_theme -numpydoc -PIL -nose diff --git a/devel_requirements_py3.txt b/devel_requirements_py3.txt deleted file mode 100644 index 3cdd2bb287..0000000000 --- a/devel_requirements_py3.txt +++ /dev/null @@ -1,6 +0,0 @@ -ipython>=1.0 -sphinx>=1.2 -sphinx_bootstrap_theme -numpydoc -pillow -nose diff --git a/doc/conf.py b/doc/conf.py index 8a6785edf7..c7cf2dcd47 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -21,7 +21,7 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) -# -- General configuration ----------------------------------------------------- +# -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' @@ -29,17 +29,16 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. sys.path.insert(0, os.path.abspath('sphinxext')) -extensions = ['sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.coverage', - 'sphinx.ext.mathjax', - 'sphinx.ext.autosummary', - 'plot_generator', - 'plot_directive', - 'numpydoc', - 'ipython_directive', - 'ipython_console_highlighting', - ] +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', + 'sphinx.ext.coverage', + 'sphinx.ext.mathjax', + 'sphinx.ext.autosummary', + 'matplotlib.sphinxext.plot_directive', + 'gallery_generator', + 'numpydoc', +] # Generate the API documentation when building autosummary_generate = True diff --git a/doc/sphinxext/plot_generator.py b/doc/sphinxext/gallery_generator.py similarity index 100% rename from doc/sphinxext/plot_generator.py rename to doc/sphinxext/gallery_generator.py diff --git a/doc/sphinxext/ipython_console_highlighting.py b/doc/sphinxext/ipython_console_highlighting.py deleted file mode 100644 index dfb489e493..0000000000 --- a/doc/sphinxext/ipython_console_highlighting.py +++ /dev/null @@ -1,116 +0,0 @@ -"""reST directive for syntax-highlighting ipython interactive sessions. - -XXX - See what improvements can be made based on the new (as of Sept 2009) -'pycon' lexer for the python console. At the very least it will give better -highlighted tracebacks. -""" - -#----------------------------------------------------------------------------- -# Needed modules - -# Standard library -import re - -# Third party -from pygments.lexer import Lexer, do_insertions -from pygments.lexers.agile import (PythonConsoleLexer, PythonLexer, - PythonTracebackLexer) -from pygments.token import Comment, Generic - -from sphinx import highlighting - -#----------------------------------------------------------------------------- -# Global constants -line_re = re.compile('.*?\n') - -#----------------------------------------------------------------------------- -# Code begins - classes and functions - - -class IPythonConsoleLexer(Lexer): - - """ - For IPython console output or doctests, such as: - - .. sourcecode:: ipython - - In [1]: a = 'foo' - - In [2]: a - Out[2]: 'foo' - - In [3]: print(a) - foo - - In [4]: 1 / 0 - - Notes: - - - Tracebacks are not currently supported. - - - It assumes the default IPython prompts, not customized ones. - """ - - name = 'IPython console session' - aliases = ['ipython'] - mimetypes = ['text/x-ipython-console'] - input_prompt = re.compile("(In \[[0-9]+\]: )|( \.\.\.+:)") - output_prompt = re.compile("(Out\[[0-9]+\]: )|( \.\.\.+:)") - continue_prompt = re.compile(" \.\.\.+:") - tb_start = re.compile("\-+") - - def get_tokens_unprocessed(self, text): - pylexer = PythonLexer(**self.options) - tblexer = PythonTracebackLexer(**self.options) - - curcode = '' - insertions = [] - for match in line_re.finditer(text): - line = match.group() - input_prompt = self.input_prompt.match(line) - continue_prompt = self.continue_prompt.match(line.rstrip()) - output_prompt = self.output_prompt.match(line) - if line.startswith("#"): - insertions.append((len(curcode), - [(0, Comment, line)])) - elif input_prompt is not None: - insertions.append((len(curcode), - [(0, Generic.Prompt, input_prompt.group())])) - curcode += line[input_prompt.end():] - elif continue_prompt is not None: - insertions.append((len(curcode), - [(0, Generic.Prompt, continue_prompt.group())])) - curcode += line[continue_prompt.end():] - elif output_prompt is not None: - # Use the 'error' token for output. We should probably make - # our own token, but error is typicaly in a bright color like - # red, so it works fine for our output prompts. - insertions.append((len(curcode), - [(0, Generic.Error, output_prompt.group())])) - curcode += line[output_prompt.end():] - else: - if curcode: - for item in do_insertions(insertions, - pylexer.get_tokens_unprocessed(curcode)): - yield item - curcode = '' - insertions = [] - yield match.start(), Generic.Output, line - if curcode: - for item in do_insertions(insertions, - pylexer.get_tokens_unprocessed(curcode)): - yield item - - -def setup(app): - """Setup as a sphinx extension.""" - - # This is only a lexer, so adding it below to pygments appears sufficient. - # But if somebody knows that the right API usage should be to do that via - # sphinx, by all means fix it here. At least having this setup.py - # suppresses the sphinx warning we'd get without it. - pass - -#----------------------------------------------------------------------------- -# Register the extension as a valid pygments lexer -highlighting.lexers['ipython'] = IPythonConsoleLexer() diff --git a/doc/sphinxext/ipython_directive.py b/doc/sphinxext/ipython_directive.py deleted file mode 100644 index 5a0a8d5e7b..0000000000 --- a/doc/sphinxext/ipython_directive.py +++ /dev/null @@ -1,1086 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Sphinx directive to support embedded IPython code. - -This directive allows pasting of entire interactive IPython sessions, prompts -and all, and their code will actually get re-executed at doc build time, with -all prompts renumbered sequentially. It also allows you to input code as a pure -python input by giving the argument python to the directive. The output looks -like an interactive ipython section. - -To enable this directive, simply list it in your Sphinx ``conf.py`` file -(making sure the directory where you placed it is visible to sphinx, as is -needed for all Sphinx directives). For example, to enable syntax highlighting -and the IPython directive:: - - extensions = ['IPython.sphinxext.ipython_console_highlighting', - 'IPython.sphinxext.ipython_directive'] - -The IPython directive outputs code-blocks with the language 'ipython'. So -if you do not have the syntax highlighting extension enabled as well, then -all rendered code-blocks will be uncolored. By default this directive assumes -that your prompts are unchanged IPython ones, but this can be customized. -The configurable options that can be placed in conf.py are: - -ipython_savefig_dir: - The directory in which to save the figures. This is relative to the - Sphinx source directory. The default is `html_static_path`. -ipython_rgxin: - The compiled regular expression to denote the start of IPython input - lines. The default is re.compile('In \[(\d+)\]:\s?(.*)\s*'). You - shouldn't need to change this. -ipython_rgxout: - The compiled regular expression to denote the start of IPython output - lines. The default is re.compile('Out\[(\d+)\]:\s?(.*)\s*'). You - shouldn't need to change this. -ipython_promptin: - The string to represent the IPython input prompt in the generated ReST. - The default is 'In [%d]:'. This expects that the line numbers are used - in the prompt. -ipython_promptout: - The string to represent the IPython prompt in the generated ReST. The - default is 'Out [%d]:'. This expects that the line numbers are used - in the prompt. -ipython_mplbackend: - The string which specifies if the embedded Sphinx shell should import - Matplotlib and set the backend. The value specifies a backend that is - passed to `matplotlib.use()` before any lines in `ipython_execlines` are - executed. If not specified in conf.py, then the default value of 'agg' is - used. To use the IPython directive without matplotlib as a dependency, set - the value to `None`. It may end up that matplotlib is still imported - if the user specifies so in `ipython_execlines` or makes use of the - @savefig pseudo decorator. -ipython_execlines: - A list of strings to be exec'd in the embedded Sphinx shell. Typical - usage is to make certain packages always available. Set this to an empty - list if you wish to have no imports always available. If specified in - conf.py as `None`, then it has the effect of making no imports available. - If omitted from conf.py altogether, then the default value of - ['import numpy as np', 'import matplotlib.pyplot as plt'] is used. -ipython_holdcount - When the @suppress pseudo-decorator is used, the execution count can be - incremented or not. The default behavior is to hold the execution count, - corresponding to a value of `True`. Set this to `False` to increment - the execution count after each suppressed command. - -As an example, to use the IPython directive when `matplotlib` is not available, -one sets the backend to `None`:: - - ipython_mplbackend = None - -An example usage of the directive is: - -.. code-block:: rst - - .. ipython:: - - In [1]: x = 1 - - In [2]: y = x**2 - - In [3]: print(y) - -See https://matplotlib.org/sampledoc/ipython_directive.html for additional -documentation. - -ToDo ----- - -- Turn the ad-hoc test() function into a real test suite. -- Break up ipython-specific functionality from matplotlib stuff into better - separated code. - -Authors -------- - -- John D Hunter: orignal author. -- Fernando Perez: refactoring, documentation, cleanups, port to 0.11. -- VáclavŠmilauer : Prompt generalizations. -- Skipper Seabold, refactoring, cleanups, pure python addition -""" -from __future__ import print_function -from __future__ import unicode_literals - -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- - -# Stdlib -import os -import re -import sys -import tempfile -import ast -from pandas.compat import zip, range, map, lmap, u, cStringIO as StringIO -import warnings - -# To keep compatibility with various python versions -try: - from hashlib import md5 -except ImportError: - from md5 import md5 - -# Third-party -import sphinx -from docutils.parsers.rst import directives -from docutils import nodes -from sphinx.util.compat import Directive - -# Our own -from traitlets.config import Config -from IPython import InteractiveShell -from IPython.core.profiledir import ProfileDir -from IPython.utils import io -from IPython.utils.py3compat import PY3 - -if PY3: - from io import StringIO - text_type = str -else: - from StringIO import StringIO - text_type = unicode - -#----------------------------------------------------------------------------- -# Globals -#----------------------------------------------------------------------------- -# for tokenizing blocks -COMMENT, INPUT, OUTPUT = range(3) - -#----------------------------------------------------------------------------- -# Functions and class declarations -#----------------------------------------------------------------------------- - -def block_parser(part, rgxin, rgxout, fmtin, fmtout): - """ - part is a string of ipython text, comprised of at most one - input, one ouput, comments, and blank lines. The block parser - parses the text into a list of:: - - blocks = [ (TOKEN0, data0), (TOKEN1, data1), ...] - - where TOKEN is one of [COMMENT | INPUT | OUTPUT ] and - data is, depending on the type of token:: - - COMMENT : the comment string - - INPUT: the (DECORATOR, INPUT_LINE, REST) where - DECORATOR: the input decorator (or None) - INPUT_LINE: the input as string (possibly multi-line) - REST : any stdout generated by the input line (not OUTPUT) - - OUTPUT: the output string, possibly multi-line - - """ - block = [] - lines = part.split('\n') - N = len(lines) - i = 0 - decorator = None - while 1: - - if i==N: - # nothing left to parse -- the last line - break - - line = lines[i] - i += 1 - line_stripped = line.strip() - if line_stripped.startswith('#'): - block.append((COMMENT, line)) - continue - - if line_stripped.startswith('@'): - # we're assuming at most one decorator -- may need to - # rethink - decorator = line_stripped - continue - - # does this look like an input line? - matchin = rgxin.match(line) - if matchin: - lineno, inputline = int(matchin.group(1)), matchin.group(2) - - # the ....: continuation string - continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2)) - Nc = len(continuation) - # input lines can continue on for more than one line, if - # we have a '\' line continuation char or a function call - # echo line 'print'. The input line can only be - # terminated by the end of the block or an output line, so - # we parse out the rest of the input line if it is - # multiline as well as any echo text - - rest = [] - while i 1: - if input_lines[-1] != "": - input_lines.append('') # make sure there's a blank line - # so splitter buffer gets reset - - continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2)) - - if is_savefig: - image_file, image_directive = self.process_image(decorator) - - ret = [] - is_semicolon = False - - # Hold the execution count, if requested to do so. - if is_suppress and self.hold_count: - store_history = False - else: - store_history = True - - # Note: catch_warnings is not thread safe - with warnings.catch_warnings(record=True) as ws: - for i, line in enumerate(input_lines): - if line.endswith(';'): - is_semicolon = True - - if i == 0: - # process the first input line - if is_verbatim: - self.process_input_line('') - self.IP.execution_count += 1 # increment it anyway - else: - # only submit the line in non-verbatim mode - self.process_input_line(line, store_history=store_history) - formatted_line = '%s %s'%(input_prompt, line) - else: - # process a continuation line - if not is_verbatim: - self.process_input_line(line, store_history=store_history) - - formatted_line = '%s %s'%(continuation, line) - - if not is_suppress: - ret.append(formatted_line) - - if not is_suppress and len(rest.strip()) and is_verbatim: - # the "rest" is the standard output of the - # input, which needs to be added in - # verbatim mode - ret.append(rest) - - self.cout.seek(0) - output = self.cout.read() - if not is_suppress and not is_semicolon: - ret.append(output) - elif is_semicolon: # get spacing right - ret.append('') - - # context information - filename = self.state.document.current_source - lineno = self.state.document.current_line - - # output any exceptions raised during execution to stdout - # unless :okexcept: has been specified. - if not is_okexcept and "Traceback" in output: - s = "\nException in %s at block ending on line %s\n" % (filename, lineno) - s += "Specify :okexcept: as an option in the ipython:: block to suppress this message\n" - sys.stdout.write('\n\n>>>' + ('-' * 73)) - sys.stdout.write(s) - sys.stdout.write(output) - sys.stdout.write('<<<' + ('-' * 73) + '\n\n') - - # output any warning raised during execution to stdout - # unless :okwarning: has been specified. - if not is_okwarning: - for w in ws: - s = "\nWarning in %s at block ending on line %s\n" % (filename, lineno) - s += "Specify :okwarning: as an option in the ipython:: block to suppress this message\n" - sys.stdout.write('\n\n>>>' + ('-' * 73)) - sys.stdout.write(s) - sys.stdout.write('-' * 76 + '\n') - s=warnings.formatwarning(w.message, w.category, - w.filename, w.lineno, w.line) - sys.stdout.write(s) - sys.stdout.write('<<<' + ('-' * 73) + '\n') - - self.cout.truncate(0) - return (ret, input_lines, output, is_doctest, decorator, image_file, - image_directive) - - - def process_output(self, data, output_prompt, - input_lines, output, is_doctest, decorator, image_file): - """ - Process data block for OUTPUT token. - - """ - TAB = ' ' * 4 - - if is_doctest and output is not None: - - found = output - found = found.strip() - submitted = data.strip() - - if self.directive is None: - source = 'Unavailable' - content = 'Unavailable' - else: - source = self.directive.state.document.current_source - content = self.directive.content - # Add tabs and join into a single string. - content = '\n'.join([TAB + line for line in content]) - - # Make sure the output contains the output prompt. - ind = found.find(output_prompt) - if ind < 0: - e = ('output does not contain output prompt\n\n' - 'Document source: {0}\n\n' - 'Raw content: \n{1}\n\n' - 'Input line(s):\n{TAB}{2}\n\n' - 'Output line(s):\n{TAB}{3}\n\n') - e = e.format(source, content, '\n'.join(input_lines), - repr(found), TAB=TAB) - raise RuntimeError(e) - found = found[len(output_prompt):].strip() - - # Handle the actual doctest comparison. - if decorator.strip() == '@doctest': - # Standard doctest - if found != submitted: - e = ('doctest failure\n\n' - 'Document source: {0}\n\n' - 'Raw content: \n{1}\n\n' - 'On input line(s):\n{TAB}{2}\n\n' - 'we found output:\n{TAB}{3}\n\n' - 'instead of the expected:\n{TAB}{4}\n\n') - e = e.format(source, content, '\n'.join(input_lines), - repr(found), repr(submitted), TAB=TAB) - raise RuntimeError(e) - else: - self.custom_doctest(decorator, input_lines, found, submitted) - - def process_comment(self, data): - """Process data fPblock for COMMENT token.""" - if not self.is_suppress: - return [data] - - def save_image(self, image_file): - """ - Saves the image file to disk. - """ - self.ensure_pyplot() - command = ('plt.gcf().savefig("%s", bbox_inches="tight", ' - 'dpi=100)' % image_file) - - #print 'SAVEFIG', command # dbg - self.process_input_line('bookmark ipy_thisdir', store_history=False) - self.process_input_line('cd -b ipy_savedir', store_history=False) - self.process_input_line(command, store_history=False) - self.process_input_line('cd -b ipy_thisdir', store_history=False) - self.process_input_line('bookmark -d ipy_thisdir', store_history=False) - self.clear_cout() - - def process_block(self, block): - """ - process block from the block_parser and return a list of processed lines - """ - ret = [] - output = None - input_lines = None - lineno = self.IP.execution_count - - input_prompt = self.promptin % lineno - output_prompt = self.promptout % lineno - image_file = None - image_directive = None - - for token, data in block: - if token == COMMENT: - out_data = self.process_comment(data) - elif token == INPUT: - (out_data, input_lines, output, is_doctest, decorator, - image_file, image_directive) = \ - self.process_input(data, input_prompt, lineno) - elif token == OUTPUT: - out_data = \ - self.process_output(data, output_prompt, - input_lines, output, is_doctest, - decorator, image_file) - if out_data: - ret.extend(out_data) - - # save the image files - if image_file is not None: - self.save_image(image_file) - - return ret, image_directive - - def ensure_pyplot(self): - """ - Ensures that pyplot has been imported into the embedded IPython shell. - - Also, makes sure to set the backend appropriately if not set already. - - """ - # We are here if the @figure pseudo decorator was used. Thus, it's - # possible that we could be here even if python_mplbackend were set to - # `None`. That's also strange and perhaps worthy of raising an - # exception, but for now, we just set the backend to 'agg'. - - if not self._pyplot_imported: - if 'matplotlib.backends' not in sys.modules: - # Then ipython_matplotlib was set to None but there was a - # call to the @figure decorator (and ipython_execlines did - # not set a backend). - #raise Exception("No backend was set, but @figure was used!") - import matplotlib - matplotlib.use('agg') - - # Always import pyplot into embedded shell. - self.process_input_line('import matplotlib.pyplot as plt', - store_history=False) - self._pyplot_imported = True - - def process_pure_python(self, content): - """ - content is a list of strings. it is unedited directive content - - This runs it line by line in the InteractiveShell, prepends - prompts as needed capturing stderr and stdout, then returns - the content as a list as if it were ipython code - """ - output = [] - savefig = False # keep up with this to clear figure - multiline = False # to handle line continuation - multiline_start = None - fmtin = self.promptin - - ct = 0 - - for lineno, line in enumerate(content): - - line_stripped = line.strip() - if not len(line): - output.append(line) - continue - - # handle decorators - if line_stripped.startswith('@'): - output.extend([line]) - if 'savefig' in line: - savefig = True # and need to clear figure - continue - - # handle comments - if line_stripped.startswith('#'): - output.extend([line]) - continue - - # deal with lines checking for multiline - continuation = u' %s:'% ''.join(['.']*(len(str(ct))+2)) - if not multiline: - modified = u"%s %s" % (fmtin % ct, line_stripped) - output.append(modified) - ct += 1 - try: - ast.parse(line_stripped) - output.append(u'') - except Exception: # on a multiline - multiline = True - multiline_start = lineno - else: # still on a multiline - modified = u'%s %s' % (continuation, line) - output.append(modified) - - # if the next line is indented, it should be part of multiline - if len(content) > lineno + 1: - nextline = content[lineno + 1] - if len(nextline) - len(nextline.lstrip()) > 3: - continue - try: - mod = ast.parse( - '\n'.join(content[multiline_start:lineno+1])) - if isinstance(mod.body[0], ast.FunctionDef): - # check to see if we have the whole function - for element in mod.body[0].body: - if isinstance(element, ast.Return): - multiline = False - else: - output.append(u'') - multiline = False - except Exception: - pass - - if savefig: # clear figure if plotted - self.ensure_pyplot() - self.process_input_line('plt.clf()', store_history=False) - self.clear_cout() - savefig = False - - return output - - def custom_doctest(self, decorator, input_lines, found, submitted): - """ - Perform a specialized doctest. - - """ - from .custom_doctests import doctests - - args = decorator.split() - doctest_type = args[1] - if doctest_type in doctests: - doctests[doctest_type](self, args, input_lines, found, submitted) - else: - e = "Invalid option to @doctest: {0}".format(doctest_type) - raise Exception(e) - - -class IPythonDirective(Directive): - - has_content = True - required_arguments = 0 - optional_arguments = 4 # python, suppress, verbatim, doctest - final_argumuent_whitespace = True - option_spec = { 'python': directives.unchanged, - 'suppress' : directives.flag, - 'verbatim' : directives.flag, - 'doctest' : directives.flag, - 'okexcept': directives.flag, - 'okwarning': directives.flag, - 'output_encoding': directives.unchanged_required - } - - shell = None - - seen_docs = set() - - def get_config_options(self): - # contains sphinx configuration variables - config = self.state.document.settings.env.config - - # get config variables to set figure output directory - confdir = self.state.document.settings.env.app.confdir - savefig_dir = config.ipython_savefig_dir - source_dir = os.path.dirname(self.state.document.current_source) - if savefig_dir is None: - savefig_dir = config.html_static_path - if isinstance(savefig_dir, list): - savefig_dir = savefig_dir[0] # safe to assume only one path? - savefig_dir = os.path.join(confdir, savefig_dir) - - # get regex and prompt stuff - rgxin = config.ipython_rgxin - rgxout = config.ipython_rgxout - promptin = config.ipython_promptin - promptout = config.ipython_promptout - mplbackend = config.ipython_mplbackend - exec_lines = config.ipython_execlines - hold_count = config.ipython_holdcount - - return (savefig_dir, source_dir, rgxin, rgxout, - promptin, promptout, mplbackend, exec_lines, hold_count) - - def setup(self): - # Get configuration values. - (savefig_dir, source_dir, rgxin, rgxout, promptin, promptout, - mplbackend, exec_lines, hold_count) = self.get_config_options() - - if self.shell is None: - # We will be here many times. However, when the - # EmbeddedSphinxShell is created, its interactive shell member - # is the same for each instance. - - if mplbackend: - import matplotlib - # Repeated calls to use() will not hurt us since `mplbackend` - # is the same each time. - matplotlib.use(mplbackend) - - # Must be called after (potentially) importing matplotlib and - # setting its backend since exec_lines might import pylab. - self.shell = EmbeddedSphinxShell(exec_lines, self.state) - - # Store IPython directive to enable better error messages - self.shell.directive = self - - # reset the execution count if we haven't processed this doc - #NOTE: this may be borked if there are multiple seen_doc tmp files - #check time stamp? - if not self.state.document.current_source in self.seen_docs: - self.shell.IP.history_manager.reset() - self.shell.IP.execution_count = 1 - self.shell.IP.prompt_manager.width = 0 - self.seen_docs.add(self.state.document.current_source) - - # and attach to shell so we don't have to pass them around - self.shell.rgxin = rgxin - self.shell.rgxout = rgxout - self.shell.promptin = promptin - self.shell.promptout = promptout - self.shell.savefig_dir = savefig_dir - self.shell.source_dir = source_dir - self.shell.hold_count = hold_count - - # setup bookmark for saving figures directory - self.shell.process_input_line('bookmark ipy_savedir %s'%savefig_dir, - store_history=False) - self.shell.clear_cout() - - return rgxin, rgxout, promptin, promptout - - def teardown(self): - # delete last bookmark - self.shell.process_input_line('bookmark -d ipy_savedir', - store_history=False) - self.shell.clear_cout() - - def run(self): - debug = False - - #TODO, any reason block_parser can't be a method of embeddable shell - # then we wouldn't have to carry these around - rgxin, rgxout, promptin, promptout = self.setup() - - options = self.options - self.shell.is_suppress = 'suppress' in options - self.shell.is_doctest = 'doctest' in options - self.shell.is_verbatim = 'verbatim' in options - self.shell.is_okexcept = 'okexcept' in options - self.shell.is_okwarning = 'okwarning' in options - - self.shell.output_encoding = [options.get('output_encoding', 'utf8')] - - # handle pure python code - if 'python' in self.arguments: - content = self.content - self.content = self.shell.process_pure_python(content) - - parts = '\n'.join(self.content).split('\n\n') - - lines = ['.. code-block:: ipython', ''] - figures = [] - - for part in parts: - block = block_parser(part, rgxin, rgxout, promptin, promptout) - if len(block): - rows, figure = self.shell.process_block(block) - for row in rows: - lines.extend([' %s'%line for line in row.split('\n')]) - - if figure is not None: - figures.append(figure) - - for figure in figures: - lines.append('') - lines.extend(figure.split('\n')) - lines.append('') - - if len(lines)>2: - if debug: - print('\n'.join(lines)) - else: - # This has to do with input, not output. But if we comment - # these lines out, then no IPython code will appear in the - # final output. - self.state_machine.insert_input( - lines, self.state_machine.input_lines.source(0)) - - # cleanup - self.teardown() - - return [] - -# Enable as a proper Sphinx directive -def setup(app): - setup.app = app - - app.add_directive('ipython', IPythonDirective) - app.add_config_value('ipython_savefig_dir', None, 'env') - app.add_config_value('ipython_rgxin', - re.compile('In \[(\d+)\]:\s?(.*)\s*'), 'env') - app.add_config_value('ipython_rgxout', - re.compile('Out\[(\d+)\]:\s?(.*)\s*'), 'env') - app.add_config_value('ipython_promptin', 'In [%d]:', 'env') - app.add_config_value('ipython_promptout', 'Out[%d]:', 'env') - - # We could just let matplotlib pick whatever is specified as the default - # backend in the matplotlibrc file, but this would cause issues if the - # backend didn't work in headless environments. For this reason, 'agg' - # is a good default backend choice. - app.add_config_value('ipython_mplbackend', 'agg', 'env') - - # If the user sets this config value to `None`, then EmbeddedSphinxShell's - # __init__ method will treat it as []. - execlines = ['import numpy as np', 'import matplotlib.pyplot as plt'] - app.add_config_value('ipython_execlines', execlines, 'env') - - app.add_config_value('ipython_holdcount', True, 'env') - -# Simple smoke test, needs to be converted to a proper automatic test. -def test(): - - examples = [ - r""" -In [9]: pwd -Out[9]: '/home/jdhunter/py4science/book' - -In [10]: cd bookdata/ -/home/jdhunter/py4science/book/bookdata - -In [2]: from pylab import * - -In [2]: ion() - -In [3]: im = imread('stinkbug.png') - -@savefig mystinkbug.png width=4in -In [4]: imshow(im) -Out[4]: - -""", - r""" - -In [1]: x = 'hello world' - -# string methods can be -# used to alter the string -@doctest -In [2]: x.upper() -Out[2]: 'HELLO WORLD' - -@verbatim -In [3]: x.st -x.startswith x.strip -""", - r""" - -In [130]: url = 'https://ichart.finance.yahoo.com/table.csv?s=CROX\ - .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv' - -In [131]: print url.split('&') -['https://ichart.finance.yahoo.com/table.csv?s=CROX', 'd=9', 'e=22', 'f=2009', 'g=d', 'a=1', 'b=8', 'c=2006', 'ignore=.csv'] - -In [60]: import urllib - -""", - r"""\ - -In [133]: import numpy.random - -@suppress -In [134]: numpy.random.seed(2358) - -@doctest -In [135]: numpy.random.rand(10,2) -Out[135]: -array([[ 0.64524308, 0.59943846], - [ 0.47102322, 0.8715456 ], - [ 0.29370834, 0.74776844], - [ 0.99539577, 0.1313423 ], - [ 0.16250302, 0.21103583], - [ 0.81626524, 0.1312433 ], - [ 0.67338089, 0.72302393], - [ 0.7566368 , 0.07033696], - [ 0.22591016, 0.77731835], - [ 0.0072729 , 0.34273127]]) - -""", - - r""" -In [106]: print x -jdh - -In [109]: for i in range(10): - .....: print i - .....: - .....: -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -""", - - r""" - -In [144]: from pylab import * - -In [145]: ion() - -# use a semicolon to suppress the output -@savefig test_hist.png width=4in -In [151]: hist(np.random.randn(10000), 100); - - -@savefig test_plot.png width=4in -In [151]: plot(np.random.randn(10000), 'o'); - """, - - r""" -# use a semicolon to suppress the output -In [151]: plt.clf() - -@savefig plot_simple.png width=4in -In [151]: plot([1,2,3]) - -@savefig hist_simple.png width=4in -In [151]: hist(np.random.randn(10000), 100); - -""", - r""" -# update the current fig -In [151]: ylabel('number') - -In [152]: title('normal distribution') - - -@savefig hist_with_text.png -In [153]: grid(True) - -@doctest float -In [154]: 0.1 + 0.2 -Out[154]: 0.3 - -@doctest float -In [155]: np.arange(16).reshape(4,4) -Out[155]: -array([[ 0, 1, 2, 3], - [ 4, 5, 6, 7], - [ 8, 9, 10, 11], - [12, 13, 14, 15]]) - -In [1]: x = np.arange(16, dtype=float).reshape(4,4) - -In [2]: x[0,0] = np.inf - -In [3]: x[0,1] = np.nan - -@doctest float -In [4]: x -Out[4]: -array([[ inf, nan, 2., 3.], - [ 4., 5., 6., 7.], - [ 8., 9., 10., 11.], - [ 12., 13., 14., 15.]]) - - - """, - ] - # skip local-file depending first example: - examples = examples[1:] - - #ipython_directive.DEBUG = True # dbg - #options = dict(suppress=True) # dbg - options = dict() - for example in examples: - content = example.split('\n') - IPythonDirective('debug', arguments=None, options=options, - content=content, lineno=0, - content_offset=None, block_text=None, - state=None, state_machine=None, - ) - -# Run test suite as a script -if __name__=='__main__': - if not os.path.isdir('_static'): - os.mkdir('_static') - test() - print('All OK? Check figures in _static/') diff --git a/doc/sphinxext/plot_directive.py b/doc/sphinxext/plot_directive.py deleted file mode 100644 index 49c1d9af8c..0000000000 --- a/doc/sphinxext/plot_directive.py +++ /dev/null @@ -1,837 +0,0 @@ -""" -A directive for including a matplotlib plot in a Sphinx document. - -By default, in HTML output, `plot` will include a .png file with a -link to a high-res .png and .pdf. In LaTeX output, it will include a -.pdf. - -The source code for the plot may be included in one of three ways: - - 1. **A path to a source file** as the argument to the directive:: - - .. plot:: path/to/plot.py - - When a path to a source file is given, the content of the - directive may optionally contain a caption for the plot:: - - .. plot:: path/to/plot.py - - This is the caption for the plot - - Additionally, one my specify the name of a function to call (with - no arguments) immediately after importing the module:: - - .. plot:: path/to/plot.py plot_function1 - - 2. Included as **inline content** to the directive:: - - .. plot:: - - import matplotlib.pyplot as plt - import matplotlib.image as mpimg - import numpy as np - img = mpimg.imread('_static/stinkbug.png') - imgplot = plt.imshow(img) - - 3. Using **doctest** syntax:: - - .. plot:: - A plotting example: - >>> import matplotlib.pyplot as plt - >>> plt.plot([1,2,3], [4,5,6]) - -Options -------- - -The ``plot`` directive supports the following options: - - format : {'python', 'doctest'} - Specify the format of the input - - include-source : bool - Whether to display the source code. The default can be changed - using the `plot_include_source` variable in conf.py - - encoding : str - If this source file is in a non-UTF8 or non-ASCII encoding, - the encoding must be specified using the `:encoding:` option. - The encoding will not be inferred using the ``-*- coding -*-`` - metacomment. - - context : bool or str - If provided, the code will be run in the context of all - previous plot directives for which the `:context:` option was - specified. This only applies to inline code plot directives, - not those run from files. If the ``:context: reset`` option is - specified, the context is reset for this and future plots, and - previous figures are closed prior to running the code. - ``:context:close-figs`` keeps the context but closes previous figures - before running the code. - - nofigs : bool - If specified, the code block will be run, but no figures will - be inserted. This is usually useful with the ``:context:`` - option. - -Additionally, this directive supports all of the options of the -`image` directive, except for `target` (since plot will add its own -target). These include `alt`, `height`, `width`, `scale`, `align` and -`class`. - -Configuration options ---------------------- - -The plot directive has the following configuration options: - - plot_include_source - Default value for the include-source option - - plot_html_show_source_link - Whether to show a link to the source in HTML. - - plot_pre_code - Code that should be executed before each plot. - - plot_basedir - Base directory, to which ``plot::`` file names are relative - to. (If None or empty, file names are relative to the - directory where the file containing the directive is.) - - plot_formats - File formats to generate. List of tuples or strings:: - - [(suffix, dpi), suffix, ...] - - that determine the file format and the DPI. For entries whose - DPI was omitted, sensible defaults are chosen. - - plot_html_show_formats - Whether to show links to the files in HTML. - - plot_rcparams - A dictionary containing any non-standard rcParams that should - be applied before each plot. - - plot_apply_rcparams - By default, rcParams are applied when `context` option is not used in - a plot directive. This configuration option overrides this behavior - and applies rcParams before each plot. - - plot_working_directory - By default, the working directory will be changed to the directory of - the example, so the code can get at its data files, if any. Also its - path will be added to `sys.path` so it can import any helper modules - sitting beside it. This configuration option can be used to specify - a central directory (also added to `sys.path`) where data files and - helper modules for all code are located. - - plot_template - Provide a customized template for preparing restructured text. -""" -from __future__ import (absolute_import, division, print_function, - unicode_literals) - -import six -from six.moves import xrange - -import sys, os, shutil, io, re, textwrap -from os.path import relpath -import traceback - -if not six.PY3: - import cStringIO - -from docutils.parsers.rst import directives -from docutils.parsers.rst.directives.images import Image -align = Image.align -import sphinx - -sphinx_version = sphinx.__version__.split(".") -# The split is necessary for sphinx beta versions where the string is -# '6b1' -sphinx_version = tuple([int(re.split('[^0-9]', x)[0]) - for x in sphinx_version[:2]]) - -try: - # Sphinx depends on either Jinja or Jinja2 - import jinja2 - def format_template(template, **kw): - return jinja2.Template(template).render(**kw) -except ImportError: - import jinja - def format_template(template, **kw): - return jinja.from_string(template, **kw) - -import matplotlib -import matplotlib.cbook as cbook -matplotlib.use('Agg') -import matplotlib.pyplot as plt -from matplotlib import _pylab_helpers - -__version__ = 2 - -#------------------------------------------------------------------------------ -# Registration hook -#------------------------------------------------------------------------------ - -def plot_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - return run(arguments, content, options, state_machine, state, lineno) -plot_directive.__doc__ = __doc__ - - -def _option_boolean(arg): - if not arg or not arg.strip(): - # no argument given, assume used as a flag - return True - elif arg.strip().lower() in ('no', '0', 'false'): - return False - elif arg.strip().lower() in ('yes', '1', 'true'): - return True - else: - raise ValueError('"%s" unknown boolean' % arg) - - -def _option_context(arg): - if arg in [None, 'reset', 'close-figs']: - return arg - raise ValueError("argument should be None or 'reset' or 'close-figs'") - - -def _option_format(arg): - return directives.choice(arg, ('python', 'doctest')) - - -def _option_align(arg): - return directives.choice(arg, ("top", "middle", "bottom", "left", "center", - "right")) - - -def mark_plot_labels(app, document): - """ - To make plots referenceable, we need to move the reference from - the "htmlonly" (or "latexonly") node to the actual figure node - itself. - """ - for name, explicit in six.iteritems(document.nametypes): - if not explicit: - continue - labelid = document.nameids[name] - if labelid is None: - continue - node = document.ids[labelid] - if node.tagname in ('html_only', 'latex_only'): - for n in node: - if n.tagname == 'figure': - sectname = name - for c in n: - if c.tagname == 'caption': - sectname = c.astext() - break - - node['ids'].remove(labelid) - node['names'].remove(name) - n['ids'].append(labelid) - n['names'].append(name) - document.settings.env.labels[name] = \ - document.settings.env.docname, labelid, sectname - break - - -def setup(app): - setup.app = app - setup.config = app.config - setup.confdir = app.confdir - - options = {'alt': directives.unchanged, - 'height': directives.length_or_unitless, - 'width': directives.length_or_percentage_or_unitless, - 'scale': directives.nonnegative_int, - 'align': _option_align, - 'class': directives.class_option, - 'include-source': _option_boolean, - 'format': _option_format, - 'context': _option_context, - 'nofigs': directives.flag, - 'encoding': directives.encoding - } - - app.add_directive('plot', plot_directive, True, (0, 2, False), **options) - app.add_config_value('plot_pre_code', None, True) - app.add_config_value('plot_include_source', False, True) - app.add_config_value('plot_html_show_source_link', True, True) - app.add_config_value('plot_formats', ['png', 'hires.png', 'pdf'], True) - app.add_config_value('plot_basedir', None, True) - app.add_config_value('plot_html_show_formats', True, True) - app.add_config_value('plot_rcparams', {}, True) - app.add_config_value('plot_apply_rcparams', False, True) - app.add_config_value('plot_working_directory', None, True) - app.add_config_value('plot_template', None, True) - - app.connect(str('doctree-read'), mark_plot_labels) - -#------------------------------------------------------------------------------ -# Doctest handling -#------------------------------------------------------------------------------ - -def contains_doctest(text): - try: - # check if it's valid Python as-is - compile(text, '', 'exec') - return False - except SyntaxError: - pass - r = re.compile(r'^\s*>>>', re.M) - m = r.search(text) - return bool(m) - - -def unescape_doctest(text): - """ - Extract code from a piece of text, which contains either Python code - or doctests. - - """ - if not contains_doctest(text): - return text - - code = "" - for line in text.split("\n"): - m = re.match(r'^\s*(>>>|\.\.\.) (.*)$', line) - if m: - code += m.group(2) + "\n" - elif line.strip(): - code += "# " + line.strip() + "\n" - else: - code += "\n" - return code - - -def split_code_at_show(text): - """ - Split code at plt.show() - - """ - - parts = [] - is_doctest = contains_doctest(text) - - part = [] - for line in text.split("\n"): - if (not is_doctest and line.strip() == 'plt.show()') or \ - (is_doctest and line.strip() == '>>> plt.show()'): - part.append(line) - parts.append("\n".join(part)) - part = [] - else: - part.append(line) - if "\n".join(part).strip(): - parts.append("\n".join(part)) - return parts - - -def remove_coding(text): - """ - Remove the coding comment, which six.exec_ doesn't like. - """ - sub_re = re.compile("^#\s*-\*-\s*coding:\s*.*-\*-$", flags=re.MULTILINE) - return sub_re.sub("", text) - -#------------------------------------------------------------------------------ -# Template -#------------------------------------------------------------------------------ - - -TEMPLATE = """ -{{ source_code }} - -{{ only_html }} - - {% if source_link or (html_show_formats and not multi_image) %} - ( - {%- if source_link -%} - `Source code <{{ source_link }}>`__ - {%- endif -%} - {%- if html_show_formats and not multi_image -%} - {%- for img in images -%} - {%- for fmt in img.formats -%} - {%- if source_link or not loop.first -%}, {% endif -%} - `{{ fmt }} <{{ dest_dir }}/{{ img.basename }}.{{ fmt }}>`__ - {%- endfor -%} - {%- endfor -%} - {%- endif -%} - ) - {% endif %} - - {% for img in images %} - .. figure:: {{ build_dir }}/{{ img.basename }}.png - {% for option in options -%} - {{ option }} - {% endfor %} - - {% if html_show_formats and multi_image -%} - ( - {%- for fmt in img.formats -%} - {%- if not loop.first -%}, {% endif -%} - `{{ fmt }} <{{ dest_dir }}/{{ img.basename }}.{{ fmt }}>`__ - {%- endfor -%} - ) - {%- endif -%} - - {{ caption }} - {% endfor %} - -{{ only_latex }} - - {% for img in images %} - {% if 'pdf' in img.formats -%} - .. image:: {{ build_dir }}/{{ img.basename }}.pdf - {% endif -%} - {% endfor %} - -{{ only_texinfo }} - - {% for img in images %} - .. image:: {{ build_dir }}/{{ img.basename }}.png - {% for option in options -%} - {{ option }} - {% endfor %} - - {% endfor %} - -""" - -exception_template = """ -.. htmlonly:: - - [`source code <%(linkdir)s/%(basename)s.py>`__] - -Exception occurred rendering plot. - -""" - -# the context of the plot for all directives specified with the -# :context: option -plot_context = dict() - -class ImageFile(object): - def __init__(self, basename, dirname): - self.basename = basename - self.dirname = dirname - self.formats = [] - - def filename(self, format): - return os.path.join(self.dirname, "%s.%s" % (self.basename, format)) - - def filenames(self): - return [self.filename(fmt) for fmt in self.formats] - - -def out_of_date(original, derived): - """ - Returns True if derivative is out-of-date wrt original, - both of which are full file paths. - """ - return (not os.path.exists(derived) or - (os.path.exists(original) and - os.stat(derived).st_mtime < os.stat(original).st_mtime)) - - -class PlotError(RuntimeError): - pass - - -def run_code(code, code_path, ns=None, function_name=None): - """ - Import a Python module from a path, and run the function given by - name, if function_name is not None. - """ - - # Change the working directory to the directory of the example, so - # it can get at its data files, if any. Add its path to sys.path - # so it can import any helper modules sitting beside it. - if six.PY2: - pwd = os.getcwdu() - else: - pwd = os.getcwd() - old_sys_path = list(sys.path) - if setup.config.plot_working_directory is not None: - try: - os.chdir(setup.config.plot_working_directory) - except OSError as err: - raise OSError(str(err) + '\n`plot_working_directory` option in' - 'Sphinx configuration file must be a valid ' - 'directory path') - except TypeError as err: - raise TypeError(str(err) + '\n`plot_working_directory` option in ' - 'Sphinx configuration file must be a string or ' - 'None') - sys.path.insert(0, setup.config.plot_working_directory) - elif code_path is not None: - dirname = os.path.abspath(os.path.dirname(code_path)) - os.chdir(dirname) - sys.path.insert(0, dirname) - - # Reset sys.argv - old_sys_argv = sys.argv - sys.argv = [code_path] - - # Redirect stdout - stdout = sys.stdout - if six.PY3: - sys.stdout = io.StringIO() - else: - sys.stdout = cStringIO.StringIO() - - # Assign a do-nothing print function to the namespace. There - # doesn't seem to be any other way to provide a way to (not) print - # that works correctly across Python 2 and 3. - def _dummy_print(*arg, **kwarg): - pass - - try: - try: - code = unescape_doctest(code) - if ns is None: - ns = {} - if not ns: - if setup.config.plot_pre_code is None: - six.exec_(six.text_type("import numpy as np\n" + - "from matplotlib import pyplot as plt\n"), ns) - else: - six.exec_(six.text_type(setup.config.plot_pre_code), ns) - ns['print'] = _dummy_print - if "__main__" in code: - six.exec_("__name__ = '__main__'", ns) - code = remove_coding(code) - six.exec_(code, ns) - if function_name is not None: - six.exec_(function_name + "()", ns) - except (Exception, SystemExit) as err: - raise PlotError(traceback.format_exc()) - finally: - os.chdir(pwd) - sys.argv = old_sys_argv - sys.path[:] = old_sys_path - sys.stdout = stdout - return ns - - -def clear_state(plot_rcparams, close=True): - if close: - plt.close('all') - matplotlib.rc_file_defaults() - matplotlib.rcParams.update(plot_rcparams) - - -def render_figures(code, code_path, output_dir, output_base, context, - function_name, config, context_reset=False, - close_figs=False): - """ - Run a pyplot script and save the low and high res PNGs and a PDF - in *output_dir*. - - Save the images under *output_dir* with file names derived from - *output_base* - """ - # -- Parse format list - default_dpi = {'png': 80, 'hires.png': 200, 'pdf': 200} - formats = [] - plot_formats = config.plot_formats - if isinstance(plot_formats, six.string_types): - plot_formats = eval(plot_formats) - for fmt in plot_formats: - if isinstance(fmt, six.string_types): - formats.append((fmt, default_dpi.get(fmt, 80))) - elif type(fmt) in (tuple, list) and len(fmt)==2: - formats.append((str(fmt[0]), int(fmt[1]))) - else: - raise PlotError('invalid image format "%r" in plot_formats' % fmt) - - # -- Try to determine if all images already exist - - code_pieces = split_code_at_show(code) - - # Look for single-figure output files first - all_exists = True - img = ImageFile(output_base, output_dir) - for format, dpi in formats: - if out_of_date(code_path, img.filename(format)): - all_exists = False - break - img.formats.append(format) - - if all_exists: - return [(code, [img])] - - # Then look for multi-figure output files - results = [] - all_exists = True - for i, code_piece in enumerate(code_pieces): - images = [] - for j in xrange(1000): - if len(code_pieces) > 1: - img = ImageFile('%s_%02d_%02d' % (output_base, i, j), output_dir) - else: - img = ImageFile('%s_%02d' % (output_base, j), output_dir) - for format, dpi in formats: - if out_of_date(code_path, img.filename(format)): - all_exists = False - break - img.formats.append(format) - - # assume that if we have one, we have them all - if not all_exists: - all_exists = (j > 0) - break - images.append(img) - if not all_exists: - break - results.append((code_piece, images)) - - if all_exists: - return results - - # We didn't find the files, so build them - - results = [] - if context: - ns = plot_context - else: - ns = {} - - if context_reset: - clear_state(config.plot_rcparams) - plot_context.clear() - - close_figs = not context or close_figs - - for i, code_piece in enumerate(code_pieces): - - if not context or config.plot_apply_rcparams: - clear_state(config.plot_rcparams, close_figs) - elif close_figs: - plt.close('all') - - run_code(code_piece, code_path, ns, function_name) - - images = [] - fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() - for j, figman in enumerate(fig_managers): - if len(fig_managers) == 1 and len(code_pieces) == 1: - img = ImageFile(output_base, output_dir) - elif len(code_pieces) == 1: - img = ImageFile("%s_%02d" % (output_base, j), output_dir) - else: - img = ImageFile("%s_%02d_%02d" % (output_base, i, j), - output_dir) - images.append(img) - for format, dpi in formats: - try: - figman.canvas.figure.savefig(img.filename(format), - dpi=dpi, - bbox_inches="tight") - except Exception as err: - raise PlotError(traceback.format_exc()) - img.formats.append(format) - - results.append((code_piece, images)) - - if not context or config.plot_apply_rcparams: - clear_state(config.plot_rcparams, close=not context) - - return results - - -def run(arguments, content, options, state_machine, state, lineno): - # The user may provide a filename *or* Python code content, but not both - if arguments and content: - raise RuntimeError("plot:: directive can't have both args and content") - - document = state_machine.document - config = document.settings.env.config - nofigs = 'nofigs' in options - - options.setdefault('include-source', config.plot_include_source) - keep_context = 'context' in options - context_opt = None if not keep_context else options['context'] - - rst_file = document.attributes['source'] - rst_dir = os.path.dirname(rst_file) - - if len(arguments): - if not config.plot_basedir: - source_file_name = os.path.join(setup.app.builder.srcdir, - directives.uri(arguments[0])) - else: - source_file_name = os.path.join(setup.confdir, config.plot_basedir, - directives.uri(arguments[0])) - - # If there is content, it will be passed as a caption. - caption = '\n'.join(content) - - # If the optional function name is provided, use it - if len(arguments) == 2: - function_name = arguments[1] - else: - function_name = None - - with io.open(source_file_name, 'r', encoding='utf-8') as fd: - code = fd.read() - output_base = os.path.basename(source_file_name) - else: - source_file_name = rst_file - code = textwrap.dedent("\n".join(map(str, content))) - counter = document.attributes.get('_plot_counter', 0) + 1 - document.attributes['_plot_counter'] = counter - base, ext = os.path.splitext(os.path.basename(source_file_name)) - output_base = '%s-%d.py' % (base, counter) - function_name = None - caption = '' - - base, source_ext = os.path.splitext(output_base) - if source_ext in ('.py', '.rst', '.txt'): - output_base = base - else: - source_ext = '' - - # ensure that LaTeX includegraphics doesn't choke in foo.bar.pdf filenames - output_base = output_base.replace('.', '-') - - # is it in doctest format? - is_doctest = contains_doctest(code) - if 'format' in options: - if options['format'] == 'python': - is_doctest = False - else: - is_doctest = True - - # determine output directory name fragment - source_rel_name = relpath(source_file_name, setup.confdir) - source_rel_dir = os.path.dirname(source_rel_name) - while source_rel_dir.startswith(os.path.sep): - source_rel_dir = source_rel_dir[1:] - - # build_dir: where to place output files (temporarily) - build_dir = os.path.join(os.path.dirname(setup.app.doctreedir), - 'plot_directive', - source_rel_dir) - # get rid of .. in paths, also changes pathsep - # see note in Python docs for warning about symbolic links on Windows. - # need to compare source and dest paths at end - build_dir = os.path.normpath(build_dir) - - if not os.path.exists(build_dir): - os.makedirs(build_dir) - - # output_dir: final location in the builder's directory - dest_dir = os.path.abspath(os.path.join(setup.app.builder.outdir, - source_rel_dir)) - if not os.path.exists(dest_dir): - os.makedirs(dest_dir) # no problem here for me, but just use built-ins - - # how to link to files from the RST file - dest_dir_link = os.path.join(relpath(setup.confdir, rst_dir), - source_rel_dir).replace(os.path.sep, '/') - build_dir_link = relpath(build_dir, rst_dir).replace(os.path.sep, '/') - source_link = dest_dir_link + '/' + output_base + source_ext - - # make figures - try: - results = render_figures(code, - source_file_name, - build_dir, - output_base, - keep_context, - function_name, - config, - context_reset=context_opt == 'reset', - close_figs=context_opt == 'close-figs') - errors = [] - except PlotError as err: - reporter = state.memo.reporter - sm = reporter.system_message( - 2, "Exception occurred in plotting %s\n from %s:\n%s" % (output_base, - source_file_name, err), - line=lineno) - results = [(code, [])] - errors = [sm] - - # Properly indent the caption - caption = '\n'.join(' ' + line.strip() - for line in caption.split('\n')) - - # generate output restructuredtext - total_lines = [] - for j, (code_piece, images) in enumerate(results): - if options['include-source']: - if is_doctest: - lines = [''] - lines += [row.rstrip() for row in code_piece.split('\n')] - else: - lines = ['.. code-block:: python', ''] - lines += [' %s' % row.rstrip() - for row in code_piece.split('\n')] - source_code = "\n".join(lines) - else: - source_code = "" - - if nofigs: - images = [] - - opts = [':%s: %s' % (key, val) for key, val in six.iteritems(options) - if key in ('alt', 'height', 'width', 'scale', 'align', 'class')] - - only_html = ".. only:: html" - only_latex = ".. only:: latex" - only_texinfo = ".. only:: texinfo" - - # Not-None src_link signals the need for a source link in the generated - # html - if j == 0 and config.plot_html_show_source_link: - src_link = source_link - else: - src_link = None - - result = format_template( - config.plot_template or TEMPLATE, - dest_dir=dest_dir_link, - build_dir=build_dir_link, - source_link=src_link, - multi_image=len(images) > 1, - only_html=only_html, - only_latex=only_latex, - only_texinfo=only_texinfo, - options=opts, - images=images, - source_code=source_code, - html_show_formats=config.plot_html_show_formats and not nofigs, - caption=caption) - - total_lines.extend(result.split("\n")) - total_lines.extend("\n") - - if total_lines: - state_machine.insert_input(total_lines, source=source_file_name) - - # copy image files to builder's output directory, if necessary - if not os.path.exists(dest_dir): - cbook.mkdirs(dest_dir) - - for code_piece, images in results: - for img in images: - for fn in img.filenames(): - destimg = os.path.join(dest_dir, os.path.basename(fn)) - if fn != destimg: - shutil.copyfile(fn, destimg) - - # copy script (if necessary) - target_name = os.path.join(dest_dir, output_base + source_ext) - with io.open(target_name, 'w', encoding="utf-8") as f: - if source_file_name == rst_file: - code_escaped = unescape_doctest(code) - else: - code_escaped = code - f.write(code_escaped) - - return errors diff --git a/examples/kde_joyplot.py b/examples/kde_ridgeplot.py similarity index 100% rename from examples/kde_joyplot.py rename to examples/kde_ridgeplot.py From 2f13b9f97da9767fcb703fc8b1d05566da20b696 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 12 May 2018 19:17:45 -0400 Subject: [PATCH 0649/1738] Rename ridgeplot example --- examples/kde_ridgeplot.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/kde_ridgeplot.py b/examples/kde_ridgeplot.py index 0b2c30ccc9..51156d1616 100644 --- a/examples/kde_ridgeplot.py +++ b/examples/kde_ridgeplot.py @@ -1,6 +1,6 @@ """ -Overlapping densities ('joy plot') -================================== +Overlapping densities ('ridge plot') +==================================== """ @@ -27,12 +27,14 @@ g.map(sns.kdeplot, "x", clip_on=False, color="w", lw=2, bw=.2) g.map(plt.axhline, y=0, lw=2, clip_on=False) + # Define and use a simple function to label the plot in axes coordinates def label(x, color, label): ax = plt.gca() - ax.text(0, .2, label, fontweight="bold", color=color, + ax.text(0, .2, label, fontweight="bold", color=color, ha="left", va="center", transform=ax.transAxes) + g.map(label, "x") # Set the subplots to overlap From 36a268a5ada7c7488d41ce70d006be8971a1e0ea Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 12 May 2018 19:18:04 -0400 Subject: [PATCH 0650/1738] Update setup.py to explicitly declare dependencies for pip --- doc/releases/v0.9.0.txt | 2 + requirements.txt | 2 - setup.py | 82 ++++++++++++++++++----------------------- 3 files changed, 38 insertions(+), 48 deletions(-) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index ebd5265909..c0c0c816ef 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -8,5 +8,7 @@ v0.9.0 (Unreleased) - Fixed :func:`jointplot`/:class:`JointGrid` so that they now accept list inputs. +- Changed the install infrastructure to explicitly declare dependencies in a way that ``pip`` is aware of. This means that ``pip install seaborn`` will now work in an empty environment. Additionally, the dependencies are specified with strict minimal versions. + - Updated the testing infrastructure to execute tests with `pytest ` (although many individual tests still use nose assertion). diff --git a/requirements.txt b/requirements.txt index e7f0562af2..8fed041814 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,5 +2,3 @@ numpy scipy matplotlib pandas -statsmodels -patsy diff --git a/setup.py b/setup.py index 1940d6e4f8..127a692c30 100644 --- a/setup.py +++ b/setup.py @@ -1,13 +1,13 @@ #! /usr/bin/env python # -# Copyright (C) 2012-2017 Michael Waskom +# Copyright (C) 2012-2018 Michael Waskom import os # temporarily redirect config directory to prevent matplotlib importing # testing that for writeable directory which results in sandbox error in # certain easy_install versions os.environ["MPLCONFIGDIR"] = "." -DESCRIPTION = "Seaborn: statistical data visualization" +DESCRIPTION = "seaborn: statistical data visualization" LONG_DESCRIPTION = """\ Seaborn is a library for making attractive and informative statistical graphics in Python. It is built on top of matplotlib and tightly integrated with the PyData stack, including support for numpy and pandas data structures and statistical routines from scipy and statsmodels. @@ -30,42 +30,44 @@ DOWNLOAD_URL = 'https://github.com/mwaskom/seaborn/' VERSION = '0.9.dev' +INSTALL_REQUIRES = [ + 'numpy>=1.9.3', + 'scipy>=0.14.0', + 'pandas>=0.15.2', + 'matplotlib>=1.4.3', +] + +PACKAGES = [ + 'seaborn', + 'seaborn.colors', + 'seaborn.external', + 'seaborn.tests', +] + +CLASSIFIERS = [ + 'Intended Audience :: Science/Research', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'License :: OSI Approved :: BSD License', + 'Topic :: Scientific/Engineering :: Visualization', + 'Topic :: Multimedia :: Graphics', + 'Operating System :: POSIX', + 'Operating System :: Unix', + 'Operating System :: MacOS' +] + try: from setuptools import setup _has_setuptools = True except ImportError: from distutils.core import setup -def check_dependencies(): - install_requires = [] - - # Just make sure dependencies exist, I haven't rigorously - # tested what the minimal versions that will work are - # (help on that would be awesome) - try: - import numpy - except ImportError: - install_requires.append('numpy') - try: - import scipy - except ImportError: - install_requires.append('scipy') - try: - import matplotlib - except ImportError: - install_requires.append('matplotlib') - try: - import pandas - except ImportError: - install_requires.append('pandas') - - return install_requires - if __name__ == "__main__": - install_requires = check_dependencies() - - setup(name=DISTNAME, + setup( + name=DISTNAME, author=MAINTAINER, author_email=MAINTAINER_EMAIL, maintainer=MAINTAINER, @@ -76,19 +78,7 @@ def check_dependencies(): url=URL, version=VERSION, download_url=DOWNLOAD_URL, - install_requires=install_requires, - packages=['seaborn', 'seaborn.colors', - 'seaborn.external', 'seaborn.tests'], - classifiers=[ - 'Intended Audience :: Science/Research', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'License :: OSI Approved :: BSD License', - 'Topic :: Scientific/Engineering :: Visualization', - 'Topic :: Multimedia :: Graphics', - 'Operating System :: POSIX', - 'Operating System :: Unix', - 'Operating System :: MacOS'], - ) + install_requires=INSTALL_REQUIRES, + packages=PACKAGES, + classifiers=CLASSIFIERS + ) From 8c185eeeb3bfb74e896a97b85e464204d099bc31 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 12 May 2018 19:27:35 -0400 Subject: [PATCH 0651/1738] Update install docs to note minimal versions --- doc/installing.rst | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/doc/installing.rst b/doc/installing.rst index 70470bda81..40d081d265 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -24,48 +24,35 @@ Another option would be to to clone the `github repository Dependencies ~~~~~~~~~~~~ -- Python 2.7 or 3.4+ +- Python 2.7 or 3.5+ Mandatory dependencies ^^^^^^^^^^^^^^^^^^^^^^ -- `numpy `__ +- `numpy `__ (>= 1.9.3) -- `scipy `__ +- `scipy `__ (>= 0.14.0) -- `matplotlib `__ +- `matplotlib `__ (>= 1.4.3) -- `pandas `__ +- `pandas `__ (>= 0.15.2) Recommended dependencies ^^^^^^^^^^^^^^^^^^^^^^^^ -- `statsmodels `__ - -The ``pip`` installation script will attempt to download the mandatory -dependencies only if they do not exist at install-time. - -There are not hard minimum version requirements for the dependencies. Unit -tests aim to keep seaborn importable and generally functional on the versions -available through the stable Debian channels, which are relatively old. There -are some known bugs when using older versions of matplotlib (generally meaning -1.3 or earlier), so you should in general try to use a modern version. For -most use cases, though, older versions of matplotlib will work fine. - -Seaborn is also tested on the most recent versions offered through ``conda``. - +- `statsmodels `__ (>= 0.5.0) Testing ~~~~~~~ To test seaborn, run ``make test`` in the root directory of the source -distribution. This runs the unit test suite (using ``pytest``). It also runs -the example code in function docstrings to smoke-test a broader and more -realistic range of example usage. +distribution. This runs the unit test suite (using ``pytest``, but many older +tests use ``nose`` asserts). It also runs the example code in function +docstrings to smoke-test a broader and more realistic range of example usage. The full set of tests requires an internet connection to download the example datasets (if they haven't been previously cached), but the unit tests should -be able to run offline. +be possible to run offline. Bugs @@ -75,9 +62,9 @@ Please report any bugs you encounter through the github `issue tracker `_. It will be most helpful to include a reproducible example on one of the example datasets (accessed through :func:`load_dataset`). It is difficult debug any issues without knowing the -versions of seaborn and matplotlib you are using, as well as what matplotlib -backend you are using to draw the plots, so please include those in your bug -report. +versions of seaborn and matplotlib you are using, as well as what `matplotlib +backend `__ you +are using to draw the plots, so please include those in your bug report. Known issues From 533e5b827364902fe7cac64e32e167fa78ba428d Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 14 May 2018 10:18:24 -0400 Subject: [PATCH 0652/1738] Initial addition of functional 'units' in lineplot --- seaborn/basic.py | 51 ++++++++++++++++++++++++++----------- seaborn/tests/test_basic.py | 18 ++++++++----- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index e7d63cb00a..8a6dff155f 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -30,7 +30,7 @@ class _BasicPlotter(object): def establish_variables(self, x=None, y=None, hue=None, size=None, style=None, - data=None): + units=None, data=None): """Parse the inputs to define data for plotting.""" # Initialize label variables x_label = y_label = hue_label = size_label = style_label = None @@ -130,6 +130,7 @@ def establish_variables(self, x=None, y=None, hue = data.get(hue, hue) size = data.get(size, size) style = data.get(style, style) + units = data.get(units, units) # Validate the inputs for input in [x, y, hue, size, style]: @@ -145,7 +146,11 @@ def establish_variables(self, x=None, y=None, style_label = getattr(style, "name", None) # Reassemble into a DataFrame - plot_data = dict(x=x, y=y, hue=hue, style=style, size=size) + plot_data = dict( + x=x, y=y, + hue=hue, style=style, size=size, + units=units + ) plot_data = pd.DataFrame(plot_data) # Option 3: @@ -160,7 +165,7 @@ def establish_variables(self, x=None, y=None, # ---- Post-processing # Assign default values for missing attribute variables - for attr in ["hue", "style", "size"]: + for attr in ["hue", "style", "size", "units"]: if attr not in plot_data: plot_data[attr] = None @@ -305,7 +310,9 @@ def __init__(self, units=None, estimator=None, ci=None, n_boot=None, sort=True, errstyle=None, legend=None): - plot_data = self.establish_variables(x, y, hue, size, style, data) + plot_data = self.establish_variables( + x, y, hue, size, style, units, data + ) self.parse_hue(plot_data["hue"], palette, hue_order, hue_limits) self.parse_size(plot_data["size"], sizes, size_order, size_limits) @@ -316,6 +323,7 @@ def __init__(self, self.ci = ci self.n_boot = n_boot self.errstyle = errstyle + self.units = units self.legend = legend @@ -335,13 +343,17 @@ def subset_data(self): style_rows = all_true if style is None else data["style"] == style rows = hue_rows & size_rows & style_rows - subset_data = data.loc[rows, ["x", "y"]].dropna() + data["units"] = data.units.fillna("") + subset_data = data.loc[rows, ["units", "x", "y"]].dropna() if not len(subset_data): continue if self.sort: - subset_data = sort_df(subset_data, ["x", "y"]) + subset_data = sort_df(subset_data, ["units", "x", "y"]) + + if self.units is None: + subset_data = subset_data.drop("units", axis=1) yield (hue, size, style), subset_data @@ -487,8 +499,10 @@ def parse_style(self, data, markers, dashes, order): self.dashes = dashes self.markers = markers - def aggregate(self, vals, grouper, func, ci): + def aggregate(self, vals, grouper, units=None): """Compute an estimate and confidence interval using grouper.""" + func = self.estimator + ci = self.ci n_boot = self.n_boot # Define a "null" CI for when we only have one value @@ -557,14 +571,13 @@ def plot(self, ax, kws): # Loop over the semantic subsets and draw a line for each - for semantics, subset_data in self.subset_data(): + for semantics, data in self.subset_data(): hue, size, style = semantics - - x, y = subset_data["x"], subset_data["y"] + x, y, units = data["x"], data["y"], data.get("units", None) if self.estimator is not None: - x, y, y_ci = self.aggregate(y, x, self.estimator, self.ci) + x, y, y_ci = self.aggregate(y, x, units) else: y_ci = None @@ -575,12 +588,19 @@ def plot(self, ax, kws): # --- Draw the main line - # TODO when not estimating, use units to get multiple lines - # with the same semantics? - - line, = ax.plot(x.values, y.values, **kws) + line, = ax.plot([], [], **kws) line_color = line.get_color() line_alpha = line.get_alpha() + line.remove() + + if self.units is None: + + line, = ax.plot(x.values, y.values, **kws) + + else: + + for u in units.unique(): + ax.plot(x[units == u].values, y[units == u].values, **kws) # --- Draw the confidence intervals @@ -765,6 +785,7 @@ def plot(self, ax=None): """), units=dedent("""\ units : {long_form_var} + TODO Grouping variable identifying sampling units. Currently has no effect.\ """), estimator=dedent("""\ diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 0ea0fc1ab6..fcf3b19a99 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -729,14 +729,17 @@ def sem(x): high=y_mean + y_sem), columns=["low", "high"]) - index, est, cis = p.aggregate(y, x, "mean", 68) + p.ci = 68 + p.estimator = "mean" + index, est, cis = p.aggregate(y, x) assert np.array_equal(index.values, x.unique()) assert est.index.equals(index) assert est.values == pytest.approx(y_mean.values) assert cis.values == pytest.approx(y_cis.values, 4) assert list(cis.columns) == ["low", "high"] - index, est, cis = p.aggregate(y, x, np.mean, 68) + p.estimator = np.mean + index, est, cis = p.aggregate(y, x) assert np.array_equal(index.values, x.unique()) assert est.index.equals(index) assert est.values == pytest.approx(y_mean.values) @@ -748,24 +751,27 @@ def sem(x): high=y_mean + y_std), columns=["low", "high"]) - index, est, cis = p.aggregate(y, x, "mean", "sd") + p.ci = "sd" + index, est, cis = p.aggregate(y, x) assert np.array_equal(index.values, x.unique()) assert est.index.equals(index) assert est.values == pytest.approx(y_mean.values) assert cis.values == pytest.approx(y_cis.values) assert list(cis.columns) == ["low", "high"] - index, est, cis = p.aggregate(y, x, "mean", None) + p.ci = None + index, est, cis = p.aggregate(y, x) assert cis is None + p.ci = 68 x, y = pd.Series([1, 2, 3]), pd.Series([4, 3, 2]) - index, est, cis = p.aggregate(y, x, "mean", 68) + index, est, cis = p.aggregate(y, x) assert np.array_equal(index.values, x) assert np.array_equal(est.values, y) assert cis is None x, y = pd.Series([1, 1, 2]), pd.Series([2, 3, 4]) - index, est, cis = p.aggregate(y, x, "mean", 68) + index, est, cis = p.aggregate(y, x) assert cis.loc[2].isnull().all() def test_legend_data(self, long_df): From edd252ab1ba59e0a98e99d7fcd9dc493687adc52 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 14 May 2018 10:39:00 -0400 Subject: [PATCH 0653/1738] Raise when 'units' string cannot be interpreted --- seaborn/basic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 8a6dff155f..6f71b9787e 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -133,9 +133,9 @@ def establish_variables(self, x=None, y=None, units = data.get(units, units) # Validate the inputs - for input in [x, y, hue, size, style]: - if isinstance(input, string_types): - err = "Could not interpret input '{}'".format(input) + for var in [x, y, hue, size, style, units]: + if isinstance(var, string_types): + err = "Could not interpret input '{}'".format(var) raise ValueError(err) # Extract variable names From a17efda296d50650bea413578a31166a87c118c9 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 19 May 2018 12:46:22 -0400 Subject: [PATCH 0654/1738] Set capstyle on error bars to match lines --- seaborn/basic.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/seaborn/basic.py b/seaborn/basic.py index 6f71b9787e..2ce7eec237 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -591,6 +591,7 @@ def plot(self, ax, kws): line, = ax.plot([], [], **kws) line_color = line.get_color() line_alpha = line.get_alpha() + line_capstyle = line.get_solid_capstyle() line.remove() if self.units is None: @@ -619,6 +620,10 @@ def plot(self, ax, kws): lines = LineCollection(ci_xy, color=line_color, alpha=line_alpha) + try: + lines.set_capstyle(line_capstyle) + except AttributeError: + pass ax.add_collection(lines) ax.autoscale_view() @@ -644,6 +649,7 @@ def plot(self, ax, kws): def add_legend_data(self, ax): """Add labeled artists to represent the different plot semantics.""" verbosity = self.legend + # TODO Use False or None? if verbosity not in ["brief", "full"]: err = "`legend` must be 'brief', 'full', or False" raise ValueError(err) From bfc5ad1bdf655f2ddf5fcaf1f53830374ecb1160 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 19 May 2018 13:52:16 -0400 Subject: [PATCH 0655/1738] Tests and docs for lineplot units --- seaborn/basic.py | 14 ++++++++++++-- seaborn/tests/test_basic.py | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 2ce7eec237..32277b3a19 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -791,8 +791,10 @@ def plot(self, ax=None): """), units=dedent("""\ units : {long_form_var} - TODO - Grouping variable identifying sampling units. Currently has no effect.\ + Grouping variable identifying sampling units. When used, a separate + line will be drawn for each unit with appropriate semantics, but no + legend entry will be added. Useful for showing distribution of + experimental replicates when exact identities are not needed. """), estimator=dedent("""\ estimator : name of pandas method or callable or None, optional @@ -984,6 +986,14 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, >>> ax = sns.lineplot(x="timepoint", y="signal", hue="event", ... errstyle="bars", ci=68, data=fmri) + Show experimental replicates instead of aggregating: + + .. plot:: + :context: close-figs + + >>> ax = sns.lineplot(x="timepoint", y="signal", hue="event", + ... units="subject", estimator=None, data=fmri) + Use a quantitative color mapping: .. plot:: diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index fcf3b19a99..759171a7ab 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -60,6 +60,18 @@ def long_df(self): s=np.take([2, 4, 8], rs.randint(0, 3, n)), )) + @pytest.fixture + def repeated_df(self): + + n = 100 + rs = np.random.RandomState() + return pd.DataFrame(dict( + x=np.tile(np.arange(n // 2), 2), + y=rs.randn(n), + a=np.take(list("abc"), rs.randint(0, 3, n)), + u=np.repeat(np.arange(2), n // 2), + )) + @pytest.fixture def null_column(self): @@ -326,6 +338,12 @@ def test_empty_input(self): p.establish_variables(data=pd.DataFrame()) p.establish_variables(x=[], y=[]) + def test_units(self, repeated_df): + + p = basic._BasicPlotter() + p.establish_variables(x="x", y="y", units="u", data=repeated_df) + assert np.array_equal(p.plot_data["units"], repeated_df["u"]) + class TestLinePlotter(TestBasicPlotter): @@ -875,7 +893,7 @@ def test_legend_data(self, long_df): with pytest.raises(ValueError): p.add_legend_data(ax) - def test_plot(self, long_df): + def test_plot(self, long_df, repeated_df): f, ax = plt.subplots() @@ -969,6 +987,22 @@ def test_plot(self, long_df): for c in ax.collections: assert isinstance(c, mpl.collections.LineCollection) + p = basic._LinePlotter(x="x", y="y", data=repeated_df, + units="u", estimator=None) + + ax.clear() + p.plot(ax, {}) + n_units = len(repeated_df["u"].unique()) + assert len(ax.lines) == n_units + + p = basic._LinePlotter(x="x", y="y", hue="a", data=repeated_df, + units="u", estimator=None) + + ax.clear() + p.plot(ax, {}) + n_units *= len(repeated_df["a"].unique()) + assert len(ax.lines) == n_units + def test_axis_labels(self, long_df): f, (ax1, ax2) = plt.subplots(1, 2, sharey=True) From 4ceb48e08875cdcf6f8fca74a5e42692d5f3b1de Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 19 May 2018 13:58:10 -0400 Subject: [PATCH 0656/1738] Raise when asking for both units and aggregation --- seaborn/basic.py | 3 +++ seaborn/tests/test_basic.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/seaborn/basic.py b/seaborn/basic.py index 32277b3a19..010e54c8fa 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -577,6 +577,9 @@ def plot(self, ax, kws): x, y, units = data["x"], data["y"], data.get("units", None) if self.estimator is not None: + if self.units is not None: + err = "estimator must be None when specifying units" + raise ValueError(err) x, y, y_ci = self.aggregate(y, x, units) else: y_ci = None diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 759171a7ab..3276110fd2 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -1003,6 +1003,10 @@ def test_plot(self, long_df, repeated_df): n_units *= len(repeated_df["a"].unique()) assert len(ax.lines) == n_units + p.estimator = "mean" + with pytest.raises(ValueError): + p.plot(ax, {}) + def test_axis_labels(self, long_df): f, (ax1, ax2) = plt.subplots(1, 2, sharey=True) From 9285514d5d88c4dd310a9823f761f9981969d395 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 20 May 2018 12:31:35 -0400 Subject: [PATCH 0657/1738] Initial functional implementation of scatterplot --- seaborn/basic.py | 156 ++++++++++++++++++++++++------------ seaborn/tests/test_basic.py | 6 +- 2 files changed, 106 insertions(+), 56 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 010e54c8fa..ac604e8687 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -284,49 +284,6 @@ def style_to_attributes(self, levels, style, defaults, name): return attrdict - def _empty_data(self, data): - """Test if a series is completely missing.""" - return data.isnull().all() - - def _semantic_type(self, data): - """Determine if data should considered numeric or categorical.""" - if self.input_format == "wide": - return "categorical" - else: - try: - data.astype(np.float) - return "numeric" - except ValueError: - return "categorical" - - -class _LinePlotter(_BasicPlotter): - - def __init__(self, - x=None, y=None, hue=None, size=None, style=None, data=None, - palette=None, hue_order=None, hue_limits=None, - sizes=None, size_order=None, size_limits=None, - dashes=None, markers=None, style_order=None, - units=None, estimator=None, ci=None, n_boot=None, - sort=True, errstyle=None, legend=None): - - plot_data = self.establish_variables( - x, y, hue, size, style, units, data - ) - - self.parse_hue(plot_data["hue"], palette, hue_order, hue_limits) - self.parse_size(plot_data["size"], sizes, size_order, size_limits) - self.parse_style(plot_data["style"], markers, dashes, style_order) - - self.sort = sort - self.estimator = estimator - self.ci = ci - self.n_boot = n_boot - self.errstyle = errstyle - self.units = units - - self.legend = legend - def subset_data(self): """Return (x, y) data for each subset defined by semantics.""" data = self.plot_data @@ -499,6 +456,49 @@ def parse_style(self, data, markers, dashes, order): self.dashes = dashes self.markers = markers + def _empty_data(self, data): + """Test if a series is completely missing.""" + return data.isnull().all() + + def _semantic_type(self, data): + """Determine if data should considered numeric or categorical.""" + if self.input_format == "wide": + return "categorical" + else: + try: + data.astype(np.float) + return "numeric" + except ValueError: + return "categorical" + + +class _LinePlotter(_BasicPlotter): + + def __init__(self, + x=None, y=None, hue=None, size=None, style=None, data=None, + palette=None, hue_order=None, hue_limits=None, + sizes=None, size_order=None, size_limits=None, + dashes=None, markers=None, style_order=None, + units=None, estimator=None, ci=None, n_boot=None, + sort=True, errstyle=None, legend=None): + + plot_data = self.establish_variables( + x, y, hue, size, style, units, data + ) + + self.parse_hue(plot_data["hue"], palette, hue_order, hue_limits) + self.parse_size(plot_data["size"], sizes, size_order, size_limits) + self.parse_style(plot_data["style"], markers, dashes, style_order) + + self.sort = sort + self.estimator = estimator + self.ci = ci + self.n_boot = n_boot + self.errstyle = errstyle + self.units = units + + self.legend = legend + def aggregate(self, vals, grouper, units=None): """Compute an estimate and confidence interval using grouper.""" func = self.estimator @@ -714,11 +714,49 @@ def update(var_name, val_name, **kws): class _ScatterPlotter(_BasicPlotter): - def __init__(self): - pass + def __init__(self, + x=None, y=None, hue=None, size=None, style=None, data=None, + palette=None, hue_order=None, hue_limits=None, + sizes=None, size_order=None, size_limits=None, + markers=None, style_order=None, + x_bins=None, y_bins=None, + units=None, estimator=None, ci=None, n_boot=None, + alpha=None, x_jitter=None, y_jitter=None, + legend=None): + + plot_data = self.establish_variables( + x, y, hue, size, style, units, data + ) - def plot(self, ax=None): - pass + self.parse_hue(plot_data["hue"], palette, hue_order, hue_limits) + self.parse_size(plot_data["size"], sizes, size_order, size_limits) + self.parse_style(plot_data["style"], markers, None, style_order) + self.units = units + + self.legend = legend + + def plot(self, ax, kws): + + kws.setdefault("linewidth", .75) # TODO scale with marker size? + kws.setdefault("edgecolor", "w") + + data = self.plot_data + + c = None if not self.palette else data["hue"].map(self.palette) + s = None if not self.sizes else data["size"].map(self.sizes) + + points = ax.scatter(data["x"], data["y"], s=s, c=c, **kws) + + paths = {} + for key, marker in self.markers.items(): + # TODO move to parse style? + if not isinstance(marker, mpl.markers.MarkerStyle): + marker = mpl.markers.MarkerStyle(marker) + path = marker.get_path().transformed(marker.get_transform()) + paths[key] = path + + if paths: + points.set_paths(data["style"].map(paths)) _basic_docs = dict( @@ -1094,14 +1132,26 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, palette=None, hue_order=None, hue_limits=None, - markers=None, style_order=None, sizes=None, size_order=None, size_limits=None, + markers=True, style_order=None, x_bins=None, y_bins=None, - estimator=None, ci=95, n_boot=1000, units=None, - errstyle="bars", alpha="auto", - x_jitter=None, y_jitter=None, + units=None, estimator=None, ci=95, n_boot=1000, + alpha="auto", x_jitter=None, y_jitter=None, ax=None, **kwargs): - # TODO auto alpha + p = _ScatterPlotter( + x=x, y=y, hue=hue, style=style, size=size, data=data, + palette=palette, hue_order=hue_order, hue_limits=hue_limits, + sizes=sizes, size_order=size_order, + markers=markers, style_order=style_order, + x_bins=x_bins, y_bins=y_bins, + estimator=estimator, ci=ci, n_boot=n_boot, + alpha=alpha, x_jitter=x_jitter, y_jitter=y_jitter + ) + + if ax is None: + ax = plt.gca() + + p.plot(ax, kwargs) - pass + return p, ax diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 3276110fd2..f60a57df99 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -344,9 +344,6 @@ def test_units(self, repeated_df): p.establish_variables(x="x", y="y", units="u", data=repeated_df) assert np.array_equal(p.plot_data["units"], repeated_df["u"]) - -class TestLinePlotter(TestBasicPlotter): - def test_parse_hue_null(self, wide_df, null_column): p = basic._LinePlotter(data=wide_df) @@ -729,6 +726,9 @@ def test_subset_data_values(self, long_df): expected = basic.sort_df(p.plot_data.loc[rows, cols], cols) assert np.array_equal(data.values, expected.values) + +class TestLinePlotter(TestBasicPlotter): + def test_aggregate(self, long_df): p = basic._LinePlotter(x="x", y="y", data=long_df) From 4f52fffd7fc124d7a244b4ea1c9e74738975281f Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 20 May 2018 18:01:39 -0400 Subject: [PATCH 0658/1738] Scatterplot legend and labels; honoring of passed kwargs --- seaborn/basic.py | 136 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 123 insertions(+), 13 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index ac604e8687..5665843107 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -397,8 +397,7 @@ def parse_size(self, data, sizes, order, limits): # Infer the range of sizes to use if sizes is None: - default = plt.rcParams["lines.linewidth"] - min_width, max_width = default * .5, default * 2 + min_width, max_width = self._default_size_range else: try: min_width, max_width = sizes @@ -486,6 +485,10 @@ def __init__(self, x, y, hue, size, style, units, data ) + self._default_size_range = ( + np.r_[.5, 2] * mpl.rcParams["lines.linewidth"] + ) + self.parse_hue(plot_data["hue"], palette, hue_order, hue_limits) self.parse_size(plot_data["size"], sizes, size_order, size_limits) self.parse_style(plot_data["style"], markers, dashes, style_order) @@ -546,14 +549,14 @@ def bootstrapped_cis(vals): def plot(self, ax, kws): """Draw the plot onto an axes, passing matplotlib kwargs.""" - # Draw a test line, using the passed in kwargs. The goal here is to + # Draw a test plot, using the passed in kwargs. The goal here is to # honor both (a) the current state of the plot cycler and (b) the # specified kwargs on all the lines we will draw, overriding when - # relevant with the lineplot semantics. Note that we won't cycle - # internally; in other words, if ``hue`` is not used, all lines - # will have the same color, but they will have the color that - # ax.plot() would have used for a single line, and calling lineplot - # will advance the axes property cycle. + # relevant with the data semantics. Note that we won't cycle + # internally; in other words, if ``hue`` is not used, all elements will + # have the same color, but they will have the color that you would have + # gotten from the corresponding matplotlib function, and calling the + # function will advance the axes property cycle. scout, = ax.plot([], [], **kws) @@ -728,6 +731,10 @@ def __init__(self, x, y, hue, size, style, units, data ) + self._default_size_range = ( + np.r_[.5, 2] * np.square(mpl.rcParams["lines.markersize"]) + ) + self.parse_hue(plot_data["hue"], palette, hue_order, hue_limits) self.parse_size(plot_data["size"], sizes, size_order, size_limits) self.parse_style(plot_data["style"], markers, None, style_order) @@ -735,17 +742,103 @@ def __init__(self, self.legend = legend + def add_legend_data(self, ax): + """Add labeled artists to represent the different plot semantics.""" + # TODO duplicating from LinePlotter; this can be substantially + # abstracted but it will be slightly trikcy + verbosity = self.legend + # TODO Use False or None? + if verbosity not in ["brief", "full"]: + err = "`legend` must be 'brief', 'full', or False" + raise ValueError(err) + + keys = [] + legend_data = {} + + def update(var_name, val_name, **kws): + + key = var_name, val_name + if key in legend_data: + legend_data[key].update(**kws) + else: + keys.append(key) + legend_data[key] = dict(**kws) + + ticker = mpl.ticker.MaxNLocator(nbins=3) + + # -- Add a legend for hue semantics + + if verbosity == "brief" and self.hue_type == "numeric": + hue_levels = (ticker.tick_values(*self.hue_limits) + .astype(self.plot_data["hue"].dtype)) + else: + hue_levels = self.hue_levels + + for level in hue_levels: + if level is not None: + color = self.color_lookup(level) + update(self.hue_label, level, color=color) + + # -- Add a legend for size semantics + + if verbosity == "brief" and self.size_type == "numeric": + size_levels = (ticker.tick_values(*self.size_limits) + .astype(self.plot_data["size"].dtype)) + else: + size_levels = self.size_levels + + for level in size_levels: + if level is not None: + s = self.size_lookup(level) + update(self.size_label, level, s=s) + + # -- Add a legend for style semantics + + for level in self.style_levels: + if level is not None: + update(self.style_label, level, + marker=self.markers.get(level, "")) + + for key in keys: + _, label = key + kws = legend_data[key] + kws.setdefault("color", ".2") + ax.scatter([], [], label=label, **kws) + def plot(self, ax, kws): + # Draw a test plot, using the passed in kwargs. The goal here is to + # honor both (a) the current state of the plot cycler and (b) the + # specified kwargs on all the lines we will draw, overriding when + # relevant with the data semantics. Note that we won't cycle + # internally; in other words, if ``hue`` is not used, all elements will + # have the same color, but they will have the color that you would have + # gotten from the corresponding matplotlib function, and calling the + # function will advance the axes property cycle. + + scout = ax.scatter([], [], **kws) + orig_s = kws.pop("s", scout.get_sizes()) + orig_c = kws.pop("c", scout.get_facecolors()) + scout.remove() + kws.setdefault("linewidth", .75) # TODO scale with marker size? kws.setdefault("edgecolor", "w") + # Assign arguments for plt.scatter and draw the plot + data = self.plot_data - c = None if not self.palette else data["hue"].map(self.palette) - s = None if not self.sizes else data["size"].map(self.sizes) + x = data["x"] + y = data["y"] + c = orig_c if not self.palette else data["hue"].map(self.palette) + s = orig_s if not self.sizes else data["size"].map(self.sizes) + + args = np.asarray(x), np.asarray(y), np.asarray(s), np.asarray(c) + points = ax.scatter(*args, **kws) - points = ax.scatter(data["x"], data["y"], s=s, c=c, **kws) + # Update the paths to get different marker shapes. This has to be + # done here because plt.scatter allows varying sizes and colors + # but only a single marker shape per call. paths = {} for key, marker in self.markers.items(): @@ -758,6 +851,23 @@ def plot(self, ax, kws): if paths: points.set_paths(data["style"].map(paths)) + # Finalize the axes details + + # TODO this should definitely go in its own method; see also lineplot + if self.x_label is not None: + x_visible = any(t.get_visible() for t in ax.get_xticklabels()) + ax.set_xlabel(self.x_label, visible=x_visible) + if self.y_label is not None: + y_visible = any(t.get_visible() for t in ax.get_yticklabels()) + ax.set_ylabel(self.y_label, visible=y_visible) + + # Add legend + if self.legend: + self.add_legend_data(ax) + handles, _ = ax.get_legend_handles_labels() + if handles: + ax.legend() + _basic_docs = dict( @@ -1137,7 +1247,7 @@ def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, x_bins=None, y_bins=None, units=None, estimator=None, ci=95, n_boot=1000, alpha="auto", x_jitter=None, y_jitter=None, - ax=None, **kwargs): + legend="brief", ax=None, **kwargs): p = _ScatterPlotter( x=x, y=y, hue=hue, style=style, size=size, data=data, @@ -1146,7 +1256,7 @@ def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, markers=markers, style_order=style_order, x_bins=x_bins, y_bins=y_bins, estimator=estimator, ci=ci, n_boot=n_boot, - alpha=alpha, x_jitter=x_jitter, y_jitter=y_jitter + alpha=alpha, x_jitter=x_jitter, y_jitter=y_jitter, legend=legend, ) if ax is None: From 2046850558f4f4b6ebd28264642b34a2766b0ddd Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 20 May 2018 20:35:15 -0400 Subject: [PATCH 0659/1738] Expand default marker list to filled line art on matplotlib >= 2.0 --- seaborn/basic.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 5665843107..6c44ca7169 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -1,6 +1,7 @@ from __future__ import division from itertools import product from textwrap import dedent +from distutils.version import LooseVersion import numpy as np import pandas as pd @@ -16,17 +17,19 @@ from .palettes import color_palette -__all__ = ["lineplot"] +__all__ = ["lineplot", "scatterplot"] class _BasicPlotter(object): - # TODO use different lists for mpl 1 and 2? # We could use "line art glyphs" (e.g. "P") on mpl 2 - default_markers = ["o", "s", "D", "v", "^", "p"] - marker_scales = {"o": 1, "s": .85, "D": .9, "v": 1.3, "^": 1.3, "p": 1.25} + if LooseVersion(mpl.__version__) >= "2.0": + default_markers = ["o", "X", "s", "P", "D", "^", "v", "p"] + else: + default_markers = ["o", "s", "D", "^", "v", "p"] default_dashes = ["", (4, 1.5), (1, 1), - (3, 1, 1.5, 1), (5, 1, 1, 1), (5, 1, 2, 1, 2, 1)] + (3, 1, 1.5, 1), (5, 1, 1, 1), + (5, 1, 2, 1, 2, 1)] def establish_variables(self, x=None, y=None, hue=None, size=None, style=None, From 4ba2dd5c82949a0eb0658ff6fbbf1037927d7ff4 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 20 May 2018 20:35:24 -0400 Subject: [PATCH 0660/1738] Abstract out axes labeling on basic plots --- seaborn/basic.py | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 6c44ca7169..1d0f59885a 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -473,6 +473,15 @@ def _semantic_type(self, data): except ValueError: return "categorical" + def label_axes(self, ax): + """Set x and y labels with visibility that matches the ticklabels.""" + if self.x_label is not None: + x_visible = any(t.get_visible() for t in ax.get_xticklabels()) + ax.set_xlabel(self.x_label, visible=x_visible) + if self.y_label is not None: + y_visible = any(t.get_visible() for t in ax.get_yticklabels()) + ax.set_ylabel(self.y_label, visible=y_visible) + class _LinePlotter(_BasicPlotter): @@ -640,15 +649,8 @@ def plot(self, ax, kws): err = "`errstyle` must by 'band' or 'bars', not {}" raise ValueError(err.format(self.errstyle)) - # TODO this should go in its own method? - if self.x_label is not None: - x_visible = any(t.get_visible() for t in ax.get_xticklabels()) - ax.set_xlabel(self.x_label, visible=x_visible) - if self.y_label is not None: - y_visible = any(t.get_visible() for t in ax.get_yticklabels()) - ax.set_ylabel(self.y_label, visible=y_visible) - - # Add legend + # Finalize the axes details + self.label_axes(ax) if self.legend: self.add_legend_data(ax) handles, _ = ax.get_legend_handles_labels() @@ -855,16 +857,7 @@ def plot(self, ax, kws): points.set_paths(data["style"].map(paths)) # Finalize the axes details - - # TODO this should definitely go in its own method; see also lineplot - if self.x_label is not None: - x_visible = any(t.get_visible() for t in ax.get_xticklabels()) - ax.set_xlabel(self.x_label, visible=x_visible) - if self.y_label is not None: - y_visible = any(t.get_visible() for t in ax.get_yticklabels()) - ax.set_ylabel(self.y_label, visible=y_visible) - - # Add legend + self.label_axes(ax) if self.legend: self.add_legend_data(ax) handles, _ = ax.get_legend_handles_labels() From e3930c19443b53dfbd9e04f6fb817c692a7b96b7 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 20 May 2018 20:37:40 -0400 Subject: [PATCH 0661/1738] Fix alignment of marker paths --- seaborn/basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 1d0f59885a..f7617fa53f 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -854,7 +854,7 @@ def plot(self, ax, kws): paths[key] = path if paths: - points.set_paths(data["style"].map(paths)) + points.set_paths(np.asarray(data["style"].map(paths))) # Finalize the axes details self.label_axes(ax) From e933619957cf43672c044c5e79594a76d8bd8c6a Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 20 May 2018 20:51:31 -0400 Subject: [PATCH 0662/1738] Handle color an alpha provisionally --- seaborn/basic.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index f7617fa53f..29fa7f8715 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -745,14 +745,14 @@ def __init__(self, self.parse_style(plot_data["style"], markers, None, style_order) self.units = units + self.alpha = alpha + self.legend = legend def add_legend_data(self, ax): """Add labeled artists to represent the different plot semantics.""" # TODO duplicating from LinePlotter; this can be substantially - # abstracted but it will be slightly trikcy verbosity = self.legend - # TODO Use False or None? if verbosity not in ["brief", "full"]: err = "`legend` must be 'brief', 'full', or False" raise ValueError(err) @@ -826,9 +826,13 @@ def plot(self, ax, kws): orig_c = kws.pop("c", scout.get_facecolors()) scout.remove() + kws.pop("color", None) # TODO is this optimal? + kws.setdefault("linewidth", .75) # TODO scale with marker size? kws.setdefault("edgecolor", "w") + kws["alpha"] = 1 if self.alpha == "auto" else self.alpha # TODO + # Assign arguments for plt.scatter and draw the plot data = self.plot_data From a407cda5c2a0f42baa9caf2405244aef50a1f016 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 26 May 2018 19:01:38 -0400 Subject: [PATCH 0663/1738] Swtch lineplot to use errorbar for bars-style CIs --- seaborn/basic.py | 47 +++++++++++++++++++------------------ seaborn/tests/test_basic.py | 4 +++- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 29fa7f8715..c00059f41a 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -7,12 +7,11 @@ import pandas as pd import matplotlib as mpl import matplotlib.pyplot as plt -from matplotlib.collections import LineCollection from .external.six import string_types from . import utils -from .utils import categorical_order, get_color_cycle, sort_df +from .utils import categorical_order, get_color_cycle, ci_to_errsize, sort_df from .algorithms import bootstrap from .palettes import color_palette @@ -604,46 +603,48 @@ def plot(self, ax, kws): kws["marker"] = self.markers.get(style, orig_marker) kws["linewidth"] = self.sizes.get(size, orig_linewidth) - # --- Draw the main line - line, = ax.plot([], [], **kws) line_color = line.get_color() line_alpha = line.get_alpha() line_capstyle = line.get_solid_capstyle() line.remove() - if self.units is None: + # --- Draw the main line - line, = ax.plot(x.values, y.values, **kws) + x, y = np.asarray(x), np.asarray(y) - else: + if self.units is None: + line, = ax.plot(x, y, **kws) + else: for u in units.unique(): - ax.plot(x[units == u].values, y[units == u].values, **kws) + rows = np.asarray(units == u) + ax.plot(x[rows], y[rows], **kws) # --- Draw the confidence intervals + # TODO we want some way to get kwargs to the error plotters if y_ci is not None: + low, high = np.asarray(y_ci["low"]), np.asarray(y_ci["high"]) + if self.errstyle == "band": - ax.fill_between(x, y_ci["low"], y_ci["high"], - color=line_color, alpha=.2) + ax.fill_between(x, low, high, color=line_color, alpha=.2) elif self.errstyle == "bars": - ci_xy = np.empty((len(x), 2, 2)) - ci_xy[:, :, 0] = x[:, np.newaxis] - ci_xy[:, :, 1] = y_ci.values - lines = LineCollection(ci_xy, - color=line_color, - alpha=line_alpha) - try: - lines.set_capstyle(line_capstyle) - except AttributeError: - pass - ax.add_collection(lines) - ax.autoscale_view() + y_err = ci_to_errsize((low, high), y) + ebars = ax.errorbar(x, y, y_err, linestyle="", + color=line_color, alpha=line_alpha) + + # Set the capstyle properly on the error bars + for obj in ebars.get_children(): + try: + obj.set_capstyle(line_capstyle) + except AttributeError: + # Does not exist on mpl < 2.2 + pass else: err = "`errstyle` must by 'band' or 'bars', not {}" @@ -1012,7 +1013,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, lineplot.__doc__ = dedent("""\ - Draw a plot with numeric x and y values where the points are connected. + Draw a line plot with up to several semantic groupings. {main_api_narrative} diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index f60a57df99..1731d6a58f 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -983,7 +983,9 @@ def test_plot(self, long_df, repeated_df): ax.clear() p.plot(ax, {}) - assert len(ax.lines) == len(ax.collections) == len(p.hue_levels) + # assert len(ax.lines) / 2 == len(ax.collections) == len(p.hue_levels) + # The # of lines is different on mpl 1.4 but I can't install to debug + assert len(ax.collections) == len(p.hue_levels) for c in ax.collections: assert isinstance(c, mpl.collections.LineCollection) From be74038c6b62dab986fd6e37d921c3b549de403a Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 28 May 2018 16:14:11 -0400 Subject: [PATCH 0664/1738] Implement reasonable abstraction for legend in lineplot/scatterplot --- seaborn/basic.py | 195 +++++++++++++++++------------------------------ 1 file changed, 72 insertions(+), 123 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index c00059f41a..d666a9dd31 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -481,9 +481,78 @@ def label_axes(self, ax): y_visible = any(t.get_visible() for t in ax.get_yticklabels()) ax.set_ylabel(self.y_label, visible=y_visible) + def add_legend_data(self, ax): + """Add labeled artists to represent the different plot semantics.""" + verbosity = self.legend + if verbosity not in ["brief", "full"]: + err = "`legend` must be 'brief', 'full', or False" + raise ValueError(err) + + keys = [] + legend_data = {} + + def update(var_name, val_name, **kws): + + key = var_name, val_name + if key in legend_data: + legend_data[key].update(**kws) + else: + keys.append(key) + legend_data[key] = dict(**kws) + + ticker = mpl.ticker.MaxNLocator(nbins=3) + + # -- Add a legend for hue semantics + + if verbosity == "brief" and self.hue_type == "numeric": + hue_levels = (ticker.tick_values(*self.hue_limits) + .astype(self.plot_data["hue"].dtype)) + else: + hue_levels = self.hue_levels + + for level in hue_levels: + if level is not None: + color = self.color_lookup(level) + update(self.hue_label, level, color=color) + + # -- Add a legend for size semantics + + if verbosity == "brief" and self.size_type == "numeric": + size_levels = (ticker.tick_values(*self.size_limits) + .astype(self.plot_data["size"].dtype)) + else: + size_levels = self.size_levels + + for level in size_levels: + if level is not None: + size = self.size_lookup(level) + update(self.size_label, level, linewidth=size, s=size) + + # -- Add a legend for style semantics + + for level in self.style_levels: + if level is not None: + update(self.style_label, level, + marker=self.markers.get(level, ""), + dashes=self.dashes.get(level, "")) + + func = getattr(ax, self._legend_func) + for key in keys: + _, label = key + kws = legend_data[key] + kws.setdefault("color", ".2") + use_kws = {} + for attr in self._legend_attributes: + if attr in kws: + use_kws[attr] = kws[attr] + func([], [], label=label, **use_kws) + class _LinePlotter(_BasicPlotter): + _legend_attributes = ["color", "linewidth", "marker", "dashes"] + _legend_func = "plot" + def __init__(self, x=None, y=None, hue=None, size=None, style=None, data=None, palette=None, hue_order=None, hue_limits=None, @@ -658,71 +727,12 @@ def plot(self, ax, kws): if handles: ax.legend() - def add_legend_data(self, ax): - """Add labeled artists to represent the different plot semantics.""" - verbosity = self.legend - # TODO Use False or None? - if verbosity not in ["brief", "full"]: - err = "`legend` must be 'brief', 'full', or False" - raise ValueError(err) - - keys = [] - legend_data = {} - - def update(var_name, val_name, **kws): - - key = var_name, val_name - if key in legend_data: - legend_data[key].update(**kws) - else: - keys.append(key) - legend_data[key] = dict(**kws) - - ticker = mpl.ticker.MaxNLocator(nbins=3) - - # -- Add a legend for hue semantics - - if verbosity == "brief" and self.hue_type == "numeric": - hue_levels = (ticker.tick_values(*self.hue_limits) - .astype(self.plot_data["hue"].dtype)) - else: - hue_levels = self.hue_levels - - for level in hue_levels: - if level is not None: - color = self.color_lookup(level) - update(self.hue_label, level, color=color) - - # -- Add a legend for size semantics - - if verbosity == "brief" and self.size_type == "numeric": - size_levels = (ticker.tick_values(*self.size_limits) - .astype(self.plot_data["size"].dtype)) - else: - size_levels = self.size_levels - - for level in size_levels: - if level is not None: - linewidth = self.size_lookup(level) - update(self.size_label, level, linewidth=linewidth) - - # -- Add a legend for style semantics - - for level in self.style_levels: - if level is not None: - update(self.style_label, level, - marker=self.markers.get(level, ""), - dashes=self.dashes.get(level, "")) - - for key in keys: - _, label = key - kws = legend_data[key] - kws.setdefault("color", ".2") - ax.plot([], [], label=label, **kws) - class _ScatterPlotter(_BasicPlotter): + _legend_attributes = ["color", "s", "marker"] + _legend_func = "scatter" + def __init__(self, x=None, y=None, hue=None, size=None, style=None, data=None, palette=None, hue_order=None, hue_limits=None, @@ -750,67 +760,6 @@ def __init__(self, self.legend = legend - def add_legend_data(self, ax): - """Add labeled artists to represent the different plot semantics.""" - # TODO duplicating from LinePlotter; this can be substantially - verbosity = self.legend - if verbosity not in ["brief", "full"]: - err = "`legend` must be 'brief', 'full', or False" - raise ValueError(err) - - keys = [] - legend_data = {} - - def update(var_name, val_name, **kws): - - key = var_name, val_name - if key in legend_data: - legend_data[key].update(**kws) - else: - keys.append(key) - legend_data[key] = dict(**kws) - - ticker = mpl.ticker.MaxNLocator(nbins=3) - - # -- Add a legend for hue semantics - - if verbosity == "brief" and self.hue_type == "numeric": - hue_levels = (ticker.tick_values(*self.hue_limits) - .astype(self.plot_data["hue"].dtype)) - else: - hue_levels = self.hue_levels - - for level in hue_levels: - if level is not None: - color = self.color_lookup(level) - update(self.hue_label, level, color=color) - - # -- Add a legend for size semantics - - if verbosity == "brief" and self.size_type == "numeric": - size_levels = (ticker.tick_values(*self.size_limits) - .astype(self.plot_data["size"].dtype)) - else: - size_levels = self.size_levels - - for level in size_levels: - if level is not None: - s = self.size_lookup(level) - update(self.size_label, level, s=s) - - # -- Add a legend for style semantics - - for level in self.style_levels: - if level is not None: - update(self.style_label, level, - marker=self.markers.get(level, "")) - - for key in keys: - _, label = key - kws = legend_data[key] - kws.setdefault("color", ".2") - ax.scatter([], [], label=label, **kws) - def plot(self, ax, kws): # Draw a test plot, using the passed in kwargs. The goal here is to From 9c64ee8c3909e513b2e07a55a7e1b6cc72ae0d92 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 28 May 2018 16:14:43 -0400 Subject: [PATCH 0665/1738] Define scatter marker paths when parsing semantic variables --- seaborn/basic.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index d666a9dd31..554207065c 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -453,9 +453,16 @@ def parse_style(self, data, markers, dashes, order): levels, dashes, self.default_dashes, "dashes" ) + paths = {} + for k, m in markers.items(): + if not isinstance(m, mpl.markers.MarkerStyle): + m = mpl.markers.MarkerStyle(m) + paths[k] = m.get_path().transformed(m.get_transform()) + self.style_levels = levels self.dashes = dashes self.markers = markers + self.paths = paths def _empty_data(self, data): """Test if a series is completely missing.""" @@ -799,16 +806,8 @@ def plot(self, ax, kws): # done here because plt.scatter allows varying sizes and colors # but only a single marker shape per call. - paths = {} - for key, marker in self.markers.items(): - # TODO move to parse style? - if not isinstance(marker, mpl.markers.MarkerStyle): - marker = mpl.markers.MarkerStyle(marker) - path = marker.get_path().transformed(marker.get_transform()) - paths[key] = path - - if paths: - points.set_paths(np.asarray(data["style"].map(paths))) + if self.paths: + points.set_paths(np.asarray(data["style"].map(self.paths))) # Finalize the axes details self.label_axes(ax) From 1f10497dd8aaea9ae93dbb092c0c7af9bb7a27f8 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 28 May 2018 16:33:21 -0400 Subject: [PATCH 0666/1738] Add tests for scatterplot legend data --- seaborn/tests/test_basic.py | 133 +++++++++++++++++++++++++++++++++++- 1 file changed, 132 insertions(+), 1 deletion(-) diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 1731d6a58f..1bf5e9cd81 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -984,7 +984,7 @@ def test_plot(self, long_df, repeated_df): ax.clear() p.plot(ax, {}) # assert len(ax.lines) / 2 == len(ax.collections) == len(p.hue_levels) - # The # of lines is different on mpl 1.4 but I can't install to debug + # The lines are different on mpl 1.4 but I can't install to debug assert len(ax.collections) == len(p.hue_levels) for c in ax.collections: assert isinstance(c, mpl.collections.LineCollection) @@ -1085,3 +1085,134 @@ def test_lineplot_smoke(self, flat_array, flat_series, basic.lineplot(x="x", y="y", hue="a", size="s", data=long_df) ax.clear() + + +class TestScatterPlotter(TestBasicPlotter): + + def scatter_rgbs(self, collections): + rgbs = [] + for col in collections: + rgb = tuple(col.get_facecolor().squeeze()[:3]) + rgbs.append(rgb) + return rgbs + + def colors_equal(self, *args): + + equal = True + for c1, c2 in zip(*args): + c1 = mpl.colors.colorConverter.to_rgb(np.squeeze(c1)) + c2 = mpl.colors.colorConverter.to_rgb(np.squeeze(c1)) + equal &= c1 == c2 + return equal + + def paths_equal(self, *args): + + equal = True + for p1, p2 in zip(*args): + equal &= np.array_equal(p1.vertices, p2.vertices) + equal &= np.array_equal(p1.codes, p2.codes) + return equal + + def test_legend_data(self, long_df): + + m = mpl.markers.MarkerStyle("o") + default_marker = m.get_path().transformed(m.get_transform()) + + f, ax = plt.subplots() + + p = basic._ScatterPlotter(x="x", y="y", data=long_df, legend="full") + p.add_legend_data(ax) + handles, _ = ax.get_legend_handles_labels() + assert handles == [] + + # -- + + ax.clear() + p = basic._ScatterPlotter(x="x", y="y", hue="a", data=long_df, + legend="full") + p.add_legend_data(ax) + handles, labels = ax.get_legend_handles_labels() + colors = [h.get_facecolors()[0] for h in handles] + assert labels == p.hue_levels + assert self.colors_equal(colors, [p.palette[l] for l in labels]) + + # -- + + ax.clear() + p = basic._ScatterPlotter(x="x", y="y", hue="a", style="a", + markers=True, legend="full", data=long_df) + p.add_legend_data(ax) + handles, labels = ax.get_legend_handles_labels() + colors = [h.get_facecolors()[0] for h in handles] + paths = [h.get_paths()[0] for h in handles] + assert labels == p.hue_levels == p.style_levels + assert self.colors_equal(colors, [p.palette[l] for l in labels]) + assert self.paths_equal(paths, [p.paths[l] for l in labels]) + + # -- + + ax.clear() + p = basic._ScatterPlotter(x="x", y="y", hue="a", style="b", + markers=True, legend="full", data=long_df) + p.add_legend_data(ax) + handles, labels = ax.get_legend_handles_labels() + colors = [h.get_facecolors()[0] for h in handles] + paths = [h.get_paths()[0] for h in handles] + expected_colors = ([p.palette[l] for l in p.hue_levels] + + [".2" for _ in p.style_levels]) + expected_paths = ([default_marker for _ in p.hue_levels] + + [p.paths[l] for l in p.style_levels]) + assert labels == p.hue_levels + p.style_levels + assert self.colors_equal(colors, expected_colors) + assert self.paths_equal(paths, expected_paths) + + # -- + + ax.clear() + p = basic._ScatterPlotter(x="x", y="y", hue="a", size="a", + data=long_df, legend="full") + p.add_legend_data(ax) + handles, labels = ax.get_legend_handles_labels() + colors = [h.get_facecolors()[0] for h in handles] + sizes = [h.get_sizes()[0] for h in handles] + assert labels == p.hue_levels == p.size_levels + assert self.colors_equal(colors, [p.palette[l] for l in labels]) + assert sizes == [p.sizes[l] for l in labels] + + # -- + + x, y = np.random.randn(2, 40) + z = np.tile(np.arange(20), 2) + + p = basic._ScatterPlotter(x=x, y=y, hue=z) + + ax.clear() + p.legend = "full" + p.add_legend_data(ax) + handles, labels = ax.get_legend_handles_labels() + assert labels == [str(l) for l in p.hue_levels] + + ax.clear() + p.legend = "brief" + p.add_legend_data(ax) + handles, labels = ax.get_legend_handles_labels() + assert len(labels) == 4 + + p = basic._ScatterPlotter(x=x, y=y, size=z) + + ax.clear() + p.legend = "full" + p.add_legend_data(ax) + handles, labels = ax.get_legend_handles_labels() + assert labels == [str(l) for l in p.size_levels] + + ax.clear() + p.legend = "brief" + p.add_legend_data(ax) + handles, labels = ax.get_legend_handles_labels() + assert len(labels) == 4 + + ax.clear() + p.legend = "bad_value" + with pytest.raises(ValueError): + p.add_legend_data(ax) From 483290210027842096b18fd00eb1ec5071425c86 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 28 May 2018 16:57:41 -0400 Subject: [PATCH 0667/1738] Unit tests for scatterplot --- seaborn/basic.py | 13 +++- seaborn/tests/test_basic.py | 129 ++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 1 deletion(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 554207065c..b86409f1b9 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -1213,4 +1213,15 @@ def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, p.plot(ax, kwargs) - return p, ax + return ax + + +scatterplot.__doc__ = dedent("""\ + Draw a scatterplot with up to several semantic groupings. + + {main_api_narrative} + + Parameters + ---------- + + """).format(**_basic_docs) diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 1bf5e9cd81..6a5e5d55a1 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -1216,3 +1216,132 @@ def test_legend_data(self, long_df): p.legend = "bad_value" with pytest.raises(ValueError): p.add_legend_data(ax) + + def test_plot(self, long_df, repeated_df): + + f, ax = plt.subplots() + + p = basic._ScatterPlotter(x="x", y="y", data=long_df) + + p.plot(ax, {}) + points = ax.collections[0] + assert np.array_equal(points.get_offsets(), long_df[["x", "y"]].values) + + ax.clear() + p.plot(ax, {"color": "k", "label": "test"}) + points = ax.collections[0] + assert self.colors_equal(points.get_facecolor(), "k") + assert points.get_label() == "test" + + p = basic._ScatterPlotter(x="x", y="y", hue="a", data=long_df) + + ax.clear() + p.plot(ax, {}) + points = ax.collections[0] + expected_colors = [p.palette[k] for k in p.plot_data["hue"]] + assert self.colors_equal(points.get_facecolors(), expected_colors) + + p = basic._ScatterPlotter(x="x", y="y", size="a", data=long_df) + + ax.clear() + p.plot(ax, {}) + points = ax.collections[0] + expected_sizes = [p.size_lookup(k) for k in p.plot_data["size"]] + assert np.array_equal(points.get_sizes(), expected_sizes) + + p = basic._ScatterPlotter(x="x", y="y", hue="a", style="a", + markers=True, data=long_df) + + ax.clear() + p.plot(ax, {}) + expected_colors = [p.palette[k] for k in p.plot_data["hue"]] + expected_paths = [p.paths[k] for k in p.plot_data["style"]] + assert self.colors_equal(points.get_facecolors(), expected_colors) + assert self.paths_equal(points.get_paths(), expected_paths) + + p = basic._ScatterPlotter(x="x", y="y", hue="a", style="b", + markers=True, data=long_df) + + ax.clear() + p.plot(ax, {}) + expected_colors = [p.palette[k] for k in p.plot_data["hue"]] + expected_paths = [p.paths[k] for k in p.plot_data["style"]] + assert self.colors_equal(points.get_facecolors(), expected_colors) + assert self.paths_equal(points.get_paths(), expected_paths) + + def test_axis_labels(self, long_df): + + f, (ax1, ax2) = plt.subplots(1, 2, sharey=True) + + p = basic._ScatterPlotter(x="x", y="y", data=long_df) + + p.plot(ax1, {}) + assert ax1.get_xlabel() == "x" + assert ax1.get_ylabel() == "y" + + p.plot(ax2, {}) + assert ax2.get_xlabel() == "x" + assert ax2.get_ylabel() == "y" + assert not ax2.yaxis.label.get_visible() + + def test_scatterplot_axes(self, wide_df): + + f1, ax1 = plt.subplots() + f2, ax2 = plt.subplots() + + ax = basic.scatterplot(data=wide_df) + assert ax is ax2 + + ax = basic.scatterplot(data=wide_df, ax=ax1) + assert ax is ax1 + + def test_scatterplot_smoke(self, flat_array, flat_series, + wide_array, wide_list, wide_list_of_series, + wide_df, long_df): + + f, ax = plt.subplots() + + basic.scatterplot(data=flat_array) + ax.clear() + + basic.scatterplot(data=flat_series) + ax.clear() + + basic.scatterplot(data=wide_array) + ax.clear() + + basic.scatterplot(data=wide_list) + ax.clear() + + basic.scatterplot(data=wide_list_of_series) + ax.clear() + + basic.scatterplot(data=wide_df) + ax.clear() + + basic.scatterplot(x="x", y="y", data=long_df) + ax.clear() + + basic.scatterplot(x=long_df.x, y=long_df.y) + ax.clear() + + basic.scatterplot(x=long_df.x, y="y", data=long_df) + ax.clear() + + basic.scatterplot(x="x", y=long_df.y.values, data=long_df) + ax.clear() + + basic.scatterplot(x="x", y="y", hue="a", data=long_df) + ax.clear() + + basic.scatterplot(x="x", y="y", hue="a", style="a", data=long_df) + ax.clear() + + basic.scatterplot(x="x", y="y", hue="a", style="b", data=long_df) + ax.clear() + + basic.scatterplot(x="x", y="y", hue="a", size="a", data=long_df) + ax.clear() + + basic.scatterplot(x="x", y="y", hue="a", size="s", data=long_df) + ax.clear() From 69c1d8f44a97938f01d30925744ec44dcacfad25 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 28 May 2018 17:48:57 -0400 Subject: [PATCH 0668/1738] Add hack to fix issues with older matplotlibs --- seaborn/basic.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/seaborn/basic.py b/seaborn/basic.py index b86409f1b9..75a43362a6 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -796,7 +796,18 @@ def plot(self, ax, kws): x = data["x"] y = data["y"] + c = orig_c if not self.palette else data["hue"].map(self.palette) + if LooseVersion(mpl.__version__) < "2.0": + + # The runs into some problems on older mpls because a Series full + # of (float) tuples gets propagated to an object array and mpl + # raises a variety of errors on older versions Seems sorted out in + # mpl 2+, and can be removed when dropping support for mpl 1.x + + if isinstance(c, pd.Series): + c = c.map(mpl.colors.colorConverter.to_rgb) + c = np.array([rgb for rgb in c]) s = orig_s if not self.sizes else data["size"].map(self.sizes) args = np.asarray(x), np.asarray(y), np.asarray(s), np.asarray(c) From d887c4676f391949853888399e13ce628fef3be7 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 28 May 2018 19:07:39 -0400 Subject: [PATCH 0669/1738] API examples for scatterplot --- doc/api.rst | 1 + seaborn/basic.py | 203 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 194 insertions(+), 10 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 6dde978a3d..9c04e92cfb 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -31,6 +31,7 @@ Basic plots :toctree: generated lineplot + scatterplot .. _categorical_api: diff --git a/seaborn/basic.py b/seaborn/basic.py index 75a43362a6..0246fd737e 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -875,7 +875,7 @@ def plot(self, ax, kws): Limits in data units to use for the colormap applied to the ``hue`` variable when it is numeric. Not relevant if it is categorical.\ """), - sizes=dedent(""" + sizes=dedent("""\ sizes : list, dict, or tuple, optional An object that determines how sizes are chosen when ``size`` is used. It can always be a list of size values or a dict mapping levels of the @@ -894,6 +894,14 @@ def plot(self, ax, kws): Limits in data units to use for the size normalization when the ``size`` variable is numeric.\ """), + markers=dedent("""\ + markers : boolean, list, or dictionary, optional + Object determining how to draw the markers for different levels of the + ``style`` variable. Setting to ``True`` will use default markers, or + you can pass a list of markers or a dictionary mapping levels of the + ``style`` variable to markers. Setting to ``False`` will draw + marker-less lines. Markers are specified as in matplotlib.\ + """), style_order=dedent("""\ style_order : list, optional Specified order for appearance of the ``style`` variable levels @@ -972,7 +980,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, lineplot.__doc__ = dedent("""\ - Draw a line plot with up to several semantic groupings. + Draw a line plot with possibility of several semantic groupings. {main_api_narrative} @@ -988,7 +996,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, Can be either categorical or numeric, although color mapping will behave differently in latter case. size : {long_form_var} - Gropuing variable that will produce lines with different widths. + Grouping variable that will produce lines with different widths. Can be either categorical or numeric, although size mapping will behave differently in latter case. style : {long_form_var} @@ -1009,12 +1017,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, ``style`` variable to dash codes. Setting to ``False`` will use solid lines for all subsets. Dashes are specified as in matplotlib: a tuple of ``(segment, gap)`` lengths, or an empty string to draw a solid line. - markers : boolean, list, or dictionary, optional - Object determining how to draw the markers for different levels of the - ``style`` variable. Setting to ``True`` will use default markers, or - you can pass a list of markers or a dictionary mapping levels of the - ``style`` variable to markers. Setting to ``False`` will draw - marker-less lines. Markers are specified as in matplotlib. + {markers} {style_order} {units} {estimator} @@ -1228,11 +1231,191 @@ def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, scatterplot.__doc__ = dedent("""\ - Draw a scatterplot with up to several semantic groupings. + Draw a scatter plot with possibility of several semantic groupings. {main_api_narrative} Parameters ---------- + {data_vars} + hue : {long_form_var} + Grouping variable that will produce points with different colors. + Can be either categorical or numeric, although color mapping will + behave differently in latter case. + size : {long_form_var} + Grouping variable that will produce points with different sizes. + Can be either categorical or numeric, although size mapping will + behave differently in latter case. + style : {long_form_var} + Grouping variable that will produce points with different markers. + Can have a numeric dtype but will always be treated as categorical. + {data} + {palette} + {hue_order} + {hue_limits} + {sizes} + {size_order} + {size_limits} + {markers} + {style_order} + {{x,y}}_bins : lists or arrays or functions + *Currently non-functional.* + {units} + *Currently non-functional.* + {estimator} + *Currently non-functional.* + {ci} + *Currently non-functional.* + {n_boot} + *Currently non-functional.* + alpha : float + Proportional opacity of the points. + {{x,y}}_jitter : booleans or floats + *Currently non-functional.* + {legend} + {ax_in} + kwargs : key, value mappings + Other keyword arguments are passed down to ``plt.scatter`` at draw + time. + + Returns + ------- + {ax_out} + + See Also + -------- + lineplot : Show the relationship between two variables connected with + lines to emphasize continuity. + swarmplot : Draw a scatter plot with one categorical variable, arranging + the points to show the distribution of values. + + Examples + -------- + + Draw a simple scatter plot between two variables: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns; sns.set() + >>> import matplotlib.pyplot as plt + >>> tips = sns.load_dataset("tips") + >>> ax = sns.scatterplot(x="total_bill", y="tip", data=tips) + + Group by another variable and show the groups with different colors: + + .. plot:: + :context: close-figs + + >>> ax = sns.scatterplot(x="total_bill", y="tip", hue="time", + ... data=tips) + + Show the grouping variable by varying both color and marker: + + .. plot:: + :context: close-figs + + >>> ax = sns.scatterplot(x="total_bill", y="tip", + ... hue="time", style="time", data=tips) + + Vary colors and markers to show two different grouping variables: + + .. plot:: + :context: close-figs + + >>> ax = sns.scatterplot(x="total_bill", y="tip", + ... hue="day", style="time", data=tips) + + Show a quantitative variable by varying the size of the points: + + .. plot:: + :context: close-figs + + >>> ax = sns.scatterplot(x="total_bill", y="tip", size="size", + ... data=tips) + + Also show the quantitative variable by also using continuous colors: + + .. plot:: + :context: close-figs + + >>> ax = sns.scatterplot(x="total_bill", y="tip", + ... hue="size", size="size", + ... data=tips) + + Use a different continuous color map: + + .. plot:: + :context: close-figs + + >>> cmap = sns.cubehelix_palette(dark=.3, light=.8, as_cmap=True) + >>> ax = sns.scatterplot(x="total_bill", y="tip", + ... hue="size", size="size", + ... palette=cmap, + ... data=tips) + + Change the minimum and maximum point size and show all sizes in legend: + + .. plot:: + :context: close-figs + + >>> cmap = sns.cubehelix_palette(dark=.3, light=.8, as_cmap=True) + >>> ax = sns.scatterplot(x="total_bill", y="tip", + ... hue="size", size="size", + ... sizes=(20, 200), palette=cmap, + ... legend="full", data=tips) + + Use a narrower range of color map intensities: + + .. plot:: + :context: close-figs + + >>> cmap = sns.cubehelix_palette(dark=.3, light=.8, as_cmap=True) + >>> ax = sns.scatterplot(x="total_bill", y="tip", + ... hue="size", size="size", + ... sizes=(20, 200), hue_limits=(0, 7), + ... legend="full", data=tips) + + Vary the size with a categorical variable, and use a different palette: + + .. plot:: + :context: close-figs + + >>> cmap = sns.cubehelix_palette(dark=.3, light=.8, as_cmap=True) + >>> ax = sns.scatterplot(x="total_bill", y="tip", + ... hue="day", size="smoker", + ... palette="Set2", + ... data=tips) + + Use a specific set of markers: + + .. plot:: + :context: close-figs + + >>> markers = {{"Lunch": "s", "Dinner": "X"}} + >>> ax = sns.scatterplot(x="total_bill", y="tip", style="time", + ... markers=markers, + ... data=tips) + + Pass data vectors instead of names in a data frame: + + .. plot:: + :context: close-figs + + >>> iris = sns.load_dataset("iris") + >>> ax = sns.scatterplot(x=iris.sepal_length, y=iris.sepal_width, + ... hue=iris.species, style=iris.species) + + Pass a wide-form dataset and plot against its index: + + .. plot:: + :context: close-figs + + >>> import numpy as np, pandas as pd; plt.close("all") + >>> index = pd.date_range("1 1 2000", periods=100, + ... freq="m", name="date") + >>> data = np.random.randn(100, 4).cumsum(axis=0) + >>> wide_df = pd.DataFrame(data, index, ["a", "b", "c", "d"]) + >>> ax = sns.scatterplot(data=wide_df) """).format(**_basic_docs) From 43dabea64f8c40133c55cd01a91e1e47fe91165b Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Tue, 29 May 2018 15:11:02 -0400 Subject: [PATCH 0670/1738] Better handling of missing data --- seaborn/basic.py | 50 ++++++++++++++++++-------------- seaborn/tests/test_basic.py | 58 +++++++++++++++++++++++++++++++++++-- 2 files changed, 85 insertions(+), 23 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 0246fd737e..0bb7c74853 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -11,7 +11,8 @@ from .external.six import string_types from . import utils -from .utils import categorical_order, get_color_cycle, ci_to_errsize, sort_df +from .utils import (categorical_order, get_color_cycle, ci_to_errsize, sort_df, + remove_na) from .algorithms import bootstrap from .palettes import color_palette @@ -171,12 +172,20 @@ def establish_variables(self, x=None, y=None, if attr not in plot_data: plot_data[attr] = None + # Determine which semantics have (some) data + plot_valid = plot_data.notnull().any() + semantics = [ + name for name in ["x", "y", "hue", "size", "style"] + if plot_valid[name] + ] + self.x_label = x_label self.y_label = y_label self.hue_label = hue_label self.size_label = size_label self.style_label = style_label self.plot_data = plot_data + self.semantics = semantics return plot_data @@ -220,7 +229,7 @@ def categorical_to_palette(self, data, order, palette): def numeric_to_palette(self, data, order, palette, limits): """Determine colors when the hue variable is quantitative.""" - levels = list(np.sort(data.unique())) + levels = list(np.sort(remove_na(data.unique()))) # TODO do we want to do something complicated to ensure contrast # at the extremes of the colormap against the background? @@ -373,7 +382,7 @@ def parse_size(self, data, sizes, order, limits): levels = categorical_order(data) numbers = np.arange(0, len(levels))[::-1] elif var_type == "numeric": - levels = numbers = np.sort(data.unique()) + levels = numbers = np.sort(remove_na(data.unique())) if isinstance(sizes, (dict, list)): @@ -476,7 +485,7 @@ def _semantic_type(self, data): try: data.astype(np.float) return "numeric" - except ValueError: + except (ValueError, TypeError): return "categorical" def label_axes(self, ax): @@ -779,8 +788,8 @@ def plot(self, ax, kws): # function will advance the axes property cycle. scout = ax.scatter([], [], **kws) - orig_s = kws.pop("s", scout.get_sizes()) - orig_c = kws.pop("c", scout.get_facecolors()) + s = kws.pop("s", scout.get_sizes()) + c = kws.pop("c", scout.get_facecolors()) scout.remove() kws.pop("color", None) # TODO is this optimal? @@ -788,27 +797,24 @@ def plot(self, ax, kws): kws.setdefault("linewidth", .75) # TODO scale with marker size? kws.setdefault("edgecolor", "w") - kws["alpha"] = 1 if self.alpha == "auto" else self.alpha # TODO + # TODO this makes it impossible to vary alpha with hue which might + # otherwise be useful? Should we just pass None? + kws["alpha"] = 1 if self.alpha == "auto" else self.alpha # Assign arguments for plt.scatter and draw the plot - data = self.plot_data + data = self.plot_data[self.semantics].dropna() + if not data.size: + return x = data["x"] y = data["y"] - c = orig_c if not self.palette else data["hue"].map(self.palette) - if LooseVersion(mpl.__version__) < "2.0": - - # The runs into some problems on older mpls because a Series full - # of (float) tuples gets propagated to an object array and mpl - # raises a variety of errors on older versions Seems sorted out in - # mpl 2+, and can be removed when dropping support for mpl 1.x + if self.palette: + c = [self.palette.get(x) for x in data["hue"]] - if isinstance(c, pd.Series): - c = c.map(mpl.colors.colorConverter.to_rgb) - c = np.array([rgb for rgb in c]) - s = orig_s if not self.sizes else data["size"].map(self.sizes) + if self.sizes: + s = [self.sizes.get(x) for x in data["size"]] args = np.asarray(x), np.asarray(y), np.asarray(s), np.asarray(c) points = ax.scatter(*args, **kws) @@ -818,7 +824,8 @@ def plot(self, ax, kws): # but only a single marker shape per call. if self.paths: - points.set_paths(np.asarray(data["style"].map(self.paths))) + p = [self.paths.get(x) for x in data["style"]] + points.set_paths(p) # Finalize the axes details self.label_axes(ax) @@ -1106,7 +1113,8 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, :context: close-figs >>> ax = sns.lineplot(x="timepoint", y="signal", hue="event", - ... units="subject", estimator=None, data=fmri) + ... units="subject", estimator=None, lw=1, + ... data=fmri.query("region == 'frontal'")) Use a quantitative color mapping: diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 6a5e5d55a1..8da1b0e0ec 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -72,6 +72,23 @@ def repeated_df(self): u=np.repeat(np.arange(2), n // 2), )) + @pytest.fixture + def missing_df(self): + + n = 100 + rs = np.random.RandomState() + df = pd.DataFrame(dict( + x=rs.randint(0, 20, n), + y=rs.randn(n), + a=np.take(list("abc"), rs.randint(0, 3, n)), + b=np.take(list("mnop"), rs.randint(0, 4, n)), + s=np.take([2, 4, 8], rs.randint(0, 3, n)), + )) + for col in df: + idx = rs.permutation(df.index)[:10] + df.loc[idx, col] = np.nan + return df + @pytest.fixture def null_column(self): @@ -82,6 +99,7 @@ def test_wide_df_variables(self, wide_df): p = basic._BasicPlotter() p.establish_variables(data=wide_df) assert p.input_format == "wide" + assert p.semantics == ["x", "y", "hue", "style"] assert len(p.plot_data) == np.product(wide_df.shape) x = p.plot_data["x"] @@ -121,6 +139,7 @@ def test_wide_array_variables(self, wide_array): p = basic._BasicPlotter() p.establish_variables(data=wide_array) assert p.input_format == "wide" + assert p.semantics == ["x", "y", "hue", "style"] assert len(p.plot_data) == np.product(wide_array.shape) nrow, ncol = wide_array.shape @@ -154,6 +173,7 @@ def test_flat_array_variables(self, flat_array): p = basic._BasicPlotter() p.establish_variables(data=flat_array) assert p.input_format == "wide" + assert p.semantics == ["x", "y"] assert len(p.plot_data) == np.product(flat_array.shape) x = p.plot_data["x"] @@ -179,6 +199,7 @@ def test_flat_series_variables(self, flat_series): p = basic._BasicPlotter() p.establish_variables(data=flat_series) assert p.input_format == "wide" + assert p.semantics == ["x", "y"] assert len(p.plot_data) == len(flat_series) x = p.plot_data["x"] @@ -200,6 +221,7 @@ def test_wide_list_variables(self, wide_list): p = basic._BasicPlotter() p.establish_variables(data=wide_list) assert p.input_format == "wide" + assert p.semantics == ["x", "y", "hue", "style"] assert len(p.plot_data) == sum(len(l) for l in wide_list) x = p.plot_data["x"] @@ -233,6 +255,7 @@ def test_wide_list_of_series_variables(self, wide_list_of_series): p = basic._BasicPlotter() p.establish_variables(data=wide_list_of_series) assert p.input_format == "wide" + assert p.semantics == ["x", "y", "hue", "style"] assert len(p.plot_data) == sum(len(l) for l in wide_list_of_series) x = p.plot_data["x"] @@ -266,6 +289,7 @@ def test_long_df(self, long_df): p = basic._BasicPlotter() p.establish_variables(x="x", y="y", data=long_df) assert p.input_format == "long" + assert p.semantics == ["x", "y"] assert np.array_equal(p.plot_data["x"], long_df["x"]) assert np.array_equal(p.plot_data["y"], long_df["y"]) @@ -277,16 +301,19 @@ def test_long_df(self, long_df): assert p.style_label is None p.establish_variables(x=long_df.x, y="y", data=long_df) + assert p.semantics == ["x", "y"] assert np.array_equal(p.plot_data["x"], long_df["x"]) assert np.array_equal(p.plot_data["y"], long_df["y"]) assert (p.x_label, p.y_label) == ("x", "y") p.establish_variables(x="x", y=long_df.y, data=long_df) + assert p.semantics == ["x", "y"] assert np.array_equal(p.plot_data["x"], long_df["x"]) assert np.array_equal(p.plot_data["y"], long_df["y"]) assert (p.x_label, p.y_label) == ("x", "y") p.establish_variables(x="x", y="y", hue="a", data=long_df) + assert p.semantics == ["x", "y", "hue"] assert np.array_equal(p.plot_data["hue"], long_df["a"]) for col in ["style", "size"]: assert p.plot_data[col].isnull().all() @@ -294,6 +321,7 @@ def test_long_df(self, long_df): assert p.size_label is None and p.style_label is None p.establish_variables(x="x", y="y", hue="a", style="a", data=long_df) + assert p.semantics == ["x", "y", "hue", "style"] assert np.array_equal(p.plot_data["hue"], long_df["a"]) assert np.array_equal(p.plot_data["style"], long_df["a"]) assert p.plot_data["size"].isnull().all() @@ -301,11 +329,13 @@ def test_long_df(self, long_df): assert p.size_label is None p.establish_variables(x="x", y="y", hue="a", style="b", data=long_df) + assert p.semantics == ["x", "y", "hue", "style"] assert np.array_equal(p.plot_data["hue"], long_df["a"]) assert np.array_equal(p.plot_data["style"], long_df["b"]) assert p.plot_data["size"].isnull().all() p.establish_variables(x="x", y="y", size="y", data=long_df) + assert p.semantics == ["x", "y", "size"] assert np.array_equal(p.plot_data["size"], long_df["y"]) assert p.size_label == "y" assert p.hue_label is None and p.style_label is None @@ -1037,7 +1067,7 @@ def test_lineplot_axes(self, wide_df): def test_lineplot_smoke(self, flat_array, flat_series, wide_array, wide_list, wide_list_of_series, - wide_df, long_df): + wide_df, long_df, missing_df): f, ax = plt.subplots() @@ -1080,12 +1110,24 @@ def test_lineplot_smoke(self, flat_array, flat_series, basic.lineplot(x="x", y="y", hue="a", style="b", data=long_df) ax.clear() + basic.lineplot(x="x", y="y", hue="a", style="a", data=missing_df) + ax.clear() + + basic.lineplot(x="x", y="y", hue="a", style="b", data=missing_df) + ax.clear() + basic.lineplot(x="x", y="y", hue="a", size="a", data=long_df) ax.clear() basic.lineplot(x="x", y="y", hue="a", size="s", data=long_df) ax.clear() + basic.lineplot(x="x", y="y", hue="a", size="a", data=missing_df) + ax.clear() + + basic.lineplot(x="x", y="y", hue="a", size="s", data=missing_df) + ax.clear() + class TestScatterPlotter(TestBasicPlotter): @@ -1297,7 +1339,7 @@ def test_scatterplot_axes(self, wide_df): def test_scatterplot_smoke(self, flat_array, flat_series, wide_array, wide_list, wide_list_of_series, - wide_df, long_df): + wide_df, long_df, missing_df): f, ax = plt.subplots() @@ -1340,8 +1382,20 @@ def test_scatterplot_smoke(self, flat_array, flat_series, basic.scatterplot(x="x", y="y", hue="a", style="b", data=long_df) ax.clear() + basic.scatterplot(x="x", y="y", hue="a", style="a", data=missing_df) + ax.clear() + + basic.scatterplot(x="x", y="y", hue="a", style="b", data=missing_df) + ax.clear() + basic.scatterplot(x="x", y="y", hue="a", size="a", data=long_df) ax.clear() basic.scatterplot(x="x", y="y", hue="a", size="s", data=long_df) ax.clear() + + basic.scatterplot(x="x", y="y", hue="a", size="a", data=missing_df) + ax.clear() + + basic.scatterplot(x="x", y="y", hue="a", size="s", data=missing_df) + ax.clear() From 49ac17f120f56b18b8b271d38335d6cb7909133f Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Tue, 29 May 2018 15:11:17 -0400 Subject: [PATCH 0671/1738] Example gallery scripts for scatterplot --- doc/api.rst | 2 +- examples/different_scatter_variables.py | 22 ++++++++++++++++++++++ examples/scatterplot_sizes.py | 19 +++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 examples/different_scatter_variables.py create mode 100644 examples/scatterplot_sizes.py diff --git a/doc/api.rst b/doc/api.rst index 9c04e92cfb..f2ca78b2ab 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -30,8 +30,8 @@ Basic plots .. autosummary:: :toctree: generated - lineplot scatterplot + lineplot .. _categorical_api: diff --git a/examples/different_scatter_variables.py b/examples/different_scatter_variables.py new file mode 100644 index 0000000000..5771dd8909 --- /dev/null +++ b/examples/different_scatter_variables.py @@ -0,0 +1,22 @@ +""" +Scatterplot with categorical and continuous semantics +===================================================== + +_thumb: .55, .5 + +""" +import seaborn as sns +import matplotlib.pyplot as plt +sns.set(style="white") + +# Load the example iris dataset +iris = sns.load_dataset("iris") + +# Draw a scatter plot while assigning point colors and sizes +# to different variables in the dataset +f, ax = plt.subplots(figsize=(6.5, 6.5)) +ax = sns.scatterplot(x="sepal_length", y="sepal_width", + hue="species", size="petal_width", + sizes=(50, 200), alpha=.75, + palette="tab10", + data=iris) diff --git a/examples/scatterplot_sizes.py b/examples/scatterplot_sizes.py new file mode 100644 index 0000000000..5c3e9dac5a --- /dev/null +++ b/examples/scatterplot_sizes.py @@ -0,0 +1,19 @@ +""" +Scatterplot with continuous hues and sizes +========================================== + +_thumb: .45, .45 + +""" + +import seaborn as sns +sns.set() + +# Load the example iris dataset +planets = sns.load_dataset("planets") + +cmap = sns.cubehelix_palette(rot=-.2, as_cmap=True) +ax = sns.scatterplot(x="distance", y="orbital_period", + hue="year", size="mass", + palette=cmap, sizes=(10, 200), + data=planets) From 9c0e82e54d9c416b63830f38f853479b28231b55 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Tue, 29 May 2018 15:27:05 -0400 Subject: [PATCH 0672/1738] Update release notes [skip ci] --- doc/releases/v0.9.0.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index c0c0c816ef..52c1f4e564 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -2,7 +2,9 @@ v0.9.0 (Unreleased) ------------------- -- Added the :func:`lineplot` function for representing relationships between numeric ``x`` and ``y`` variables with lines, potentially after conditioning on up to three other variables and semantically mapping those conditions with the color, size, or style of the lines. This function replaces :func:`tsplot`, but with an API that is more consistent with other modern seaborn functions and has both more flexibility (more dimensions of semantic mapping, better handling of dates) and less flexibility (fewer options for visual representing uncertainty). There is considerable new API documentation and there should also be a new tutorial. +- Added the :func:`scatterplot` function for representing the relationship between ``x`` and ``y``, potentially after conditioning on up to three other variables and semantically mapping those conditions with the color, size, or style of the points. + +- Added the :func:`lineplot` function for representing relationships between``x`` and ``y`` variables with lines, potentially after conditioning on up to three other variables and semantically mapping those conditions with the color, size, or style of the lines. This function replaces :func:`tsplot`, but with an API that is more consistent with other modern seaborn functions and has both more flexibility (more dimensions of semantic mapping, better handling of dates) and less flexibility (fewer options for visual representing uncertainty). There is considerable new API documentation and a few gallery examples. - Final removal of the previously-deprecated ``coefplot`` and ``interactplot`` functions. From abb0885b6e1cfa07fea4004c85392dbf127f1247 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Tue, 29 May 2018 15:35:56 -0400 Subject: [PATCH 0673/1738] Python 2 compatability --- seaborn/basic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 0bb7c74853..e5ea9c83c5 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -811,10 +811,10 @@ def plot(self, ax, kws): y = data["y"] if self.palette: - c = [self.palette.get(x) for x in data["hue"]] + c = [self.palette.get(val) for val in data["hue"]] if self.sizes: - s = [self.sizes.get(x) for x in data["size"]] + s = [self.sizes.get(val) for val in data["size"]] args = np.asarray(x), np.asarray(y), np.asarray(s), np.asarray(c) points = ax.scatter(*args, **kws) @@ -824,7 +824,7 @@ def plot(self, ax, kws): # but only a single marker shape per call. if self.paths: - p = [self.paths.get(x) for x in data["style"]] + p = [self.paths.get(val) for val in data["style"]] points.set_paths(p) # Finalize the axes details From 21680b0e8d085bf7b8300626e532bf48da65a99d Mon Sep 17 00:00:00 2001 From: Thomas Brunner Date: Mon, 23 Apr 2018 21:35:44 +0200 Subject: [PATCH 0674/1738] Change rugplot to draw all lines in a single call --- seaborn/distributions.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 29eb10e359..98c71f931b 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -5,6 +5,7 @@ import pandas as pd import matplotlib as mpl import matplotlib.pyplot as plt +import matplotlib.transforms as tx import warnings from distutils.version import LooseVersion @@ -681,7 +682,7 @@ def rugplot(a, height=.05, axis="x", ax=None, **kwargs): ax : matplotlib axes, optional Axes to draw plot into; otherwise grabs current axes. kwargs : key, value pairings - Other keyword arguments are passed to ``axvline`` or ``axhline``. + Other keyword arguments are passed to ``plot``. Returns ------- @@ -693,9 +694,15 @@ def rugplot(a, height=.05, axis="x", ax=None, **kwargs): ax = plt.gca() a = np.asarray(a) vertical = kwargs.pop("vertical", axis == "y") - func = ax.axhline if vertical else ax.axvline kwargs.setdefault("linewidth", 1) - for pt in a: - func(pt, 0, height, **kwargs) + + if vertical: + trans = tx.blended_transform_factory(ax.transAxes, ax.transData) + ax.plot(np.tile([0, height, np.nan], len(a)), np.repeat(a, 3), + transform=trans, **kwargs) + else: + trans = tx.blended_transform_factory(ax.transData, ax.transAxes) + ax.plot(np.repeat(a, 3), np.tile([0, height, np.nan], len(a)), + transform=trans, **kwargs) return ax From 758789b91585f65b4e5c72cc2722ee658956e7c0 Mon Sep 17 00:00:00 2001 From: Thomas Brunner Date: Thu, 26 Apr 2018 22:22:11 +0200 Subject: [PATCH 0675/1738] Now using LineCollection to draw rugplot --- seaborn/distributions.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 98c71f931b..1cf6413dea 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -6,6 +6,7 @@ import matplotlib as mpl import matplotlib.pyplot as plt import matplotlib.transforms as tx +from matplotlib.collections import LineCollection import warnings from distutils.version import LooseVersion @@ -698,11 +699,14 @@ def rugplot(a, height=.05, axis="x", ax=None, **kwargs): if vertical: trans = tx.blended_transform_factory(ax.transAxes, ax.transData) - ax.plot(np.tile([0, height, np.nan], len(a)), np.repeat(a, 3), - transform=trans, **kwargs) + xy_pairs = np.column_stack([np.tile([0, height], len(a)), + np.repeat(a, 2)]) else: trans = tx.blended_transform_factory(ax.transData, ax.transAxes) - ax.plot(np.repeat(a, 3), np.tile([0, height, np.nan], len(a)), - transform=trans, **kwargs) + xy_pairs = np.column_stack([np.repeat(a, 2), + np.tile([0, height], len(a))]) + + line_segs = xy_pairs.reshape([len(a), 2, 2]) + ax.add_collection(LineCollection(line_segs, transform=trans, **kwargs)) return ax From a8725b122ec48dfdd43993607e14ee3e3fd8e203 Mon Sep 17 00:00:00 2001 From: Thomas Brunner <35266133+ttbrunner@users.noreply.github.com> Date: Wed, 16 May 2018 09:31:09 +0200 Subject: [PATCH 0676/1738] Updated docstring for rugplot. --- seaborn/distributions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 1cf6413dea..dba98d6d94 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -683,7 +683,7 @@ def rugplot(a, height=.05, axis="x", ax=None, **kwargs): ax : matplotlib axes, optional Axes to draw plot into; otherwise grabs current axes. kwargs : key, value pairings - Other keyword arguments are passed to ``plot``. + Other keyword arguments are passed to ``LineCollection``. Returns ------- From 34e8157e9d60de6d64e7c3e47291ce75a1d84774 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 2 Jun 2018 18:49:45 -0400 Subject: [PATCH 0677/1738] Improve rugplot line kwarg flexibility --- seaborn/distributions.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index dba98d6d94..fa164f710f 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -6,6 +6,7 @@ import matplotlib as mpl import matplotlib.pyplot as plt import matplotlib.transforms as tx +from matplotlib.cbook import normalize_kwargs from matplotlib.collections import LineCollection import warnings from distutils.version import LooseVersion @@ -695,6 +696,11 @@ def rugplot(a, height=.05, axis="x", ax=None, **kwargs): ax = plt.gca() a = np.asarray(a) vertical = kwargs.pop("vertical", axis == "y") + + alias_map = dict( + linewidth=["lw"], linestyle=["ls"], color=["c"] + ) + kwargs = normalize_kwargs(kwargs, alias_mapping=alias_map) kwargs.setdefault("linewidth", 1) if vertical: @@ -705,8 +711,8 @@ def rugplot(a, height=.05, axis="x", ax=None, **kwargs): trans = tx.blended_transform_factory(ax.transData, ax.transAxes) xy_pairs = np.column_stack([np.repeat(a, 2), np.tile([0, height], len(a))]) - line_segs = xy_pairs.reshape([len(a), 2, 2]) ax.add_collection(LineCollection(line_segs, transform=trans, **kwargs)) + ax.autoscale_view() return ax From e5d175d7b95039b061b205a442aab9253e5220b3 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 2 Jun 2018 18:49:57 -0400 Subject: [PATCH 0678/1738] Add a few basic rugplot tests --- seaborn/tests/test_distributions.py | 54 +++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/seaborn/tests/test_distributions.py b/seaborn/tests/test_distributions.py index 1baa1dda92..20211e852e 100644 --- a/seaborn/tests/test_distributions.py +++ b/seaborn/tests/test_distributions.py @@ -3,6 +3,7 @@ import matplotlib as mpl import matplotlib.pyplot as plt +import pytest from distutils.version import LooseVersion import nose.tools as nt import numpy.testing as npt @@ -122,3 +123,56 @@ def test_bivariate_kde_colorbar(self): ax=ax) nt.assert_equal(len(f.axes), 2) nt.assert_equal(f.axes[1].get_ylabel(), "density") + + +class TestRugPlot(object): + + @pytest.fixture + def list_data(self): + return np.random.randn(20).tolist() + + @pytest.fixture + def array_data(self): + return np.random.randn(20) + + @pytest.fixture + def series_data(self): + return pd.Series(np.random.randn(20)) + + def test_rugplot(self, list_data, array_data, series_data): + + h = .1 + + for data in [list_data, array_data, series_data]: + + f, ax = plt.subplots() + dist.rugplot(data, h) + rug, = ax.collections + segments = np.array(rug.get_segments()) + + assert len(segments) == len(data) + assert np.array_equal(segments[:, 0, 0], data) + assert np.array_equal(segments[:, 1, 0], data) + assert np.array_equal(segments[:, 0, 1], np.zeros_like(data)) + assert np.array_equal(segments[:, 1, 1], np.ones_like(data) * h) + + plt.close(f) + + f, ax = plt.subplots() + dist.rugplot(data, h, axis="y") + rug, = ax.collections + segments = np.array(rug.get_segments()) + + assert len(segments) == len(data) + assert np.array_equal(segments[:, 0, 1], data) + assert np.array_equal(segments[:, 1, 1], data) + assert np.array_equal(segments[:, 0, 0], np.zeros_like(data)) + assert np.array_equal(segments[:, 1, 0], np.ones_like(data) * h) + + plt.close(f) + + f, ax = plt.subplots() + dist.rugplot(data, axis="y") + dist.rugplot(data, vertical=True) + c1, c2 = ax.collections + assert np.array_equal(c1.get_segments(), c2.get_segments()) From 98a4817d7930645d11dfdf5085fcf4d6daf7f479 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 2 Jun 2018 18:50:02 -0400 Subject: [PATCH 0679/1738] Update release notes --- doc/releases/v0.9.0.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index 52c1f4e564..f34f0187e6 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -10,7 +10,8 @@ v0.9.0 (Unreleased) - Fixed :func:`jointplot`/:class:`JointGrid` so that they now accept list inputs. +- Changed :func:`rugplot` to plot a matplotlib ``LineCollection`` instead of many ``Line2D`` objects, providing a big speedup for large arrays. + - Changed the install infrastructure to explicitly declare dependencies in a way that ``pip`` is aware of. This means that ``pip install seaborn`` will now work in an empty environment. Additionally, the dependencies are specified with strict minimal versions. - Updated the testing infrastructure to execute tests with `pytest ` (although many individual tests still use nose assertion). - From be8744c99faf8b014d32731406936e36586088fe Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 2 Jun 2018 19:14:18 -0400 Subject: [PATCH 0680/1738] Normalize kwargs by hand --- seaborn/distributions.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index fa164f710f..ac9328929c 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -6,7 +6,6 @@ import matplotlib as mpl import matplotlib.pyplot as plt import matplotlib.transforms as tx -from matplotlib.cbook import normalize_kwargs from matplotlib.collections import LineCollection import warnings from distutils.version import LooseVersion @@ -697,10 +696,10 @@ def rugplot(a, height=.05, axis="x", ax=None, **kwargs): a = np.asarray(a) vertical = kwargs.pop("vertical", axis == "y") - alias_map = dict( - linewidth=["lw"], linestyle=["ls"], color=["c"] - ) - kwargs = normalize_kwargs(kwargs, alias_mapping=alias_map) + alias_map = dict(linewidth="lw", linestyle="ls", color="c") + for attr, alias in alias_map.items(): + if alias in kwargs: + kwargs[attr] = kwargs.pop(alias) kwargs.setdefault("linewidth", 1) if vertical: From 1b8eef4f3ea82b6cbbabcda1da5506eacd870556 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 2 Jun 2018 19:41:16 -0400 Subject: [PATCH 0681/1738] Test rugplot kwargs --- seaborn/tests/test_distributions.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/seaborn/tests/test_distributions.py b/seaborn/tests/test_distributions.py index 20211e852e..5624138d96 100644 --- a/seaborn/tests/test_distributions.py +++ b/seaborn/tests/test_distributions.py @@ -176,3 +176,13 @@ def test_rugplot(self, list_data, array_data, series_data): dist.rugplot(data, vertical=True) c1, c2 = ax.collections assert np.array_equal(c1.get_segments(), c2.get_segments()) + plt.close(f) + + f, ax = plt.subplots() + dist.rugplot(data) + dist.rugplot(data, lw=2) + dist.rugplot(data, linewidth=3, alpha=.5) + for c, lw in zip(ax.collections, [1, 2, 3]): + assert np.squeeze(c.get_linewidth()).item() == lw + assert c.get_alpha() == .5 + plt.close(f) From f4d86644aee4c85e2d73b355a664c03a3d191c88 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Mon, 4 Jun 2018 18:29:14 -0400 Subject: [PATCH 0682/1738] Remove mention of deprecated corrplot in color tutorial --- doc/tutorial/color_palettes.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index e3b8254674..f51e3982c3 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -262,7 +262,7 @@ "Sequential color palettes\n", "-------------------------\n", "\n", - "The second major class of color palettes is called \"sequential\". This kind of color mapping is appropriate when data range from relatively low or unintersting values to relatively high or interesting values. Although there are cases where you will want discrete colors in a sequential palette, it's more common to use them as a colormap in functions like :func:`kdeplot` or :func:`corrplot` (along with similar matplotlib functions).\n", + "The second major class of color palettes is called \"sequential\". This kind of color mapping is appropriate when data range from relatively low or unintersting values to relatively high or interesting values. Although there are cases where you will want discrete colors in a sequential palette, it's more common to use them as a colormap in functions like :func:`kdeplot` (along with similar matplotlib functions).\n", "\n", "It's common to see colormaps like ``jet`` (or other rainbow palettes) used in this case, becuase the range of hues gives the impression of providing additional information about the data. However, colormaps with large hue shifts tend to introduce discontinuities that don't exist in the data, and our visual system isn't able to naturally map the rainbow to quantitative distinctions like \"high\" or \"low\". The result is that these visualizations end up being more like a puzzle, and they obscure patterns in the data rather than revealing them. The jet palette is because the brightest colors, yellow and cyan, are used for intermediate data values. This has the effect of emphasizing uninteresting (and arbitrary) values while demphasizing the extremes.\n", "\n", From 64471d50004bfe7ca2a7f6f347fb069bea616963 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Mon, 4 Jun 2018 18:39:22 -0400 Subject: [PATCH 0683/1738] Add mention of heatmap --- doc/tutorial/color_palettes.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index f51e3982c3..70ec22fcbc 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -262,7 +262,7 @@ "Sequential color palettes\n", "-------------------------\n", "\n", - "The second major class of color palettes is called \"sequential\". This kind of color mapping is appropriate when data range from relatively low or unintersting values to relatively high or interesting values. Although there are cases where you will want discrete colors in a sequential palette, it's more common to use them as a colormap in functions like :func:`kdeplot` (along with similar matplotlib functions).\n", + "The second major class of color palettes is called \"sequential\". This kind of color mapping is appropriate when data range from relatively low or unintersting values to relatively high or interesting values. Although there are cases where you will want discrete colors in a sequential palette, it's more common to use them as a colormap in functions like :func:`kdeplot` and :func:`heatmap` (along with similar matplotlib functions).\n", "\n", "It's common to see colormaps like ``jet`` (or other rainbow palettes) used in this case, becuase the range of hues gives the impression of providing additional information about the data. However, colormaps with large hue shifts tend to introduce discontinuities that don't exist in the data, and our visual system isn't able to naturally map the rainbow to quantitative distinctions like \"high\" or \"low\". The result is that these visualizations end up being more like a puzzle, and they obscure patterns in the data rather than revealing them. The jet palette is because the brightest colors, yellow and cyan, are used for intermediate data values. This has the effect of emphasizing uninteresting (and arbitrary) values while demphasizing the extremes.\n", "\n", From db41845dd512b80a6023931c6ecebd5f84135e5b Mon Sep 17 00:00:00 2001 From: joelostblom Date: Tue, 5 Jun 2018 12:50:06 -0400 Subject: [PATCH 0684/1738] Add 'swarm' and 'lv' as possible plots with factorplot --- seaborn/categorical.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 83593fde8f..b2b1465355 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -3557,7 +3557,8 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, The default plot that is shown is a point plot, but other seaborn categorical plots can be chosen with the ``kind`` parameter, including - box plots, violin plots, bar plots, or strip plots. + box plots, violin plots, bar plots, strip plots, count plots, swarm plots, + and lvplots. It is important to choose how variables get mapped to the plot structure such that the most important comparisons are easiest to make. As a general @@ -3594,7 +3595,8 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, row_order, col_order : lists of strings, optional Order to organize the rows and/or columns of the grid in, otherwise the orders are inferred from the data objects. - kind : {{``point``, ``bar``, ``count``, ``box``, ``violin``, ``strip``}} + kind : {{``point``, ``bar``, ``count``, ``box``, ``violin``, ``strip``, + ``swarm``, ``lv``}} The kind of plot to draw. {size} {aspect} From 5b651f74e2d09db2505002d725a1962406f68c69 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Tue, 5 Jun 2018 15:17:59 -0400 Subject: [PATCH 0685/1738] Fix spelling mistakes --- doc/tutorial/axis_grids.ipynb | 8 ++++---- doc/tutorial/basic.ipynb | 6 +++--- doc/tutorial/color_palettes.ipynb | 8 ++++---- doc/tutorial/distributions.ipynb | 6 +++--- doc/tutorial/regression.ipynb | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/tutorial/axis_grids.ipynb b/doc/tutorial/axis_grids.ipynb index 07305a112a..1248af820a 100644 --- a/doc/tutorial/axis_grids.ipynb +++ b/doc/tutorial/axis_grids.ipynb @@ -73,7 +73,7 @@ "\n", "The class is used by initializing a :class:`FacetGrid` object with a dataframe and the names of the variables that will form the row, column, or hue dimensions of the grid. These variables should be categorical or discrete, and then the data at each level of the variable will be used for a facet along that axis. For example, say we wanted to examine differences between lunch and dinner in the ``tips`` dataset.\n", "\n", - "Additionally, both :func:`lmplot` and :func:`factorplot` use this object internally, and they return the object when they are finsihed so that it can be used for further tweaking." + "Additionally, both :func:`lmplot` and :func:`factorplot` use this object internally, and they return the object when they are finished so that it can be used for further tweaking." ] }, { @@ -191,7 +191,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "The default ordering of the facets is derived from the information in the DataFrame. If the variable used to define facets has a categorical type, then the order of the categories is used. Otherwise, the facets will be in the order of appearence of the category levels. It is possible, however, to specify an ordering of any facet dimension with the appropriate ``*_order`` parameter:" + "The default ordering of the facets is derived from the information in the DataFrame. If the variable used to define facets has a categorical type, then the order of the categories is used. Otherwise, the facets will be in the order of appearance of the category levels. It is possible, however, to specify an ordering of any facet dimension with the appropriate ``*_order`` parameter:" ] }, { @@ -378,7 +378,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "This approach also lets us use additional aesthetics to distinguish the levels of the hue variable, along with keyword arguments that won't be depdendent on the faceting variables:" + "This approach also lets us use additional aesthetics to distinguish the levels of the hue variable, along with keyword arguments that won't be dependent on the faceting variables:" ] }, { @@ -397,7 +397,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "Sometimes, though, you'll want to map a function that doesn't work the way you expect with the ``color`` and ``label`` keyword arguments. In this case, you'll want to explictly catch them and handle them in the logic of your custom function. For example, this approach will allow use to map ``plt.hexbin``, which otherwise does not play well with the :class:`FacetGrid` API:" + "Sometimes, though, you'll want to map a function that doesn't work the way you expect with the ``color`` and ``label`` keyword arguments. In this case, you'll want to explicitly catch them and handle them in the logic of your custom function. For example, this approach will allow use to map ``plt.hexbin``, which otherwise does not play well with the :class:`FacetGrid` API:" ] }, { diff --git a/doc/tutorial/basic.ipynb b/doc/tutorial/basic.ipynb index 6a2f3c6d35..8d0ae4ceca 100644 --- a/doc/tutorial/basic.ipynb +++ b/doc/tutorial/basic.ipynb @@ -209,7 +209,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "Be cautious about making plots with multiple subset variables. While sometimes informative, they can also be very difficult to parse and interperet. However, even when you are only examining changes across one subset variable, it can be useful to alter both the color and style of the lines, which can make the plot more accessible when printed to black-and-white or viewed by someone with colorblindness:" + "Be cautious about making plots with multiple subset variables. While sometimes informative, they can also be very difficult to parse and interpret. However, even when you are only examining changes across one subset variable, it can be useful to alter both the color and style of the lines, which can make the plot more accessible when printed to black-and-white or viewed by someone with colorblindness:" ] }, { @@ -265,7 +265,7 @@ "collapsed": true }, "source": [ - "In the above examples, the ``hue`` variable takes different categorical values, and the colors of the lines are chosen with an appropriate qualitative colormap. When the ``hue`` variable is instead numeric (specifcally, if it can be cast to float), the default behavior is to use a sequential colormap and to make a legend with \"ticks\" instead of an entry for each line (allowing it to scale to showing many lines):" + "In the above examples, the ``hue`` variable takes different categorical values, and the colors of the lines are chosen with an appropriate qualitative colormap. When the ``hue`` variable is instead numeric (specifically, if it can be cast to float), the default behavior is to use a sequential colormap and to make a legend with \"ticks\" instead of an entry for each line (allowing it to scale to showing many lines):" ] }, { @@ -421,7 +421,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "You can even pass in a list of objects with heterogreneous indices:" + "You can even pass in a list of objects with heterogeneous indices:" ] }, { diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index 70ec22fcbc..28ecb5b3e6 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -262,9 +262,9 @@ "Sequential color palettes\n", "-------------------------\n", "\n", - "The second major class of color palettes is called \"sequential\". This kind of color mapping is appropriate when data range from relatively low or unintersting values to relatively high or interesting values. Although there are cases where you will want discrete colors in a sequential palette, it's more common to use them as a colormap in functions like :func:`kdeplot` and :func:`heatmap` (along with similar matplotlib functions).\n", + "The second major class of color palettes is called \"sequential\". This kind of color mapping is appropriate when data range from relatively low or uninteresting values to relatively high or interesting values. Although there are cases where you will want discrete colors in a sequential palette, it's more common to use them as a colormap in functions like :func:`kdeplot` and :func:`heatmap` (along with similar matplotlib functions).\n", "\n", - "It's common to see colormaps like ``jet`` (or other rainbow palettes) used in this case, becuase the range of hues gives the impression of providing additional information about the data. However, colormaps with large hue shifts tend to introduce discontinuities that don't exist in the data, and our visual system isn't able to naturally map the rainbow to quantitative distinctions like \"high\" or \"low\". The result is that these visualizations end up being more like a puzzle, and they obscure patterns in the data rather than revealing them. The jet palette is because the brightest colors, yellow and cyan, are used for intermediate data values. This has the effect of emphasizing uninteresting (and arbitrary) values while demphasizing the extremes.\n", + "It's common to see colormaps like ``jet`` (or other rainbow palettes) used in this case, because the range of hues gives the impression of providing additional information about the data. However, colormaps with large hue shifts tend to introduce discontinuities that don't exist in the data, and our visual system isn't able to naturally map the rainbow to quantitative distinctions like \"high\" or \"low\". The result is that these visualizations end up being more like a puzzle, and they obscure patterns in the data rather than revealing them. The jet palette is because the brightest colors, yellow and cyan, are used for intermediate data values. This has the effect of emphasizing uninteresting (and arbitrary) values while deemphasizing the extremes.\n", "\n", "For sequential data, it's better to use palettes that have at most a relatively subtle shift in hue accompanied by a large shift in brightness and saturation. This approach will naturally draw the eye to the relatively important parts of the data.\n", "\n", @@ -527,7 +527,7 @@ "\n", "It's also important to emphasize here that using red and green should be avoided, as a substantial population of potential viewers will be `unable to distinguish them `_.\n", "\n", - "It should not surprise you that the Color Brewer library comes with a set of well-choosen diverging colormaps." + "It should not surprise you that the Color Brewer library comes with a set of well-chosen diverging colormaps." ] }, { @@ -571,7 +571,7 @@ "Custom diverging palettes\n", "~~~~~~~~~~~~~~~~~~~~~~~~~\n", "\n", - "You can also use the seaborn function :func:`diverging_palette` to create a custom colormap for diverging data. (Naturally there is also a companion interactive widget, :func:`choose_diverging_palette`). This function makes diverging palettes using the ``husl`` color system. You pass it two hues (in degrees) and, optionally, the lightness and saturation values for the extremes. Using ``husl`` means that the extreme values, and the resulting ramps to the midpoint, will be well-balanced" + "You can also use the seaborn function :func:`diverging_palette` to create a custom colormap for diverging data. (Naturally there is also a companion interactive widget, :func:`choose_diverging_palette`). This function makes diverging palettes using the ``husl`` color system. You pass it two hues (in degrees) and, optionally, the lightness and saturation values for the extremes. Using ``husl`` means that the extreme values, and the resulting ramps to the midpoint, will be well-balanced." ] }, { diff --git a/doc/tutorial/distributions.ipynb b/doc/tutorial/distributions.ipynb index fc69327ffb..2f69152e2b 100644 --- a/doc/tutorial/distributions.ipynb +++ b/doc/tutorial/distributions.ipynb @@ -124,10 +124,10 @@ "cell_type": "raw", "metadata": {}, "source": [ - "Kernel density estimaton\n", + "Kernel density estimation\n", "^^^^^^^^^^^^^^^^^^^^^^^^\n", "\n", - "The kernel density estimate may be less familiar, but it can be a useful tool for plotting the shape of a distribution. Like the histogram, the KDE plots encodes the density of observations on one axis with height along the other axis:" + "The kernel density estimate may be less familiar, but it can be a useful tool for plotting the shape of a distribution. Like the histogram, the KDE plots encode the density of observations on one axis with height along the other axis:" ] }, { @@ -324,7 +324,7 @@ "Kernel density estimation\n", "^^^^^^^^^^^^^^^^^^^^^^^^^\n", "\n", - "It is also posible to use the kernel density estimation procedure described above to visualize a bivariate distribution. In seaborn, this kind of plot is shown with a contour plot and is available as a style in :func:`jointplot`:" + "It is also possible to use the kernel density estimation procedure described above to visualize a bivariate distribution. In seaborn, this kind of plot is shown with a contour plot and is available as a style in :func:`jointplot`:" ] }, { diff --git a/doc/tutorial/regression.ipynb b/doc/tutorial/regression.ipynb index b429bdfd6a..998e587af3 100644 --- a/doc/tutorial/regression.ipynb +++ b/doc/tutorial/regression.ipynb @@ -412,7 +412,7 @@ "Controlling the size and shape of the plot\n", "------------------------------------------\n", "\n", - "Before we noted that the default plots made by :func:`regplot` and :func:`lmplot` look the same but on axes that have a different size and shape. This is because :func:`regplot` is an \"axes-level\" function draws onto a specific axes. This means that you can make multi-panel figures yourself and control exactly where the regression plot goes. If no axes object is explictly provided, it simply uses the \"currently active\" axes, which is why the default plot has the same size and shape as most other matplotlib functions. To control the size, you need to create a figure object yourself." + "Before we noted that the default plots made by :func:`regplot` and :func:`lmplot` look the same but on axes that have a different size and shape. This is because :func:`regplot` is an \"axes-level\" function draws onto a specific axes. This means that you can make multi-panel figures yourself and control exactly where the regression plot goes. If no axes object is explicitly provided, it simply uses the \"currently active\" axes, which is why the default plot has the same size and shape as most other matplotlib functions. To control the size, you need to create a figure object yourself." ] }, { From c8a1fa3eb0ba59bf9fee54cff5e943ae94f40809 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Tue, 5 Jun 2018 15:20:53 -0400 Subject: [PATCH 0686/1738] Fix link formatting --- doc/tutorial/color_palettes.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index 28ecb5b3e6..93eed777b4 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -173,7 +173,7 @@ "\n", "Another source of visually pleasing categorical palettes comes from the `Color Brewer `_ tool (which also has sequential and diverging palettes, as we'll see below). These also exist as matplotlib colormaps, but they are not handled properly. In seaborn, when you ask for a qualitative Color Brewer palette, you'll always get the discrete colors, but this means that at a certain point they will begin to cycle.\n", "\n", - "A nice feature of the Color Brewer website is that it provides some guidance on which palettes are color blind safe. There are a variety of [kinds](https://en.wikipedia.org/wiki/Color_blindness) of color blindness, but the most common variant leads to difficulty distinguishing reds and greens. It's generally a good idea to avoid using red and green for plot elements that need to be discriminated based on color." + "A nice feature of the Color Brewer website is that it provides some guidance on which palettes are color blind safe. There is a variety of `kinds `_ of color blindness, but the most common variant leads to difficulty distinguishing reds and greens. It's generally a good idea to avoid using red and green for plot elements that need to be discriminated based on color." ] }, { From fd54a845a6b78bffd0c777bbb0414c470d1145f1 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Tue, 5 Jun 2018 15:21:18 -0400 Subject: [PATCH 0687/1738] Fix function formatting --- doc/tutorial/categorical.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial/categorical.ipynb b/doc/tutorial/categorical.ipynb index 10ea0f1195..1d4955d844 100644 --- a/doc/tutorial/categorical.ipynb +++ b/doc/tutorial/categorical.ipynb @@ -369,7 +369,7 @@ "Point plots\n", "^^^^^^^^^^^\n", "\n", - "An alternative style for visualizing the same information is offered by the :func:`pointplot` function. This function also encodes the value of the estimate with height on the other axis, but rather than show a full bar it just plots the point estimate and confidence interval. Additionally, pointplot connects points from the same ``hue`` category. This makes it easy to see how the main relationship is changing as a function of a second variable, because your eyes are quite good at picking up on differences of slopes:" + "An alternative style for visualizing the same information is offered by the :func:`pointplot` function. This function also encodes the value of the estimate with height on the other axis, but rather than show a full bar it just plots the point estimate and confidence interval. Additionally, :func:`pointplot` connects points from the same ``hue`` category. This makes it easy to see how the main relationship is changing as a function of a second variable, because your eyes are quite good at picking up on differences of slopes:" ] }, { From 3fd2c7e6d24fb560ebd1d658d4c7fcdb65491533 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Tue, 5 Jun 2018 15:21:59 -0400 Subject: [PATCH 0688/1738] Update IPython to Jupyter --- doc/tutorial/color_palettes.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index 93eed777b4..eca38cde19 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -198,7 +198,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "To help you choose palettes from the Color Brewer library, there is the :func:`choose_colorbrewer_palette` function. This function, which must be used in an IPython notebook, will launch an interactive widget that lets you browse the various options and tweak their parameters.\n", + "To help you choose palettes from the Color Brewer library, there is the :func:`choose_colorbrewer_palette` function. This function, which must be used in a Jupyter notebook, will launch an interactive widget that lets you browse the various options and tweak their parameters.\n", "\n", "Of course, you might just want to use a set of colors you particularly like together. Because :func:`color_palette` accepts a list of colors, this is easy to do." ] From 4a297a1bf893c1d82787d08c057db11b048c2dd2 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Tue, 5 Jun 2018 15:22:36 -0400 Subject: [PATCH 0689/1738] Include lvplot --- doc/tutorial/categorical.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial/categorical.ipynb b/doc/tutorial/categorical.ipynb index 1d4955d844..3bd5c0cc37 100644 --- a/doc/tutorial/categorical.ipynb +++ b/doc/tutorial/categorical.ipynb @@ -22,7 +22,7 @@ "source": [ "We :ref:`previously ` learned how to use scatterplots and regression model fits to visualize the relationship between two variables and how it changes across levels of additional categorical variables. However, what if one of the main variables you are interested in is categorical? In this case, the scatterplot and regression model approach won't work. There are several options, however, for visualizing such a relationship, which we will discuss in this tutorial.\n", "\n", - "It's useful to divide seaborn's categorical plots into three groups: those that show each observation at each level of the categorical variable, those that show an abstract representation of each *distribution* of observations, and those that apply a statistical estimation to show a measure of central tendency and confidence interval. The first includes the functions :func:`swarmplot` and :func:`stripplot`, the second includes :func:`boxplot` and :func:`violinplot`, and the third includes :func:`barplot` and :func:`pointplot`. These functions all share a basic API for how they accept data, although each has specific parameters that control the particulars of the visualization that is applied to that data.\n", + "It's useful to divide seaborn's categorical plots into three groups: those that show each observation at each level of the categorical variable, those that show an abstract representation of each *distribution* of observations, and those that apply a statistical estimation to show a measure of central tendency and confidence interval. The first includes the functions :func:`swarmplot` and :func:`stripplot`, the second includes :func:`boxplot`, :func:`violinplot` and :func:`lvplot`, and the third includes :func:`barplot` and :func:`pointplot`. These functions all share a basic API for how they accept data, although each has specific parameters that control the particulars of the visualization that is applied to that data.\n", "\n", "Much like the relationship between :func:`regplot` and :func:`lmplot`, in seaborn there are both relatively low-level and relatively high-level approaches for making categorical plots. The functions named above are all low-level in that they plot onto a specific matplotlib axes. There is also the higher-level :func:`factorplot`, which combines these functions with a :class:`FacetGrid` to apply a categorical plot across a grid of figure panels.\n", "\n", From 52f658215cfa0eaeb8f8043445bdff645c629345 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Tue, 5 Jun 2018 15:23:06 -0400 Subject: [PATCH 0690/1738] Avoid duplicate mention of swarmplot --- doc/tutorial/categorical.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial/categorical.ipynb b/doc/tutorial/categorical.ipynb index 3bd5c0cc37..f4a557b7f8 100644 --- a/doc/tutorial/categorical.ipynb +++ b/doc/tutorial/categorical.ipynb @@ -293,7 +293,7 @@ "cell_type": "raw", "metadata": {}, "source": [ - "It can also be useful to combine :func:`swarmplot` or :func:`swarmplot` with :func:`violinplot` or :func:`boxplot` to show each observation along with a summary of the distribution:" + "It can also be useful to combine :func:`swarmplot` or :func:`striplot` with :func:`violinplot` or :func:`boxplot` to show each observation along with a summary of the distribution:" ] }, { From 85f5bc114993a1fa74f1d8cd3dc98d9a6455d835 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Tue, 5 Jun 2018 15:24:10 -0400 Subject: [PATCH 0691/1738] Merge cells for consistency with the rest of this tutorial --- doc/tutorial/aesthetics.ipynb | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/doc/tutorial/aesthetics.ipynb b/doc/tutorial/aesthetics.ipynb index 9a830915df..92132653e9 100644 --- a/doc/tutorial/aesthetics.ipynb +++ b/doc/tutorial/aesthetics.ipynb @@ -141,15 +141,7 @@ "metadata": {}, "outputs": [], "source": [ - "sns.set_style(\"dark\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ + "sns.set_style(\"dark\")\n", "sinplot()" ] }, @@ -159,15 +151,7 @@ "metadata": {}, "outputs": [], "source": [ - "sns.set_style(\"white\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ + "sns.set_style(\"white\")\n", "sinplot()" ] }, From 11f62f2d34440f0d249539c2fd03c7740c424ccf Mon Sep 17 00:00:00 2001 From: joelostblom Date: Tue, 5 Jun 2018 15:27:28 -0400 Subject: [PATCH 0692/1738] Remove mention of that this is impossible in mpl Maybe I am misunderstanding, but why does it not count that you can do it via ax.spine? --- doc/tutorial/aesthetics.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial/aesthetics.ipynb b/doc/tutorial/aesthetics.ipynb index 92132653e9..dd8b7cb593 100644 --- a/doc/tutorial/aesthetics.ipynb +++ b/doc/tutorial/aesthetics.ipynb @@ -181,7 +181,7 @@ "Removing axes spines\n", "--------------------\n", "\n", - "Both the ``white`` and ``ticks`` styles can benefit from removing the top and right axes spines, which are not needed. It's impossible to do this through the matplotlib parameters, but you can call the seaborn function :func:`despine` to remove them:" + "Both the ``white`` and ``ticks`` styles can benefit from removing the top and right axes spines, which are not needed. The seaborn function :func:`despine` can be called to remove them:" ] }, { From 117958f7a155af2358b8595a4b29bd7a9a9a722c Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 13 Jun 2018 17:57:52 -0400 Subject: [PATCH 0693/1738] Fix FacetGrid bug with single facet level Fixes #890 --- doc/releases/v0.9.0.txt | 2 ++ seaborn/axisgrid.py | 20 ++++++++++---------- seaborn/tests/test_axisgrid.py | 3 +++ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index f34f0187e6..44dcc86ce1 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -10,6 +10,8 @@ v0.9.0 (Unreleased) - Fixed :func:`jointplot`/:class:`JointGrid` so that they now accept list inputs. +- Fixed a bug in :class:`FacetGrid` when using a single row/column level or using ``col_wrap=1``. + - Changed :func:`rugplot` to plot a matplotlib ``LineCollection`` instead of many ``Line2D`` objects, providing a big speedup for large arrays. - Changed the install infrastructure to explicitly declare dependencies in a way that ``pip`` is aware of. This means that ``pip install seaborn`` will now work in an empty environment. Additionally, the dependencies are specified with strict minimal versions. diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index ada3a5330a..4a09a93095 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -644,22 +644,22 @@ def facet_data(self): data = self.data # Construct masks for the row variable - if self._nrow == 1 or self._col_wrap is not None: - row_masks = [np.repeat(True, len(self.data))] - else: + if self.row_names: row_masks = [data[self._row_var] == n for n in self.row_names] + else: + row_masks = [np.repeat(True, len(self.data))] # Construct masks for the column variable - if self._ncol == 1: - col_masks = [np.repeat(True, len(self.data))] - else: + if self.col_names: col_masks = [data[self._col_var] == n for n in self.col_names] + else: + col_masks = [np.repeat(True, len(self.data))] # Construct masks for the hue variable - if len(self._colors) == 1: - hue_masks = [np.repeat(True, len(self.data))] - else: + if self.hue_names: hue_masks = [data[self._hue_var] == n for n in self.hue_names] + else: + hue_masks = [np.repeat(True, len(self.data))] # Here is the main generator loop for (i, row), (j, col), (k, hue) in product(enumerate(row_masks), @@ -2238,7 +2238,7 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, if color is None: color = color_palette()[0] color_rgb = mpl.colors.colorConverter.to_rgb(color) - colors = [utils.set_hls_values(color_rgb, l=l) + colors = [utils.set_hls_values(color_rgb, l=l) # noqa for l in np.linspace(1, 0, 12)] cmap = blend_palette(colors, as_cmap=True) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index eb1e90877d..edc4e590c0 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -113,6 +113,9 @@ def test_col_wrap(self): g_missing_wrap = ag.FacetGrid(df, col="d", col_wrap=4) nt.assert_equal(g_missing_wrap.axes.shape, (9,)) + g = ag.FacetGrid(self.df, col="d", col_wrap=1) + assert len(list(g.facet_data())) == len(self.df.d.unique()) + def test_normal_axes(self): null = np.empty(0, object).flat From f126f0eb84a68595597efe38ebcac3ef1e8ace95 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 13 Jun 2018 18:14:43 -0400 Subject: [PATCH 0694/1738] Accept list inputs in regplot Closes #28 --- doc/releases/v0.9.0.txt | 2 +- seaborn/regression.py | 2 ++ seaborn/tests/test_regression.py | 10 ++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index f34f0187e6..59d4e5272c 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -8,7 +8,7 @@ v0.9.0 (Unreleased) - Final removal of the previously-deprecated ``coefplot`` and ``interactplot`` functions. -- Fixed :func:`jointplot`/:class:`JointGrid` so that they now accept list inputs. +- Fixed :func:`jointplot`/:class:`JointGrid` and :func:`regplot` so that they now accept list inputs. - Changed :func:`rugplot` to plot a matplotlib ``LineCollection`` instead of many ``Line2D`` objects, providing a big speedup for large arrays. diff --git a/seaborn/regression.py b/seaborn/regression.py index 14bb411819..d26d2632dc 100644 --- a/seaborn/regression.py +++ b/seaborn/regression.py @@ -45,6 +45,8 @@ def establish_variables(self, data, **kws): for var, val in kws.items(): if isinstance(val, string_types): setattr(self, var, data[val]) + elif isinstance(val, list): + setattr(self, var, np.asarray(val)) else: setattr(self, var, val) diff --git a/seaborn/tests/test_regression.py b/seaborn/tests/test_regression.py index 2e114cca6d..680f937534 100644 --- a/seaborn/tests/test_regression.py +++ b/seaborn/tests/test_regression.py @@ -61,6 +61,16 @@ def test_establish_variables_from_array(self): npt.assert_array_equal(p.y, self.df.y) nt.assert_is(p.data, None) + def test_establish_variables_from_lists(self): + + p = lm._LinearPlotter() + p.establish_variables(None, + x=self.df.x.values.tolist(), + y=self.df.y.values.tolist()) + npt.assert_array_equal(p.x, self.df.x) + npt.assert_array_equal(p.y, self.df.y) + nt.assert_is(p.data, None) + def test_establish_variables_from_mix(self): p = lm._LinearPlotter() From 0ff2c1faf3dfb554aa30e7e82ae02b5eef0f5c25 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 16 Jun 2018 12:51:34 -0400 Subject: [PATCH 0695/1738] Allow control over the error representation in lineplot --- seaborn/basic.py | 45 +++++++++++++++++++++++-------------- seaborn/tests/test_basic.py | 26 ++++++++++++++++++--- 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index e5ea9c83c5..ffc148db86 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -575,7 +575,7 @@ def __init__(self, sizes=None, size_order=None, size_limits=None, dashes=None, markers=None, style_order=None, units=None, estimator=None, ci=None, n_boot=None, - sort=True, errstyle=None, legend=None): + sort=True, err_style=None, err_kws=None, legend=None): plot_data = self.establish_variables( x, y, hue, size, style, units, data @@ -589,12 +589,13 @@ def __init__(self, self.parse_size(plot_data["size"], sizes, size_order, size_limits) self.parse_style(plot_data["style"], markers, dashes, style_order) - self.sort = sort + self.units = units self.estimator = estimator self.ci = ci self.n_boot = n_boot - self.errstyle = errstyle - self.units = units + self.sort = sort + self.err_style = err_style + self.err_kws = {} if err_kws is None else err_kws self.legend = legend @@ -668,6 +669,16 @@ def plot(self, ax, kws): scout.remove() + # Set default error kwargs + err_kws = self.err_kws.copy() + if self.err_style == "band": + err_kws.setdefault("alpha", .2) + elif self.err_style == "bars": + pass + elif self.err_style is not None: + err = "`err_style` must be 'band' or 'bars', not {}" + raise ValueError(err.format(self.err_style)) + # Loop over the semantic subsets and draw a line for each for semantics, data in self.subset_data(): @@ -707,21 +718,21 @@ def plot(self, ax, kws): ax.plot(x[rows], y[rows], **kws) # --- Draw the confidence intervals - # TODO we want some way to get kwargs to the error plotters if y_ci is not None: low, high = np.asarray(y_ci["low"]), np.asarray(y_ci["high"]) - if self.errstyle == "band": + if self.err_style == "band": - ax.fill_between(x, low, high, color=line_color, alpha=.2) + ax.fill_between(x, low, high, color=line_color, **err_kws) - elif self.errstyle == "bars": + elif self.err_style == "bars": y_err = ci_to_errsize((low, high), y) ebars = ax.errorbar(x, y, y_err, linestyle="", - color=line_color, alpha=line_alpha) + color=line_color, alpha=line_alpha, + **err_kws) # Set the capstyle properly on the error bars for obj in ebars.get_children(): @@ -731,10 +742,6 @@ def plot(self, ax, kws): # Does not exist on mpl < 2.2 pass - else: - err = "`errstyle` must by 'band' or 'bars', not {}" - raise ValueError(err.format(self.errstyle)) - # Finalize the axes details self.label_axes(ax) if self.legend: @@ -966,7 +973,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, sizes=None, size_order=None, size_limits=None, dashes=True, markers=None, style_order=None, units=None, estimator="mean", ci=95, n_boot=1000, - sort=True, errstyle="band", + sort=True, err_style="band", err_kws=None, legend="brief", ax=None, **kwargs): p = _LinePlotter( @@ -975,7 +982,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, sizes=sizes, size_order=size_order, size_limits=size_limits, dashes=dashes, markers=markers, style_order=style_order, units=units, estimator=estimator, ci=ci, n_boot=n_boot, - sort=sort, errstyle=errstyle, legend=legend, + sort=sort, err_style=err_style, err_kws=err_kws, legend=legend, ) if ax is None: @@ -1033,9 +1040,13 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, sort : boolean, optional If True, the data will be sorted by the x and y variables, otherwise lines will connect points in the order they appear in the dataset. - errstyle : "band" or "bars", optional + err_style : "band" or "bars", optional Whether to draw the confidence intervals with translucent error bands or discrete error bars. + err_band : dict of keyword arguments + Additional paramters to control the aesthetics of the error bars. The + kwargs are passed either to ``ax.fill_between`` or ``ax.errorbar``, + depending on the ``err_style``. {legend} {ax_in} kwargs : key, value mappings @@ -1105,7 +1116,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, :context: close-figs >>> ax = sns.lineplot(x="timepoint", y="signal", hue="event", - ... errstyle="bars", ci=68, data=fmri) + ... err_style="bars", ci=68, data=fmri) Show experimental replicates instead of aggregating: diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 8da1b0e0ec..32825b331f 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -988,7 +988,7 @@ def test_plot(self, long_df, repeated_df): assert line.get_marker() == p.markers[style] p = basic._LinePlotter(x="x", y="y", data=long_df, - estimator="mean", errstyle="band", ci="sd", + estimator="mean", err_style="band", ci="sd", sort=True) ax.clear() @@ -1000,7 +1000,7 @@ def test_plot(self, long_df, repeated_df): assert len(ax.collections) == 1 p = basic._LinePlotter(x="x", y="y", hue="a", data=long_df, - estimator="mean", errstyle="band", ci="sd") + estimator="mean", err_style="band", ci="sd") ax.clear() p.plot(ax, {}) @@ -1009,7 +1009,7 @@ def test_plot(self, long_df, repeated_df): assert isinstance(c, mpl.collections.PolyCollection) p = basic._LinePlotter(x="x", y="y", hue="a", data=long_df, - estimator="mean", errstyle="bars", ci="sd") + estimator="mean", err_style="bars", ci="sd") ax.clear() p.plot(ax, {}) @@ -1039,6 +1039,26 @@ def test_plot(self, long_df, repeated_df): with pytest.raises(ValueError): p.plot(ax, {}) + p = basic._LinePlotter(x="x", y="y", hue="a", data=long_df, + err_style="band", err_kws={"alpha": .5}) + + ax.clear() + p.plot(ax, {}) + for band in ax.collections: + assert band.get_alpha() == .5 + + p = basic._LinePlotter(x="x", y="y", hue="a", data=long_df, + err_style="bars", err_kws={"elinewidth": 2}) + + ax.clear() + p.plot(ax, {}) + for lines in ax.collections: + assert lines.get_linestyles() == 2 + + p.err_style = "invalid" + with pytest.raises(ValueError): + p.plot(ax, {}) + def test_axis_labels(self, long_df): f, (ax1, ax2) = plt.subplots(1, 2, sharey=True) From b3f89ee8e7ad411b45e218ff42e64ce8ae2010e2 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 13 Jun 2018 20:21:10 -0400 Subject: [PATCH 0696/1738] Fix test that fails when using matplotlib defaults --- seaborn/tests/test_regression.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/seaborn/tests/test_regression.py b/seaborn/tests/test_regression.py index 680f937534..ba644121f4 100644 --- a/seaborn/tests/test_regression.py +++ b/seaborn/tests/test_regression.py @@ -532,9 +532,10 @@ def test_lmplot_marker_linewidths(self): g = lm.lmplot("x", "y", data=self.df, hue="h", fit_reg=False, markers=["o", "+"]) c = g.axes[0, 0].collections - nt.assert_equal(c[0].get_linewidths()[0], 0) - rclw = mpl.rcParams["lines.linewidth"] - nt.assert_equal(c[1].get_linewidths()[0], rclw) + nt.assert_equal(c[0].get_linewidths()[0], + mpl.rcParams["patch.linewidth"]) + nt.assert_equal(c[1].get_linewidths()[0], + mpl.rcParams["lines.linewidth"]) def test_lmplot_facets(self): From 0b7f68fd1f7dc0a428d0913775c9f7a97f2825e1 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 13 Jun 2018 20:21:32 -0400 Subject: [PATCH 0697/1738] Update calls to set axis limits to older autolim default Fixes #1381 --- seaborn/axisgrid.py | 4 ++-- seaborn/categorical.py | 8 ++++---- seaborn/regression.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 4a09a93095..5522e25cd3 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1708,9 +1708,9 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, self.y = y_array if xlim is not None: - ax_joint.set_xlim(xlim) + ax_joint.set_xlim(xlim, auto=None) if ylim is not None: - ax_joint.set_ylim(ylim) + ax_joint.set_ylim(ylim, auto=None) # Make the grid look nice utils.despine(f) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index b2b1465355..ee338bb95a 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -403,10 +403,10 @@ def annotate_axes(self, ax): if self.orient == "v": ax.xaxis.grid(False) - ax.set_xlim(-.5, len(self.plot_data) - .5) + ax.set_xlim(-.5, len(self.plot_data) - .5, auto=None) else: ax.yaxis.grid(False) - ax.set_ylim(-.5, len(self.plot_data) - .5) + ax.set_ylim(-.5, len(self.plot_data) - .5, auto=None) if self.hue_names is not None: leg = ax.legend(loc="best") @@ -1368,9 +1368,9 @@ def draw_swarmplot(self, ax, kws): # Set the categorical axes limits here for the swarm math if self.orient == "v": - ax.set_xlim(-.5, len(self.plot_data) - .5) + ax.set_xlim(-.5, len(self.plot_data) - .5, auto=None) else: - ax.set_ylim(-.5, len(self.plot_data) - .5) + ax.set_ylim(-.5, len(self.plot_data) - .5, auto=None) # Plot each swarm for i, group_data in enumerate(self.plot_data): diff --git a/seaborn/regression.py b/seaborn/regression.py index d26d2632dc..8659612b8b 100644 --- a/seaborn/regression.py +++ b/seaborn/regression.py @@ -396,7 +396,7 @@ def lineplot(self, ax, kws): ax.plot(grid, yhat, **kws) if err_bands is not None: ax.fill_between(grid, *err_bands, facecolor=fill_color, alpha=.15) - ax.set_xlim(*xlim) + ax.set_xlim(*xlim, auto=None) _regression_docs = dict( From 59464b456dd48a0fd883e4a877431d02cc0c4fc2 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 13 Jun 2018 20:56:18 -0400 Subject: [PATCH 0698/1738] Skip test that fails inconsistently across states --- seaborn/tests/test_regression.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/seaborn/tests/test_regression.py b/seaborn/tests/test_regression.py index ba644121f4..be7ed322c9 100644 --- a/seaborn/tests/test_regression.py +++ b/seaborn/tests/test_regression.py @@ -532,8 +532,6 @@ def test_lmplot_marker_linewidths(self): g = lm.lmplot("x", "y", data=self.df, hue="h", fit_reg=False, markers=["o", "+"]) c = g.axes[0, 0].collections - nt.assert_equal(c[0].get_linewidths()[0], - mpl.rcParams["patch.linewidth"]) nt.assert_equal(c[1].get_linewidths()[0], mpl.rcParams["lines.linewidth"]) From fd806e8d1aeb18ff503400bc0c80b0703236bb01 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 13 Jun 2018 20:59:08 -0400 Subject: [PATCH 0699/1738] Invert matrix y axis before forcing a draw Closes #1333 --- seaborn/matrix.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 0dd45663aa..74bd86ee8d 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -286,6 +286,9 @@ def plot(self, ax, cax, kws): # Set the axis limits ax.set(xlim=(0, self.data.shape[1]), ylim=(0, self.data.shape[0])) + # Invert the y axis to show the plot in matrix form + ax.invert_yaxis() + # Possibly add a colorbar if self.cbar: cb = ax.figure.colorbar(mesh, cax, ax, **self.cbar_kws) @@ -324,9 +327,6 @@ def plot(self, ax, cax, kws): if self.annot: self._annotate_heatmap(ax, mesh) - # Invert the y axis to show the plot in matrix form - ax.invert_yaxis() - def heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, annot=None, fmt=".2g", annot_kws=None, From 1aea0ff64e1c636f802a9d473ce55fb083079480 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 14 Jun 2018 11:29:46 -0400 Subject: [PATCH 0700/1738] PEP8 --- seaborn/matrix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 74bd86ee8d..32f53ae970 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -238,8 +238,8 @@ def _annotate_heatmap(self, ax, mesh): mesh.get_array(), mesh.get_facecolors(), self.annot_data.flat): if m is not np.ma.masked: - l = relative_luminance(color) - text_color = ".15" if l > .408 else "w" + lum = relative_luminance(color) + text_color = ".15" if lum > .408 else "w" annotation = ("{:" + self.fmt + "}").format(val) text_kwargs = dict(color=text_color, ha="center", va="center") text_kwargs.update(self.annot_kws) From 388147e7314fb23c09e1206707db9a0bc2228ebf Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 17 Jun 2018 10:35:34 -0400 Subject: [PATCH 0701/1738] Ignore new pytest cache directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ec2677f315..2ac96fb5b7 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ seaborn.egg-info/ .coverage cover/ .idea +.pytest_cache/ From d4db015edad8a5daaca65694831066e86f4840e0 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 17 Jun 2018 10:38:10 -0400 Subject: [PATCH 0702/1738] PEP8 --- seaborn/categorical.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index ee338bb95a..3a595b8b5e 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -271,7 +271,7 @@ def establish_colors(self, color, palette, saturation): if n_colors <= len(current_palette): colors = color_palette(n_colors=n_colors) else: - colors = husl_palette(n_colors, l=.7) + colors = husl_palette(n_colors, l=.7) # noqa elif palette is None: # When passing a specific color, the interpretation depends @@ -308,8 +308,8 @@ def establish_colors(self, color, palette, saturation): # Determine the gray color to use for the lines framing the plot light_vals = [colorsys.rgb_to_hls(*c)[1] for c in rgb_colors] - l = min(light_vals) * .6 - gray = mpl.colors.rgb2hex((l, l, l)) + lum = min(light_vals) * .6 + gray = mpl.colors.rgb2hex((lum, lum, lum)) # Assign object attributes self.colors = rgb_colors From c01fb9d9fe90ad9fb8559f2d0b646dfdc5e06cf9 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 17 Jun 2018 10:38:38 -0400 Subject: [PATCH 0703/1738] Fix function reference Fixes #1437 --- seaborn/categorical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 3a595b8b5e..06642e04e7 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -2344,7 +2344,7 @@ def boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> ax = sns.boxplot(x="day", y="total_bill", data=tips) >>> ax = sns.swarmplot(x="day", y="total_bill", data=tips, color=".25") - Use :func:`factorplot` to combine a :func:`boxplot` and a + Use :func:`factorplot` to combine a :func:`pointplot` and a :class:`FacetGrid`. This allows grouping within additional categorical variables. Using :func:`factorplot` is safer than using :class:`FacetGrid` directly, as it ensures synchronization of variable order across facets: From 77ffd988857770d50c64d44e73763fe6d8bab4e3 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 17 Jun 2018 10:46:32 -0400 Subject: [PATCH 0704/1738] Add more information to share_x,y API docs Fixes #864 --- seaborn/axisgrid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 5522e25cd3..53c31d6547 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -183,7 +183,7 @@ def _get_palette(self, data, hue, hue_order, palette): span multiple rows. Incompatible with a ``row`` facet.\ """), share_xy=dedent("""\ - share{x,y} : bool, optional + share{x,y} : bool, 'col', or 'row' optional If true, the facets will share y axes across columns and/or x axes across rows.\ """), From 64bde2b1fff1940be752426ecb4140128318b374 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 17 Jun 2018 11:05:14 -0400 Subject: [PATCH 0705/1738] Accept dict of additional subplot kws in PairGrid Closes #1032 --- seaborn/axisgrid.py | 13 +++++++++---- seaborn/tests/test_axisgrid.py | 7 +++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 53c31d6547..e836f95257 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1073,7 +1073,7 @@ class PairGrid(Grid): def __init__(self, data, hue=None, hue_order=None, palette=None, hue_kws=None, vars=None, x_vars=None, y_vars=None, diag_sharey=True, size=2.5, aspect=1, - despine=True, dropna=True): + despine=True, dropna=True, subplot_kws=None): """Initialize the plot figure and PairGrid object. Parameters @@ -1106,6 +1106,8 @@ def __init__(self, data, hue=None, hue_order=None, palette=None, Remove the top and right spines from the plots. dropna : boolean, optional Drop missing values from the data before plotting. + subplot_kws : dict, optional + Additional keyword arguments to pass to ``plt.subplots``. See Also -------- @@ -1232,10 +1234,13 @@ def __init__(self, data, hue=None, hue_order=None, palette=None, # Create the figure and the array of subplots figsize = len(x_vars) * size * aspect, len(y_vars) * size + all_subplot_kws = dict(sharex="col", sharey="row") + if subplot_kws is not None: + all_subplot_kws.update(subplot_kws) + fig, axes = plt.subplots(len(y_vars), len(x_vars), - figsize=figsize, - sharex="col", sharey="row", - squeeze=False) + figsize=figsize, squeeze=False, + subplot_kw=all_subplot_kws) self.fig = fig self.axes = axes diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index edc4e590c0..4431bedd55 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -1133,6 +1133,13 @@ def test_hue_order_missing_level(self): plt.close("all") + def test_subplot_kws(self): + + g = ag.PairGrid(self.df, despine=False, + subplot_kws=dict(projection="polar")) + for ax in g.axes.flat: + assert "PolarAxesSubplot" in str(type(ax)) + def test_nondefault_index(self): df = self.df.copy().set_index("b") From 27b3d8dae68157069db679b65fa0fc43c7184134 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 17 Jun 2018 11:21:07 -0400 Subject: [PATCH 0706/1738] Update release notes [ci skip] --- doc/releases/v0.9.0.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index da9f6d0849..b02d83efa6 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -14,6 +14,10 @@ v0.9.0 (Unreleased) - Changed :func:`rugplot` to plot a matplotlib ``LineCollection`` instead of many ``Line2D`` objects, providing a big speedup for large arrays. +- Added the ``subplot_kws`` parameter to :class:`PairGrid` for more flexibility. + +- Fixed functions that set axis limits so that they preserve auto-scaling state on matplotlib 2.0. + - Changed the install infrastructure to explicitly declare dependencies in a way that ``pip`` is aware of. This means that ``pip install seaborn`` will now work in an empty environment. Additionally, the dependencies are specified with strict minimal versions. - Updated the testing infrastructure to execute tests with `pytest ` (although many individual tests still use nose assertion). From 58a18528ac6b0804141e1464ea38a4fc087f3808 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 17 Jun 2018 11:24:21 -0400 Subject: [PATCH 0707/1738] Revert "Accept dict of additional subplot kws in PairGrid" This reverts commit 64bde2b1fff1940be752426ecb4140128318b374. --- seaborn/axisgrid.py | 13 ++++--------- seaborn/tests/test_axisgrid.py | 7 ------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index e836f95257..53c31d6547 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1073,7 +1073,7 @@ class PairGrid(Grid): def __init__(self, data, hue=None, hue_order=None, palette=None, hue_kws=None, vars=None, x_vars=None, y_vars=None, diag_sharey=True, size=2.5, aspect=1, - despine=True, dropna=True, subplot_kws=None): + despine=True, dropna=True): """Initialize the plot figure and PairGrid object. Parameters @@ -1106,8 +1106,6 @@ def __init__(self, data, hue=None, hue_order=None, palette=None, Remove the top and right spines from the plots. dropna : boolean, optional Drop missing values from the data before plotting. - subplot_kws : dict, optional - Additional keyword arguments to pass to ``plt.subplots``. See Also -------- @@ -1234,13 +1232,10 @@ def __init__(self, data, hue=None, hue_order=None, palette=None, # Create the figure and the array of subplots figsize = len(x_vars) * size * aspect, len(y_vars) * size - all_subplot_kws = dict(sharex="col", sharey="row") - if subplot_kws is not None: - all_subplot_kws.update(subplot_kws) - fig, axes = plt.subplots(len(y_vars), len(x_vars), - figsize=figsize, squeeze=False, - subplot_kw=all_subplot_kws) + figsize=figsize, + sharex="col", sharey="row", + squeeze=False) self.fig = fig self.axes = axes diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 4431bedd55..edc4e590c0 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -1133,13 +1133,6 @@ def test_hue_order_missing_level(self): plt.close("all") - def test_subplot_kws(self): - - g = ag.PairGrid(self.df, despine=False, - subplot_kws=dict(projection="polar")) - for ax in g.axes.flat: - assert "PolarAxesSubplot" in str(type(ax)) - def test_nondefault_index(self): df = self.df.copy().set_index("b") From 8ed670ec86939de221b8fc8d97eac1a5a3712e46 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 17 Jun 2018 12:33:54 -0400 Subject: [PATCH 0708/1738] Avoid error when backend has no canvas renderer Closes #1397 --- doc/releases/v0.9.0.txt | 2 ++ seaborn/axisgrid.py | 6 ++++-- seaborn/matrix.py | 6 ++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index b02d83efa6..30742d5778 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -18,6 +18,8 @@ v0.9.0 (Unreleased) - Fixed functions that set axis limits so that they preserve auto-scaling state on matplotlib 2.0. +- Avoided an error when using matplotlib backends that cannot render a canvas (e.g. PDF). + - Changed the install infrastructure to explicitly declare dependencies in a way that ``pip`` is aware of. This means that ``pip install seaborn`` will now work in an empty environment. Additionally, the dependencies are specified with strict minimal versions. - Updated the testing infrastructure to execute tests with `pytest ` (although many individual tests still use nose assertion). diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 53c31d6547..0cca5daecb 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -93,7 +93,8 @@ def add_legend(self, legend_data=None, title=None, label_order=None, figlegend._legend_title_box._text.set_font_properties(prop) # Draw the plot to set the bounding boxes correctly - self.fig.draw(self.fig.canvas.get_renderer()) + if hasattr(self.fig.canvas, "get_renderer"): + self.fig.draw(self.fig.canvas.get_renderer()) # Calculate and set the new width of the figure so the legend fits legend_width = figlegend.get_window_extent().width / self.fig.dpi @@ -101,7 +102,8 @@ def add_legend(self, legend_data=None, title=None, label_order=None, self.fig.set_figwidth(figure_width + legend_width) # Draw the plot again to get the new transformations - self.fig.draw(self.fig.canvas.get_renderer()) + if hasattr(self.fig.canvas, "get_renderer"): + self.fig.draw(self.fig.canvas.get_renderer()) # Now calculate how much space we need on the right side legend_width = figlegend.get_window_extent().width / self.fig.dpi diff --git a/seaborn/matrix.py b/seaborn/matrix.py index 32f53ae970..ccb8e9205e 100644 --- a/seaborn/matrix.py +++ b/seaborn/matrix.py @@ -314,7 +314,8 @@ def plot(self, ax, cax, kws): ytl = ax.set_yticklabels(yticklabels, rotation="vertical") # Possibly rotate them if they overlap - ax.figure.draw(ax.figure.canvas.get_renderer()) + if hasattr(ax.figure.canvas, "get_renderer"): + ax.figure.draw(ax.figure.canvas.get_renderer()) if axis_ticklabels_overlap(xtl): plt.setp(xtl, rotation="vertical") if axis_ticklabels_overlap(ytl): @@ -696,7 +697,8 @@ def plot(self, ax): ytl = ax.set_yticklabels(self.yticklabels, rotation='vertical') # Force a draw of the plot to avoid matplotlib window error - ax.figure.draw(ax.figure.canvas.get_renderer()) + if hasattr(ax.figure.canvas, "get_renderer"): + ax.figure.draw(ax.figure.canvas.get_renderer()) if len(ytl) > 0 and axis_ticklabels_overlap(ytl): plt.setp(ytl, rotation="horizontal") if len(xtl) > 0 and axis_ticklabels_overlap(xtl): From 9d74ee4675162e7a311fff562e547932aee2c3f3 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 21 Jun 2018 16:40:15 -0400 Subject: [PATCH 0709/1738] Remove special-cased stacked diagonal histograms in PairGrid --- doc/releases/v0.9.0.txt | 2 ++ seaborn/axisgrid.py | 42 +++++++++++------------------------------ 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index 30742d5778..775cc2707a 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -16,6 +16,8 @@ v0.9.0 (Unreleased) - Added the ``subplot_kws`` parameter to :class:`PairGrid` for more flexibility. +- Removed a special case in :class:`PairGrid` that defaulted to drawing stacked histograms on the diagonal axes. + - Fixed functions that set axis limits so that they preserve auto-scaling state on matplotlib 2.0. - Avoided an error when using matplotlib backends that cannot render a canvas (e.g. PDF). diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 0cca5daecb..b82ff9d0b1 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1351,42 +1351,22 @@ def map_diag(self, func, **kwargs): ax = self.diag_axes[i] hue_grouped = self.data[var].groupby(self.hue_vals) - # Special-case plt.hist with stacked bars - if func is plt.hist: - plt.sca(ax) + plt.sca(ax) - vals = [] - for label in self.hue_names: - # Attempt to get data for this level, allowing for empty - try: - vals.append(np.asarray(hue_grouped.get_group(label))) - except KeyError: - vals.append(np.array([])) + for k, label_k in enumerate(self.hue_names): - color = self.palette if fixed_color is None else fixed_color + # Attempt to get data for this level, allowing for empty + try: + data_k = hue_grouped.get_group(label_k) + except KeyError: + data_k = np.array([]) - if "histtype" in kwargs: - func(vals, color=color, **kwargs) + if fixed_color is None: + color = self.palette[k] else: - func(vals, color=color, histtype="barstacked", **kwargs) - - else: - plt.sca(ax) - - for k, label_k in enumerate(self.hue_names): - - # Attempt to get data for this level, allowing for empty - try: - data_k = hue_grouped.get_group(label_k) - except KeyError: - data_k = np.array([]) - - if fixed_color is None: - color = self.palette[k] - else: - color = fixed_color + color = fixed_color - func(data_k, label=label_k, color=color, **kwargs) + func(data_k, label=label_k, color=color, **kwargs) self._clean_axis(ax) From fdc181897335a6fd6d423862f7925b89f8df1949 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 21 Jun 2018 17:15:13 -0400 Subject: [PATCH 0710/1738] Change pairplot defaults to use scatterplot and kdeplot --- doc/releases/v0.9.0.txt | 2 ++ seaborn/axisgrid.py | 8 +++++--- seaborn/tests/test_axisgrid.py | 5 +++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index 775cc2707a..f46ac3a0aa 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -16,6 +16,8 @@ v0.9.0 (Unreleased) - Added the ``subplot_kws`` parameter to :class:`PairGrid` for more flexibility. +- Changed the default diagonal plots in :func:`pairplot` to use :func:`kdeplot` and changed the default off-diagonal plots to use :func:`scatterplot`. + - Removed a special case in :class:`PairGrid` that defaulted to drawing stacked histograms on the diagonal axes. - Fixed functions that set axis limits so that they preserve auto-scaling state on matplotlib 2.0. diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index b82ff9d0b1..00adb53625 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -13,6 +13,7 @@ from . import utils from .palettes import color_palette, blend_palette from .external.six import string_types +from .basic import scatterplot from .distributions import distplot, kdeplot, _freedman_diaconis_bins @@ -1862,7 +1863,7 @@ def savefig(self, *args, **kwargs): def pairplot(data, hue=None, hue_order=None, palette=None, vars=None, x_vars=None, y_vars=None, - kind="scatter", diag_kind="hist", markers=None, + kind="scatter", diag_kind="kde", markers=None, size=2.5, aspect=1, dropna=True, plot_kws=None, diag_kws=None, grid_kws=None): """Plot pairwise relationships in a dataset. @@ -2044,10 +2045,12 @@ def pairplot(data, hue=None, hue_order=None, palette=None, grid.hue_kws = {"marker": markers} # Maybe plot on the diagonal + diag_kws = diag_kws.copy() if grid.square_grid: if diag_kind == "hist": grid.map_diag(plt.hist, **diag_kws) elif diag_kind == "kde": + diag_kws.setdefault("shade", True) diag_kws["legend"] = False grid.map_diag(kdeplot, **diag_kws) @@ -2058,8 +2061,7 @@ def pairplot(data, hue=None, hue_order=None, palette=None, plotter = grid.map if kind == "scatter": - plot_kws.setdefault("edgecolor", "white") - plotter(plt.scatter, **plot_kws) + plotter(scatterplot, **plot_kws) elif kind == "reg": from .regression import regplot # Avoid circular import plotter(regplot, **plot_kws) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index edc4e590c0..1d630761ef 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -1170,7 +1170,8 @@ def test_pairplot(self): g = ag.pairplot(self.df) for ax in g.diag_axes: - nt.assert_equal(len(ax.patches), 10) + assert len(ax.lines) == 1 + assert len(ax.collections) == 1 for i, j in zip(*np.triu_indices_from(g.axes, 1)): ax = g.axes[i, j] @@ -1196,7 +1197,7 @@ def test_pairplot(self): def test_pairplot_reg(self): vars = ["x", "y", "z"] - g = ag.pairplot(self.df, kind="reg") + g = ag.pairplot(self.df, diag_kind="hist", kind="reg") for ax in g.diag_axes: nt.assert_equal(len(ax.patches), 10) From 892d9406d4f4b92c6bff8488849f21f614575c69 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 21 Jun 2018 17:45:43 -0400 Subject: [PATCH 0711/1738] Remove old mpl 1.1 compat that used private method --- seaborn/axisgrid.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 00adb53625..c4aa211ce5 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -86,12 +86,7 @@ def add_legend(self, legend_data=None, title=None, label_order=None, figlegend = self.fig.legend(handles, label_order, "center right", **kwargs) self._legend = figlegend - figlegend.set_title(title) - - # Set the title size a roundabout way to maintain - # compatability with matplotlib 1.1 - prop = mpl.font_manager.FontProperties(size=title_size) - figlegend._legend_title_box._text.set_font_properties(prop) + figlegend.set_title(title, prop={"size": title_size}) # Draw the plot to set the bounding boxes correctly if hasattr(self.fig.canvas, "get_renderer"): @@ -120,12 +115,7 @@ def add_legend(self, legend_data=None, title=None, label_order=None, # Draw a legend in the first axis ax = self.axes.flat[0] leg = ax.legend(handles, label_order, loc="best", **kwargs) - leg.set_title(title) - - # Set the title size a roundabout way to maintain - # compatability with matplotlib 1.1 - prop = mpl.font_manager.FontProperties(size=title_size) - leg._legend_title_box._text.set_font_properties(prop) + leg.set_title(title, prop={"size": title_size}) return self From b3981872425fcfb1ca462541920a5e622260c6e7 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 21 Jun 2018 17:50:49 -0400 Subject: [PATCH 0712/1738] Coerce diag data to array --- seaborn/axisgrid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index c4aa211ce5..03495cb4b9 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1348,7 +1348,7 @@ def map_diag(self, func, **kwargs): # Attempt to get data for this level, allowing for empty try: - data_k = hue_grouped.get_group(label_k) + data_k = np.asarray(hue_grouped.get_group(label_k)) except KeyError: data_k = np.array([]) From bfaa5a2286361182af5c404c47d6916fa06afedd Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 21 Jun 2018 18:04:15 -0400 Subject: [PATCH 0713/1738] Fix PairGrid method docs [ci skip] --- seaborn/axisgrid.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 03495cb4b9..ddbc5a9ca9 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1273,7 +1273,8 @@ def map(self, func, **kwargs): ---------- func : callable plotting function Must take x, y arrays as positional arguments and draw onto the - "currently active" matplotlib Axes. + "currently active" matplotlib Axes. Also needs to accept kwargs + called ``color`` and ``label``. """ kw_color = kwargs.pop("color", None) @@ -1315,10 +1316,9 @@ def map_diag(self, func, **kwargs): Parameters ---------- func : callable plotting function - Must take an x array as a positional arguments and draw onto the - "currently active" matplotlib Axes. There is a special case when - using a ``hue`` variable and ``plt.hist``; the histogram will be - plotted with stacked bars. + Must take an x array as a positional argument and draw onto the + "currently active" matplotlib Axes. Also needs to accept kwargs + called ``color`` and ``label``. """ # Add special diagonal axes for the univariate plot @@ -1372,7 +1372,8 @@ def map_lower(self, func, **kwargs): ---------- func : callable plotting function Must take x, y arrays as positional arguments and draw onto the - "currently active" matplotlib Axes. + "currently active" matplotlib Axes. Also needs to accept kwargs + called ``color`` and ``label``. """ kw_color = kwargs.pop("color", None) @@ -1417,7 +1418,8 @@ def map_upper(self, func, **kwargs): ---------- func : callable plotting function Must take x, y arrays as positional arguments and draw onto the - "currently active" matplotlib Axes. + "currently active" matplotlib Axes. Also needs to accept kwargs + called ``color`` and ``label``. """ kw_color = kwargs.pop("color", None) @@ -1463,7 +1465,8 @@ def map_offdiag(self, func, **kwargs): ---------- func : callable plotting function Must take x, y arrays as positional arguments and draw onto the - "currently active" matplotlib Axes. + "currently active" matplotlib Axes. Also needs to accept kwargs + called ``color`` and ``label``. """ From 7ee98df4a46beb7e142ecb7783b4e31c3cb1ad69 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 21 Jun 2018 19:08:23 -0400 Subject: [PATCH 0714/1738] Fix rugplot autoscaling --- seaborn/distributions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index ac9328929c..80fd9564de 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -712,6 +712,7 @@ def rugplot(a, height=.05, axis="x", ax=None, **kwargs): np.tile([0, height], len(a))]) line_segs = xy_pairs.reshape([len(a), 2, 2]) ax.add_collection(LineCollection(line_segs, transform=trans, **kwargs)) - ax.autoscale_view() + + ax.autoscale_view(scalex=not vertical, scaley=vertical) return ax From 0ef2c2e4561e77054bb0f321089cd6ed0d50c038 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 21 Jun 2018 19:05:03 -0400 Subject: [PATCH 0715/1738] Deprecate JointGrid statistical annotation --- examples/hexbin_marginals.py | 3 +-- seaborn/axisgrid.py | 24 +++++++++--------------- seaborn/tests/test_axisgrid.py | 2 +- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/examples/hexbin_marginals.py b/examples/hexbin_marginals.py index 4bb6f8b9a9..5dff7885cc 100644 --- a/examples/hexbin_marginals.py +++ b/examples/hexbin_marginals.py @@ -5,7 +5,6 @@ _thumb: .45, .4 """ import numpy as np -from scipy.stats import kendalltau import seaborn as sns sns.set(style="ticks") @@ -13,4 +12,4 @@ x = rs.gamma(2, size=1000) y = -.5 * x + rs.normal(size=1000) -sns.jointplot(x, y, kind="hex", stat_func=kendalltau, color="#4CB391") +sns.jointplot(x, y, kind="hex", color="#4CB391") diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index ddbc5a9ca9..d180b7fce9 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1771,6 +1771,8 @@ def plot_marginals(self, func, **kwargs): def annotate(self, func, template=None, stat=None, loc="best", **kwargs): """Annotate the plot with a statistic about the relationship. + *Deprecated and will be removed in a future version*. + Parameters ---------- func : callable @@ -1794,6 +1796,10 @@ def annotate(self, func, template=None, stat=None, loc="best", **kwargs): Returns `self`. """ + msg = ("JointGrid annotation is deprecated and will be removed " + "in a future release.") + warnings.warn(UserWarning(msg)) + default_template = "{stat} = {val:.2g}; p = {p:.2g}" # Call the function and determine the form of the return value(s) @@ -2066,7 +2072,7 @@ def pairplot(data, hue=None, hue_order=None, palette=None, return grid -def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, +def jointplot(x, y, data=None, kind="scatter", stat_func=None, color=None, size=6, ratio=5, space=.2, dropna=True, xlim=None, ylim=None, joint_kws=None, marginal_kws=None, annot_kws=None, **kwargs): @@ -2086,10 +2092,7 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, kind : { "scatter" | "reg" | "resid" | "kde" | "hex" }, optional Kind of plot to draw. stat_func : callable or None, optional - Function used to calculate a statistic about the relationship and - annotate the plot. Should map `x` and `y` either to a single value - or to a (value, p) tuple. Set to ``None`` if you don't want to - annotate the plot. + *Deprecated* color : matplotlib color, optional Color used for the plot elements. size : numeric, optional @@ -2156,15 +2159,6 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, >>> g = sns.jointplot("sepal_width", "petal_length", data=iris, ... kind="kde", space=0, color="g") - Use a different statistic for the annotation: - - .. plot:: - :context: close-figs - - >>> from scipy.stats import spearmanr - >>> g = sns.jointplot("size", "total_bill", data=tips, - ... stat_func=spearmanr, color="m") - Draw a scatterplot, then add a joint density estimate: .. plot:: @@ -2180,7 +2174,7 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=stats.pearsonr, :context: close-figs >>> x, y = np.random.randn(2, 300) - >>> g = (sns.jointplot(x, y, kind="hex", stat_func=None) + >>> g = (sns.jointplot(x, y, kind="hex") ... .set_axis_labels("x", "y")) Draw a smaller figure with more space devoted to the marginal plots: diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 1d630761ef..a01c20838f 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -1501,7 +1501,7 @@ def test_color(self): def test_annotation(self): - g = ag.jointplot("x", "y", self.data) + g = ag.jointplot("x", "y", self.data, stat_func=stats.pearsonr) nt.assert_equal(len(g.ax_joint.legend_.get_texts()), 1) g = ag.jointplot("x", "y", self.data, stat_func=None) From 3c0cb46ca58f29b54d9194f1bd2f4ffe389e9fdf Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 21 Jun 2018 20:16:57 -0400 Subject: [PATCH 0716/1738] Update release notes [ci skip] --- doc/releases/v0.9.0.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index f46ac3a0aa..6fbf311b11 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -8,6 +8,8 @@ v0.9.0 (Unreleased) - Final removal of the previously-deprecated ``coefplot`` and ``interactplot`` functions. +- Deprecated the statistical annotation component of :class:`JointGrid`. The method is still available but will be removed in a future version. + - Fixed :func:`jointplot`/:class:`JointGrid` and :func:`regplot` so that they now accept list inputs. - Fixed a bug in :class:`FacetGrid` when using a single row/column level or using ``col_wrap=1``. From 6b381dbf68e8508cb3c86c88aa79dd6a3f19e0db Mon Sep 17 00:00:00 2001 From: ajalexei Date: Fri, 22 Jun 2018 14:53:59 +0900 Subject: [PATCH 0717/1738] seaborn/distributions.py --- seaborn/distributions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 80fd9564de..97309a88c7 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -73,7 +73,7 @@ def distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, color : matplotlib color, optional Color to plot everything but the fitted curve in. vertical : bool, optional - If True, oberved values are on y-axis. + If True, observed values are on y-axis. norm_hist : bool, optional If True, the histogram height shows a density rather than a count. This is implied if a KDE or fitted density is plotted. From e31c0fe8f5277cc8f2b3d7b61baaaab361f72b16 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Fri, 22 Jun 2018 18:40:55 -0400 Subject: [PATCH 0718/1738] Silence linters about l= in palettes --- seaborn/palettes.py | 12 ++++++------ seaborn/tests/test_palettes.py | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/seaborn/palettes.py b/seaborn/palettes.py index 86ff656dcf..49754de980 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -194,7 +194,7 @@ def color_palette(palette=None, n_colors=None, desat=None): return palette -def hls_palette(n_colors=6, h=.01, l=.6, s=.65): +def hls_palette(n_colors=6, h=.01, l=.6, s=.65): # noqa """Get a set of evenly spaced colors in HLS hue space. h, l, and s should be between 0 and 1 @@ -262,7 +262,7 @@ def hls_palette(n_colors=6, h=.01, l=.6, s=.65): return _ColorPalette(palette) -def husl_palette(n_colors=6, h=.01, s=.9, l=.65): +def husl_palette(n_colors=6, h=.01, s=.9, l=.65): # noqa """Get a set of evenly spaced colors in HUSL hue space. h, s, and l should be between 0 and 1 @@ -327,7 +327,7 @@ def husl_palette(n_colors=6, h=.01, s=.9, l=.65): hues %= 1 hues *= 359 s *= 99 - l *= 99 + l *= 99 # noqa palette = [husl.husl_to_rgb(h_i, s, l) for h_i in hues] return _ColorPalette(palette) @@ -585,7 +585,7 @@ def light_palette(color, n_colors=6, reverse=False, as_cmap=False, """ color = _color_to_rgb(color, input) - light = set_hls_values(color, l=.95) + light = set_hls_values(color, l=.95) # noqa colors = [color, light] if reverse else [light, color] return blend_palette(colors, n_colors, as_cmap) @@ -617,8 +617,8 @@ def _flat_palette(color, n_colors=6, reverse=False, as_cmap=False, return blend_palette(colors, n_colors, as_cmap) -def diverging_palette(h_neg, h_pos, s=75, l=50, sep=10, n=6, center="light", - as_cmap=False): +def diverging_palette(h_neg, h_pos, s=75, l=50, sep=10, n=6, # noqa + center="light", as_cmap=False): """Make a diverging palette between two HUSL colors. If you are using the IPython notebook, you can also choose this palette diff --git a/seaborn/tests/test_palettes.py b/seaborn/tests/test_palettes.py index 10c52595d1..ea44b520b5 100644 --- a/seaborn/tests/test_palettes.py +++ b/seaborn/tests/test_palettes.py @@ -124,8 +124,8 @@ def test_hls_values(self): pal2 = pal2[3:] + pal2[:3] npt.assert_array_almost_equal(pal1, pal2) - pal_dark = palettes.hls_palette(5, l=.2) - pal_bright = palettes.hls_palette(5, l=.8) + pal_dark = palettes.hls_palette(5, l=.2) # noqa + pal_bright = palettes.hls_palette(5, l=.8) # noqa npt.assert_array_less(list(map(sum, pal_dark)), list(map(sum, pal_bright))) @@ -141,8 +141,8 @@ def test_husl_values(self): pal2 = pal2[3:] + pal2[:3] npt.assert_array_almost_equal(pal1, pal2) - pal_dark = palettes.husl_palette(5, l=.2) - pal_bright = palettes.husl_palette(5, l=.8) + pal_dark = palettes.husl_palette(5, l=.2) # noqa + pal_bright = palettes.husl_palette(5, l=.8) # noqa npt.assert_array_less(list(map(sum, pal_dark)), list(map(sum, pal_bright))) From 1208d51575d608946ba5f5aac0455d4e00af8b24 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Fri, 22 Jun 2018 18:55:39 -0400 Subject: [PATCH 0719/1738] Accept cubehelix params in a string in color_palette --- seaborn/palettes.py | 38 +++++++++++++++++++++++++++++++--- seaborn/tests/test_palettes.py | 12 +++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/seaborn/palettes.py b/seaborn/palettes.py index 49754de980..0da96f7f5f 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -163,16 +163,30 @@ def color_palette(palette=None, n_colors=None, desat=None): if n_colors is None: n_colors = 6 - if palette == "hls": + if palette in SEABORN_PALETTES: + # Named "seaborn variant" of old matplotlib default palette + palette = SEABORN_PALETTES[palette] + + elif palette == "hls": + # Evenly spaced colors in cylindrical RGB palette = hls_palette(n_colors) + elif palette == "husl": + # Evenly spaced colors in cylindrical Lab palette = husl_palette(n_colors) + elif palette.lower() == "jet": + # Paternalism raise ValueError("No.") - elif palette in SEABORN_PALETTES: - palette = SEABORN_PALETTES[palette] + + elif palette.startswith("cube:"): + # Cubehelix palette with params specified in string + args, kwargs = _parse_cube_args(palette) + palette = cubehelix_palette(n_colors, *args, **kwargs) + else: try: + # Perhaps a named matplotlib colormap? palette = mpl_palette(palette, n_colors) except ValueError: raise ValueError("%s is not a valid palette name" % palette) @@ -905,6 +919,24 @@ def cubehelix_palette(n_colors=6, start=0, rot=.4, gamma=1.0, hue=0.8, return _ColorPalette(pal) +def _parse_cube_args(argstr): + """Turn stringified cubehelix params into args/kwargs.""" + if argstr.startswith("cube:"): + argstr = argstr[5:] + + if not argstr: + return [], {} + + all_args = argstr.split(",") + + args = [float(a) for a in all_args if "=" not in a] + + kwargs = [a.split("=") for a in all_args if "=" in a] + kwargs = {k: float(v) for k, v in kwargs} + + return args, kwargs + + def set_color_codes(palette="deep"): """Change how matplotlib color shorthands are interpreted. diff --git a/seaborn/tests/test_palettes.py b/seaborn/tests/test_palettes.py index ea44b520b5..9c34f8d95f 100644 --- a/seaborn/tests/test_palettes.py +++ b/seaborn/tests/test_palettes.py @@ -254,6 +254,18 @@ def test_cubehelix_cmap(self): pal_reverse = cmap_rev(x[::-1]).tolist() nt.assert_list_equal(pal_forward, pal_reverse) + def test_cubehelix_code(self): + + pal1 = palettes.color_palette("cube:", 8) + pal2 = palettes.color_palette(palettes.cubehelix_palette(8)) + assert pal1 == pal2 + + pal1 = palettes.color_palette("cube:.5,-.25,hue=.5,light=.75", 8) + pal2 = palettes.color_palette( + palettes.cubehelix_palette(8, .5, -.25, hue=.5, light=.75) + ) + assert pal1 == pal2 + def test_xkcd_palette(self): names = list(xkcd_rgb.keys())[10:15] From 047a45955282b3518ea13e515942e50b8f07822b Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Fri, 22 Jun 2018 19:04:59 -0400 Subject: [PATCH 0720/1738] Do cubehelix math locally --- seaborn/palettes.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/seaborn/palettes.py b/seaborn/palettes.py index 0da96f7f5f..832bf3f1c2 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -900,7 +900,28 @@ def cubehelix_palette(n_colors=6, start=0, rot=.4, gamma=1.0, hue=0.8, >>> ax = sns.heatmap(x, cmap=cmap) """ - cdict = mpl._cm.cubehelix(gamma, start, rot, hue) + def get_color_function(p0, p1): + # Copied from matplotlib because it lives in private module + def color(x): + # Apply gamma factor to emphasise low or high intensity values + xg = x ** gamma + + # Calculate amplitude and angle of deviation from the black + # to white diagonal in the plane of constant + # perceived intensity. + a = hue * xg * (1 - xg) / 2 + + phi = 2 * np.pi * (start / 3 + rot * x) + + return xg + a * (p0 * np.cos(phi) + p1 * np.sin(phi)) + return color + + cdict = { + "red": get_color_function(-0.14861, 1.78277), + "green": get_color_function(-0.29227, -0.90649), + "blue": get_color_function(1.97294, 0.0), + } + cmap = mpl.colors.LinearSegmentedColormap("cubehelix", cdict) x = np.linspace(light, dark, n_colors) From 9153a392aae911985b7b4c0b6a626dea3fa19be8 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Fri, 22 Jun 2018 19:10:29 -0400 Subject: [PATCH 0721/1738] Strip whitespace around args --- seaborn/palettes.py | 4 ++-- seaborn/tests/test_palettes.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/seaborn/palettes.py b/seaborn/palettes.py index 832bf3f1c2..2fb668cdb7 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -950,10 +950,10 @@ def _parse_cube_args(argstr): all_args = argstr.split(",") - args = [float(a) for a in all_args if "=" not in a] + args = [float(a.strip(" ")) for a in all_args if "=" not in a] kwargs = [a.split("=") for a in all_args if "=" in a] - kwargs = {k: float(v) for k, v in kwargs} + kwargs = {k.strip(" "): float(v.strip(" ")) for k, v in kwargs} return args, kwargs diff --git a/seaborn/tests/test_palettes.py b/seaborn/tests/test_palettes.py index 9c34f8d95f..4a562dbd56 100644 --- a/seaborn/tests/test_palettes.py +++ b/seaborn/tests/test_palettes.py @@ -260,7 +260,7 @@ def test_cubehelix_code(self): pal2 = palettes.color_palette(palettes.cubehelix_palette(8)) assert pal1 == pal2 - pal1 = palettes.color_palette("cube:.5,-.25,hue=.5,light=.75", 8) + pal1 = palettes.color_palette("cube:.5, -.25,hue = .5,light=.75", 8) pal2 = palettes.color_palette( palettes.cubehelix_palette(8, .5, -.25, hue=.5, light=.75) ) From 699928c08f260fcb6a646a5a3f8d65e03db3751f Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 23 Jun 2018 11:17:30 -0400 Subject: [PATCH 0722/1738] Change cubehelix shorthand prefix to ch: and add example --- seaborn/palettes.py | 47 ++++++++++++++++++++++------------ seaborn/tests/test_palettes.py | 4 +-- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/seaborn/palettes.py b/seaborn/palettes.py index 2fb668cdb7..29d0f7e566 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -62,13 +62,14 @@ def color_palette(palette=None, n_colors=None, desat=None): deep, muted, bright, pastel, dark, colorblind Other options: - hls, husl, any named matplotlib palette, list of colors + name of matplotlib cmap, 'ch:', 'hls', 'husl', + or a list of colors in any format matplotlib accepts Calling this function with ``palette=None`` will return the current matplotlib color cycle. Matplotlib palettes can be specified as reversed palettes by appending - "_r" to the name or as dark palettes by appending "_d" to the name. + "_r" to the name or as "dark" palettes by appending "_d" to the name. (These options are mutually exclusive, but the resulting list of colors can also be reversed). @@ -105,40 +106,52 @@ def color_palette(palette=None, n_colors=None, desat=None): Examples -------- - Show one of the "seaborn palettes", which have the same basic order of hues - as the default matplotlib color cycle but more attractive colors. + Calling with no arguments returns the current default color cycle: .. plot:: :context: close-figs >>> import seaborn as sns; sns.set() + >>> sns.palplot(sns.color_palette()) + + Show one of the other "seaborn palettes", which have the same basic order + of hues as the default matplotlib color cycle but more attractive colors: + + .. plot:: + :context: close-figs + >>> sns.palplot(sns.color_palette("muted")) - Use discrete values from one of the built-in matplotlib colormaps. + Use discrete values from one of the built-in matplotlib colormaps: .. plot:: :context: close-figs >>> sns.palplot(sns.color_palette("RdBu", n_colors=7)) - Make a "dark" matplotlib sequential palette variant. (This can be good - when coloring multiple lines or points that correspond to an ordered - variable, where you don't want the lightest lines to be invisible). + Make a customized cubehelix color palette: .. plot:: :context: close-figs - >>> sns.palplot(sns.color_palette("Blues_d")) + >>> sns.palplot(sns.color_palette("ch:2.5,-.2,dark=.3")) - Use a categorical matplotlib palette, add some desaturation. (This can be - good when making plots with large patches, which look best with dimmer - colors). + Use a categorical matplotlib palette and add some desaturation: .. plot:: :context: close-figs >>> sns.palplot(sns.color_palette("Set1", n_colors=8, desat=.5)) + Make a "dark" matplotlib sequential palette variant. (This can be good + when coloring multiple lines or points that correspond to an ordered + variable, where you don't want the lightest lines to be invisible): + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.color_palette("Blues_d")) + Use as a context manager: .. plot:: @@ -179,9 +192,9 @@ def color_palette(palette=None, n_colors=None, desat=None): # Paternalism raise ValueError("No.") - elif palette.startswith("cube:"): + elif palette.startswith("ch:"): # Cubehelix palette with params specified in string - args, kwargs = _parse_cube_args(palette) + args, kwargs = _parse_cubehelix_args(palette) palette = cubehelix_palette(n_colors, *args, **kwargs) else: @@ -940,10 +953,10 @@ def color(x): return _ColorPalette(pal) -def _parse_cube_args(argstr): +def _parse_cubehelix_args(argstr): """Turn stringified cubehelix params into args/kwargs.""" - if argstr.startswith("cube:"): - argstr = argstr[5:] + if argstr.startswith("ch:"): + argstr = argstr[3:] if not argstr: return [], {} diff --git a/seaborn/tests/test_palettes.py b/seaborn/tests/test_palettes.py index 4a562dbd56..792b24cbb4 100644 --- a/seaborn/tests/test_palettes.py +++ b/seaborn/tests/test_palettes.py @@ -256,11 +256,11 @@ def test_cubehelix_cmap(self): def test_cubehelix_code(self): - pal1 = palettes.color_palette("cube:", 8) + pal1 = palettes.color_palette("ch:", 8) pal2 = palettes.color_palette(palettes.cubehelix_palette(8)) assert pal1 == pal2 - pal1 = palettes.color_palette("cube:.5, -.25,hue = .5,light=.75", 8) + pal1 = palettes.color_palette("ch:.5, -.25,hue = .5,light=.75", 8) pal2 = palettes.color_palette( palettes.cubehelix_palette(8, .5, -.25, hue=.5, light=.75) ) From ded457bc15ac878b16141c3760be0aba9c1f928e Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 23 Jun 2018 11:32:04 -0400 Subject: [PATCH 0723/1738] Parse stringified cubehelix parameterization in basic plots to get cmap --- doc/releases/v0.9.0.txt | 2 ++ seaborn/basic.py | 6 ++++-- seaborn/tests/test_basic.py | 5 +++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index 6fbf311b11..664f1752c2 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -18,6 +18,8 @@ v0.9.0 (Unreleased) - Added the ``subplot_kws`` parameter to :class:`PairGrid` for more flexibility. +- Enhanced :func:`color_palette` to accept a parameterized specification of a cubehelix palette in in a string, prefixed with ``"ch:"`` (e.g. ``"ch:-.1,.2,light=.7"``). This will be accepted by any seaborn function with a ``palette=`` parameter. + - Changed the default diagonal plots in :func:`pairplot` to use :func:`kdeplot` and changed the default off-diagonal plots to use :func:`scatterplot`. - Removed a special case in :class:`PairGrid` that defaulted to drawing stacked histograms on the diagonal axes. diff --git a/seaborn/basic.py b/seaborn/basic.py index ffc148db86..04784b01fd 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -14,7 +14,7 @@ from .utils import (categorical_order, get_color_cycle, ci_to_errsize, sort_df, remove_na) from .algorithms import bootstrap -from .palettes import color_palette +from .palettes import color_palette, cubehelix_palette, _parse_cubehelix_args __all__ = ["lineplot", "scatterplot"] @@ -22,7 +22,6 @@ class _BasicPlotter(object): - # We could use "line art glyphs" (e.g. "P") on mpl 2 if LooseVersion(mpl.__version__) >= "2.0": default_markers = ["o", "X", "s", "P", "D", "^", "v", "p"] else: @@ -239,6 +238,9 @@ def numeric_to_palette(self, data, order, palette, limits): cmap = mpl.cm.get_cmap(plt.rcParams["image.cmap"]) elif isinstance(palette, mpl.colors.Colormap): cmap = palette + elif str(palette).startswith("ch:"): + args, kwargs = _parse_cubehelix_args(palette) + cmap = cubehelix_palette(0, *args, as_cmap=True, **kwargs) else: try: cmap = mpl.cm.get_cmap(palette) diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 32825b331f..a297577880 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -462,6 +462,11 @@ def test_parse_hue_numeric(self, long_df): p.parse_hue(p.plot_data.hue, palette, None, None) assert p.cmap is palette + # Test cubehelix shorthand + palette = "ch:2,0,light=.2" + p.parse_hue(p.plot_data.hue, palette, None, None) + assert isinstance(p.cmap, mpl.colors.ListedColormap) + # Test default hue limits p.parse_hue(p.plot_data.hue, None, None, None) assert p.hue_limits == (p.plot_data.hue.min(), p.plot_data.hue.max()) From a8ab1dc16db9f3d503903418fc35a9655939474e Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 23 Jun 2018 18:21:54 -0400 Subject: [PATCH 0724/1738] Add 10-color versions of seaborn palettes --- seaborn/palettes.py | 40 +++++++++++++++++++++++----------- seaborn/tests/test_palettes.py | 12 +++++----- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/seaborn/palettes.py b/seaborn/palettes.py index 29d0f7e566..d9ad1f7cff 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -20,18 +20,30 @@ SEABORN_PALETTES = dict( - deep=["#4C72B0", "#55A868", "#C44E52", - "#8172B2", "#CCB974", "#64B5CD"], - muted=["#4878CF", "#6ACC65", "#D65F5F", - "#B47CC7", "#C4AD66", "#77BEDB"], - pastel=["#92C6FF", "#97F0AA", "#FF9F9A", - "#D0BBFF", "#FFFEA3", "#B0E0E6"], - bright=["#003FFF", "#03ED3A", "#E8000B", - "#8A2BE2", "#FFC400", "#00D7FF"], - dark=["#001C7F", "#017517", "#8C0900", - "#7600A1", "#B8860B", "#006374"], - colorblind=["#0072B2", "#009E73", "#D55E00", - "#CC79A7", "#F0E442", "#56B4E9"] + deep=["#4C72B0", "#DD8452", "#55A868", "#C44E52", "#8172B3", + "#937860", "#DA8BC3", "#8C8C8C", "#CCB974", "#64B5CD"], + deep6=["#4C72B0", "#55A868", "#C44E52", + "#8172B3", "#CCB974", "#64B5CD"], + muted=["#4878D0", "#EE854A", "#6ACC64", "#D65F5F", "#956CB4", + "#8C613C", "#DC7EC0", "#797979", "#D5BB67", "#82C6E2"], + muted6=["#4878D0", "#6ACC64", "#D65F5F", + "#956CB4", "#D5BB67", "#82C6E2"], + pastel=["#A1C9F4", "#FFB482", "#8DE5A1", "#FF9F9B", "#D0BBFF", + "#DEBB9B", "#FAB0E4", "#CFCFCF", "#FFFEA3", "#B9F2F0"], + pastel6=["#A1C9F4", "#8DE5A1", "#FF9F9B", + "#D0BBFF", "#FFFEA3", "#B9F2F0"], + bright=["#023EFF", "#FF6000", "#1AC938", "#E8000B", "#8B2BE2", + "#9F4800", "#F14CC1", "#A3A3A3", "#FFC400", "#00D7FF"], + bright6=["#023EFF", "#1AC938", "#E8000B", + "#8B2BE2", "#FFC400", "#00D7FF"], + dark=["#001C7F", "#B1400D", "#12711C", "#8C0800", "#591E71", + "#592F0D", "#A23582", "#3C3C3C", "#B8850A", "#006374"], + dark6=["#001C7F", "#12711C", "#8C0800", + "#591E71", "#B8850A", "#006374"], + colorblind=["#0173B2", "#DE8F05", "#029E73", "#D55E00", "#CC78BC", + "#CA9161", "#FBAFE4", "#949494", "#ECE133", "#56B4E9"], + colorblind6=["#0173B2", "#029E73", "#D55E00", + "#CC78BC", "#ECE133", "#56B4E9"] ) @@ -1015,7 +1027,9 @@ def set_color_codes(palette="deep"): if palette == "reset": colors = [(0., 0., 1.), (0., .5, 0.), (1., 0., 0.), (.75, .75, 0.), (.75, .75, 0.), (0., .75, .75), (0., 0., 0.)] - else: + elif palette in SEABORN_PALETTES: + if not palette.endswith("6"): + palette = palette + "6" colors = SEABORN_PALETTES[palette] + [(.1, .1, .1)] for code, color in zip("bgrmyck", colors): rgb = mpl.colors.colorConverter.to_rgb(color) diff --git a/seaborn/tests/test_palettes.py b/seaborn/tests/test_palettes.py index 792b24cbb4..061434eaaf 100644 --- a/seaborn/tests/test_palettes.py +++ b/seaborn/tests/test_palettes.py @@ -51,8 +51,10 @@ def test_seaborn_palettes(self): pals = "deep", "muted", "pastel", "bright", "dark", "colorblind" for name in pals: - pal_out = palettes.color_palette(name) - nt.assert_equal(len(pal_out), 6) + full = palettes.color_palette(name, 10).as_hex() + short = palettes.color_palette(name + "6", 6).as_hex() + b, _, g, r, m, _, _, _, y, c = full + assert [b, g, r, m, y, c] == list(short) def test_hls_palette(self): @@ -113,8 +115,8 @@ def test_palette_is_list_of_tuples(self): def test_palette_cycles(self): - deep = palettes.color_palette("deep") - double_deep = palettes.color_palette("deep", 12) + deep = palettes.color_palette("deep6") + double_deep = palettes.color_palette("deep6", 12) nt.assert_equal(double_deep, deep + deep) def test_hls_values(self): @@ -285,7 +287,7 @@ def test_crayon_palette(self): def test_color_codes(self): palettes.set_color_codes("deep") - colors = palettes.color_palette("deep") + [".1"] + colors = palettes.color_palette("deep6") + [".1"] for code, color in zip("bgrmyck", colors): rgb_want = mpl.colors.colorConverter.to_rgb(color) rgb_got = mpl.colors.colorConverter.to_rgb(code) From 11fca655c7a3f56a1f5f5d5087f01b73d02d001f Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 23 Jun 2018 18:34:33 -0400 Subject: [PATCH 0725/1738] Set all colors for named qualitative palettes --- seaborn/palettes.py | 27 ++++++++++++++++++--------- seaborn/rcmod.py | 3 +++ seaborn/tests/test_palettes.py | 6 +++--- seaborn/tests/test_rcmod.py | 19 ++++++++++++++++++- 4 files changed, 42 insertions(+), 13 deletions(-) diff --git a/seaborn/palettes.py b/seaborn/palettes.py index d9ad1f7cff..83ac126461 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -47,6 +47,18 @@ ) +MPL_QUAL_PALS = { + "tab10": 10, "tab20": 20, "tab20b": 20, "tab20c": 20, + "Set1": 9, "Set2": 8, "Set3": 12, + "Accent": 8, "Paired": 12, + "Pastel1": 9, "Pastel2": 8, "Dark2": 8, +} + + +QUAL_PALETTE_SIZES = MPL_QUAL_PALS.copy() +QUAL_PALETTE_SIZES.update({k: len(v) for k, v in SEABORN_PALETTES.items()}) + + class _ColorPalette(list): """Set the color palette in a with statement, otherwise be a list.""" def __enter__(self): @@ -118,7 +130,8 @@ def color_palette(palette=None, n_colors=None, desat=None): Examples -------- - Calling with no arguments returns the current default color cycle: + Calling with no arguments returns all colors from the current default + color cycle: .. plot:: :context: close-figs @@ -127,7 +140,8 @@ def color_palette(palette=None, n_colors=None, desat=None): >>> sns.palplot(sns.color_palette()) Show one of the other "seaborn palettes", which have the same basic order - of hues as the default matplotlib color cycle but more attractive colors: + of hues as the default matplotlib color cycle but more attractive colors. + Calling with the name of a palette will return 6 colors by default: .. plot:: :context: close-figs @@ -431,11 +445,6 @@ def mpl_palette(name, n_colors=6): >>> sns.palplot(sns.mpl_palette("GnBu_d")) """ - mpl_qual_pals = {"Accent": 8, "Dark2": 8, "Paired": 12, - "Pastel1": 9, "Pastel2": 8, - "Set1": 9, "Set2": 8, "Set3": 12, - "tab10": 10, "tab20": 20, "tab20b": 20, "tab20c": 20} - if name.endswith("_d"): pal = ["#333333"] pal.extend(color_palette(name.replace("_d", "_r"), 2)) @@ -444,8 +453,8 @@ def mpl_palette(name, n_colors=6): cmap = mpl.cm.get_cmap(name) if cmap is None: raise ValueError("{} is not a valid colormap".format(name)) - if name in mpl_qual_pals: - bins = np.linspace(0, 1, mpl_qual_pals[name])[:n_colors] + if name in MPL_QUAL_PALS: + bins = np.linspace(0, 1, MPL_QUAL_PALS[name])[:n_colors] else: bins = np.linspace(0, 1, n_colors + 2)[1:-1] palette = list(map(tuple, cmap(bins)[:, :3])) diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index 80ccd3d299..4af2f21397 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -2,6 +2,7 @@ from distutils.version import LooseVersion import functools import matplotlib as mpl +from .external.six import string_types from . import palettes, _orig_rc_params @@ -497,6 +498,8 @@ def set_palette(palette, n_colors=None, desat=None, color_codes=False): set_style : set the default parameters for figure style """ + if n_colors is None and isinstance(palette, string_types): + n_colors = palettes.QUAL_PALETTE_SIZES.get(palette, None) colors = palettes.color_palette(palette, n_colors, desat) if mpl_ge_150: from cycler import cycler diff --git a/seaborn/tests/test_palettes.py b/seaborn/tests/test_palettes.py index 061434eaaf..36e8a731e9 100644 --- a/seaborn/tests/test_palettes.py +++ b/seaborn/tests/test_palettes.py @@ -18,9 +18,9 @@ class TestColorPalettes(object): def test_current_palette(self): - pal = palettes.color_palette(["red", "blue", "green"], 3) - rcmod.set_palette(pal, 3) - nt.assert_equal(pal, utils.get_color_cycle()) + pal = palettes.color_palette(["red", "blue", "green"]) + rcmod.set_palette(pal) + assert pal == utils.get_color_cycle() rcmod.set() def test_palette_context(self): diff --git a/seaborn/tests/test_rcmod.py b/seaborn/tests/test_rcmod.py index abcc619df2..3564bcf56e 100644 --- a/seaborn/tests/test_rcmod.py +++ b/seaborn/tests/test_rcmod.py @@ -6,7 +6,7 @@ import nose.tools as nt import numpy.testing as npt -from .. import rcmod +from .. import rcmod, palettes, utils class RCParamTester(object): @@ -175,6 +175,23 @@ def func(): self.assert_rc_params(orig_params) +class TestPalette(object): + + def test_set_palette(self): + + rcmod.set_palette("deep") + assert utils.get_color_cycle() == palettes.color_palette("deep", 10) + + rcmod.set_palette("pastel6") + assert utils.get_color_cycle() == palettes.color_palette("pastel6", 6) + + rcmod.set_palette("dark", 4) + assert utils.get_color_cycle() == palettes.color_palette("dark", 4) + + rcmod.set_palette("Set2") + assert utils.get_color_cycle() == palettes.color_palette("Set2", 8) + + class TestFonts(object): def test_set_font(self): From b222a7f1e06e05ad128fcc67bc3fd39a946d5b39 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 23 Jun 2018 18:34:43 -0400 Subject: [PATCH 0726/1738] PEP8 --- seaborn/tests/test_rcmod.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/seaborn/tests/test_rcmod.py b/seaborn/tests/test_rcmod.py index 3564bcf56e..da5dbc6968 100644 --- a/seaborn/tests/test_rcmod.py +++ b/seaborn/tests/test_rcmod.py @@ -230,8 +230,7 @@ def test_different_sans_serif(self): raise nose.SkipTest rcmod.set() - rcmod.set_style(rc={"font.sans-serif": - ["Verdana"]}) + rcmod.set_style(rc={"font.sans-serif": ["Verdana"]}) _, ax = plt.subplots() ax.set_xlabel("foo") @@ -256,7 +255,7 @@ def has_verdana(): import matplotlib.font_manager as mplfm try: verdana_font = mplfm.findfont('Verdana', fallback_to_default=False) - except: + except: # noqa # if https://github.com/matplotlib/matplotlib/pull/3435 # gets accepted return False @@ -264,7 +263,7 @@ def has_verdana(): try: unlikely_font = mplfm.findfont("very_unlikely_to_exist1234", fallback_to_default=False) - except: + except: # noqa # if matched verdana but not unlikely, Verdana must exist return True # otherwise -- if they match, must be the same default From bbaa83deb97ba1e2da18b078f3d2c8b0fea4368a Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 23 Jun 2018 18:35:01 -0400 Subject: [PATCH 0727/1738] Add image with all seaborn palettes to tutorial --- doc/tutorial/color_palettes.ipynb | 53 ++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/doc/tutorial/color_palettes.ipynb b/doc/tutorial/color_palettes.ipynb index eca38cde19..60613c20a9 100644 --- a/doc/tutorial/color_palettes.ipynb +++ b/doc/tutorial/color_palettes.ipynb @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "sns.set(rc={\"figure.figsize\": (6, 6)})\n", + "sns.set()\n", "np.random.seed(sum(map(ord, \"palettes\")))" ] }, @@ -107,12 +107,57 @@ "cell_type": "raw", "metadata": {}, "source": [ - "There are six variations of the default theme, called ``deep``, ``muted``, ``pastel``, ``bright``, ``dark``, and ``colorblind``.\n", + "There are six variations of the default theme, called ``deep``, ``muted``, ``pastel``, ``bright``, ``dark``, and ``colorblind``." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# TODO hide input here when merged with doc updating branch\n", + "f = plt.figure(figsize=(6, 6))\n", + "\n", + "ax_locs = dict(\n", + " deep=(.4, .4),\n", + " bright=(.8, .8),\n", + " muted=(.49, .71),\n", + " dark=(.8, .2),\n", + " pastel=(.2, .8),\n", + " colorblind=(.71, .49),\n", + ")\n", "\n", + "s = .35\n", + "\n", + "for pal, (x, y) in ax_locs.items():\n", + " ax = f.add_axes([x - s / 2, y - s / 2, s, s])\n", + " ax.pie(np.ones(10),\n", + " colors=sns.color_palette(pal, 10),\n", + " counterclock=False, startangle=180,\n", + " wedgeprops=dict(linewidth=1, edgecolor=\"w\"))\n", + " f.text(x, y, pal, ha=\"center\", va=\"center\", size=14,\n", + " bbox=dict(facecolor=\"white\", alpha=0.85, boxstyle=\"round,pad=0.2\"))\n", + "\n", + "f.text(.1, .05, \"Saturation\", size=18, ha=\"left\", va=\"center\",\n", + " bbox=dict(facecolor=\"white\", edgecolor=\"w\"))\n", + "f.text(.05, .1, \"Luminance\", size=18, ha=\"center\", va=\"bottom\", rotation=90,\n", + " bbox=dict(facecolor=\"white\", edgecolor=\"w\"))\n", + "\n", + "ax = f.add_axes([0, 0, 1, 1])\n", + "ax.set_axis_off()\n", + "ax.arrow(.15, .05, .4, 0, width=.002, head_width=.015, color=\"k\")\n", + "ax.arrow(.05, .15, 0, .4, width=.002, head_width=.015, color=\"k\");" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ "Using circular color systems\n", "~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", "\n", - "When you have more than six categories to distinguish, the easiest thing is to draw evenly-spaced colors in a circular color space (such that the hue changes which keeping the brightness and saturation constant). This is what most seaborn functions default to when they need to use more colors than are currently set in the default color cycle.\n", + "When you have an arbitrary number of categories to distinguish without emphasizing any one, the easiest approach is to draw evenly-spaced colors in a circular color space (one where the hue changes while keeping the brightness and saturation constant). This is what most seaborn functions default to when they need to use more colors than are currently set in the default color cycle.\n", "\n", "The most common way to do this uses the ``hls`` color space, which is a simple transformation of RGB values." ] @@ -191,7 +236,7 @@ "metadata": {}, "outputs": [], "source": [ - "sns.palplot(sns.color_palette(\"Set2\", 10))" + "sns.palplot(sns.color_palette(\"Set2\"))" ] }, { From b498b8c028b352d250ada63916c102dc469102ab Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 23 Jun 2018 19:33:45 -0400 Subject: [PATCH 0728/1738] Update axisgrid palette-related tests --- seaborn/tests/test_axisgrid.py | 82 ++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index a01c20838f..7cc27b14a5 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -7,6 +7,7 @@ import matplotlib.pyplot as plt from distutils.version import LooseVersion +import pytest import nose.tools as nt import numpy.testing as npt from numpy.testing.decorators import skipif @@ -35,7 +36,7 @@ class TestFacetGrid(object): a=np.repeat(list("abc"), 20), b=np.tile(list("mn"), 30), c=np.tile(list("tuv"), 20), - d=np.tile(list("abcdefghij"), 6))) + d=np.tile(list("abcdefghijkl"), 5))) def test_self_data(self): @@ -92,29 +93,31 @@ def test_single_axes(self): def test_col_wrap(self): + n = len(self.df.d.unique()) + g = ag.FacetGrid(self.df, col="d") - nt.assert_equal(g.axes.shape, (1, 10)) - nt.assert_is(g.facet_axis(0, 8), g.axes[0, 8]) + assert g.axes.shape == (1, n) + assert g.facet_axis(0, 8) is g.axes[0, 8] g_wrap = ag.FacetGrid(self.df, col="d", col_wrap=4) - nt.assert_equal(g_wrap.axes.shape, (10,)) - nt.assert_is(g_wrap.facet_axis(0, 8), g_wrap.axes[8]) - nt.assert_equal(g_wrap._ncol, 4) - nt.assert_equal(g_wrap._nrow, 3) + assert g_wrap.axes.shape == (n,) + assert g_wrap.facet_axis(0, 8) is g_wrap.axes[8] + assert g_wrap._ncol == 4 + assert g_wrap._nrow == (n / 4) - with nt.assert_raises(ValueError): + with pytest.raises(ValueError): g = ag.FacetGrid(self.df, row="b", col="d", col_wrap=4) df = self.df.copy() df.loc[df.d == "j"] = np.nan g_missing = ag.FacetGrid(df, col="d") - nt.assert_equal(g_missing.axes.shape, (1, 9)) + assert g_missing.axes.shape == (1, n - 1) g_missing_wrap = ag.FacetGrid(df, col="d", col_wrap=4) - nt.assert_equal(g_missing_wrap.axes.shape, (9,)) + assert g_missing_wrap.axes.shape == (n - 1,) g = ag.FacetGrid(self.df, col="d", col_wrap=1) - assert len(list(g.facet_data())) == len(self.df.d.unique()) + assert len(list(g.facet_data())) == n def test_normal_axes(self): @@ -306,7 +309,6 @@ def test_gridspec_kws(self): @skipif(old_matplotlib) def test_gridspec_kws_col_wrap(self): ratios = [3, 1, 2, 1, 1] - sizes = [0.46, 0.15, 0.31] gskws = dict(width_ratios=ratios) with warnings.catch_warnings(): @@ -318,7 +320,6 @@ def test_gridspec_kws_col_wrap(self): @skipif(not old_matplotlib) def test_gridsic_kws_old_mpl(self): ratios = [3, 1, 2] - sizes = [0.46, 0.15, 0.31] gskws = dict(width_ratios=ratios, height_ratios=ratios) with warnings.catch_warnings(): @@ -385,7 +386,10 @@ def test_map(self): def test_map_dataframe(self): g = ag.FacetGrid(self.df, row="a", col="b", hue="c") - plot = lambda x, y, data=None, **kws: plt.plot(data[x], data[y], **kws) + + def plot(x, y, data=None, **kws): + plt.plot(data[x], data[y], **kws) + g.map_dataframe(plot, "x", "y", linestyle="--") lines = g.axes[0, 0].lines @@ -547,23 +551,23 @@ def test_palette(self): rcmod.set() g = ag.FacetGrid(self.df, hue="c") - nt.assert_equal(g._colors, color_palette(n_colors=3)) + assert g._colors == color_palette(n_colors=len(self.df.c.unique())) g = ag.FacetGrid(self.df, hue="d") - nt.assert_equal(g._colors, color_palette("husl", 10)) + assert g._colors == color_palette("husl", len(self.df.d.unique())) g = ag.FacetGrid(self.df, hue="c", palette="Set2") - nt.assert_equal(g._colors, color_palette("Set2", 3)) + assert g._colors == color_palette("Set2", len(self.df.c.unique())) dict_pal = dict(t="red", u="green", v="blue") list_pal = color_palette(["red", "green", "blue"], 3) g = ag.FacetGrid(self.df, hue="c", palette=dict_pal) - nt.assert_equal(g._colors, list_pal) + assert g._colors == list_pal list_pal = color_palette(["green", "blue", "red"], 3) g = ag.FacetGrid(self.df, hue="c", hue_order=list("uvt"), palette=dict_pal) - nt.assert_equal(g._colors, list_pal) + assert g._colors == list_pal def test_hue_kws(self): @@ -741,11 +745,11 @@ def test_categorical_warning(self): class TestPairGrid(object): rs = np.random.RandomState(sum(map(ord, "PairGrid"))) - df = pd.DataFrame(dict(x=rs.normal(size=80), - y=rs.randint(0, 4, size=(80)), - z=rs.gamma(3, size=80), - a=np.repeat(list("abcd"), 20), - b=np.repeat(list("abcdefgh"), 10))) + df = pd.DataFrame(dict(x=rs.normal(size=60), + y=rs.randint(0, 4, size=(60)), + z=rs.gamma(3, size=60), + a=np.repeat(list("abc"), 20), + b=np.repeat(list("abcdefghijkl"), 5))) def test_self_data(self): @@ -858,7 +862,7 @@ def test_map(self): for j, ax in enumerate(axes_i): x_in = self.df[vars[j]] y_in = self.df[vars[i]] - for k, k_level in enumerate("abcd"): + for k, k_level in enumerate(self.df.a.unique()): x_in_k = x_in[self.df.a == k_level] y_in_k = y_in[self.df.a == k_level] x_out, y_out = ax.collections[k].get_offsets().T @@ -935,7 +939,7 @@ def test_map_diag(self): g3.map_diag(plt.hist) for ax in g3.diag_axes: - nt.assert_equal(len(ax.patches), 40) + nt.assert_equal(len(ax.patches), 30) g4 = ag.PairGrid(self.df, hue="a") g4.map_diag(plt.hist, histtype='step') @@ -1011,23 +1015,23 @@ def test_palette(self): rcmod.set() g = ag.PairGrid(self.df, hue="a") - nt.assert_equal(g.palette, color_palette(n_colors=4)) + assert g.palette == color_palette(n_colors=len(self.df.a.unique())) g = ag.PairGrid(self.df, hue="b") - nt.assert_equal(g.palette, color_palette("husl", 8)) + assert g.palette == color_palette("husl", len(self.df.b.unique())) g = ag.PairGrid(self.df, hue="a", palette="Set2") - nt.assert_equal(g.palette, color_palette("Set2", 4)) + assert g.palette == color_palette("Set2", len(self.df.a.unique())) - dict_pal = dict(a="red", b="green", c="blue", d="purple") - list_pal = color_palette(["red", "green", "blue", "purple"], 4) + dict_pal = dict(a="red", b="green", c="blue") + list_pal = color_palette(["red", "green", "blue"]) g = ag.PairGrid(self.df, hue="a", palette=dict_pal) - nt.assert_equal(g.palette, list_pal) + assert g.palette == list_pal - list_pal = color_palette(["purple", "blue", "red", "green"], 4) - g = ag.PairGrid(self.df, hue="a", hue_order=list("dcab"), + list_pal = color_palette(["blue", "red", "green"]) + g = ag.PairGrid(self.df, hue="a", hue_order=list("cab"), palette=dict_pal) - nt.assert_equal(g.palette, list_pal) + assert g.palette == list_pal def test_hue_kws(self): @@ -1156,7 +1160,7 @@ def test_nondefault_index(self): for j, ax in enumerate(axes_i): x_in = self.df[vars[j]] y_in = self.df[vars[i]] - for k, k_level in enumerate("abcd"): + for k, k_level in enumerate(self.df.a.unique()): x_in_k = x_in[self.df.a == k_level] y_in_k = y_in[self.df.a == k_level] x_out, y_out = ax.collections[k].get_offsets().T @@ -1261,12 +1265,12 @@ def test_pairplot_kde(self): def test_pairplot_markers(self): vars = ["x", "y", "z"] - markers = ["o", "x", "s", "d"] + markers = ["o", "x", "s"] g = ag.pairplot(self.df, hue="a", vars=vars, markers=markers) - nt.assert_equal(g.hue_kws["marker"], markers) + assert g.hue_kws["marker"] == markers plt.close("all") - with nt.assert_raises(ValueError): + with pytest.raises(ValueError): g = ag.pairplot(self.df, hue="a", vars=vars, markers=markers[:-2]) From e168a0af6529bed1602c28f2454bab48ac0d0601 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 23 Jun 2018 19:39:47 -0400 Subject: [PATCH 0729/1738] Differentiate bright orange and red --- seaborn/palettes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/palettes.py b/seaborn/palettes.py index 83ac126461..0b2a20937f 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -32,7 +32,7 @@ "#DEBB9B", "#FAB0E4", "#CFCFCF", "#FFFEA3", "#B9F2F0"], pastel6=["#A1C9F4", "#8DE5A1", "#FF9F9B", "#D0BBFF", "#FFFEA3", "#B9F2F0"], - bright=["#023EFF", "#FF6000", "#1AC938", "#E8000B", "#8B2BE2", + bright=["#023EFF", "#FF7C00", "#1AC938", "#E8000B", "#8B2BE2", "#9F4800", "#F14CC1", "#A3A3A3", "#FFC400", "#00D7FF"], bright6=["#023EFF", "#1AC938", "#E8000B", "#8B2BE2", "#FFC400", "#00D7FF"], From b339cd372b6bc296f4e40945797b5c8919723d49 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 23 Jun 2018 20:31:16 -0400 Subject: [PATCH 0730/1738] Update release notes [ci skip] --- doc/releases/v0.9.0.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index 664f1752c2..a22a852175 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -10,6 +10,10 @@ v0.9.0 (Unreleased) - Deprecated the statistical annotation component of :class:`JointGrid`. The method is still available but will be removed in a future version. +- Updated the seaborn palettes ("deep", "muted", "colorblind", etc.) to correspond with the new 10-color matplotlib default. The legacy palettes are now available at "deep6", "muted6", "colorblind6", etc. Additionally, a few individual colors were tweaked for better consistency, aesthetics, and accessibility. + +- Calling :func:`set_palette` with a named qualitative palettes (i.e. one of the seaborn palettes, the colorbrewer qualitative palettes, or the matplotlib matplotlib tableau-derived palettes) and no specified number of colors will use all of the colors in the palette. Note that this behavior is different from what :func:`color_palette` has and continues to do, which is to default to 6 colors for named palettes. + - Fixed :func:`jointplot`/:class:`JointGrid` and :func:`regplot` so that they now accept list inputs. - Fixed a bug in :class:`FacetGrid` when using a single row/column level or using ``col_wrap=1``. From 057b2c30e7b6e8bef6bfebebe7c665bf54fc82d9 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 24 Jun 2018 10:39:17 -0400 Subject: [PATCH 0731/1738] Move logic of producing full qualitative palettes to color_palette --- doc/releases/v0.9.0.txt | 2 +- seaborn/palettes.py | 7 ++++--- seaborn/rcmod.py | 5 +---- seaborn/tests/test_palettes.py | 17 +++++++++++++++++ 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index a22a852175..ea5cce21b2 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -12,7 +12,7 @@ v0.9.0 (Unreleased) - Updated the seaborn palettes ("deep", "muted", "colorblind", etc.) to correspond with the new 10-color matplotlib default. The legacy palettes are now available at "deep6", "muted6", "colorblind6", etc. Additionally, a few individual colors were tweaked for better consistency, aesthetics, and accessibility. -- Calling :func:`set_palette` with a named qualitative palettes (i.e. one of the seaborn palettes, the colorbrewer qualitative palettes, or the matplotlib matplotlib tableau-derived palettes) and no specified number of colors will use all of the colors in the palette. Note that this behavior is different from what :func:`color_palette` has and continues to do, which is to default to 6 colors for named palettes. +- Calling :func:`color_palette` (or :func:`set_palette`) with a named qualitative palettes (i.e. one of the seaborn palettes, the colorbrewer qualitative palettes, or the matplotlib matplotlib tableau-derived palettes) and no specified number of colors will return all of the colors in the palette. This means that for some palettes, the returned list will have a different length than it did in previous versions. - Fixed :func:`jointplot`/:class:`JointGrid` and :func:`regplot` so that they now accept list inputs. diff --git a/seaborn/palettes.py b/seaborn/palettes.py index 0b2a20937f..a7ebbdde7e 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -200,18 +200,19 @@ def color_palette(palette=None, n_colors=None, desat=None): else: if n_colors is None: - n_colors = 6 + # Use all colors in a qualitative palette or 6 of another kind + n_colors = QUAL_PALETTE_SIZES.get(palette, 6) if palette in SEABORN_PALETTES: # Named "seaborn variant" of old matplotlib default palette palette = SEABORN_PALETTES[palette] elif palette == "hls": - # Evenly spaced colors in cylindrical RGB + # Evenly spaced colors in cylindrical RGB space palette = hls_palette(n_colors) elif palette == "husl": - # Evenly spaced colors in cylindrical Lab + # Evenly spaced colors in cylindrical Lab space palette = husl_palette(n_colors) elif palette.lower() == "jet": diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index 4af2f21397..cc173fe336 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -2,7 +2,6 @@ from distutils.version import LooseVersion import functools import matplotlib as mpl -from .external.six import string_types from . import palettes, _orig_rc_params @@ -471,7 +470,7 @@ def set_palette(palette, n_colors=None, desat=None, color_codes=False): Parameters ---------- - palette : hls | husl | matplotlib colormap | seaborn color palette + palette : seaborn color paltte | matplotlib colormap | hls | husl Palette definition. Should be something that :func:`color_palette` can process. n_colors : int @@ -498,8 +497,6 @@ def set_palette(palette, n_colors=None, desat=None, color_codes=False): set_style : set the default parameters for figure style """ - if n_colors is None and isinstance(palette, string_types): - n_colors = palettes.QUAL_PALETTE_SIZES.get(palette, None) colors = palettes.color_palette(palette, n_colors, desat) if mpl_ge_150: from cycler import cycler diff --git a/seaborn/tests/test_palettes.py b/seaborn/tests/test_palettes.py index 36e8a731e9..1b4c7e4d4f 100644 --- a/seaborn/tests/test_palettes.py +++ b/seaborn/tests/test_palettes.py @@ -47,6 +47,23 @@ def test_big_palette_context(self): # Reset default rcmod.set() + def test_palette_size(self): + + pal = palettes.color_palette("deep") + assert len(pal) == palettes.QUAL_PALETTE_SIZES["deep"] + + pal = palettes.color_palette("pastel6") + assert len(pal) == palettes.QUAL_PALETTE_SIZES["pastel6"] + + pal = palettes.color_palette("Set3") + assert len(pal) == palettes.QUAL_PALETTE_SIZES["Set3"] + + pal = palettes.color_palette("husl") + assert len(pal) == 6 + + pal = palettes.color_palette("Greens") + assert len(pal) == 6 + def test_seaborn_palettes(self): pals = "deep", "muted", "pastel", "bright", "dark", "colorblind" From fc0bb5865a527748af396713349f4d9df9c788f2 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 17 Jun 2018 16:21:38 -0400 Subject: [PATCH 0732/1738] Use matplotlib defaults for marker edge widths --- doc/installing.rst | 10 ---------- seaborn/rcmod.py | 11 ----------- 2 files changed, 21 deletions(-) diff --git a/doc/installing.rst b/doc/installing.rst index 40d081d265..a5ae87df52 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -65,13 +65,3 @@ include a reproducible example on one of the example datasets (accessed through versions of seaborn and matplotlib you are using, as well as what `matplotlib backend `__ you are using to draw the plots, so please include those in your bug report. - - -Known issues -~~~~~~~~~~~~ - -An unfortunate consequence of how the matplotlib marker styles work is that -line-art markers (e.g. ``"+"``) or markers with ``facecolor`` set to ``"none"`` -will be invisible when the default seaborn style is in effect. This can be -changed by using a different ``markeredgewidth`` (aliased to ``mew``) either in -the function call or globally in the ``rcParams``. diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index cc173fe336..e70ff7732e 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -60,9 +60,7 @@ "grid.linewidth", "lines.linewidth", - "patch.linewidth", "lines.markersize", - "lines.markeredgewidth", "xtick.major.width", "ytick.major.width", @@ -353,9 +351,7 @@ def plotting_context(context=None, font_scale=1, rc=None): "grid.linewidth": 1, "lines.linewidth": 1.75, - "patch.linewidth": .3, "lines.markersize": 7, - "lines.markeredgewidth": 0, "xtick.major.width": 1, "ytick.major.width": 1, @@ -377,13 +373,6 @@ def plotting_context(context=None, font_scale=1, rc=None): font_dict = {k: context_dict[k] * font_scale for k in font_keys} context_dict.update(font_dict) - # Implement hack workaround for matplotlib bug - # See https://github.com/mwaskom/seaborn/issues/344 - # There is a bug in matplotlib 1.4.2 that makes points invisible when - # they don't have an edgewidth. It will supposedly be fixed in 1.4.3. - if mpl.__version__ == "1.4.2": - context_dict["lines.markeredgewidth"] = 0.01 - # Override these settings with the provided rc dictionary if rc is not None: rc = {k: v for k, v in rc.items() if k in _context_keys} From 4ba58c807a38621f59438949b4c9e480a819a4b1 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 17 Jun 2018 16:22:00 -0400 Subject: [PATCH 0733/1738] Use matplotlib defaults for legend --- seaborn/rcmod.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index e70ff7732e..2b4a34eb5d 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -38,10 +38,6 @@ "xtick.minor.size", "ytick.minor.size", - "legend.frameon", - "legend.numpoints", - "legend.scatterpoints", - "lines.solid_capstyle", "image.cmap", @@ -172,9 +168,6 @@ def axes_style(style=None, rc=None): "figure.facecolor": "white", "text.color": dark_gray, "axes.labelcolor": dark_gray, - "legend.frameon": False, - "legend.numpoints": 1, - "legend.scatterpoints": 1, "xtick.direction": "out", "ytick.direction": "out", "xtick.color": dark_gray, From d506a8dc9c2148f5bf0ccbc365e0373d236f5ac4 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 17 Jun 2018 16:25:23 -0400 Subject: [PATCH 0734/1738] Default to setting color_codes in seaborn.set --- seaborn/rcmod.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index 2b4a34eb5d..5858d38dd4 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -69,7 +69,7 @@ def set(context="notebook", style="darkgrid", palette="deep", - font="sans-serif", font_scale=1, color_codes=False, rc=None): + font="sans-serif", font_scale=1, color_codes=True, rc=None): """Set aesthetic parameters in one step. Each set of parameters can be set directly or temporarily, see the @@ -488,4 +488,7 @@ def set_palette(palette, n_colors=None, desat=None, color_codes=False): mpl.rcParams["axes.color_cycle"] = list(colors) mpl.rcParams["patch.facecolor"] = colors[0] if color_codes: - palettes.set_color_codes(palette) + try: + palettes.set_color_codes(palette) + except KeyError: + pass From 0b2caf1efdc67eba71839e089d5459a6283af20d Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 24 Jun 2018 14:07:57 -0400 Subject: [PATCH 0735/1738] Make default diagonal kind in pairplot depend on use of hue --- doc/releases/v0.9.0.txt | 4 +++- seaborn/axisgrid.py | 10 +++++++--- seaborn/tests/test_axisgrid.py | 10 ++++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index ea5cce21b2..63cca85081 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -24,7 +24,9 @@ v0.9.0 (Unreleased) - Enhanced :func:`color_palette` to accept a parameterized specification of a cubehelix palette in in a string, prefixed with ``"ch:"`` (e.g. ``"ch:-.1,.2,light=.7"``). This will be accepted by any seaborn function with a ``palette=`` parameter. -- Changed the default diagonal plots in :func:`pairplot` to use :func:`kdeplot` and changed the default off-diagonal plots to use :func:`scatterplot`. +- Changed the default diagonal plots in :func:`pairplot` to use `func`:kdeplot` when a ``"hue"`` dimension is used. + +- Changed the default off-diagonal plots to use :func:`scatterplot`. (Note that the ``"hue"`` currently draws three separate scatterplots instead of using the hue semantic of the scatterplot function. - Removed a special case in :class:`PairGrid` that defaulted to drawing stacked histograms on the diagonal axes. diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index d180b7fce9..5e3d5071a5 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -1862,7 +1862,7 @@ def savefig(self, *args, **kwargs): def pairplot(data, hue=None, hue_order=None, palette=None, vars=None, x_vars=None, y_vars=None, - kind="scatter", diag_kind="kde", markers=None, + kind="scatter", diag_kind="auto", markers=None, size=2.5, aspect=1, dropna=True, plot_kws=None, diag_kws=None, grid_kws=None): """Plot pairwise relationships in a dataset. @@ -1900,8 +1900,9 @@ def pairplot(data, hue=None, hue_order=None, palette=None, columns of the figure; i.e. to make a non-square plot. kind : {'scatter', 'reg'}, optional Kind of plot for the non-identity relationships. - diag_kind : {'hist', 'kde'}, optional - Kind of plot for the diagonal subplots. + diag_kind : {'auto', 'hist', 'kde'}, optional + Kind of plot for the diagonal subplots. The default depends on whether + ``"hue"`` is used or not. markers : single matplotlib marker code or list, optional Either the marker to use for all datapoints or a list of markers with a length the same as the number of levels in the hue variable so that @@ -2044,6 +2045,9 @@ def pairplot(data, hue=None, hue_order=None, palette=None, grid.hue_kws = {"marker": markers} # Maybe plot on the diagonal + if diag_kind == "auto": + diag_kind = "hist" if hue is None else "kde" + diag_kws = diag_kws.copy() if grid.square_grid: if diag_kind == "hist": diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 7cc27b14a5..8c4773faee 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -1174,8 +1174,7 @@ def test_pairplot(self): g = ag.pairplot(self.df) for ax in g.diag_axes: - assert len(ax.lines) == 1 - assert len(ax.collections) == 1 + assert len(ax.patches) > 1 for i, j in zip(*np.triu_indices_from(g.axes, 1)): ax = g.axes[i, j] @@ -1197,6 +1196,13 @@ def test_pairplot(self): ax = g.axes[i, j] nt.assert_equal(len(ax.collections), 0) + g = ag.pairplot(self.df, hue="a") + n = len(self.df.a.unique()) + + for ax in g.diag_axes: + assert len(ax.lines) == n + assert len(ax.collections) == n + @skipif(old_matplotlib) def test_pairplot_reg(self): From b8e3b765f066fd815154f31b563b2720e5b0a7c2 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 24 Jun 2018 14:08:14 -0400 Subject: [PATCH 0736/1738] Add white edges to patches in all styles --- examples/faceted_histogram.py | 2 +- seaborn/rcmod.py | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/examples/faceted_histogram.py b/examples/faceted_histogram.py index 70694a992c..995dbe47a7 100644 --- a/examples/faceted_histogram.py +++ b/examples/faceted_histogram.py @@ -12,4 +12,4 @@ tips = sns.load_dataset("tips") g = sns.FacetGrid(tips, row="sex", col="time", margin_titles=True) bins = np.linspace(0, 60, 13) -g.map(plt.hist, "total_bill", color="steelblue", bins=bins, lw=0) +g.map(plt.hist, "total_bill", color="steelblue", bins=bins) diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index 5858d38dd4..bdd6574487 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -40,6 +40,9 @@ "lines.solid_capstyle", + "patch.edgecolor", + "patch.force_edgecolor", + "image.cmap", "font.family", "font.sans-serif", @@ -165,20 +168,31 @@ def axes_style(style=None, rc=None): # Common parameters style_dict = { + "figure.facecolor": "white", - "text.color": dark_gray, "axes.labelcolor": dark_gray, + "xtick.direction": "out", "ytick.direction": "out", "xtick.color": dark_gray, "ytick.color": dark_gray, + "axes.axisbelow": True, - "image.cmap": "rocket", + "grid.linestyle": "-", + + + "text.color": dark_gray, "font.family": ["sans-serif"], "font.sans-serif": ["Arial", "DejaVu Sans", "Liberation Sans", "Bitstream Vera Sans", "sans-serif"], - "grid.linestyle": "-", + + "lines.solid_capstyle": "round", + "patch.edgecolor": "w", + "patch.force_edgecolor": True, + + "image.cmap": "rocket", + } # Set grid on or off From bbca1348e9b499d85da4a3a7cddfc9f13cee4295 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 24 Jun 2018 14:19:38 -0400 Subject: [PATCH 0737/1738] Slightly increase font sizes and boost talk/poster scaling --- seaborn/rcmod.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index bdd6574487..017198ce64 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -350,11 +350,11 @@ def plotting_context(context=None, font_scale=1, rc=None): base_context = { "font.size": 12, - "axes.labelsize": 11, + "axes.labelsize": 12, "axes.titlesize": 12, - "xtick.labelsize": 10, - "ytick.labelsize": 10, - "legend.fontsize": 10, + "xtick.labelsize": 11, + "ytick.labelsize": 11, + "legend.fontsize": 11, "grid.linewidth": 1, "lines.linewidth": 1.75, @@ -371,7 +371,7 @@ def plotting_context(context=None, font_scale=1, rc=None): } # Scale all the parameters by the same factor depending on the context - scaling = dict(paper=.8, notebook=1, talk=1.3, poster=1.6)[context] + scaling = dict(paper=.8, notebook=1, talk=1.5, poster=2)[context] context_dict = {k: v * scaling for k, v in base_context.items()} # Now independently scale the fonts From 4cd8021545d17216983be9b76bc029481f6571be Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 24 Jun 2018 14:52:37 -0400 Subject: [PATCH 0738/1738] Reorganize some style/context params for more scaling control --- seaborn/rcmod.py | 111 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 76 insertions(+), 35 deletions(-) diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index 017198ce64..ad22945c7b 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -1,4 +1,4 @@ -"""Functions that alter the matplotlib rc dictionary on the fly.""" +"""Control plot style and scaling using the matplotlib rcParams interface.""" from distutils.version import LooseVersion import functools import matplotlib as mpl @@ -6,6 +6,7 @@ mpl_ge_150 = LooseVersion(mpl.__version__) >= '1.5.0' +mpl_ge_2 = LooseVersion(mpl.__version__) >= '2.0' __all__ = ["set", "reset_defaults", "reset_orig", @@ -13,13 +14,12 @@ "set_palette"] -_style_keys = ( +_style_keys = [ "axes.facecolor", "axes.edgecolor", "axes.grid", "axes.axisbelow", - "axes.linewidth", "axes.labelcolor", "figure.facecolor", @@ -33,22 +33,35 @@ "ytick.color", "xtick.direction", "ytick.direction", - "xtick.major.size", - "ytick.major.size", - "xtick.minor.size", - "ytick.minor.size", - "lines.solid_capstyle", "patch.edgecolor", - "patch.force_edgecolor", "image.cmap", "font.family", "font.sans-serif", - ) -_context_keys = ( + ] + +if mpl_ge_2: + + _style_keys.extend([ + + "patch.force_edgecolor", + + "xtick.bottom", + "xtick.top", + "ytick.left", + "ytick.right", + + "axes.spines.left", + "axes.spines.bottom", + "axes.spines.right", + "axes.spines.top", + + ]) + +_context_keys = [ "font.size", "axes.labelsize", @@ -57,6 +70,7 @@ "ytick.labelsize", "legend.fontsize", + "axes.linewidth", "grid.linewidth", "lines.linewidth", "lines.markersize", @@ -66,9 +80,12 @@ "xtick.minor.width", "ytick.minor.width", - "xtick.major.pad", - "ytick.major.pad" - ) + "xtick.major.size", + "ytick.major.size", + "xtick.minor.size", + "ytick.minor.size", + + ] def set(context="notebook", style="darkgrid", palette="deep", @@ -193,6 +210,9 @@ def axes_style(style=None, rc=None): "image.cmap": "rocket", + "xtick.top": False, + "ytick.right": False, + } # Set grid on or off @@ -208,44 +228,62 @@ def axes_style(style=None, rc=None): # Set the color of the background, spines, and grids if style.startswith("dark"): style_dict.update({ + "axes.facecolor": "#EAEAF2", "axes.edgecolor": "white", - "axes.linewidth": 0, "grid.color": "white", + + "axes.spines.left": True, + "axes.spines.bottom": True, + "axes.spines.right": True, + "axes.spines.top": True, + }) elif style == "whitegrid": style_dict.update({ + "axes.facecolor": "white", "axes.edgecolor": light_gray, - "axes.linewidth": 1, "grid.color": light_gray, + + "axes.spines.left": True, + "axes.spines.bottom": True, + "axes.spines.right": True, + "axes.spines.top": True, + }) elif style in ["white", "ticks"]: style_dict.update({ + "axes.facecolor": "white", "axes.edgecolor": dark_gray, - "axes.linewidth": 1.25, "grid.color": light_gray, + + "axes.spines.left": True, + "axes.spines.bottom": True, + "axes.spines.right": True, + "axes.spines.top": True, + }) # Show or hide the axes ticks if style == "ticks": style_dict.update({ - "xtick.major.size": 6, - "ytick.major.size": 6, - "xtick.minor.size": 3, - "ytick.minor.size": 3, + "xtick.bottom": True, + "ytick.left": True, }) else: style_dict.update({ - "xtick.major.size": 0, - "ytick.major.size": 0, - "xtick.minor.size": 0, - "ytick.minor.size": 0, + "xtick.bottom": False, + "ytick.left": False, }) + # Remove entries that are not defined in the base list of valid keys + # This lets us handle matplotlib <=/> 2.0 + style_dict = {k: v for k, v in style_dict.items() if k in _style_keys} + # Override these settings with the provided rc dictionary if rc is not None: rc = {k: v for k, v in rc.items() if k in _style_keys} @@ -356,17 +394,20 @@ def plotting_context(context=None, font_scale=1, rc=None): "ytick.labelsize": 11, "legend.fontsize": 11, + "axes.linewidth": 1.25, "grid.linewidth": 1, - "lines.linewidth": 1.75, - "lines.markersize": 7, - - "xtick.major.width": 1, - "ytick.major.width": 1, - "xtick.minor.width": .5, - "ytick.minor.width": .5, - - "xtick.major.pad": 7, - "ytick.major.pad": 7, + "lines.linewidth": 1.5, + "lines.markersize": 6, + + "xtick.major.width": 1.25, + "ytick.major.width": 1.25, + "xtick.minor.width": 1, + "ytick.minor.width": 1, + + "xtick.major.size": 6, + "ytick.major.size": 6, + "xtick.minor.size": 4, + "ytick.minor.size": 4, } From 6d8a82705ebf4bdb4374f13f3690ba3205fbca11 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 24 Jun 2018 16:36:19 -0400 Subject: [PATCH 0739/1738] Fix bad gridspec test --- seaborn/tests/test_axisgrid.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 8c4773faee..02633a25f0 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -282,29 +282,19 @@ def test_subplot_kws(self): @skipif(old_matplotlib) def test_gridspec_kws(self): ratios = [3, 1, 2] - sizes = [0.46, 0.15, 0.31] - gskws = dict(width_ratios=ratios, height_ratios=ratios) + gskws = dict(width_ratios=ratios) g = ag.FacetGrid(self.df, col='c', row='a', gridspec_kws=gskws) - # clear out all ticks for ax in g.axes.flat: ax.set_xticks([]) ax.set_yticks([]) g.fig.tight_layout() - widths, heights = np.meshgrid(sizes, sizes) - for n, ax in enumerate(g.axes.flat): - npt.assert_almost_equal( - ax.get_position().width, - widths.flatten()[n], - decimal=2 - ) - npt.assert_almost_equal( - ax.get_position().height, - heights.flatten()[n], - decimal=2 - ) + + for (l, m, r) in g.axes: + assert l.get_position().width > m.get_position().width + assert r.get_position().width > m.get_position().width @skipif(old_matplotlib) def test_gridspec_kws_col_wrap(self): From b3b6d8a89a10cd37f7e028c71fc934339dfb344f Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 24 Jun 2018 17:51:13 -0400 Subject: [PATCH 0740/1738] Move ticks when despining more robustly --- seaborn/tests/test_utils.py | 34 ++++++++++++++++++++++++++++++++++ seaborn/utils.py | 29 +++++++++++++++++++---------- 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/seaborn/tests/test_utils.py b/seaborn/tests/test_utils.py index fe7156cc7a..dab32aeb02 100644 --- a/seaborn/tests/test_utils.py +++ b/seaborn/tests/test_utils.py @@ -232,6 +232,40 @@ def test_despine_trim_noticks(self): utils.despine(trim=True) nt.assert_equal(ax.get_yticks().size, 0) + def test_despine_moved_tickes(self): + + f, ax = plt.subplots() + for t in ax.yaxis.majorTicks: + t.tick1On = True + utils.despine(ax=ax, left=True, right=False) + for y in ax.yaxis.majorTicks: + assert t.tick2On + plt.close(f) + + f, ax = plt.subplots() + for t in ax.yaxis.majorTicks: + t.tick1On = False + utils.despine(ax=ax, left=True, right=False) + for y in ax.yaxis.majorTicks: + assert not t.tick2On + plt.close(f) + + f, ax = plt.subplots() + for t in ax.xaxis.majorTicks: + t.tick1On = True + utils.despine(ax=ax, bottom=True, top=False) + for y in ax.xaxis.majorTicks: + assert t.tick2On + plt.close(f) + + f, ax = plt.subplots() + for t in ax.xaxis.majorTicks: + t.tick1On = False + utils.despine(ax=ax, bottom=True, top=False) + for y in ax.xaxis.majorTicks: + assert not t.tick2On + plt.close(f) + def test_ticklabels_overlap(): diff --git a/seaborn/utils.py b/seaborn/utils.py index 38544a27df..defae8c93b 100644 --- a/seaborn/utils.py +++ b/seaborn/utils.py @@ -155,7 +155,7 @@ def saturate(color): return set_hls_values(color, s=1) -def set_hls_values(color, h=None, l=None, s=None): +def set_hls_values(color, h=None, l=None, s=None): # noqa """Independently manipulate the h, l, or s channels of a color. Parameters @@ -233,15 +233,24 @@ def despine(fig=None, ax=None, top=True, right=True, left=False, val = offset _set_spine_position(ax_i.spines[side], ('outward', val)) - # Set the ticks appropriately - if bottom: - ax_i.xaxis.tick_top() - if top: - ax_i.xaxis.tick_bottom() - if left: - ax_i.yaxis.tick_right() - if right: - ax_i.yaxis.tick_left() + # Potentially move the ticks + if left and not right: + maj_on = any(t.tick1On for t in ax_i.yaxis.majorTicks) + min_on = any(t.tick1On for t in ax_i.yaxis.minorTicks) + ax_i.yaxis.set_ticks_position("right") + for t in ax_i.yaxis.majorTicks: + t.tick2On = maj_on + for t in ax_i.yaxis.minorTicks: + t.tick2On = min_on + + if bottom and not top: + maj_on = any(t.tick1On for t in ax_i.xaxis.majorTicks) + min_on = any(t.tick1On for t in ax_i.xaxis.minorTicks) + ax_i.xaxis.set_ticks_position("top") + for t in ax_i.xaxis.majorTicks: + t.tick2On = maj_on + for t in ax_i.xaxis.minorTicks: + t.tick2On = min_on if trim: # clip off the parts of the spines that extend past major ticks From a715c7aef7533eed199c652c855f4354ca18e190 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 24 Jun 2018 17:51:23 -0400 Subject: [PATCH 0741/1738] Add patch linewidth to context spec --- seaborn/rcmod.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py index ad22945c7b..51c6f365ea 100644 --- a/seaborn/rcmod.py +++ b/seaborn/rcmod.py @@ -74,6 +74,7 @@ "grid.linewidth", "lines.linewidth", "lines.markersize", + "patch.linewidth", "xtick.major.width", "ytick.major.width", @@ -398,6 +399,7 @@ def plotting_context(context=None, font_scale=1, rc=None): "grid.linewidth": 1, "lines.linewidth": 1.5, "lines.markersize": 6, + "patch.linewidth": 1, "xtick.major.width": 1.25, "ytick.major.width": 1.25, From b4de70570bec5f1e27e37722b48b651f3da05cad Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 24 Jun 2018 18:00:53 -0400 Subject: [PATCH 0742/1738] Update releast notes --- doc/releases/v0.8.0.txt | 2 +- doc/releases/v0.9.0.txt | 8 +++++++- doc/whatsnew.rst | 6 ++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/doc/releases/v0.8.0.txt b/doc/releases/v0.8.0.txt index 274d92a79e..58c9ec6d07 100644 --- a/doc/releases/v0.8.0.txt +++ b/doc/releases/v0.8.0.txt @@ -7,7 +7,7 @@ v0.8.0 (July 2017) - The default style is no longer applied when seaborn is imported. It is now necessary to explicitly call :func:`set` or one or more of :func:`set_style`, :func:`set_context`, and :func:`set_palette`. Correspondingly, the ``seaborn.apionly`` module has been deprecated. -- Changed the behavior of :func:`heatmap` (and by extension :func:`clustermap`) when plotting divergent dataesets (i.e. when the ``center`` parameter is used). Instead of extending the lower and upper limits of the colormap to be symettrical around the ``center`` value, the colormap is modified so that its middle color corresponds to ``center``. This means that the full range of the colormap will not be used (unless the data or specified ``vmin`` and ``vmax`` are symettric), but the upper and lower limits of the colorbar will correspond to the range of the data. See the Github pull request `(#1184) `_ for examples of the behavior. +- Changed the behavior of :func:`heatmap` (and by extension :func:`clustermap`) when plotting divergent dataesets (i.e. when the ``center`` parameter is used). Instead of extending the lower and upper limits of the colormap to be symmetrical around the ``center`` value, the colormap is modified so that its middle color corresponds to ``center``. This means that the full range of the colormap will not be used (unless the data or specified ``vmin`` and ``vmax`` are symmetric), but the upper and lower limits of the colorbar will correspond to the range of the data. See the Github pull request `(#1184) `_ for examples of the behavior. - Removed automatic detection of diverging data in :func:`heatmap` (and by extension :func:`clustermap`). If you want the colormap to be treated as diverging (see above), it is now necessary to specify the ``center`` value. When no colormap is specified, specifying ``center`` will still change the default to be one that is more appropriate for displaying diverging data. diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index 63cca85081..11eb3e365d 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -14,6 +14,12 @@ v0.9.0 (Unreleased) - Calling :func:`color_palette` (or :func:`set_palette`) with a named qualitative palettes (i.e. one of the seaborn palettes, the colorbrewer qualitative palettes, or the matplotlib matplotlib tableau-derived palettes) and no specified number of colors will return all of the colors in the palette. This means that for some palettes, the returned list will have a different length than it did in previous versions. +- Reorganized and updated some :func:`axes_style`/:func:`plotting_context` parameters to take advantage of improvements in the `matplotlib2 update `_. The biggest change involves using several new params in the "style" spec while moving parameters that used to implement the corresponding aesthetics to the "context" spec. For example, axes spines and ticks are now off instead of having their width/length zeroed out for the darkgrid style. That means the width/length of these elements can now be scaled in different contexts. The effect is a more cohesive appearance of the plots, especially in larger contexts. These changes include only minimal support for the 1.x matplotlib series. Users who are stuck on matplotlib 1.5 but wish to use seaborn styling may want to use the seaborn parameters that can be accessed through the `matplotlib stylesheet interface `_. + +- Slightly increased the base font sizes in :func:`plotting_context` and increased the scaling factors for ``"talk"`` and ``"poster"`` contexts. + +- Calling :func:`set` will now call :func:`set_color_codes` to re-assign the single letter color codes by default + - Fixed :func:`jointplot`/:class:`JointGrid` and :func:`regplot` so that they now accept list inputs. - Fixed a bug in :class:`FacetGrid` when using a single row/column level or using ``col_wrap=1``. @@ -36,4 +42,4 @@ v0.9.0 (Unreleased) - Changed the install infrastructure to explicitly declare dependencies in a way that ``pip`` is aware of. This means that ``pip install seaborn`` will now work in an empty environment. Additionally, the dependencies are specified with strict minimal versions. -- Updated the testing infrastructure to execute tests with `pytest ` (although many individual tests still use nose assertion). +- Updated the testing infrastructure to execute tests with `pytest `_ (although many individual tests still use nose assertion). diff --git a/doc/whatsnew.rst b/doc/whatsnew.rst index 49956eb77a..b38fbef1aa 100644 --- a/doc/whatsnew.rst +++ b/doc/whatsnew.rst @@ -2,8 +2,10 @@ .. currentmodule:: seaborn -What's new in the package -========================= +Release notes +============= + +.. include:: releases/v0.9.0.txt .. include:: releases/v0.8.1.txt From 5fe10d9f2bf84b168b1d4e4efcb5be9306794e54 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 24 Jun 2018 20:57:12 -0400 Subject: [PATCH 0743/1738] Pass bandwidth to scipy bivariate kde Fixes #1411 --- seaborn/distributions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 97309a88c7..c1238c40fe 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -451,7 +451,7 @@ def _statsmodels_bivariate_kde(x, y, bw, gridsize, cut, clip): def _scipy_bivariate_kde(x, y, bw, gridsize, cut, clip): """Compute a bivariate kde using scipy.""" data = np.c_[x, y] - kde = stats.gaussian_kde(data.T) + kde = stats.gaussian_kde(data.T, bw_method=bw) data_std = data.std(axis=0, ddof=1) if isinstance(bw, string_types): bw = "scotts" if bw == "scott" else bw From 3d9bd2638bd53b72542cf825fa02084ea8f8e2d7 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 25 Jun 2018 08:48:40 -0400 Subject: [PATCH 0744/1738] Make 2D kdeplot accept color/label and follow color cycle --- seaborn/distributions.py | 24 ++++++++++++++++++++-- seaborn/tests/test_distributions.py | 31 +++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index c1238c40fe..4343cded60 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -19,7 +19,7 @@ _has_statsmodels = False from .utils import iqr, _kde_support -from .palettes import color_palette, blend_palette +from .palettes import color_palette, light_palette, dark_palette, blend_palette __all__ = ["distplot", "kdeplot", "rugplot"] @@ -396,7 +396,18 @@ def _bivariate_kdeplot(x, y, filled, fill_lowest, # Plot the contours n_levels = kwargs.pop("n_levels", 10) - cmap = kwargs.get("cmap", "BuGn" if filled else "BuGn_d") + + scout, = ax.plot([], []) + default_color = scout.get_color() + scout.remove() + + color = kwargs.pop("color", default_color) + cmap = kwargs.pop("cmap", None) + if cmap is None: + if filled: + cmap = light_palette(color, as_cmap=True) + else: + cmap = dark_palette(color, as_cmap=True) if isinstance(cmap, string_types): if cmap.endswith("_d"): pal = ["#333333"] @@ -405,6 +416,8 @@ def _bivariate_kdeplot(x, y, filled, fill_lowest, else: cmap = mpl.cm.get_cmap(cmap) + label = kwargs.pop("label", None) + kwargs["cmap"] = cmap contour_func = ax.contourf if filled else ax.contour cset = contour_func(xx, yy, z, n_levels, **kwargs) @@ -422,6 +435,13 @@ def _bivariate_kdeplot(x, y, filled, fill_lowest, if hasattr(y, "name") and axlabel: ax.set_ylabel(y.name) + if label is not None: + legend_color = cmap(.95) if color is None else color + if filled: + ax.fill_between([], [], color=legend_color, label=label) + else: + ax.plot([], [], color=legend_color, label=label) + return ax diff --git a/seaborn/tests/test_distributions.py b/seaborn/tests/test_distributions.py index 5624138d96..9ae2425312 100644 --- a/seaborn/tests/test_distributions.py +++ b/seaborn/tests/test_distributions.py @@ -124,6 +124,37 @@ def test_bivariate_kde_colorbar(self): nt.assert_equal(len(f.axes), 2) nt.assert_equal(f.axes[1].get_ylabel(), "density") + def test_legend(self): + + f, ax = plt.subplots() + dist.kdeplot(self.x, self.y, label="test1") + line = ax.lines[-1] + assert line.get_label() == "test1" + + f, ax = plt.subplots() + dist.kdeplot(self.x, self.y, shade=True, label="test2") + fill = ax.collections[-1] + assert fill.get_label() == "test2" + + def test_contour_color(self): + + rgb = (.1, .5, .7) + f, ax = plt.subplots() + + dist.kdeplot(self.x, self.y, color=rgb) + contour = ax.collections[-1] + assert np.array_equal(contour.get_color()[0, :3], rgb) + low = ax.collections[0].get_color().mean() + high = ax.collections[-1].get_color().mean() + assert low < high + + f, ax = plt.subplots() + dist.kdeplot(self.x, self.y, shade=True, color=rgb) + contour = ax.collections[-1] + low = ax.collections[0].get_facecolor().mean() + high = ax.collections[-1].get_facecolor().mean() + assert low > high + class TestRugPlot(object): From 40c9b366ef74b65b75e0a06251899819a03bd081 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 25 Jun 2018 08:51:01 -0400 Subject: [PATCH 0745/1738] Update release notes --- doc/releases/v0.9.0.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index 11eb3e365d..79880e593e 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -34,6 +34,8 @@ v0.9.0 (Unreleased) - Changed the default off-diagonal plots to use :func:`scatterplot`. (Note that the ``"hue"`` currently draws three separate scatterplots instead of using the hue semantic of the scatterplot function. +- Changed color handling when using :func:`kdeplot` with two variables. The default colormap for the 2D density now follows the color cycle, and the function can use ``color`` and ``label`` kwargs, adding more flexibility and avoiding a warning when using with multi-plot grids. + - Removed a special case in :class:`PairGrid` that defaulted to drawing stacked histograms on the diagonal axes. - Fixed functions that set axis limits so that they preserve auto-scaling state on matplotlib 2.0. From 8f1823e8030620a53ed8c0a4f5f1ed34d314c041 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 25 Jun 2018 08:54:46 -0400 Subject: [PATCH 0746/1738] Add a note about different bw behavior on scipy/statsmodels Closes #1337 (long-term fix will probably be simplifying KDE computation to always uses scipy) [ci skip] --- seaborn/distributions.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/seaborn/distributions.py b/seaborn/distributions.py index 4343cded60..fe93e928ea 100644 --- a/seaborn/distributions.py +++ b/seaborn/distributions.py @@ -512,7 +512,11 @@ def kdeplot(data, data2=None, shade=False, vertical=False, kernel="gau", gaussian kernel. bw : {'scott' | 'silverman' | scalar | pair of scalars }, optional Name of reference method to determine kernel size, scalar factor, - or scalar for each dimension of the bivariate plot. + or scalar for each dimension of the bivariate plot. Note that the + underlying computational libraries have different interperetations + for this parameter: ``statsmodels`` uses it directly, but ``scipy`` + treats it as a scaling factor for the standard deviation of the + data. gridsize : int, optional Number of discrete points in the evaluation grid. cut : scalar, optional From 3f7ef67c425e3cae033ee091e2eeecd4a0d8923e Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 25 Jun 2018 10:25:25 -0400 Subject: [PATCH 0747/1738] Pass size_limits to scatterplot --- seaborn/basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 04784b01fd..fd78691d4d 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -1236,7 +1236,7 @@ def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, p = _ScatterPlotter( x=x, y=y, hue=hue, style=style, size=size, data=data, palette=palette, hue_order=hue_order, hue_limits=hue_limits, - sizes=sizes, size_order=size_order, + sizes=sizes, size_order=size_order, size_limits=size_limits, markers=markers, style_order=style_order, x_bins=x_bins, y_bins=y_bins, estimator=estimator, ci=ci, n_boot=n_boot, From 12865f083922e401d3178fc40300f3bb12481636 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Mon, 25 Jun 2018 20:39:21 -0400 Subject: [PATCH 0748/1738] Raise on mixture of filled and unfilled markers in basic plots --- seaborn/basic.py | 11 +++++++++++ seaborn/tests/test_basic.py | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/seaborn/basic.py b/seaborn/basic.py index fd78691d4d..0c727a44a8 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -465,10 +465,21 @@ def parse_style(self, data, markers, dashes, order): ) paths = {} + filled_markers = [] for k, m in markers.items(): if not isinstance(m, mpl.markers.MarkerStyle): m = mpl.markers.MarkerStyle(m) paths[k] = m.get_path().transformed(m.get_transform()) + filled_markers.append(m.is_filled()) + + # Mixture of filled and unfilled markers will show line art markers + # in the edge color, which defaults to white. This can be handled, + # but there would be additional complexity with specifying the + # weight of the line art markers without overwhelming the filled + # ones with the edges. So for now, we will disallow mixtures. + if not all(filled_markers) and any(filled_markers): + err = "Filled and line art markers cannot be mixed" + raise ValueError(err) self.style_levels = levels self.dashes = dashes diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index a297577880..f6bfd7d62d 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -623,6 +623,11 @@ def test_parse_style(self, long_df): with pytest.raises(ValueError): p.parse_style(p.plot_data["style"], markers, dashes, None) + # Test mixture of filled and unfilled markers + markers, dashes = ["o", "x", "s"], None + with pytest.raises(ValueError): + p.parse_style(p.plot_data["style"], markers, dashes, None) + def test_subset_data_quantities(self, long_df): p = basic._LinePlotter(x="x", y="y", data=long_df) From c6c0d02aaf1e31b0f64658cc39b15db72ce82d0d Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Tue, 26 Jun 2018 22:44:19 -0400 Subject: [PATCH 0749/1738] Accept Normalize object for hue; needs tests --- seaborn/basic.py | 73 +++++++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 0c727a44a8..62c9d1abf8 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -226,7 +226,7 @@ def categorical_to_palette(self, data, order, palette): return levels, palette - def numeric_to_palette(self, data, order, palette, limits): + def numeric_to_palette(self, data, order, palette, norm): """Determine colors when the hue variable is quantitative.""" levels = list(np.sort(remove_na(data.unique()))) @@ -248,24 +248,25 @@ def numeric_to_palette(self, data, order, palette, limits): err = "Palette {} not understood" raise ValueError(err) - if limits is None: - limits = data.min(), data.max() + if norm is None: + norm = mpl.colors.Normalize() + elif isinstance(norm, tuple): + norm = mpl.colors.Normalize(*norm) + elif not isinstance(norm, mpl.colors.Normalize): + err = "``hue_norm`` must be None, tuple, or Normalize object." + raise ValueError(err) - hue_min, hue_max = limits - hue_min = data.min() if hue_min is None else hue_min - hue_max = data.max() if hue_max is None else hue_max + if not norm.scaled(): + norm(np.asarray(data.dropna())) - limits = hue_min, hue_max - normalize = mpl.colors.Normalize(hue_min, hue_max, clip=True) - palette = {l: cmap(normalize(l)) for l in levels} + palette = {l: cmap(norm(l)) for l in levels} - return levels, palette, cmap, limits + return levels, palette, cmap, norm def color_lookup(self, key): """Return the color corresponding to the hue level.""" if self.hue_type == "numeric": - norm = mpl.colors.Normalize(*self.hue_limits, clip=True) - return self.cmap(norm(key)) + return self.cmap(self.hue_norm(key)) elif self.hue_type == "categorical": return self.palette[key] @@ -327,12 +328,14 @@ def subset_data(self): yield (hue, size, style), subset_data - def parse_hue(self, data, palette, order, limits): + def parse_hue(self, data, palette, order, norm): """Determine what colors to use given data characteristics.""" if self._empty_data(data): # Set default values when not using a hue mapping levels = [None] + limits = None + norm = None palette = {} var_type = None cmap = None @@ -351,6 +354,7 @@ def parse_hue(self, data, palette, order, limits): if var_type == "categorical": cmap = None + limits = None levels, palette = self.categorical_to_palette( data, order, palette ) @@ -359,11 +363,13 @@ def parse_hue(self, data, palette, order, limits): elif var_type == "numeric": - levels, palette, cmap, limits = self.numeric_to_palette( - data, order, palette, limits + levels, palette, cmap, norm = self.numeric_to_palette( + data, order, palette, norm ) + limits = norm.vmin, norm.vmax self.hue_levels = levels + self.hue_norm = norm self.hue_limits = limits self.hue_type = var_type self.palette = palette @@ -477,7 +483,7 @@ def parse_style(self, data, markers, dashes, order): # but there would be additional complexity with specifying the # weight of the line art markers without overwhelming the filled # ones with the edges. So for now, we will disallow mixtures. - if not all(filled_markers) and any(filled_markers): + if any(filled_markers) and not all(filled_markers): err = "Filled and line art markers cannot be mixed" raise ValueError(err) @@ -529,7 +535,10 @@ def update(var_name, val_name, **kws): keys.append(key) legend_data[key] = dict(**kws) - ticker = mpl.ticker.MaxNLocator(nbins=3) + if isinstance(self.hue_norm, mpl.colors.LogNorm): + ticker = mpl.ticker.LogLocator(numticks=3) + else: + ticker = mpl.ticker.MaxNLocator(nbins=3) # -- Add a legend for hue semantics @@ -584,7 +593,7 @@ class _LinePlotter(_BasicPlotter): def __init__(self, x=None, y=None, hue=None, size=None, style=None, data=None, - palette=None, hue_order=None, hue_limits=None, + palette=None, hue_order=None, hue_norm=None, sizes=None, size_order=None, size_limits=None, dashes=None, markers=None, style_order=None, units=None, estimator=None, ci=None, n_boot=None, @@ -598,7 +607,7 @@ def __init__(self, np.r_[.5, 2] * mpl.rcParams["lines.linewidth"] ) - self.parse_hue(plot_data["hue"], palette, hue_order, hue_limits) + self.parse_hue(plot_data["hue"], palette, hue_order, hue_norm) self.parse_size(plot_data["size"], sizes, size_order, size_limits) self.parse_style(plot_data["style"], markers, dashes, style_order) @@ -771,7 +780,7 @@ class _ScatterPlotter(_BasicPlotter): def __init__(self, x=None, y=None, hue=None, size=None, style=None, data=None, - palette=None, hue_order=None, hue_limits=None, + palette=None, hue_order=None, hue_norm=None, sizes=None, size_order=None, size_limits=None, markers=None, style_order=None, x_bins=None, y_bins=None, @@ -787,7 +796,7 @@ def __init__(self, np.r_[.5, 2] * np.square(mpl.rcParams["lines.markersize"]) ) - self.parse_hue(plot_data["hue"], palette, hue_order, hue_limits) + self.parse_hue(plot_data["hue"], palette, hue_order, hue_norm) self.parse_size(plot_data["size"], sizes, size_order, size_limits) self.parse_style(plot_data["style"], markers, None, style_order) self.units = units @@ -897,9 +906,9 @@ def plot(self, ax, kws): otherwise they are determined from the data. Not relevant when the ``hue`` variable is numeric.\ """), - hue_limits=dedent("""\ - hue_limits : tuple, optional - Limits in data units to use for the colormap applied to the ``hue`` + hue_norm=dedent("""\ + hue_norm : tuple or Normalize object, optional + Normalization in data units for colormap applied to the ``hue`` variable when it is numeric. Not relevant if it is categorical.\ """), sizes=dedent("""\ @@ -982,7 +991,7 @@ def plot(self, ax, kws): def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, - palette=None, hue_order=None, hue_limits=None, + palette=None, hue_order=None, hue_norm=None, sizes=None, size_order=None, size_limits=None, dashes=True, markers=None, style_order=None, units=None, estimator="mean", ci=95, n_boot=1000, @@ -991,7 +1000,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, p = _LinePlotter( x=x, y=y, hue=hue, size=size, style=style, data=data, - palette=palette, hue_order=hue_order, hue_limits=hue_limits, + palette=palette, hue_order=hue_order, hue_norm=hue_norm, sizes=sizes, size_order=size_order, size_limits=size_limits, dashes=dashes, markers=markers, style_order=style_order, units=units, estimator=estimator, ci=ci, n_boot=n_boot, @@ -1033,7 +1042,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, {data} {palette} {hue_order} - {hue_limits} + {hue_norm} {sizes} {size_order} {size_limits} @@ -1157,7 +1166,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, >>> ax = sns.lineplot(x="time", y="firing_rate", ... hue="coherence", style="choice", - ... hue_limits=(0, 100), data=dots) + ... hue_norm=(0, 100), data=dots) Use a different color palette: @@ -1236,7 +1245,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, - palette=None, hue_order=None, hue_limits=None, + palette=None, hue_order=None, hue_norm=None, sizes=None, size_order=None, size_limits=None, markers=True, style_order=None, x_bins=None, y_bins=None, @@ -1246,7 +1255,7 @@ def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, p = _ScatterPlotter( x=x, y=y, hue=hue, style=style, size=size, data=data, - palette=palette, hue_order=hue_order, hue_limits=hue_limits, + palette=palette, hue_order=hue_order, hue_norm=hue_norm, sizes=sizes, size_order=size_order, size_limits=size_limits, markers=markers, style_order=style_order, x_bins=x_bins, y_bins=y_bins, @@ -1284,7 +1293,7 @@ def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, {data} {palette} {hue_order} - {hue_limits} + {hue_norm} {sizes} {size_order} {size_limits} @@ -1405,7 +1414,7 @@ def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, >>> cmap = sns.cubehelix_palette(dark=.3, light=.8, as_cmap=True) >>> ax = sns.scatterplot(x="total_bill", y="tip", ... hue="size", size="size", - ... sizes=(20, 200), hue_limits=(0, 7), + ... sizes=(20, 200), hue_norm=(0, 7), ... legend="full", data=tips) Vary the size with a categorical variable, and use a different palette: From 8b065bb3b40860cb390e5d7305355f2bf0b3497f Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 27 Jun 2018 11:37:12 -0400 Subject: [PATCH 0750/1738] Test norm object --- seaborn/tests/test_basic.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index f6bfd7d62d..173ee12926 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -472,9 +472,18 @@ def test_parse_hue_numeric(self, long_df): assert p.hue_limits == (p.plot_data.hue.min(), p.plot_data.hue.max()) # Test specified hue limits - hue_limits = 1, 4 - p.parse_hue(p.plot_data.hue, None, None, hue_limits) - assert p.hue_limits == hue_limits + hue_norm = 1, 4 + p.parse_hue(p.plot_data.hue, None, None, hue_norm) + assert p.hue_limits == hue_norm + assert isinstance(p.hue_norm, mpl.colors.Normalize) + assert p.hue_norm.vmin == hue_norm[0] + assert p.hue_norm.vmax == hue_norm[1] + + # Test Normalize object + hue_norm = mpl.colors.PowerNorm(2, vmin=1, vmax=10) + p.parse_hue(p.plot_data.hue, None, None, hue_norm) + assert p.hue_limits == (hue_norm.vmin, hue_norm.vmax) + assert p.hue_norm is hue_norm # Test default colormap values hmin, hmax = p.plot_data.hue.min(), p.plot_data.hue.max() @@ -483,9 +492,9 @@ def test_parse_hue_numeric(self, long_df): assert p.palette[hmax] == pytest.approx(p.cmap(1.0)) # Test specified colormap values - hue_limits = hmin - 1, hmax - 1 - p.parse_hue(p.plot_data.hue, None, None, hue_limits) - norm_min = (hmin - hue_limits[0]) / (hue_limits[1] - hue_limits[0]) + hue_norm = hmin - 1, hmax - 1 + p.parse_hue(p.plot_data.hue, None, None, hue_norm) + norm_min = (hmin - hue_norm[0]) / (hue_norm[1] - hue_norm[0]) assert p.palette[hmin] == pytest.approx(p.cmap(norm_min)) assert p.palette[hmax] == pytest.approx(p.cmap(1.0)) From ccdd30a5ddffd93cdab521ab3d4673c4ef4398ec Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 27 Jun 2018 11:37:46 -0400 Subject: [PATCH 0751/1738] Fix cmap lookup to handle log normalization more robustly --- seaborn/basic.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 62c9d1abf8..18c9f83786 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -259,14 +259,20 @@ def numeric_to_palette(self, data, order, palette, norm): if not norm.scaled(): norm(np.asarray(data.dropna())) - palette = {l: cmap(norm(l)) for l in levels} + # TODO this should also use color_lookup, but that needs the + # class attributes that get set after using this function... + palette = dict(zip(levels, cmap(norm(levels)))) + # palette = {l: cmap(norm([l, 1]))[0] for l in levels} return levels, palette, cmap, norm def color_lookup(self, key): """Return the color corresponding to the hue level.""" if self.hue_type == "numeric": - return self.cmap(self.hue_norm(key)) + normed = self.hue_norm(key) + if np.ma.is_masked(normed): + normed = np.nan + return self.cmap(normed) elif self.hue_type == "categorical": return self.palette[key] @@ -1159,14 +1165,15 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, ... hue="coherence", style="choice", ... data=dots) - Change the data limits over which the colormap is normalized: + Use a different normalization for the colormap: .. plot:: :context: close-figs + >>> from matplotlib.colors import LogNorm >>> ax = sns.lineplot(x="time", y="firing_rate", ... hue="coherence", style="choice", - ... hue_norm=(0, 100), data=dots) + ... hue_norm=LogNorm(), data=dots) Use a different color palette: @@ -1175,7 +1182,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, >>> ax = sns.lineplot(x="time", y="firing_rate", ... hue="coherence", style="choice", - ... palette="viridis_r", data=dots) + ... palette="ch:2.5,.25", data=dots) Use specific color values, treating the hue variable as categorical: @@ -1203,7 +1210,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, >>> ax = sns.lineplot(x="time", y="firing_rate", ... size="coherence", hue="choice", - ... sizes=(.2, 1), data=dots) + ... sizes=(.25, 2.5), data=dots) Plot from a wide-form DataFrame: From 4bdabd80fa17c54c1a85d243f06374276d4840e1 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 27 Jun 2018 15:37:19 -0400 Subject: [PATCH 0752/1738] Accept a Normalize object for size_norm --- seaborn/basic.py | 89 ++++++++++++++++++++++--------------- seaborn/tests/test_basic.py | 36 +++++++++++++-- 2 files changed, 85 insertions(+), 40 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 18c9f83786..771aba98b1 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -279,9 +279,11 @@ def color_lookup(self, key): def size_lookup(self, key): """Return the size corresponding to the size level.""" if self.size_type == "numeric": - norm = mpl.colors.Normalize(*self.size_limits, clip=True) min_size, max_size = self.size_range - return min_size + norm(key) * (max_size - min_size) + val = self.size_norm(key) + if np.ma.is_masked(val): + return 0 + return min_size + val * (max_size - min_size) elif self.size_type == "categorical": return self.sizes[key] @@ -381,10 +383,15 @@ def parse_hue(self, data, palette, order, norm): self.palette = palette self.cmap = cmap - def parse_size(self, data, sizes, order, limits): + def parse_size(self, data, sizes, order, norm): """Determine the linewidths given data characteristics.""" + + # TODO could break out two options like parse_hue does for clarity + if self._empty_data(data): levels = [None] + limits = None + norm = None sizes = {} var_type = None width_range = None @@ -393,8 +400,8 @@ def parse_size(self, data, sizes, order, limits): var_type = self._semantic_type(data) if var_type == "categorical": - levels = categorical_order(data) - numbers = np.arange(0, len(levels))[::-1] + levels = categorical_order(data, order) + numbers = np.arange(1, 1 + len(levels))[::-1] elif var_type == "numeric": levels = numbers = np.sort(remove_na(data.unique())) @@ -431,24 +438,31 @@ def parse_size(self, data, sizes, order, limits): raise ValueError(err) width_range = min_width, max_width - # Infer the range of numeric values to map to sizes - if limits is None: - s_min, s_max = numbers.min(), numbers.max() - else: - s_min, s_max = limits - s_min = numbers.min() if s_min is None else s_min - s_max = numbers.max() if s_max is None else s_max + if norm is None: + norm = mpl.colors.Normalize() + elif isinstance(norm, tuple): + norm = mpl.colors.Normalize(*norm) + elif not isinstance(norm, mpl.colors.Normalize): + err = ("``size_norm`` must be None, tuple, " + "or Normalize object.") + raise ValueError(err) + + norm.clip = True + if not norm.scaled(): + norm(np.asarray(numbers)) + limits = norm.vmin, norm.vmax - # Map the numeric labels into the range of sizes - # TODO rework to use size_lookup from above - limits = s_min, s_max - normalize = mpl.colors.Normalize(s_min, s_max, clip=True) - sizes = {l: min_width + normalize(n) * (max_width - min_width) - for l, n in zip(levels, numbers)} + scl = norm(numbers) + widths = np.asarray(min_width + scl * (max_width - min_width)) + widths[scl.mask] = 0 + sizes = dict(zip(levels, widths)) + # sizes = {l: min_width + norm(n) * (max_width - min_width) + # for l, n in zip(levels, numbers)} self.sizes = sizes self.size_type = var_type self.size_levels = levels + self.size_norm = norm self.size_limits = limits self.size_range = width_range @@ -541,14 +555,13 @@ def update(var_name, val_name, **kws): keys.append(key) legend_data[key] = dict(**kws) - if isinstance(self.hue_norm, mpl.colors.LogNorm): - ticker = mpl.ticker.LogLocator(numticks=3) - else: - ticker = mpl.ticker.MaxNLocator(nbins=3) - # -- Add a legend for hue semantics if verbosity == "brief" and self.hue_type == "numeric": + if isinstance(self.hue_norm, mpl.colors.LogNorm): + ticker = mpl.ticker.LogLocator(numticks=3) + else: + ticker = mpl.ticker.MaxNLocator(nbins=3) hue_levels = (ticker.tick_values(*self.hue_limits) .astype(self.plot_data["hue"].dtype)) else: @@ -562,6 +575,10 @@ def update(var_name, val_name, **kws): # -- Add a legend for size semantics if verbosity == "brief" and self.size_type == "numeric": + if isinstance(self.size_norm, mpl.colors.LogNorm): + ticker = mpl.ticker.LogLocator(numticks=3) + else: + ticker = mpl.ticker.MaxNLocator(nbins=3) size_levels = (ticker.tick_values(*self.size_limits) .astype(self.plot_data["size"].dtype)) else: @@ -600,7 +617,7 @@ class _LinePlotter(_BasicPlotter): def __init__(self, x=None, y=None, hue=None, size=None, style=None, data=None, palette=None, hue_order=None, hue_norm=None, - sizes=None, size_order=None, size_limits=None, + sizes=None, size_order=None, size_norm=None, dashes=None, markers=None, style_order=None, units=None, estimator=None, ci=None, n_boot=None, sort=True, err_style=None, err_kws=None, legend=None): @@ -614,7 +631,7 @@ def __init__(self, ) self.parse_hue(plot_data["hue"], palette, hue_order, hue_norm) - self.parse_size(plot_data["size"], sizes, size_order, size_limits) + self.parse_size(plot_data["size"], sizes, size_order, size_norm) self.parse_style(plot_data["style"], markers, dashes, style_order) self.units = units @@ -787,7 +804,7 @@ class _ScatterPlotter(_BasicPlotter): def __init__(self, x=None, y=None, hue=None, size=None, style=None, data=None, palette=None, hue_order=None, hue_norm=None, - sizes=None, size_order=None, size_limits=None, + sizes=None, size_order=None, size_norm=None, markers=None, style_order=None, x_bins=None, y_bins=None, units=None, estimator=None, ci=None, n_boot=None, @@ -803,7 +820,7 @@ def __init__(self, ) self.parse_hue(plot_data["hue"], palette, hue_order, hue_norm) - self.parse_size(plot_data["size"], sizes, size_order, size_limits) + self.parse_size(plot_data["size"], sizes, size_order, size_norm) self.parse_style(plot_data["style"], markers, None, style_order) self.units = units @@ -931,9 +948,9 @@ def plot(self, ax, kws): otherwise they are determined from the data. Not relevant when the ``size`` variable is numeric.\ """), - size_limits=dedent("""\ - size_limits : tuple, optional - Limits in data units to use for the size normalization when the + size_norm=dedent("""\ + size_norm : tuple or Normalize object, optional + Normalization in data units for scaling plot objects when the ``size`` variable is numeric.\ """), markers=dedent("""\ @@ -998,7 +1015,7 @@ def plot(self, ax, kws): def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, palette=None, hue_order=None, hue_norm=None, - sizes=None, size_order=None, size_limits=None, + sizes=None, size_order=None, size_norm=None, dashes=True, markers=None, style_order=None, units=None, estimator="mean", ci=95, n_boot=1000, sort=True, err_style="band", err_kws=None, @@ -1007,7 +1024,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, p = _LinePlotter( x=x, y=y, hue=hue, size=size, style=style, data=data, palette=palette, hue_order=hue_order, hue_norm=hue_norm, - sizes=sizes, size_order=size_order, size_limits=size_limits, + sizes=sizes, size_order=size_order, size_norm=size_norm, dashes=dashes, markers=markers, style_order=style_order, units=units, estimator=estimator, ci=ci, n_boot=n_boot, sort=sort, err_style=err_style, err_kws=err_kws, legend=legend, @@ -1051,7 +1068,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, {hue_norm} {sizes} {size_order} - {size_limits} + {size_norm} dashes : boolean, list, or dictionary, optional Object determining how to draw the lines for different levels of the ``style`` variable. Setting to ``True`` will use default dash codes, or @@ -1253,7 +1270,7 @@ def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, palette=None, hue_order=None, hue_norm=None, - sizes=None, size_order=None, size_limits=None, + sizes=None, size_order=None, size_norm=None, markers=True, style_order=None, x_bins=None, y_bins=None, units=None, estimator=None, ci=95, n_boot=1000, @@ -1263,7 +1280,7 @@ def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, p = _ScatterPlotter( x=x, y=y, hue=hue, style=style, size=size, data=data, palette=palette, hue_order=hue_order, hue_norm=hue_norm, - sizes=sizes, size_order=size_order, size_limits=size_limits, + sizes=sizes, size_order=size_order, size_norm=size_norm, markers=markers, style_order=style_order, x_bins=x_bins, y_bins=y_bins, estimator=estimator, ci=ci, n_boot=n_boot, @@ -1303,7 +1320,7 @@ def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, {hue_norm} {sizes} {size_order} - {size_limits} + {size_norm} {markers} {style_order} {{x,y}}_bins : lists or arrays or functions diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 173ee12926..4093d5903a 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -522,6 +522,11 @@ def test_parse_hue_numeric(self, long_df): with pytest.raises(ValueError): p.parse_hue(p.plot_data.hue, palette, None, None) + # Test bad norm argument + hue_norm = "not a norm" + with pytest.raises(ValueError): + p.parse_hue(p.plot_data.hue, None, None, hue_norm) + def test_parse_size(self, long_df): p = basic._LinePlotter(x="x", y="y", size="s", data=long_df) @@ -545,14 +550,31 @@ def test_parse_size(self, long_df): p.parse_size(p.plot_data["size"], sizes, None, None) assert p.size_limits == default_limits - # Test size values inferred from ranges + # Test size values with normalization range sizes = (1, 5) - size_limits = (1, 10) - p.parse_size(p.plot_data["size"], sizes, None, size_limits) - normalize = mpl.colors.Normalize(*size_limits, clip=False) + size_norm = (1, 10) + p.parse_size(p.plot_data["size"], sizes, None, size_norm) + normalize = mpl.colors.Normalize(*size_norm, clip=True) for level, width in p.sizes.items(): assert width == sizes[0] + (sizes[1] - sizes[0]) * normalize(level) + # Test size values with normalization object + sizes = (1, 5) + size_norm = mpl.colors.LogNorm(1, 10, clip=False) + p.parse_size(p.plot_data["size"], sizes, None, size_norm) + assert p.size_norm.clip + for level, width in p.sizes.items(): + assert width == sizes[0] + (sizes[1] - sizes[0]) * size_norm(level) + + # Test specified size order + var = "a" + levels = long_df[var].unique() + sizes = [1, 4, 6] + size_order = [levels[1], levels[2], levels[0]] + p = basic._LinePlotter(x="x", y="y", size=var, data=long_df) + p.parse_size(p.plot_data["size"], sizes, size_order, None) + assert p.sizes == dict(zip(size_order, sizes)) + # Test list of sizes var = "a" levels = categorical_order(long_df[var]) @@ -584,6 +606,12 @@ def test_parse_size(self, long_df): with pytest.raises(ValueError): p.parse_size(p.plot_data["size"], sizes, None, None) + # Test bad norm argument + size_norm = "not a norm" + p = basic._LinePlotter(x="x", y="y", size="s", data=long_df) + with pytest.raises(ValueError): + p.parse_size(p.plot_data["size"], None, None, size_norm) + def test_parse_style(self, long_df): p = basic._LinePlotter(x="x", y="y", style="a", data=long_df) From d8bfdc3ea82461c524bc5d499c83678d65e50c0a Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 27 Jun 2018 15:43:57 -0400 Subject: [PATCH 0753/1738] Treat binary hue data as categorical --- seaborn/basic.py | 5 ++++- seaborn/tests/test_basic.py | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 771aba98b1..d0b94da40f 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -522,7 +522,10 @@ def _semantic_type(self, data): return "categorical" else: try: - data.astype(np.float) + float_data = data.astype(np.float) + values = np.unique(float_data.dropna()) + if np.array_equal(values, np.array([0., 1.])): + return "categorical" return "numeric" except (ValueError, TypeError): return "categorical" diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 4093d5903a..3e968a0d61 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -57,6 +57,7 @@ def long_df(self): y=rs.randn(n), a=np.take(list("abc"), rs.randint(0, 3, n)), b=np.take(list("mnop"), rs.randint(0, 4, n)), + c=np.take(list([0, 1]), rs.randint(0, 2, n)), s=np.take([2, 4, 8], rs.randint(0, 3, n)), )) @@ -444,6 +445,11 @@ def test_parse_hue_categorical(self, wide_df, long_df): expected_palette = dict(zip(levels, expected_colors)) assert p.palette == expected_palette + # Test binary data + p = basic._LinePlotter(x="x", y="y", hue="c", data=long_df) + assert p.hue_levels == [0, 1] + assert p.hue_type is "categorical" + def test_parse_hue_numeric(self, long_df): p = basic._LinePlotter(x="x", y="y", hue="s", data=long_df) From 4789e71055e1cece7aafa654c19e5bfcbc5bf92f Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 27 Jun 2018 16:32:45 -0400 Subject: [PATCH 0754/1738] Old numpy/matplotlib compat --- seaborn/basic.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index d0b94da40f..3c3e3da464 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -423,7 +423,7 @@ def parse_size(self, data, sizes, order, norm): try: limits = min(sizes.keys()), max(sizes.keys()) except TypeError: - pass + limits = None else: @@ -454,7 +454,8 @@ def parse_size(self, data, sizes, order, norm): scl = norm(numbers) widths = np.asarray(min_width + scl * (max_width - min_width)) - widths[scl.mask] = 0 + if scl.mask.any(): + widths[scl.mask] = 0 sizes = dict(zip(levels, widths)) # sizes = {l: min_width + norm(n) * (max_width - min_width) # for l, n in zip(levels, numbers)} @@ -525,7 +526,7 @@ def _semantic_type(self, data): float_data = data.astype(np.float) values = np.unique(float_data.dropna()) if np.array_equal(values, np.array([0., 1.])): - return "categorical" + return "categorical" return "numeric" except (ValueError, TypeError): return "categorical" From 27b43de97c672beecab993c2ff62278ace44a556 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 27 Jun 2018 17:11:07 -0400 Subject: [PATCH 0755/1738] Change default basic numerical palette to cubehelix --- seaborn/basic.py | 5 ++--- seaborn/palettes.py | 2 +- seaborn/tests/test_basic.py | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 3c3e3da464..7f1aed3b49 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -234,9 +234,8 @@ def numeric_to_palette(self, data, order, palette, norm): # at the extremes of the colormap against the background? # Identify the colormap to use - if palette is None: - cmap = mpl.cm.get_cmap(plt.rcParams["image.cmap"]) - elif isinstance(palette, mpl.colors.Colormap): + palette = "ch:" if palette is None else palette + if isinstance(palette, mpl.colors.Colormap): cmap = palette elif str(palette).startswith("ch:"): args, kwargs = _parse_cubehelix_args(palette) diff --git a/seaborn/palettes.py b/seaborn/palettes.py index a7ebbdde7e..63070d6661 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -969,7 +969,7 @@ def color(x): if reverse: x_256 = x_256[::-1] pal_256 = cmap(x_256) - cmap = mpl.colors.ListedColormap(pal_256) + cmap = mpl.colors.ListedColormap(pal_256, "seaborn_cubehelix") return cmap else: return _ColorPalette(pal) diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 3e968a0d61..8a25f43664 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -456,7 +456,7 @@ def test_parse_hue_numeric(self, long_df): hue_levels = list(np.sort(long_df.s.unique())) assert p.hue_levels == hue_levels assert p.hue_type is "numeric" - assert p.cmap is mpl.cm.get_cmap(mpl.rcParams["image.cmap"]) + assert p.cmap.name == "seaborn_cubehelix" # Test named colormap palette = "Purples" From b6c3ba094b62371457e94ce7d944389b24ce2482 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 27 Jun 2018 20:04:05 -0400 Subject: [PATCH 0756/1738] Allow shorthand for cubehelix kwargs --- doc/releases/v0.9.0.txt | 2 +- seaborn/palettes.py | 30 +++++++++++++++++++++++++++++- seaborn/tests/test_palettes.py | 21 +++++++++++++++------ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index 79880e593e..8e04b958b9 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -28,7 +28,7 @@ v0.9.0 (Unreleased) - Added the ``subplot_kws`` parameter to :class:`PairGrid` for more flexibility. -- Enhanced :func:`color_palette` to accept a parameterized specification of a cubehelix palette in in a string, prefixed with ``"ch:"`` (e.g. ``"ch:-.1,.2,light=.7"``). This will be accepted by any seaborn function with a ``palette=`` parameter. +- Enhanced :func:`color_palette` to accept a parameterized specification of a cubehelix palette in in a string, prefixed with ``"ch:"`` (e.g. ``"ch:-.1,.2,l=.7"``). None that keyword arguments can be spelled out or referenced using only their first letter, except reversing the palette is accomplished by appending ``"_r"`` as with other matplotlib colormaps. This specification will be accepted by any seaborn function with a ``palette=`` parameter. - Changed the default diagonal plots in :func:`pairplot` to use `func`:kdeplot` when a ``"hue"`` dimension is used. diff --git a/seaborn/palettes.py b/seaborn/palettes.py index 63070d6661..85b2d1cfde 100644 --- a/seaborn/palettes.py +++ b/seaborn/palettes.py @@ -842,6 +842,10 @@ def cubehelix_palette(n_colors=6, start=0, rot=.4, gamma=1.0, hue=0.8, user more control over the look of the palette and has a different set of defaults. + In addition to using this function, it is also possible to generate a + cubehelix palette generally in seaborn using a string-shorthand; see the + example below. + Parameters ---------- n_colors : int @@ -934,6 +938,13 @@ def cubehelix_palette(n_colors=6, start=0, rot=.4, gamma=1.0, hue=0.8, >>> cmap = sns.cubehelix_palette(dark=0, light=1, as_cmap=True) >>> ax = sns.heatmap(x, cmap=cmap) + Use through the :func:`color_palette` interface: + + .. plot:: + :context: close-figs + + >>> sns.palplot(sns.color_palette("ch:2,r=.2,l=.6")) + """ def get_color_function(p0, p1): # Copied from matplotlib because it lives in private module @@ -977,11 +988,18 @@ def color(x): def _parse_cubehelix_args(argstr): """Turn stringified cubehelix params into args/kwargs.""" + if argstr.startswith("ch:"): argstr = argstr[3:] + if argstr.endswith("_r"): + reverse = True + argstr = argstr[:-2] + else: + reverse = False + if not argstr: - return [], {} + return [], {"reverse": reverse} all_args = argstr.split(",") @@ -990,6 +1008,16 @@ def _parse_cubehelix_args(argstr): kwargs = [a.split("=") for a in all_args if "=" in a] kwargs = {k.strip(" "): float(v.strip(" ")) for k, v in kwargs} + kwarg_map = dict( + s="start", r="rot", g="gamma", + h="hue", l="light", d="dark", # noqa: E741 + ) + + kwargs = {kwarg_map.get(k, k): v for k, v in kwargs.items()} + + if reverse: + kwargs["reverse"] = True + return args, kwargs diff --git a/seaborn/tests/test_palettes.py b/seaborn/tests/test_palettes.py index 1b4c7e4d4f..a6b4b2c142 100644 --- a/seaborn/tests/test_palettes.py +++ b/seaborn/tests/test_palettes.py @@ -275,14 +275,23 @@ def test_cubehelix_cmap(self): def test_cubehelix_code(self): - pal1 = palettes.color_palette("ch:", 8) - pal2 = palettes.color_palette(palettes.cubehelix_palette(8)) + color_palette = palettes.color_palette + cubehelix_palette = palettes.cubehelix_palette + + pal1 = color_palette("ch:", 8) + pal2 = color_palette(cubehelix_palette(8)) + assert pal1 == pal2 + + pal1 = color_palette("ch:.5, -.25,hue = .5,light=.75", 8) + pal2 = color_palette(cubehelix_palette(8, .5, -.25, hue=.5, light=.75)) + assert pal1 == pal2 + + pal1 = color_palette("ch:h=1,r=.5", 9) + pal2 = color_palette(cubehelix_palette(9, hue=1, rot=.5)) assert pal1 == pal2 - pal1 = palettes.color_palette("ch:.5, -.25,hue = .5,light=.75", 8) - pal2 = palettes.color_palette( - palettes.cubehelix_palette(8, .5, -.25, hue=.5, light=.75) - ) + pal1 = color_palette("ch:_r", 6) + pal2 = color_palette(cubehelix_palette(6, reverse=True)) assert pal1 == pal2 def test_xkcd_palette(self): From 0447ae7a2598ec3eba838c42389c01b4ddc0bd4a Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 27 Jun 2018 20:47:43 -0400 Subject: [PATCH 0757/1738] Improve test coverage --- seaborn/tests/test_basic.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 8a25f43664..94a806a47d 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -976,6 +976,22 @@ def test_legend_data(self, long_df): with pytest.raises(ValueError): p.add_legend_data(ax) + ax.clear() + p = basic._LinePlotter(x=x, y=y, hue=z, + hue_norm=mpl.colors.LogNorm(), + legend="brief") + p.add_legend_data(ax) + handles, labels = ax.get_legend_handles_labels() + assert float(labels[2]) / float(labels[1]) == 10 + + ax.clear() + p = basic._LinePlotter(x=x, y=y, size=z, + size_norm=mpl.colors.LogNorm(), + legend="brief") + p.add_legend_data(ax) + handles, labels = ax.get_legend_handles_labels() + assert float(labels[2]) / float(labels[1]) == 10 + def test_plot(self, long_df, repeated_df): f, ax = plt.subplots() @@ -1144,6 +1160,9 @@ def test_lineplot_smoke(self, flat_array, flat_series, f, ax = plt.subplots() + basic.lineplot([], []) + ax.clear() + basic.lineplot(data=flat_array) ax.clear() @@ -1416,6 +1435,9 @@ def test_scatterplot_smoke(self, flat_array, flat_series, f, ax = plt.subplots() + basic.scatterplot([], []) + ax.clear() + basic.scatterplot(data=flat_array) ax.clear() From 6945613db950f5a834aff2e5dc623fb3ce4c6443 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 30 Jun 2018 17:55:17 -0400 Subject: [PATCH 0758/1738] Initial relplot implementation --- seaborn/basic.py | 77 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 7f1aed3b49..bd0e90414c 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -15,9 +15,10 @@ remove_na) from .algorithms import bootstrap from .palettes import color_palette, cubehelix_palette, _parse_cubehelix_args +from .axisgrid import FacetGrid, _facet_docs -__all__ = ["lineplot", "scatterplot"] +__all__ = ["relplot", "scatterplot", "lineplot"] class _BasicPlotter(object): @@ -808,7 +809,7 @@ def __init__(self, x=None, y=None, hue=None, size=None, style=None, data=None, palette=None, hue_order=None, hue_norm=None, sizes=None, size_order=None, size_norm=None, - markers=None, style_order=None, + dashes=None, markers=None, style_order=None, x_bins=None, y_bins=None, units=None, estimator=None, ci=None, n_boot=None, alpha=None, x_jitter=None, y_jitter=None, @@ -1487,3 +1488,75 @@ def scatterplot(x=None, y=None, hue=None, style=None, size=None, data=None, >>> ax = sns.scatterplot(data=wide_df) """).format(**_basic_docs) + + +def relplot(x=None, y=None, hue=None, size=None, style=None, data=None, + row=None, col=None, col_wrap=None, row_order=None, col_order=None, + palette=None, hue_order=None, hue_norm=None, + sizes=None, size_order=None, size_norm=None, + markers=None, dashes=None, style_order=None, + legend="brief", kind="scatter", facet_kws=None, **kwargs): + + if kind == "scatter": + + plotter = _ScatterPlotter + func = scatterplot + markers = True if markers is None else markers + + elif kind == "line": + + plotter = _LinePlotter + func = lineplot + dashes = True if dashes is None else dashes + + else: + err = "Plot kind {} not recognized".format(kind) + raise ValueError(err) + + p = plotter( + x=x, y=y, hue=hue, size=size, style=style, data=data, + palette=palette, hue_order=hue_order, hue_norm=hue_norm, + sizes=sizes, size_order=size_order, size_norm=size_norm, + markers=markers, dashes=dashes, style_order=style_order, + ) + + palette = p.palette if p.palette else None + hue_order = p.hue_levels if any(p.hue_levels) else None + hue_norm = p.hue_norm if p.hue_norm is not None else None + + sizes = p.sizes if p.sizes else None + size_order = p.size_levels if any(p.size_levels) else None + size_norm = p.size_norm if p.size_norm is not None else None + + markers = p.markers if p.markers else None + dashes = p.dashes if p.dashes else None + style_order = p.style_levels if any(p.style_levels) else None + + plot_kws = dict( + palette=palette, hue_order=hue_order, hue_norm=p.hue_norm, + sizes=sizes, size_order=size_order, size_norm=p.size_norm, + markers=markers, dashes=dashes, style_order=style_order, + ) + plot_kws.update(kwargs) + if kind == "scatter": + plot_kws.pop("dashes") # TODO + + # TODO need to get facetgrid size ... needs rename to height? + facet_kws = {} if facet_kws is None else facet_kws + g = FacetGrid( + data=data, row=row, col=col, col_wrap=col_wrap, + row_order=row_order, col_order=col_order, + dropna=False, + **facet_kws + ) + + g.map_dataframe(func, x, y, + hue=hue, size=size, style=style, + **plot_kws) + + if legend and g._legend_data: + g.add_legend() + + g._plotter = p # TODO + + return g From 31dacfebadd3623ffbea51cb318845ec2276e050 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 30 Jun 2018 18:40:35 -0400 Subject: [PATCH 0759/1738] Default to not drawing a frame for an external Grid legend --- seaborn/axisgrid.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 5e3d5071a5..8c524cbeb8 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -82,6 +82,9 @@ def add_legend(self, legend_data=None, title=None, label_order=None, kwargs.setdefault("scatterpoints", 1) if self._legend_out: + + kwargs.setdefault("frameon", False) + # Draw a full-figure legend outside the grid figlegend = self.fig.legend(handles, label_order, "center right", **kwargs) From b9117b98067c7b019bdcc4c1f9237a13a8af93d3 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 30 Jun 2018 18:40:53 -0400 Subject: [PATCH 0760/1738] Defer scatterplot import in axisgrid module --- seaborn/axisgrid.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 8c524cbeb8..b117383bbb 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -13,7 +13,6 @@ from . import utils from .palettes import color_palette, blend_palette from .external.six import string_types -from .basic import scatterplot from .distributions import distplot, kdeplot, _freedman_diaconis_bins @@ -29,7 +28,6 @@ def set(self, **kwargs): """Set attributes on each subplot Axes.""" for ax in self.axes.flat: ax.set(**kwargs) - return self def savefig(self, *args, **kwargs): @@ -2067,6 +2065,7 @@ def pairplot(data, hue=None, hue_order=None, palette=None, plotter = grid.map if kind == "scatter": + from .basic import scatterplot # Avoid circular import plotter(scatterplot, **plot_kws) elif kind == "reg": from .regression import regplot # Avoid circular import From 4264106170ec6495249323aaa26bee62b9ac0b99 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 30 Jun 2018 18:41:28 -0400 Subject: [PATCH 0761/1738] Always include x and y in relational semantics --- seaborn/basic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index bd0e90414c..10a4be19b2 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -174,8 +174,8 @@ def establish_variables(self, x=None, y=None, # Determine which semantics have (some) data plot_valid = plot_data.notnull().any() - semantics = [ - name for name in ["x", "y", "hue", "size", "style"] + semantics = ["x", "y"] + [ + name for name in ["hue", "size", "style"] if plot_valid[name] ] From 95822be08d28761445aff3850fcfecf2df4fc398 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 30 Jun 2018 18:53:59 -0400 Subject: [PATCH 0762/1738] Initialize the scatterplot with a representative marker --- seaborn/basic.py | 7 +++++++ seaborn/tests/test_basic.py | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/seaborn/basic.py b/seaborn/basic.py index 10a4be19b2..a261c51cf8 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -853,6 +853,13 @@ def plot(self, ax, kws): kws.setdefault("linewidth", .75) # TODO scale with marker size? kws.setdefault("edgecolor", "w") + if self.markers: + # Use a representative marker so scatter sets the edgecolor + # properly for line art markers. We currently enforce either + # all or none line art so this works. + example_marker = list(self.markers.values())[0] + kws.setdefault("marker", example_marker) + # TODO this makes it impossible to vary alpha with hue which might # otherwise be useful? Should we just pass None? kws["alpha"] = 1 if self.alpha == "auto" else self.alpha diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 94a806a47d..3cf3d246b0 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -1375,6 +1375,15 @@ def test_plot(self, long_df, repeated_df): expected_colors = [p.palette[k] for k in p.plot_data["hue"]] assert self.colors_equal(points.get_facecolors(), expected_colors) + p = basic._ScatterPlotter(x="x", y="y", style="c", + markers=["+", "x"], data=long_df) + + ax.clear() + color = (1, .3, .8) + p.plot(ax, {"color": color}) + points = ax.collections[0] + assert self.colors_equal(points.get_edgecolors(), [color]) + p = basic._ScatterPlotter(x="x", y="y", size="a", data=long_df) ax.clear() From 4e4724e5e8365aed4a2511850ade5bfcebc559b9 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 30 Jun 2018 19:27:02 -0400 Subject: [PATCH 0763/1738] Very basic relplot test --- seaborn/basic.py | 17 +++++++++++++---- seaborn/tests/test_basic.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index a261c51cf8..2a67438166 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -1023,6 +1023,8 @@ def plot(self, ax, kws): ) +_basic_docs.update(_facet_docs) + def lineplot(x=None, y=None, hue=None, size=None, style=None, data=None, palette=None, hue_order=None, hue_norm=None, @@ -1520,6 +1522,7 @@ def relplot(x=None, y=None, hue=None, size=None, style=None, data=None, err = "Plot kind {} not recognized".format(kind) raise ValueError(err) + # Use the full dataset to establish how to draw the semantics p = plotter( x=x, y=y, hue=hue, size=size, style=style, data=data, palette=palette, hue_order=hue_order, hue_norm=hue_norm, @@ -1546,9 +1549,9 @@ def relplot(x=None, y=None, hue=None, size=None, style=None, data=None, ) plot_kws.update(kwargs) if kind == "scatter": - plot_kws.pop("dashes") # TODO + plot_kws.pop("dashes") - # TODO need to get facetgrid size ... needs rename to height? + # Set up the FacetGrid object facet_kws = {} if facet_kws is None else facet_kws g = FacetGrid( data=data, row=row, col=col, col_wrap=col_wrap, @@ -1557,13 +1560,19 @@ def relplot(x=None, y=None, hue=None, size=None, style=None, data=None, **facet_kws ) + # Draw the plot g.map_dataframe(func, x, y, hue=hue, size=size, style=style, **plot_kws) + # Show the legend if legend and g._legend_data: g.add_legend() - g._plotter = p # TODO - return g + + +relplot.__doc__ = dedent("""\ + Draw a relational plot onto a FacetGrid. + + """).format(**_basic_docs) diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 3cf3d246b0..e3499b1756 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -1503,3 +1503,33 @@ def test_scatterplot_smoke(self, flat_array, flat_series, basic.scatterplot(x="x", y="y", hue="a", size="s", data=missing_df) ax.clear() + + +class TestRelPlotter(TestBasicPlotter): + + def test_relplot_simple(self, long_df): + + g = basic.relplot(x="x", y="y", kind="scatter", data=long_df) + x, y = g.ax.collections[0].get_offsets().T + assert np.array_equal(x, long_df["x"]) + assert np.array_equal(y, long_df["y"]) + + g = basic.relplot(x="x", y="y", kind="line", data=long_df) + x, y = g.ax.lines[0].get_xydata().T + expected = long_df.groupby("x").y.mean() + assert np.array_equal(x, expected.index) + assert y == pytest.approx(expected.values) + + with pytest.raises(ValueError): + g = basic.relplot(x="x", y="y", kind="not_a_kind", data=long_df) + + def test_relplot_legend(self, long_df): + + g = basic.relplot(x="x", y="y", data=long_df) + assert g._legend is None + + g = basic.relplot(x="x", y="y", hue="a", data=long_df) + assert isinstance(g._legend, mpl.legend.Legend) + + g = basic.relplot(x="x", y="y", hue="a", legend=False, data=long_df) + assert g._legend is None From a94b6508c42a67b5ae73d4ad5f2554962477f6b6 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sat, 30 Jun 2018 20:45:55 -0400 Subject: [PATCH 0764/1738] More relplot tests --- seaborn/tests/test_basic.py | 39 ++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index e3499b1756..37904a323d 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -1523,13 +1523,50 @@ def test_relplot_simple(self, long_df): with pytest.raises(ValueError): g = basic.relplot(x="x", y="y", kind="not_a_kind", data=long_df) + def test_relplot_complex(self, long_df): + + for sem in ["hue", "size", "style"]: + g = basic.relplot(x="x", y="y", data=long_df, **{sem: "a"}) + x, y = g.ax.collections[0].get_offsets().T + assert np.array_equal(x, long_df["x"]) + assert np.array_equal(y, long_df["y"]) + + for sem in ["hue", "size", "style"]: + g = basic.relplot(x="x", y="y", col="c", data=long_df, + **{sem: "a"}) + grouped = long_df.groupby("c") + for (_, grp_df), ax in zip(grouped, g.axes.flat): + x, y = ax.collections[0].get_offsets().T + assert np.array_equal(x, grp_df["x"]) + assert np.array_equal(y, grp_df["y"]) + + for sem in ["size", "style"]: + g = basic.relplot(x="x", y="y", hue="b", col="c", data=long_df, + **{sem: "a"}) + grouped = long_df.groupby("c") + for (_, grp_df), ax in zip(grouped, g.axes.flat): + x, y = ax.collections[0].get_offsets().T + assert np.array_equal(x, grp_df["x"]) + assert np.array_equal(y, grp_df["y"]) + + for sem in ["hue", "size", "style"]: + g = basic.relplot(x="x", y="y", col="b", row="c", + data=long_df.sort_values(["c", "b"]), + **{sem: "a"}) + grouped = long_df.groupby(["c", "b"]) + for (_, grp_df), ax in zip(grouped, g.axes.flat): + x, y = ax.collections[0].get_offsets().T + assert np.array_equal(x, grp_df["x"]) + assert np.array_equal(y, grp_df["y"]) + def test_relplot_legend(self, long_df): g = basic.relplot(x="x", y="y", data=long_df) assert g._legend is None g = basic.relplot(x="x", y="y", hue="a", data=long_df) - assert isinstance(g._legend, mpl.legend.Legend) + texts = [t.get_text() for t in g._legend.texts] + assert np.array_equal(texts, long_df["a"].unique()) g = basic.relplot(x="x", y="y", hue="a", legend=False, data=long_df) assert g._legend is None From 4e891b62a21c40d01e5ffb677333c9ca0e993c10 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 1 Jul 2018 09:19:34 -0400 Subject: [PATCH 0765/1738] Ensure legend order --- seaborn/axisgrid.py | 6 +++--- seaborn/basic.py | 3 ++- seaborn/tests/test_basic.py | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index b117383bbb..264457129c 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -95,8 +95,8 @@ def add_legend(self, legend_data=None, title=None, label_order=None, # Calculate and set the new width of the figure so the legend fits legend_width = figlegend.get_window_extent().width / self.fig.dpi - figure_width = self.fig.get_figwidth() - self.fig.set_figwidth(figure_width + legend_width) + fig_width, fig_height = self.fig.get_size_inches() + self.fig.set_size_inches(fig_width + legend_width, fig_height) # Draw the plot again to get the new transformations if hasattr(self.fig.canvas, "get_renderer"): @@ -104,7 +104,7 @@ def add_legend(self, legend_data=None, title=None, label_order=None, # Now calculate how much space we need on the right side legend_width = figlegend.get_window_extent().width / self.fig.dpi - space_needed = legend_width / (figure_width + legend_width) + space_needed = legend_width / (fig_width + legend_width) margin = .04 if self._margin_titles else .01 self._space_needed = margin + space_needed right = 1 - self._space_needed diff --git a/seaborn/basic.py b/seaborn/basic.py index 2a67438166..1654419da4 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -1567,7 +1567,8 @@ def relplot(x=None, y=None, hue=None, size=None, style=None, data=None, # Show the legend if legend and g._legend_data: - g.add_legend() + _, labels = g.axes.flat[0].get_legend_handles_labels() + g.add_legend(label_order=labels) return g diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 37904a323d..2b3ac46369 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -7,7 +7,7 @@ import pytest from .. import basic from ..palettes import color_palette -from ..utils import categorical_order +from ..utils import categorical_order, sort_df class TestBasicPlotter(object): @@ -1551,7 +1551,7 @@ def test_relplot_complex(self, long_df): for sem in ["hue", "size", "style"]: g = basic.relplot(x="x", y="y", col="b", row="c", - data=long_df.sort_values(["c", "b"]), + data=sort_df(long_df, ["c", "b"]), **{sem: "a"}) grouped = long_df.groupby(["c", "b"]) for (_, grp_df), ax in zip(grouped, g.axes.flat): From 2631d4d4024ecda421e79b1b5543cd8e600f7152 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 1 Jul 2018 12:01:08 -0400 Subject: [PATCH 0766/1738] Deeper tests of relplot --- seaborn/tests/test_basic.py | 92 +++++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 24 deletions(-) diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 2b3ac46369..4c13261a40 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -12,6 +12,30 @@ class TestBasicPlotter(object): + def scatter_rgbs(self, collections): + rgbs = [] + for col in collections: + rgb = tuple(col.get_facecolor().squeeze()[:3]) + rgbs.append(rgb) + return rgbs + + def colors_equal(self, *args): + + equal = True + for c1, c2 in zip(*args): + c1 = mpl.colors.colorConverter.to_rgb(np.squeeze(c1)) + c2 = mpl.colors.colorConverter.to_rgb(np.squeeze(c1)) + equal &= c1 == c2 + return equal + + def paths_equal(self, *args): + + equal = True + for p1, p2 in zip(*args): + equal &= np.array_equal(p1.vertices, p2.vertices) + equal &= np.array_equal(p1.codes, p2.codes) + return equal + @pytest.fixture def wide_df(self): @@ -1223,30 +1247,6 @@ def test_lineplot_smoke(self, flat_array, flat_series, class TestScatterPlotter(TestBasicPlotter): - def scatter_rgbs(self, collections): - rgbs = [] - for col in collections: - rgb = tuple(col.get_facecolor().squeeze()[:3]) - rgbs.append(rgb) - return rgbs - - def colors_equal(self, *args): - - equal = True - for c1, c2 in zip(*args): - c1 = mpl.colors.colorConverter.to_rgb(np.squeeze(c1)) - c2 = mpl.colors.colorConverter.to_rgb(np.squeeze(c1)) - equal &= c1 == c2 - return equal - - def paths_equal(self, *args): - - equal = True - for p1, p2 in zip(*args): - equal &= np.array_equal(p1.vertices, p2.vertices) - equal &= np.array_equal(p1.codes, p2.codes) - return equal - def test_legend_data(self, long_df): m = mpl.markers.MarkerStyle("o") @@ -1559,6 +1559,50 @@ def test_relplot_complex(self, long_df): assert np.array_equal(x, grp_df["x"]) assert np.array_equal(y, grp_df["y"]) + def test_relplot_hues(self, long_df): + + palette = ["r", "b", "g"] + g = basic.relplot(x="x", y="y", hue="a", style="b", col="c", + palette=palette, data=long_df) + + palette = dict(zip(long_df["a"].unique(), palette)) + grouped = long_df.groupby("c") + for (_, grp_df), ax in zip(grouped, g.axes.flat): + points = ax.collections[0] + expected_hues = [palette[val] for val in grp_df["a"]] + assert self.colors_equal(points.get_facecolors(), expected_hues) + + def test_relplot_sizes(self, long_df): + + sizes = [5, 12, 7] + g = basic.relplot(x="x", y="y", size="a", hue="b", col="c", + sizes=sizes, data=long_df) + + sizes = dict(zip(long_df["a"].unique(), sizes)) + grouped = long_df.groupby("c") + for (_, grp_df), ax in zip(grouped, g.axes.flat): + points = ax.collections[0] + expected_sizes = [sizes[val] for val in grp_df["a"]] + assert np.array_equal(points.get_sizes(), expected_sizes) + + def test_relplot_styles(self, long_df): + + markers = ["o", "d", "s"] + g = basic.relplot(x="x", y="y", style="a", hue="b", col="c", + markers=markers, data=long_df) + + paths = [] + for m in markers: + m = mpl.markers.MarkerStyle(m) + paths.append(m.get_path().transformed(m.get_transform())) + paths = dict(zip(long_df["a"].unique(), paths)) + + grouped = long_df.groupby("c") + for (_, grp_df), ax in zip(grouped, g.axes.flat): + points = ax.collections[0] + expected_paths = [paths[val] for val in grp_df["a"]] + assert self.paths_equal(points.get_paths(), expected_paths) + def test_relplot_legend(self, long_df): g = basic.relplot(x="x", y="y", data=long_df) From afcd9496e7557e656c9d9ab094b339bc4707298a Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 1 Jul 2018 17:32:04 -0400 Subject: [PATCH 0767/1738] Rename `size` to `height` in multiplot grid objects and related functions --- doc/releases/v0.9.0.txt | 2 + doc/tutorial/axis_grids.ipynb | 37 +++++------- doc/tutorial/categorical.ipynb | 4 +- doc/tutorial/regression.ipynb | 6 +- seaborn/axisgrid.py | 102 ++++++++++++++++++++++----------- seaborn/basic.py | 5 +- seaborn/categorical.py | 32 +++++++---- seaborn/regression.py | 27 ++++++--- seaborn/tests/test_axisgrid.py | 14 ++--- 9 files changed, 139 insertions(+), 90 deletions(-) diff --git a/doc/releases/v0.9.0.txt b/doc/releases/v0.9.0.txt index 8e04b958b9..d3e31f46a4 100644 --- a/doc/releases/v0.9.0.txt +++ b/doc/releases/v0.9.0.txt @@ -12,6 +12,8 @@ v0.9.0 (Unreleased) - Updated the seaborn palettes ("deep", "muted", "colorblind", etc.) to correspond with the new 10-color matplotlib default. The legacy palettes are now available at "deep6", "muted6", "colorblind6", etc. Additionally, a few individual colors were tweaked for better consistency, aesthetics, and accessibility. +- Renamed the ``size`` parameter to ``height`` in multiplot grid objects (:class:`FacetGrid`, :class:`PairGrid`, and :class:`JointGrid`) along with functions that use them (:func:`factorplot`, :func:`lmplot`, :func:`pairplot`, and :func:`jointplot`) to avoid conflicts with the ``size`` parameter that is used in ``scatterplot`` and ``lineplot`` (necessary to make :func:`relplot` work) and also makes the meaning of the parameter a bit more clear. + - Calling :func:`color_palette` (or :func:`set_palette`) with a named qualitative palettes (i.e. one of the seaborn palettes, the colorbrewer qualitative palettes, or the matplotlib matplotlib tableau-derived palettes) and no specified number of colors will return all of the colors in the palette. This means that for some palettes, the returned list will have a different length than it did in previous versions. - Reorganized and updated some :func:`axes_style`/:func:`plotting_context` parameters to take advantage of improvements in the `matplotlib2 update `_. The biggest change involves using several new params in the "style" spec while moving parameters that used to implement the corresponding aesthetics to the "context" spec. For example, axes spines and ticks are now off instead of having their width/length zeroed out for the darkgrid style. That means the width/length of these elements can now be scaled in different contexts. The effect is a more cohesive appearance of the plots, especially in larger contexts. These changes include only minimal support for the 1.x matplotlib series. Users who are stuck on matplotlib 1.5 but wish to use seaborn styling may want to use the seaborn parameters that can be accessed through the `matplotlib stylesheet interface `_. diff --git a/doc/tutorial/axis_grids.ipynb b/doc/tutorial/axis_grids.ipynb index 1248af820a..45f14fa66c 100644 --- a/doc/tutorial/axis_grids.ipynb +++ b/doc/tutorial/axis_grids.ipynb @@ -163,7 +163,7 @@ "metadata": {}, "outputs": [], "source": [ - "g = sns.FacetGrid(tips, col=\"day\", size=4, aspect=.5)\n", + "g = sns.FacetGrid(tips, col=\"day\", height=4, aspect=.5)\n", "g.map(sns.barplot, \"sex\", \"total_bill\");" ] }, @@ -202,7 +202,7 @@ "source": [ "ordered_days = tips.day.value_counts().index\n", "g = sns.FacetGrid(tips, row=\"day\", row_order=ordered_days,\n", - " size=1.7, aspect=4,)\n", + " height=1.7, aspect=4,)\n", "g.map(sns.distplot, \"total_bill\", hist=False, rug=True);" ] }, @@ -220,7 +220,7 @@ "outputs": [], "source": [ "pal = dict(Lunch=\"seagreen\", Dinner=\"gray\")\n", - "g = sns.FacetGrid(tips, hue=\"time\", palette=pal, size=5)\n", + "g = sns.FacetGrid(tips, hue=\"time\", palette=pal, height=5)\n", "g.map(plt.scatter, \"total_bill\", \"tip\", s=50, alpha=.7, linewidth=.5, edgecolor=\"white\")\n", "g.add_legend();" ] @@ -238,7 +238,7 @@ "metadata": {}, "outputs": [], "source": [ - "g = sns.FacetGrid(tips, hue=\"sex\", palette=\"Set1\", size=5, hue_kws={\"marker\": [\"^\", \"v\"]})\n", + "g = sns.FacetGrid(tips, hue=\"sex\", palette=\"Set1\", height=5, hue_kws={\"marker\": [\"^\", \"v\"]})\n", "g.map(plt.scatter, \"total_bill\", \"tip\", s=100, linewidth=.5, edgecolor=\"white\")\n", "g.add_legend();" ] @@ -257,7 +257,7 @@ "outputs": [], "source": [ "attend = sns.load_dataset(\"attention\").query(\"subject <= 12\")\n", - "g = sns.FacetGrid(attend, col=\"subject\", col_wrap=4, size=2, ylim=(0, 10))\n", + "g = sns.FacetGrid(attend, col=\"subject\", col_wrap=4, height=2, ylim=(0, 10))\n", "g.map(sns.pointplot, \"solutions\", \"score\", color=\".3\", ci=None);" ] }, @@ -275,7 +275,7 @@ "outputs": [], "source": [ "with sns.axes_style(\"white\"):\n", - " g = sns.FacetGrid(tips, row=\"sex\", col=\"smoker\", margin_titles=True, size=2.5)\n", + " g = sns.FacetGrid(tips, row=\"sex\", col=\"smoker\", margin_titles=True, height=2.5)\n", "g.map(plt.scatter, \"total_bill\", \"tip\", color=\"#334488\", edgecolor=\"white\", lw=.5);\n", "g.set_axis_labels(\"Total bill (US Dollars)\", \"Tip\");\n", "g.set(xticks=[10, 30, 50], yticks=[2, 6, 10]);\n", @@ -295,7 +295,7 @@ "metadata": {}, "outputs": [], "source": [ - "g = sns.FacetGrid(tips, col=\"smoker\", margin_titles=True, size=4)\n", + "g = sns.FacetGrid(tips, col=\"smoker\", margin_titles=True, height=4)\n", "g.map(plt.scatter, \"total_bill\", \"tip\", color=\"#338844\", edgecolor=\"white\", s=50, lw=1)\n", "for ax in g.axes.flat:\n", " ax.plot((0, 50), (0, .2 * 50), c=\".2\", ls=\"--\")\n", @@ -330,7 +330,7 @@ " qntls, xr = stats.probplot(x, fit=False)\n", " plt.scatter(xr, qntls, **kwargs)\n", " \n", - "g = sns.FacetGrid(tips, col=\"sex\", size=4)\n", + "g = sns.FacetGrid(tips, col=\"sex\", height=4)\n", "g.map(quantile_plot, \"total_bill\");" ] }, @@ -352,7 +352,7 @@ " _, yr = stats.probplot(y, fit=False)\n", " plt.scatter(xr, yr, **kwargs)\n", " \n", - "g = sns.FacetGrid(tips, col=\"smoker\", size=4)\n", + "g = sns.FacetGrid(tips, col=\"smoker\", height=4)\n", "g.map(qqplot, \"total_bill\", \"tip\");" ] }, @@ -369,7 +369,7 @@ "metadata": {}, "outputs": [], "source": [ - "g = sns.FacetGrid(tips, hue=\"time\", col=\"sex\", size=4)\n", + "g = sns.FacetGrid(tips, hue=\"time\", col=\"sex\", height=4)\n", "g.map(qqplot, \"total_bill\", \"tip\")\n", "g.add_legend();" ] @@ -387,7 +387,7 @@ "metadata": {}, "outputs": [], "source": [ - "g = sns.FacetGrid(tips, hue=\"time\", col=\"sex\", size=4,\n", + "g = sns.FacetGrid(tips, hue=\"time\", col=\"sex\", height=4,\n", " hue_kws={\"marker\": [\"s\", \"D\"]})\n", "g.map(qqplot, \"total_bill\", \"tip\", s=40, edgecolor=\"w\")\n", "g.add_legend();" @@ -411,7 +411,7 @@ " plt.hexbin(x, y, gridsize=15, cmap=cmap, **kwargs)\n", "\n", "with sns.axes_style(\"dark\"):\n", - " g = sns.FacetGrid(tips, hue=\"time\", col=\"time\", size=4)\n", + " g = sns.FacetGrid(tips, hue=\"time\", col=\"time\", height=4)\n", "g.map(hexbin, \"total_bill\", \"tip\", extent=[0, 50, 0, 10]);" ] }, @@ -528,7 +528,7 @@ "metadata": {}, "outputs": [], "source": [ - "g = sns.PairGrid(tips, y_vars=[\"tip\"], x_vars=[\"total_bill\", \"size\"], size=4)\n", + "g = sns.PairGrid(tips, y_vars=[\"tip\"], x_vars=[\"total_bill\", \"size\"], height=4)\n", "g.map(sns.regplot, color=\".3\")\n", "g.set(ylim=(-1, 11), yticks=[0, 5, 10]);" ] @@ -564,7 +564,7 @@ "metadata": {}, "outputs": [], "source": [ - "sns.pairplot(iris, hue=\"species\", size=2.5);" + "sns.pairplot(iris, hue=\"species\", height=2.5);" ] }, { @@ -580,15 +580,8 @@ "metadata": {}, "outputs": [], "source": [ - "g = sns.pairplot(iris, hue=\"species\", palette=\"Set2\", diag_kind=\"kde\", size=2.5)" + "g = sns.pairplot(iris, hue=\"species\", palette=\"Set2\", diag_kind=\"kde\", height=2.5)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/doc/tutorial/categorical.ipynb b/doc/tutorial/categorical.ipynb index f4a557b7f8..dfa80a869d 100644 --- a/doc/tutorial/categorical.ipynb +++ b/doc/tutorial/categorical.ipynb @@ -517,7 +517,7 @@ "outputs": [], "source": [ "sns.factorplot(x=\"time\", y=\"total_bill\", hue=\"smoker\",\n", - " col=\"day\", data=tips, kind=\"box\", size=4, aspect=.5);" + " col=\"day\", data=tips, kind=\"box\", height=4, aspect=.5);" ] }, { @@ -538,7 +538,7 @@ "g = sns.PairGrid(tips,\n", " x_vars=[\"smoker\", \"time\", \"sex\"],\n", " y_vars=[\"total_bill\", \"tip\"],\n", - " aspect=.75, size=3.5)\n", + " aspect=.75, height=3.5)\n", "g.map(sns.violinplot, palette=\"pastel\");" ] }, diff --git a/doc/tutorial/regression.ipynb b/doc/tutorial/regression.ipynb index 998e587af3..fb23d536ae 100644 --- a/doc/tutorial/regression.ipynb +++ b/doc/tutorial/regression.ipynb @@ -439,7 +439,7 @@ "outputs": [], "source": [ "sns.lmplot(x=\"total_bill\", y=\"tip\", col=\"day\", data=tips,\n", - " col_wrap=2, size=3);" + " col_wrap=2, height=3);" ] }, { @@ -485,7 +485,7 @@ "outputs": [], "source": [ "sns.pairplot(tips, x_vars=[\"total_bill\", \"size\"], y_vars=[\"tip\"],\n", - " size=5, aspect=.8, kind=\"reg\");" + " height=5, aspect=.8, kind=\"reg\");" ] }, { @@ -502,7 +502,7 @@ "outputs": [], "source": [ "sns.pairplot(tips, x_vars=[\"total_bill\", \"size\"], y_vars=[\"tip\"],\n", - " hue=\"smoker\", size=5, aspect=.8, kind=\"reg\");" + " hue=\"smoker\", height=5, aspect=.8, kind=\"reg\");" ] } ], diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py index 264457129c..d03d4309e8 100644 --- a/seaborn/axisgrid.py +++ b/seaborn/axisgrid.py @@ -181,13 +181,13 @@ def _get_palette(self, data, hue, hue_order, palette): If true, the facets will share y axes across columns and/or x axes across rows.\ """), - size=dedent("""\ - size : scalar, optional + height=dedent("""\ + height : scalar, optional Height (in inches) of each facet. See also: ``aspect``.\ """), aspect=dedent("""\ aspect : scalar, optional - Aspect ratio of each facet, so that ``aspect * size`` gives the width + Aspect ratio of each facet, so that ``aspect * height`` gives the width of each facet in inches.\ """), palette=dedent("""\ @@ -214,15 +214,22 @@ class FacetGrid(Grid): """Subplot grid for plotting conditional relationships.""" def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, - sharex=True, sharey=True, size=3, aspect=1, palette=None, + sharex=True, sharey=True, height=3, aspect=1, palette=None, row_order=None, col_order=None, hue_order=None, hue_kws=None, dropna=True, legend_out=True, despine=True, margin_titles=False, xlim=None, ylim=None, subplot_kws=None, - gridspec_kws=None): + gridspec_kws=None, size=None): MPL_GRIDSPEC_VERSION = LooseVersion('1.4') OLD_MPL = LooseVersion(mpl.__version__) < MPL_GRIDSPEC_VERSION + # Handle deprecations + if size is not None: + height = size + msg = ("The `size` paramter has been renamed to `height`; " + "please update your code.") + warnings.warn(msg, UserWarning) + # Determine the hue facet layer information hue_var = hue if hue is None: @@ -274,7 +281,8 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, # Calculate the base figure size # This can get stretched later by a legend - figsize = (ncol * size * aspect, nrow * size) + # TODO this doesn't account for axis labels + figsize = (ncol * height * aspect, nrow * height) # Validate some inputs if col_wrap is not None: @@ -404,7 +412,7 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, control the order of levels of this variable. {col_wrap} {share_xy} - {size} + {height} {aspect} {palette} {{row,col,hue}}_order : lists, optional @@ -488,12 +496,12 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, >>> g = (g.map(plt.scatter, "total_bill", "tip", edgecolor="w") ... .add_legend()) - Change the size and aspect ratio of each facet: + Change the height and aspect ratio of each facet: .. plot:: :context: close-figs - >>> g = sns.FacetGrid(tips, col="day", size=4, aspect=.5) + >>> g = sns.FacetGrid(tips, col="day", height=4, aspect=.5) >>> g = g.map(plt.hist, "total_bill", bins=bins) Specify the order for plot elements: @@ -542,8 +550,8 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, .. plot:: :context: close-figs - >>> attend = sns.load_dataset("attention") - >>> g = sns.FacetGrid(attend, col="subject", col_wrap=5, size=1.5) + >>> att = sns.load_dataset("attention") + >>> g = sns.FacetGrid(att, col="subject", col_wrap=5, height=1.5) >>> g = g.map(plt.plot, "solutions", "score", marker=".") Define a custom bivariate function to map onto the grid: @@ -577,7 +585,7 @@ def __init__(self, data, row=None, col=None, hue=None, col_wrap=None, ... ax = plt.gca() ... data = kwargs.pop("data") ... data.plot(x=x, y=y, ax=ax, grid=False, **kwargs) - >>> g = sns.FacetGrid(df, col="walk", col_wrap=2, size=3.5) + >>> g = sns.FacetGrid(df, col="walk", col_wrap=2, height=3.5) >>> g = g.map_dataframe(dateplot, "date", "val") Use different axes labels after plotting: @@ -1066,8 +1074,8 @@ class PairGrid(Grid): def __init__(self, data, hue=None, hue_order=None, palette=None, hue_kws=None, vars=None, x_vars=None, y_vars=None, - diag_sharey=True, size=2.5, aspect=1, - despine=True, dropna=True): + diag_sharey=True, height=2.5, aspect=1, + despine=True, dropna=True, size=None): """Initialize the plot figure and PairGrid object. Parameters @@ -1092,10 +1100,10 @@ def __init__(self, data, hue=None, hue_order=None, palette=None, {x, y}_vars : lists of variable names, optional Variables within ``data`` to use separately for the rows and columns of the figure; i.e. to make a non-square plot. - size : scalar, optional + height : scalar, optional Height (in inches) of each facet. aspect : scalar, optional - Aspect * size gives the width (in inches) of each facet. + Aspect * height gives the width (in inches) of each facet. despine : boolean, optional Remove the top and right spines from the plots. dropna : boolean, optional @@ -1202,6 +1210,13 @@ def __init__(self, data, hue=None, hue_order=None, palette=None, """ + # Handle deprecations + if size is not None: + height = size + msg = ("The `size` paramter has been renamed to `height`; " + "please update your code.") + warnings.warn(UserWarning(msg)) + # Sort out the variables that define the grid if vars is not None: x_vars = list(vars) @@ -1224,7 +1239,7 @@ def __init__(self, data, hue=None, hue_order=None, palette=None, self.square_grid = self.x_vars == self.y_vars # Create the figure and the array of subplots - figsize = len(x_vars) * size * aspect, len(y_vars) * size + figsize = len(x_vars) * height * aspect, len(y_vars) * height fig, axes = plt.subplots(len(y_vars), len(x_vars), figsize=figsize, @@ -1499,8 +1514,8 @@ def _find_numeric_cols(self, data): class JointGrid(object): """Grid for drawing a bivariate plot with marginal univariate plots.""" - def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, - dropna=True, xlim=None, ylim=None): + def __init__(self, x, y, data=None, height=6, ratio=5, space=.2, + dropna=True, xlim=None, ylim=None, size=None): """Set up the grid of subplots. Parameters @@ -1509,7 +1524,7 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, Data or names of variables in ``data``. data : DataFrame, optional DataFrame when ``x`` and ``y`` are variable names. - size : numeric + height : numeric Size of each side of the figure in inches (it will be square). ratio : numeric Ratio of joint axes size to marginal axes height. @@ -1611,7 +1626,7 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, :context: close-figs >>> g = sns.JointGrid(x="total_bill", y="tip", data=tips, - ... size=5, ratio=2) + ... height=5, ratio=2) >>> g = g.plot_joint(sns.kdeplot, cmap="Reds_d") >>> g = g.plot_marginals(sns.kdeplot, color="r", shade=True) @@ -1626,8 +1641,15 @@ def __init__(self, x, y, data=None, size=6, ratio=5, space=.2, >>> g = g.plot_marginals(sns.kdeplot, color="m", shade=True) """ + # Handle deprecations + if size is not None: + height = size + msg = ("The `size` parameter has been renamed to `height`; " + "pleaes update your code.") + warnings.warn(msg, UserWarning) + # Set up the subplot grid - f = plt.figure(figsize=(size, size)) + f = plt.figure(figsize=(height, height)) gs = plt.GridSpec(ratio + 1, ratio + 1) ax_joint = f.add_subplot(gs[1:, :-1]) @@ -1864,8 +1886,8 @@ def savefig(self, *args, **kwargs): def pairplot(data, hue=None, hue_order=None, palette=None, vars=None, x_vars=None, y_vars=None, kind="scatter", diag_kind="auto", markers=None, - size=2.5, aspect=1, dropna=True, - plot_kws=None, diag_kws=None, grid_kws=None): + height=2.5, aspect=1, dropna=True, + plot_kws=None, diag_kws=None, grid_kws=None, size=None): """Plot pairwise relationships in a dataset. By default, this function will create a grid of Axes such that each @@ -1909,10 +1931,10 @@ def pairplot(data, hue=None, hue_order=None, palette=None, a length the same as the number of levels in the hue variable so that differently colored points will also have different scatterplot markers. - size : scalar, optional + height : scalar, optional Height (in inches) of each facet. aspect : scalar, optional - Aspect * size gives the width (in inches) of each facet. + Aspect * height gives the width (in inches) of each facet. dropna : boolean, optional Drop missing values from the data before plotting. {plot, diag, grid}_kws : dicts, optional @@ -1975,7 +1997,7 @@ def pairplot(data, hue=None, hue_order=None, palette=None, .. plot:: :context: close-figs - >>> g = sns.pairplot(iris, size=3, + >>> g = sns.pairplot(iris, height=3, ... vars=["sepal_width", "sepal_length"]) Plot different variables in the rows and columns: @@ -2012,6 +2034,13 @@ def pairplot(data, hue=None, hue_order=None, palette=None, ... diag_kws=dict(shade=True)) """ + # Handle deprecations + if size is not None: + height = size + msg = ("The `size` parameter has been renamed to `height`; " + "pleaes update your code.") + warnings.warn(msg, UserWarning) + if not isinstance(data, pd.DataFrame): raise TypeError( "'data' must be pandas DataFrame object, not: {typefound}".format( @@ -2029,7 +2058,7 @@ def pairplot(data, hue=None, hue_order=None, palette=None, grid = PairGrid(data, vars=vars, x_vars=x_vars, y_vars=y_vars, hue=hue, hue_order=hue_order, palette=palette, diag_sharey=diag_sharey, - size=size, aspect=aspect, dropna=dropna, **grid_kws) + height=height, aspect=aspect, dropna=dropna, **grid_kws) # Add the markers here as PairGrid has figured out how many levels of the # hue variable are needed and we don't want to duplicate that process @@ -2079,7 +2108,7 @@ def pairplot(data, hue=None, hue_order=None, palette=None, def jointplot(x, y, data=None, kind="scatter", stat_func=None, - color=None, size=6, ratio=5, space=.2, + color=None, height=6, ratio=5, space=.2, dropna=True, xlim=None, ylim=None, joint_kws=None, marginal_kws=None, annot_kws=None, **kwargs): """Draw a plot of two variables with bivariate and univariate graphs. @@ -2101,10 +2130,10 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=None, *Deprecated* color : matplotlib color, optional Color used for the plot elements. - size : numeric, optional + height : numeric, optional Size of the figure (it will be square). ratio : numeric, optional - Ratio of joint axes size to marginal axes height. + Ratio of joint axes height to marginal axes height. space : numeric, optional Space between the joint and marginal axes dropna : bool, optional @@ -2189,7 +2218,7 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=None, :context: close-figs >>> g = sns.jointplot("total_bill", "tip", data=tips, - ... size=5, ratio=3, color="g") + ... height=5, ratio=3, color="g") Pass keyword arguments down to the underlying plots: @@ -2202,6 +2231,13 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=None, ... s=40, edgecolor="w", linewidth=1) """ + # Handle deprecations + if "size" in kwargs: + height = kwargs.pop("size") + msg = ("The `size` paramter has been renamed to `height`; " + "please update your code.") + warnings.warn(msg, UserWarning) + # Set up empty default kwarg dicts if joint_kws is None: joint_kws = {} @@ -2221,7 +2257,7 @@ def jointplot(x, y, data=None, kind="scatter", stat_func=None, # Initialize the JointGrid object grid = JointGrid(x, y, data, dropna=dropna, - size=size, ratio=ratio, space=space, + height=height, ratio=ratio, space=space, xlim=xlim, ylim=ylim) # Plot the data using the grid diff --git a/seaborn/basic.py b/seaborn/basic.py index 1654419da4..ea49200a1e 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -1504,7 +1504,8 @@ def relplot(x=None, y=None, hue=None, size=None, style=None, data=None, palette=None, hue_order=None, hue_norm=None, sizes=None, size_order=None, size_norm=None, markers=None, dashes=None, style_order=None, - legend="brief", kind="scatter", facet_kws=None, **kwargs): + legend="brief", kind="scatter", + height=4, aspect=1, facet_kws=None, **kwargs): if kind == "scatter": @@ -1556,7 +1557,7 @@ def relplot(x=None, y=None, hue=None, size=None, style=None, data=None, g = FacetGrid( data=data, row=row, col=col, col_wrap=col_wrap, row_order=row_order, col_order=col_order, - dropna=False, + height=height, aspect=aspect, dropna=False, **facet_kws ) diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 06642e04e7..7036f4ebb9 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -415,6 +415,7 @@ def annotate_axes(self, ax): # Set the title size a roundabout way to maintain # compatibility with matplotlib 1.1 + # TODO no longer needed try: title_size = mpl.rcParams["axes.labelsize"] * .85 except TypeError: # labelsize is something like "large" @@ -2355,7 +2356,7 @@ def boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> g = sns.factorplot(x="sex", y="total_bill", ... hue="smoker", col="time", ... data=tips, kind="box", - ... size=4, aspect=.7); + ... height=4, aspect=.7); """).format(**_categorical_docs) @@ -2951,7 +2952,7 @@ def swarmplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> g = sns.factorplot(x="sex", y="total_bill", ... hue="smoker", col="time", ... data=tips, kind="swarm", - ... size=4, aspect=.7); + ... height=4, aspect=.7); """).format(**_categorical_docs) @@ -3135,7 +3136,7 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> g = sns.factorplot(x="sex", y="total_bill", ... hue="smoker", col="time", ... data=tips, kind="bar", - ... size=4, aspect=.7); + ... height=4, aspect=.7); """).format(**_categorical_docs) @@ -3334,7 +3335,7 @@ def pointplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, ... hue="smoker", col="time", ... data=tips, kind="point", ... dodge=True, - ... size=4, aspect=.7); + ... height=4, aspect=.7); """).format(**_categorical_docs) @@ -3462,7 +3463,7 @@ def countplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> g = sns.factorplot(x="class", hue="who", col="survived", ... data=titanic, kind="count", - ... size=4, aspect=.7); + ... height=4, aspect=.7); """).format(**_categorical_docs) @@ -3470,11 +3471,18 @@ def countplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, col_wrap=None, estimator=np.mean, ci=95, n_boot=1000, units=None, order=None, hue_order=None, row_order=None, - col_order=None, kind="point", size=4, aspect=1, + col_order=None, kind="point", height=4, aspect=1, orient=None, color=None, palette=None, legend=True, legend_out=True, sharex=True, sharey=True, margin_titles=False, facet_kws=None, **kwargs): + # Handle deprecations + if "size" in kwargs: + height = kwargs.pop("size") + msg = ("The `size` paramter has been renamed to `height`; " + "please update your code.") + warnings.warn(msg, UserWarning) + # Determine the plotting function try: plot_func = globals()[kind + "plot"] @@ -3514,7 +3522,7 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, facet_kws.update( data=data, row=row, col=col, row_order=row_order, col_order=col_order, - col_wrap=col_wrap, size=size, aspect=aspect, + col_wrap=col_wrap, height=height, aspect=aspect, sharex=sharex, sharey=sharey, legend_out=legend_out, margin_titles=margin_titles, dropna=False, @@ -3598,7 +3606,7 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, kind : {{``point``, ``bar``, ``count``, ``box``, ``violin``, ``strip``, ``swarm``, ``lv``}} The kind of plot to draw. - {size} + {height} {aspect} {orient} {color} @@ -3649,14 +3657,14 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, >>> g = sns.factorplot(x="time", y="pulse", hue="kind", ... col="diet", data=exercise) - Use a different size and aspect ratio for the facets: + Use a different height and aspect ratio for the facets: .. plot:: :context: close-figs >>> g = sns.factorplot(x="time", y="pulse", hue="kind", ... col="diet", data=exercise, - ... size=5, aspect=.8) + ... height=5, aspect=.8) Make many column facets and wrap them into the rows of the grid: @@ -3666,7 +3674,7 @@ def factorplot(x=None, y=None, hue=None, data=None, row=None, col=None, >>> titanic = sns.load_dataset("titanic") >>> g = sns.factorplot("alive", col="deck", col_wrap=4, ... data=titanic[titanic.deck.notnull()], - ... kind="count", size=2.5, aspect=.8) + ... kind="count", height=2.5, aspect=.8) Plot horizontally and pass other keyword arguments to the plot function: @@ -3840,6 +3848,6 @@ def lvplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, >>> g = sns.factorplot(x="sex", y="total_bill", ... hue="smoker", col="time", ... data=tips, kind="lv", - ... size=4, aspect=.7); + ... height=4, aspect=.7); """).format(**_categorical_docs) diff --git a/seaborn/regression.py b/seaborn/regression.py index 8659612b8b..eda908bc3b 100644 --- a/seaborn/regression.py +++ b/seaborn/regression.py @@ -2,6 +2,7 @@ from __future__ import division import copy from textwrap import dedent +import warnings import numpy as np import pandas as pd from scipy.spatial import distance @@ -539,13 +540,21 @@ def lineplot(self, ax, kws): def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, - col_wrap=None, size=5, aspect=1, markers="o", sharex=True, + col_wrap=None, height=5, aspect=1, markers="o", sharex=True, sharey=True, hue_order=None, col_order=None, row_order=None, legend=True, legend_out=True, x_estimator=None, x_bins=None, x_ci="ci", scatter=True, fit_reg=True, ci=95, n_boot=1000, units=None, order=1, logistic=False, lowess=False, robust=False, logx=False, x_partial=None, y_partial=None, truncate=False, - x_jitter=None, y_jitter=None, scatter_kws=None, line_kws=None): + x_jitter=None, y_jitter=None, scatter_kws=None, line_kws=None, + size=None): + + # Handle deprecations + if size is not None: + height = size + msg = ("The `size` paramter has been renamed to `height`; " + "please update your code.") + warnings.warn(msg, UserWarning) # Reduce the dataframe to only needed columns need_cols = [x, y, hue, col, row, units, x_partial, y_partial] @@ -555,7 +564,7 @@ def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, # Initialize the grid facets = FacetGrid(data, row, col, hue, palette=palette, row_order=row_order, col_order=col_order, - hue_order=hue_order, size=size, aspect=aspect, + hue_order=hue_order, height=height, aspect=aspect, col_wrap=col_wrap, sharex=sharex, sharey=sharey, legend_out=legend_out) @@ -626,7 +635,7 @@ def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, the order of levels of this variable. {palette} {col_wrap} - {size} + {height} {aspect} markers : matplotlib marker code or list of marker codes, optional Markers for the scatterplot. If a list, each marker in the list will be @@ -726,13 +735,13 @@ def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, >>> g = sns.lmplot(x="total_bill", y="tip", col="smoker", data=tips) - Change the size and aspect ratio of the facets: + Change the height and aspect ratio of the facets: .. plot:: :context: close-figs >>> g = sns.lmplot(x="size", y="total_bill", hue="day", col="day", - ... data=tips, aspect=.4, x_jitter=.1) + ... data=tips, height=6, aspect=.4, x_jitter=.1) Wrap the levels of the column variable into multiple rows: @@ -740,7 +749,7 @@ def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, :context: close-figs >>> g = sns.lmplot(x="total_bill", y="tip", col="day", hue="day", - ... data=tips, col_wrap=2, size=3) + ... data=tips, col_wrap=2, height=3) Condition on two variables to make a full grid: @@ -748,7 +757,7 @@ def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, :context: close-figs >>> g = sns.lmplot(x="total_bill", y="tip", row="sex", col="time", - ... data=tips, size=3) + ... data=tips, height=3) Use methods on the returned :class:`FacetGrid` instance to further tweak the plot: @@ -757,7 +766,7 @@ def lmplot(x, y, data, hue=None, col=None, row=None, palette=None, :context: close-figs >>> g = sns.lmplot(x="total_bill", y="tip", row="sex", col="time", - ... data=tips, size=3) + ... data=tips, height=3) >>> g = (g.set_axis_labels("Total bill (US Dollars)", "Tip") ... .set(xlim=(0, 60), ylim=(0, 12), ... xticks=[10, 30, 50], yticks=[2, 6, 10]) diff --git a/seaborn/tests/test_axisgrid.py b/seaborn/tests/test_axisgrid.py index 02633a25f0..e111195d78 100644 --- a/seaborn/tests/test_axisgrid.py +++ b/seaborn/tests/test_axisgrid.py @@ -168,20 +168,20 @@ def test_figure_size(self): g = ag.FacetGrid(self.df, row="a", col="b") npt.assert_array_equal(g.fig.get_size_inches(), (6, 9)) - g = ag.FacetGrid(self.df, row="a", col="b", size=6) + g = ag.FacetGrid(self.df, row="a", col="b", height=6) npt.assert_array_equal(g.fig.get_size_inches(), (12, 18)) - g = ag.FacetGrid(self.df, col="c", size=4, aspect=.5) + g = ag.FacetGrid(self.df, col="c", height=4, aspect=.5) npt.assert_array_equal(g.fig.get_size_inches(), (6, 4)) def test_figure_size_with_legend(self): - g1 = ag.FacetGrid(self.df, col="a", hue="c", size=4, aspect=.5) + g1 = ag.FacetGrid(self.df, col="a", hue="c", height=4, aspect=.5) npt.assert_array_equal(g1.fig.get_size_inches(), (6, 4)) g1.add_legend() nt.assert_greater(g1.fig.get_size_inches()[0], 6) - g2 = ag.FacetGrid(self.df, col="a", hue="c", size=4, aspect=.5, + g2 = ag.FacetGrid(self.df, col="a", hue="c", height=4, aspect=.5, legend_out=False) npt.assert_array_equal(g2.fig.get_size_inches(), (6, 4)) g2.add_legend() @@ -821,14 +821,14 @@ def test_specific_nonsquare_axes_with_array(self): def test_size(self): - g1 = ag.PairGrid(self.df, size=3) + g1 = ag.PairGrid(self.df, height=3) npt.assert_array_equal(g1.fig.get_size_inches(), (9, 9)) - g2 = ag.PairGrid(self.df, size=4, aspect=.5) + g2 = ag.PairGrid(self.df, height=4, aspect=.5) npt.assert_array_equal(g2.fig.get_size_inches(), (6, 12)) g3 = ag.PairGrid(self.df, y_vars=["z"], x_vars=["x", "y"], - size=2, aspect=2) + height=2, aspect=2) npt.assert_array_equal(g3.fig.get_size_inches(), (8, 2)) def test_map(self): From 62fb1ee93543484405b4a8c15de72d5480a5ee2e Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Sun, 1 Jul 2018 17:56:57 -0400 Subject: [PATCH 0768/1738] Add docstring examples for relplot --- doc/api.rst | 1 + seaborn/basic.py | 123 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/doc/api.rst b/doc/api.rst index f2ca78b2ab..14eaf33d7e 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -30,6 +30,7 @@ Basic plots .. autosummary:: :toctree: generated + relplot scatterplot lineplot diff --git a/seaborn/basic.py b/seaborn/basic.py index ea49200a1e..1f864f8690 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -1577,4 +1577,127 @@ def relplot(x=None, y=None, hue=None, size=None, style=None, data=None, relplot.__doc__ = dedent("""\ Draw a relational plot onto a FacetGrid. + {main_api_narrative} + + By default a :func:`scatterplot` is drawn, but it is also possible + to draw a :func:`lineplot` using ``kind='line'``. + + After plotting, the :class:`FacetGrid` with the plot is returned and can + be used directly to tweak supporting plot details or add other layers. + + Note that, unlike when using the underlying plotting functions directly, + data must be passed in a long-form DataFrame with variables specified by + passing strings to ``x``, ``y``, and other parameters. + + Parameters + ---------- + x, y : names of variables in ``data`` + Input data variables; must be numeric. + hue : name in ``data``, optional + Grouping variable that will produce elements with different colors. + Can be either categorical or numeric, although color mapping will + behave differently in latter case. + size : name in ``data``, optional + Grouping variable that will produce elements with different sizes. + Can be either categorical or numeric, although size mapping will + behave differently in latter case. + style : name in ``data``, optional + Grouping variable that will produce elements with different styles. + Can have a numeric dtype but will always be treated as categorical. + {data} + row, col : names of variables in ``data``, optional + Categorical variables that will determine the faceting of the grid. + {col_wrap} + row_order, col_order : lists of strings, optional + Order to organize the rows and/or columns of the grid in, otherwise the + orders are inferred from the data objects. + {palette} + {hue_order} + {hue_norm} + {sizes} + {size_order} + {size_norm} + {legend} + kind : string, optional + Kind of plot to draw, corresponding to a seaborn relational plot. + Options are {{``scatter`` and ``line``}}. + {height} + {aspect} + facet_kws : dict, optional + Dictionary of other keyword arguments to pass to :class:`FacetGrid`. + kwargs : key, value pairings + Other keyword arguments are passed through to the underlying plotting + function. + + Returns + ------- + g : :class:`FacetGrid` + Returns the :class:`FacetGrid` object with the plot on it for further + tweaking. + + Examples + -------- + + Draw a single facet to use the :class:`FacetGrid` legend placement: + + .. plot:: + :context: close-figs + + >>> import seaborn as sns + >>> sns.set(style="ticks") + >>> tips = sns.load_dataset("tips") + >>> g = sns.relplot(x="total_bill", y="tip", hue="day", data=tips) + + Facet on the columns with another variable: + + .. plot:: + :context: close-figs + + >>> g = sns.relplot(x="total_bill", y="tip", + ... hue="day", col="time", data=tips) + + Facet on the columns and rows: + + .. plot:: + :context: close-figs + + >>> g = sns.relplot(x="total_bill", y="tip", hue="day", + ... col="time", row="sex", data=tips) + + "Wrap" many column facets into multiple rows: + + .. plot:: + :context: close-figs + + >>> g = sns.relplot(x="total_bill", y="tip", hue="time", + ... col="day", col_wrap=2, data=tips) + + Use multiple semantic variables on each facet with specified attributes: + + .. plot:: + :context: close-figs + + >>> g = sns.relplot(x="total_bill", y="tip", hue="time", size="size", + ... palette=["b", "r"], sizes=(10, 100), + ... col="time", data=tips) + + Use a different kind of plot: + + .. plot:: + :context: close-figs + + >>> fmri = sns.load_dataset("fmri") + >>> g = sns.relplot(x="timepoint", y="signal", + ... hue="event", style="event", col="region", + ... kind="line", data=fmri) + + Change the size of each facet: + + .. plot:: + :context: close-figs + + >>> g = sns.relplot(x="timepoint", y="signal", + ... hue="event", style="event", col="region", + ... height=5, aspect=.7, kind="line", data=fmri) + """).format(**_basic_docs) From 3eefe73f2b32644cdd595d3c1678bfda9b13e81f Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Wed, 4 Jul 2018 14:56:37 -0400 Subject: [PATCH 0769/1738] Fix relplot legend --- seaborn/basic.py | 34 +++++++++++++++++++++++++--------- seaborn/tests/test_basic.py | 4 ++++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/seaborn/basic.py b/seaborn/basic.py index 1f864f8690..2826824aa8 100644 --- a/seaborn/basic.py +++ b/seaborn/basic.py @@ -399,6 +399,9 @@ def parse_size(self, data, sizes, order, norm): else: var_type = self._semantic_type(data) + + # TODO override for list/dict like in parse_hue? + if var_type == "categorical": levels = categorical_order(data, order) numbers = np.arange(1, 1 + len(levels))[::-1] @@ -548,16 +551,16 @@ def add_legend_data(self, ax): raise ValueError(err) keys = [] - legend_data = {} + legend_kwargs = {} def update(var_name, val_name, **kws): key = var_name, val_name - if key in legend_data: - legend_data[key].update(**kws) + if key in legend_kwargs: + legend_kwargs[key].update(**kws) else: keys.append(key) - legend_data[key] = dict(**kws) + legend_kwargs[key] = dict(**kws) # -- Add a legend for hue semantics @@ -602,15 +605,24 @@ def update(var_name, val_name, **kws): dashes=self.dashes.get(level, "")) func = getattr(ax, self._legend_func) + + legend_data = {} + legend_order = [] + for key in keys: _, label = key - kws = legend_data[key] + kws = legend_kwargs[key] kws.setdefault("color", ".2") use_kws = {} for attr in self._legend_attributes: if attr in kws: use_kws[attr] = kws[attr] - func([], [], label=label, **use_kws) + artist = func([], [], label=label, **use_kws) + legend_data[label] = artist + legend_order.append(label) + + self.legend_data = legend_data + self.legend_order = legend_order class _LinePlotter(_BasicPlotter): @@ -1529,6 +1541,7 @@ def relplot(x=None, y=None, hue=None, size=None, style=None, data=None, palette=palette, hue_order=hue_order, hue_norm=hue_norm, sizes=sizes, size_order=size_order, size_norm=size_norm, markers=markers, dashes=dashes, style_order=style_order, + legend=legend, ) palette = p.palette if p.palette else None @@ -1547,6 +1560,7 @@ def relplot(x=None, y=None, hue=None, size=None, style=None, data=None, palette=palette, hue_order=hue_order, hue_norm=p.hue_norm, sizes=sizes, size_order=size_order, size_norm=p.size_norm, markers=markers, dashes=dashes, style_order=style_order, + legend=False, ) plot_kws.update(kwargs) if kind == "scatter": @@ -1567,9 +1581,11 @@ def relplot(x=None, y=None, hue=None, size=None, style=None, data=None, **plot_kws) # Show the legend - if legend and g._legend_data: - _, labels = g.axes.flat[0].get_legend_handles_labels() - g.add_legend(label_order=labels) + if legend: + p.add_legend_data(g.axes.flat[0]) + if p.legend_data: + g.add_legend(legend_data=p.legend_data, + label_order=p.legend_order) return g diff --git a/seaborn/tests/test_basic.py b/seaborn/tests/test_basic.py index 4c13261a40..d413dd14bb 100644 --- a/seaborn/tests/test_basic.py +++ b/seaborn/tests/test_basic.py @@ -1612,5 +1612,9 @@ def test_relplot_legend(self, long_df): texts = [t.get_text() for t in g._legend.texts] assert np.array_equal(texts, long_df["a"].unique()) + g = basic.relplot(x="x", y="y", hue="s", size="s", data=long_df) + texts = [t.get_text() for t in g._legend.texts] + assert np.array_equal(texts, np.sort(texts)) + g = basic.relplot(x="x", y="y", hue="a", legend=False, data=long_df) assert g._legend is None From 1db205e068c699ce12c2873b6fb69d7ccea99127 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 21 Jun 2018 15:25:23 -0400 Subject: [PATCH 0770/1738] Tweak homepage examples --- examples/different_scatter_variables.py | 8 ++++---- examples/errorband_lineplots.py | 4 ++-- examples/many_facets.py | 7 ++++--- examples/regression_marginals.py | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/examples/different_scatter_variables.py b/examples/different_scatter_variables.py index 5771dd8909..433b6be78d 100644 --- a/examples/different_scatter_variables.py +++ b/examples/different_scatter_variables.py @@ -1,6 +1,6 @@ """ -Scatterplot with categorical and continuous semantics -===================================================== +Scatterplot with categorical and numerical semantics +==================================================== _thumb: .55, .5 @@ -17,6 +17,6 @@ f, ax = plt.subplots(figsize=(6.5, 6.5)) ax = sns.scatterplot(x="sepal_length", y="sepal_width", hue="species", size="petal_width", - sizes=(50, 200), alpha=.75, - palette="tab10", + sizes=(50, 250), alpha=.75, + palette="Set2", data=iris) diff --git a/examples/errorband_lineplots.py b/examples/errorband_lineplots.py index 9c1ba16abe..52cc945a40 100644 --- a/examples/errorband_lineplots.py +++ b/examples/errorband_lineplots.py @@ -1,6 +1,6 @@ """ -Timeseries plots with error bands -================================= +Timeseries plot with error bands +================================ _thumb: .5, .45 diff --git a/examples/many_facets.py b/examples/many_facets.py index c4f23ea21f..b505012f52 100644 --- a/examples/many_facets.py +++ b/examples/many_facets.py @@ -2,7 +2,7 @@ Plotting on a large number of facets ==================================== -_thumb: .35, .35 +_thumb: .4, .3 """ import numpy as np @@ -22,13 +22,14 @@ columns=["position", "step", "walk"]) # Initialize a grid of plots with an Axes for each walk -grid = sns.FacetGrid(df, col="walk", hue="walk", col_wrap=5, size=1.5) +grid = sns.FacetGrid(df, col="walk", hue="walk", palette="tab20c", + col_wrap=4, size=1.5) # Draw a horizontal line to show the starting point grid.map(plt.axhline, y=0, ls=":", c=".5") # Draw a line plot to show the trajectory of each random walk -grid.map(plt.plot, "step", "position", marker="o", ms=4) +grid.map(plt.plot, "step", "position", marker="o") # Adjust the tick positions and labels grid.set(xticks=np.arange(5), yticks=[-3, 3], diff --git a/examples/regression_marginals.py b/examples/regression_marginals.py index e4e2d1c7cb..a3e6686b83 100644 --- a/examples/regression_marginals.py +++ b/examples/regression_marginals.py @@ -9,4 +9,4 @@ tips = sns.load_dataset("tips") g = sns.jointplot("total_bill", "tip", data=tips, kind="reg", - xlim=(0, 60), ylim=(0, 12), color="r", size=7) + xlim=(0, 60), ylim=(0, 12), color="m", size=7) From 3b793bbed45b8dbee9eec0804a0b1bb98d3ed743 Mon Sep 17 00:00:00 2001 From: Michael Waskom Date: Thu, 21 Jun 2018 15:26:03 -0400 Subject: [PATCH 0771/1738] Update main and tutorial homepages --- doc/_static/style.css | 8 ++++++ doc/index.rst | 54 ++++++++++++++++++++++--------------- doc/tutorial.rst | 62 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 89 insertions(+), 35 deletions(-) diff --git a/doc/_static/style.css b/doc/_static/style.css index f64910e5c6..9462201c43 100644 --- a/doc/_static/style.css +++ b/doc/_static/style.css @@ -30,6 +30,14 @@ code { background-color: #ffffff !important; } +ul { + padding-left: 20px !important; +} + +ul.dropdown-menu { + padding-left: 0px !important; +} + .alert-info { background-color: #adb8cb !important; border-color: #adb8cb !important; diff --git a/doc/index.rst b/doc/index.rst index f230b1609d..a4dd1c4e46 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -24,38 +24,37 @@ seaborn: statistical data visualization .. raw:: html -