├── .gitignore
├── LICENSE
├── MANIFEST
├── README.md
├── docs
├── barplot.html
├── boxplot.html
├── catplot.html
├── countplot.html
├── facetgrid.html
├── index.html
├── lmplot.html
├── lvplot.html
├── pointplot.html
├── pyplot-hist.html
├── pyplot-plot.html
├── pyplot-scatter.html
├── regplot.html
├── scatterplot.html
└── stripplot.html
├── img
└── visualization.png
├── notebooks
├── barplot.ipynb
├── boxplot.ipynb
├── catplot.ipynb
├── countplot.ipynb
├── facetgrid.ipynb
├── lmplot.ipynb
├── pointplot.ipynb
├── pyplot-hist.ipynb
├── pyplot-plot.ipynb
├── pyplot-scatter.ipynb
├── regplot.ipynb
├── scatterplot.ipynb
└── stripplot.ipynb
├── requirements.txt
├── scripts
├── docgen.py
└── nb2html.py
├── seaborn_altair
├── __init__.py
├── axisgrid.py
├── categorical.py
├── pyplot.py
├── regression.py
├── relational.py
└── util.py
├── setup.cfg
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | /dist
2 | *.egg-info
3 | .ipynb_checkpoints
4 | .vs
5 | .vscode
6 | *.pyc
7 | __pycache__
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/MANIFEST:
--------------------------------------------------------------------------------
1 | # file GENERATED by distutils, do NOT edit
2 | setup.cfg
3 | setup.py
4 | seaborn_altair/__init__.py
5 | seaborn_altair/categorical.py
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # seaborn_altair
2 |
3 | Seaborn-compatible API for interactive Vega-Lite plots via Altair.
4 |
5 | ## Installation
6 |
7 | pip install seaborn_altair
8 |
9 | Works in Jupyter lab or Jupyter notebooks.
10 |
11 | ## Usage
12 |
13 | import seaborn_altair as salt
14 | import seaborn as sns
15 | tips = sns.load_dataset("tips")
16 |
17 | # Use salt as you would sns
18 | salt.barplot(x="day", y="total_bill", data=tips)
19 |
20 | 
21 |
22 | ## API
23 |
24 | This is only a proof of concept at this time. Only a subset of Seaborn plots (the ones with example links) are currently available with limited support.
25 |
26 | ### Axis grids
27 | * [FacetGrid](http://kitware.github.io/seaborn_altair/facetgrid.html) Subplot grid for plotting conditional relationships.
28 | * [catplot](http://kitware.github.io/seaborn_altair/catplot.html) Draw a categorical plot onto a FacetGrid.
29 | * [lmplot](http://kitware.github.io/seaborn_altair/lmplot.html) Plot data and regression model fits across a FacetGrid.
30 | * PairGrid Subplot grid for plotting pairwise relationships in a dataset.
31 | * pairplot Plot pairwise relationships in a dataset.
32 | * JointGrid Grid for drawing a bivariate plot with marginal univariate plots.
33 | * jointplot Draw a plot of two variables with bivariate and univariate graphs.
34 |
35 | ### Categorical plots
36 | * [stripplot](http://kitware.github.io/seaborn_altair/stripplot.html) Draw a scatterplot where one variable is categorical.
37 | * swarmplot Draw a categorical scatterplot with non-overlapping points.
38 | * [boxplot](http://kitware.github.io/seaborn_altair/boxplot.html) Draw a box plot to show distributions with respect to categories.
39 | * violinplot Draw a combination of boxplot and kernel density estimate.
40 | * lvplot Draw a letter value plot to show distributions of large datasets.
41 | * [pointplot](http://kitware.github.io/seaborn_altair/pointplot.html) Show point estimates and confidence intervals using scatter plot glyphs.
42 | * [barplot](http://kitware.github.io/seaborn_altair/barplot.html) Show point estimates and confidence intervals as rectangular bars.
43 | * [countplot](http://kitware.github.io/seaborn_altair/countplot.html) Show the counts of observations in each categorical bin using bars.
44 |
45 | ### Distribution plots
46 | * distplot Flexibly plot a univariate distribution of observations.
47 | * kdeplot Fit and plot a univariate or bivariate kernel density estimate.
48 | * rugplot Plot datapoints in an array as sticks on an axis.
49 |
50 | ### Regression plots
51 | * [regplot](http://kitware.github.io/seaborn_altair/regplot.html) Plot data and a linear regression model fit.
52 | * residplot Plot the residuals of a linear regression.
53 |
54 | ### Matrix plots
55 | * heatmap Plot rectangular data as a color-encoded matrix.
56 | * clustermap Plot a matrix dataset as a hierarchically-clustered heatmap.
57 |
58 | ### Timeseries plots
59 | * tsplot Plot one or more timeseries with flexible representation of uncertainty.
60 |
61 | ### Miscellaneous plots
62 | * palplot Plot the values in a color palette as a horizontal array.
63 |
64 | ### matplotlib.pyplot utilities
65 | * [pyplot.hist](http://kitware.github.io/seaborn_altair/pyplot-hist.html)
66 | * [pyplot.plot](http://kitware.github.io/seaborn_altair/pyplot-plot.html)
67 | * [pyplot.scatter](http://kitware.github.io/seaborn_altair/pyplot-scatter.html)
68 |
69 | ## Credit
70 |
71 | Idea from [Jake VanderPlas](https://twitter.com/jakevdp/status/996041414596214784). [I](https://twitter.com/jeffbaumes) know Python and Vega-Lite reasonably well, so decided to give it a shot.
72 |
73 | Contributions and suggestions welcome!
74 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
25 |
26 |
27 | seaborn_altair
28 | Seaborn-compatible API for interactive Vega-Lite plots via Altair.
29 | Installation
30 | pip install seaborn_altair
31 |
32 | Works in Jupyter lab or Jupyter notebooks.
33 | Usage
34 | import seaborn_altair as salt
35 | import seaborn as sns
36 | tips = sns.load_dataset("tips")
37 |
38 | # Use salt as you would sns
39 | salt.barplot(x="day", y="total_bill", data=tips)
40 |
41 | 
42 | API
43 | This is only a proof of concept at this time. Only a subset of Seaborn plots (the ones with example links) are currently available with limited support.
44 | Axis grids
45 |
46 | - FacetGrid Subplot grid for plotting conditional relationships.
47 | - catplot Draw a categorical plot onto a FacetGrid.
48 | - lmplot Plot data and regression model fits across a FacetGrid.
49 | - PairGrid Subplot grid for plotting pairwise relationships in a dataset.
50 | - pairplot Plot pairwise relationships in a dataset.
51 | - JointGrid Grid for drawing a bivariate plot with marginal univariate plots.
52 | - jointplot Draw a plot of two variables with bivariate and univariate graphs.
53 |
54 | Categorical plots
55 |
56 | - stripplot Draw a scatterplot where one variable is categorical.
57 | - swarmplot Draw a categorical scatterplot with non-overlapping points.
58 | - boxplot Draw a box plot to show distributions with respect to categories.
59 | - violinplot Draw a combination of boxplot and kernel density estimate.
60 | - lvplot Draw a letter value plot to show distributions of large datasets.
61 | - pointplot Show point estimates and confidence intervals using scatter plot glyphs.
62 | - barplot Show point estimates and confidence intervals as rectangular bars.
63 | - countplot Show the counts of observations in each categorical bin using bars.
64 |
65 | Distribution plots
66 |
67 | - distplot Flexibly plot a univariate distribution of observations.
68 | - kdeplot Fit and plot a univariate or bivariate kernel density estimate.
69 | - rugplot Plot datapoints in an array as sticks on an axis.
70 |
71 | Regression plots
72 |
73 | - regplot Plot data and a linear regression model fit.
74 | - residplot Plot the residuals of a linear regression.
75 |
76 | Matrix plots
77 |
78 | - heatmap Plot rectangular data as a color-encoded matrix.
79 | - clustermap Plot a matrix dataset as a hierarchically-clustered heatmap.
80 |
81 | Timeseries plots
82 |
83 | - tsplot Plot one or more timeseries with flexible representation of uncertainty.
84 |
85 | Miscellaneous plots
86 |
87 | - palplot Plot the values in a color palette as a horizontal array.
88 |
89 | matplotlib.pyplot utilities
90 |
95 | Credit
96 | Idea from Jake VanderPlas. I know Python and Vega-Lite reasonably well, so decided to give it a shot.
97 | Contributions and suggestions welcome!
98 |
99 |
100 |
--------------------------------------------------------------------------------
/docs/lvplot.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
25 |
26 |
27 | import seaborn as sns
28 | import numpy as np
29 | import altair as alt
30 | import pandas as pd
31 | import warnings
32 | from scipy import stats
33 |
34 | from seaborn.categorical import (
35 | _BarPlotter,
36 | _BoxPlotter,
37 | _LVPlotter,
38 | _PointPlotter,
39 | _StripPlotter,
40 | )
41 | from seaborn.palettes import color_palette
42 | from seaborn.external.six import string_types
43 |
44 | class _LVPlotterAltair(_LVPlotter):
45 |
46 | def _lvplot(self, box_data, positions,
47 | color=[255. / 256., 185. / 256., 0.],
48 | vert=True, widths=1, k_depth='proportion',
49 | ax=None, outlier_prop=None, scale='exponential',
50 | **kws):
51 |
52 | x = positions[0]
53 | box_data = np.asarray(box_data)
54 |
55 | # If we only have one data point, plot a line
56 | if len(box_data) == 1:
57 | kws.update({'color': self.gray, 'linestyle': '-'})
58 | ys = [box_data[0], box_data[0]]
59 | xs = [x - widths / 2, x + widths / 2]
60 | if vert:
61 | xx, yy = xs, ys
62 | else:
63 | xx, yy = ys, xs
64 | ax.plot(xx, yy, **kws)
65 | else:
66 | # Get the number of data points and calculate "depth" of
67 | # letter-value plot
68 | box_ends, k = self._lv_box_ends(box_data, k_depth=k_depth,
69 | outlier_prop=outlier_prop)
70 |
71 | # Anonymous functions for calculating the width and height
72 | # of the letter value boxes
73 | width = self._width_functions(scale)
74 |
75 | # Function to find height of boxes
76 | def height(b):
77 | return b[1] - b[0]
78 |
79 | # Functions to construct the letter value boxes
80 | def vert_perc_box(x, b, i, k, w):
81 | rect = Patches.Rectangle((x - widths*w / 2, b[0]),
82 | widths*w,
83 | height(b), fill=True)
84 | return rect
85 |
86 | def horz_perc_box(x, b, i, k, w):
87 | rect = Patches.Rectangle((b[0], x - widths*w / 2),
88 | height(b), widths*w,
89 | fill=True)
90 | return rect
91 |
92 | # Scale the width of the boxes so the biggest starts at 1
93 | w_area = np.array([width(height(b), i, k)
94 | for i, b in enumerate(box_ends)])
95 | w_area = w_area / np.max(w_area)
96 |
97 | # Calculate the medians
98 | y = np.median(box_data)
99 |
100 | # Calculate the outliers and plot
101 | outliers = self._lv_outliers(box_data, k)
102 |
103 | if vert:
104 | boxes = [vert_perc_box(x, b[0], i, k, b[1])
105 | for i, b in enumerate(zip(box_ends, w_area))]
106 |
107 | # Plot the medians
108 | ax.plot([x - widths / 2, x + widths / 2], [y, y],
109 | c='.15', alpha=.45, **kws)
110 |
111 | ax.scatter(np.repeat(x, len(outliers)), outliers,
112 | marker='d', c=mpl.colors.rgb2hex(color), **kws)
113 | else:
114 | boxes = [horz_perc_box(x, b[0], i, k, b[1])
115 | for i, b in enumerate(zip(box_ends, w_area))]
116 |
117 | # Plot the medians
118 | ax.plot([y, y], [x - widths / 2, x + widths / 2],
119 | c='.15', alpha=.45, **kws)
120 |
121 | ax.scatter(outliers, np.repeat(x, len(outliers)),
122 | marker='d', c=color, **kws)
123 |
124 | # Construct a color map from the input color
125 | rgb = [[1, 1, 1], list(color)]
126 | cmap = mpl.colors.LinearSegmentedColormap.from_list('new_map', rgb)
127 | collection = PatchCollection(boxes, cmap=cmap)
128 |
129 | # Set the color gradation
130 | collection.set_array(np.array(np.linspace(0, 1, len(boxes))))
131 |
132 | # Plot the boxes
133 | ax.add_collection(collection)
134 |
135 | def plot(self, ax, boxplot_kws):
136 | """Make the plot."""
137 | return self.draw_letter_value_plot(ax, boxplot_kws)
138 |
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/docs/pyplot-hist.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
25 |
26 |
27 | seaborn_altair.pyplot.hist
import seaborn as sns
28 | import seaborn_altair.pyplot as palt
29 | tips = sns.load_dataset("tips")
palt.hist('tip', data=tips, color="green")
30 |
31 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/docs/pyplot-plot.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
25 |
26 |
27 | seaborn_altair.pyplot.plot
import seaborn as sns
28 | import seaborn_altair.pyplot as palt
29 | attend = sns.load_dataset("attention")
palt.plot('solutions', 'score', color="green", data=attend[attend['subject'] == 1]).interactive()
30 |
31 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/docs/pyplot-scatter.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
25 |
26 |
27 | seaborn_altair.pyplot.scatter
import seaborn as sns
28 | import seaborn_altair.pyplot as palt
29 | tips = sns.load_dataset("tips")
palt.scatter('tip', 'total_bill', data=tips).interactive()
30 |
31 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/img/visualization.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kitware/seaborn_altair/716fbf10e2bf207f4de5aaad87fddba43d4e4dfe/img/visualization.png
--------------------------------------------------------------------------------
/notebooks/pyplot-hist.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# seaborn_altair.pyplot.hist"
8 | ]
9 | },
10 | {
11 | "cell_type": "code",
12 | "execution_count": 1,
13 | "metadata": {},
14 | "outputs": [],
15 | "source": [
16 | "import seaborn as sns\n",
17 | "import seaborn_altair.pyplot as palt\n",
18 | "tips = sns.load_dataset(\"tips\")"
19 | ]
20 | },
21 | {
22 | "cell_type": "code",
23 | "execution_count": 2,
24 | "metadata": {},
25 | "outputs": [
26 | {
27 | "data": {
28 | "application/vnd.vegalite.v2+json": {
29 | "$schema": "https://vega.github.io/schema/vega-lite/v2.4.1.json",
30 | "config": {
31 | "range": {
32 | "category": [
33 | "rgba(31,119,180,1)",
34 | "rgba(255,127,14,1)",
35 | "rgba(44,160,44,1)",
36 | "rgba(214,39,40,1)",
37 | "rgba(148,103,189,1)",
38 | "rgba(140,86,75,1)",
39 | "rgba(227,119,194,1)",
40 | "rgba(127,127,127,1)",
41 | "rgba(188,189,34,1)",
42 | "rgba(23,190,207,1)"
43 | ]
44 | },
45 | "view": {
46 | "height": 300,
47 | "width": 400
48 | }
49 | },
50 | "data": {
51 | "values": [
52 | {
53 | "day": "Sun",
54 | "sex": "Female",
55 | "size": 2,
56 | "smoker": "No",
57 | "time": "Dinner",
58 | "tip": 1.01,
59 | "total_bill": 16.99
60 | },
61 | {
62 | "day": "Sun",
63 | "sex": "Male",
64 | "size": 3,
65 | "smoker": "No",
66 | "time": "Dinner",
67 | "tip": 1.66,
68 | "total_bill": 10.34
69 | },
70 | {
71 | "day": "Sun",
72 | "sex": "Male",
73 | "size": 3,
74 | "smoker": "No",
75 | "time": "Dinner",
76 | "tip": 3.5,
77 | "total_bill": 21.01
78 | },
79 | {
80 | "day": "Sun",
81 | "sex": "Male",
82 | "size": 2,
83 | "smoker": "No",
84 | "time": "Dinner",
85 | "tip": 3.31,
86 | "total_bill": 23.68
87 | },
88 | {
89 | "day": "Sun",
90 | "sex": "Female",
91 | "size": 4,
92 | "smoker": "No",
93 | "time": "Dinner",
94 | "tip": 3.61,
95 | "total_bill": 24.59
96 | },
97 | {
98 | "day": "Sun",
99 | "sex": "Male",
100 | "size": 4,
101 | "smoker": "No",
102 | "time": "Dinner",
103 | "tip": 4.71,
104 | "total_bill": 25.29
105 | },
106 | {
107 | "day": "Sun",
108 | "sex": "Male",
109 | "size": 2,
110 | "smoker": "No",
111 | "time": "Dinner",
112 | "tip": 2,
113 | "total_bill": 8.77
114 | },
115 | {
116 | "day": "Sun",
117 | "sex": "Male",
118 | "size": 4,
119 | "smoker": "No",
120 | "time": "Dinner",
121 | "tip": 3.12,
122 | "total_bill": 26.88
123 | },
124 | {
125 | "day": "Sun",
126 | "sex": "Male",
127 | "size": 2,
128 | "smoker": "No",
129 | "time": "Dinner",
130 | "tip": 1.96,
131 | "total_bill": 15.04
132 | },
133 | {
134 | "day": "Sun",
135 | "sex": "Male",
136 | "size": 2,
137 | "smoker": "No",
138 | "time": "Dinner",
139 | "tip": 3.23,
140 | "total_bill": 14.78
141 | },
142 | {
143 | "day": "Sun",
144 | "sex": "Male",
145 | "size": 2,
146 | "smoker": "No",
147 | "time": "Dinner",
148 | "tip": 1.71,
149 | "total_bill": 10.27
150 | },
151 | {
152 | "day": "Sun",
153 | "sex": "Female",
154 | "size": 4,
155 | "smoker": "No",
156 | "time": "Dinner",
157 | "tip": 5,
158 | "total_bill": 35.26
159 | },
160 | {
161 | "day": "Sun",
162 | "sex": "Male",
163 | "size": 2,
164 | "smoker": "No",
165 | "time": "Dinner",
166 | "tip": 1.57,
167 | "total_bill": 15.42
168 | },
169 | {
170 | "day": "Sun",
171 | "sex": "Male",
172 | "size": 4,
173 | "smoker": "No",
174 | "time": "Dinner",
175 | "tip": 3,
176 | "total_bill": 18.43
177 | },
178 | {
179 | "day": "Sun",
180 | "sex": "Female",
181 | "size": 2,
182 | "smoker": "No",
183 | "time": "Dinner",
184 | "tip": 3.02,
185 | "total_bill": 14.83
186 | },
187 | {
188 | "day": "Sun",
189 | "sex": "Male",
190 | "size": 2,
191 | "smoker": "No",
192 | "time": "Dinner",
193 | "tip": 3.92,
194 | "total_bill": 21.58
195 | },
196 | {
197 | "day": "Sun",
198 | "sex": "Female",
199 | "size": 3,
200 | "smoker": "No",
201 | "time": "Dinner",
202 | "tip": 1.67,
203 | "total_bill": 10.33
204 | },
205 | {
206 | "day": "Sun",
207 | "sex": "Male",
208 | "size": 3,
209 | "smoker": "No",
210 | "time": "Dinner",
211 | "tip": 3.71,
212 | "total_bill": 16.29
213 | },
214 | {
215 | "day": "Sun",
216 | "sex": "Female",
217 | "size": 3,
218 | "smoker": "No",
219 | "time": "Dinner",
220 | "tip": 3.5,
221 | "total_bill": 16.97
222 | },
223 | {
224 | "day": "Sat",
225 | "sex": "Male",
226 | "size": 3,
227 | "smoker": "No",
228 | "time": "Dinner",
229 | "tip": 3.35,
230 | "total_bill": 20.65
231 | },
232 | {
233 | "day": "Sat",
234 | "sex": "Male",
235 | "size": 2,
236 | "smoker": "No",
237 | "time": "Dinner",
238 | "tip": 4.08,
239 | "total_bill": 17.92
240 | },
241 | {
242 | "day": "Sat",
243 | "sex": "Female",
244 | "size": 2,
245 | "smoker": "No",
246 | "time": "Dinner",
247 | "tip": 2.75,
248 | "total_bill": 20.29
249 | },
250 | {
251 | "day": "Sat",
252 | "sex": "Female",
253 | "size": 2,
254 | "smoker": "No",
255 | "time": "Dinner",
256 | "tip": 2.23,
257 | "total_bill": 15.77
258 | },
259 | {
260 | "day": "Sat",
261 | "sex": "Male",
262 | "size": 4,
263 | "smoker": "No",
264 | "time": "Dinner",
265 | "tip": 7.58,
266 | "total_bill": 39.42
267 | },
268 | {
269 | "day": "Sat",
270 | "sex": "Male",
271 | "size": 2,
272 | "smoker": "No",
273 | "time": "Dinner",
274 | "tip": 3.18,
275 | "total_bill": 19.82
276 | },
277 | {
278 | "day": "Sat",
279 | "sex": "Male",
280 | "size": 4,
281 | "smoker": "No",
282 | "time": "Dinner",
283 | "tip": 2.34,
284 | "total_bill": 17.81
285 | },
286 | {
287 | "day": "Sat",
288 | "sex": "Male",
289 | "size": 2,
290 | "smoker": "No",
291 | "time": "Dinner",
292 | "tip": 2,
293 | "total_bill": 13.37
294 | },
295 | {
296 | "day": "Sat",
297 | "sex": "Male",
298 | "size": 2,
299 | "smoker": "No",
300 | "time": "Dinner",
301 | "tip": 2,
302 | "total_bill": 12.69
303 | },
304 | {
305 | "day": "Sat",
306 | "sex": "Male",
307 | "size": 2,
308 | "smoker": "No",
309 | "time": "Dinner",
310 | "tip": 4.3,
311 | "total_bill": 21.7
312 | },
313 | {
314 | "day": "Sat",
315 | "sex": "Female",
316 | "size": 2,
317 | "smoker": "No",
318 | "time": "Dinner",
319 | "tip": 3,
320 | "total_bill": 19.65
321 | },
322 | {
323 | "day": "Sat",
324 | "sex": "Male",
325 | "size": 2,
326 | "smoker": "No",
327 | "time": "Dinner",
328 | "tip": 1.45,
329 | "total_bill": 9.55
330 | },
331 | {
332 | "day": "Sat",
333 | "sex": "Male",
334 | "size": 4,
335 | "smoker": "No",
336 | "time": "Dinner",
337 | "tip": 2.5,
338 | "total_bill": 18.35
339 | },
340 | {
341 | "day": "Sat",
342 | "sex": "Female",
343 | "size": 2,
344 | "smoker": "No",
345 | "time": "Dinner",
346 | "tip": 3,
347 | "total_bill": 15.06
348 | },
349 | {
350 | "day": "Sat",
351 | "sex": "Female",
352 | "size": 4,
353 | "smoker": "No",
354 | "time": "Dinner",
355 | "tip": 2.45,
356 | "total_bill": 20.69
357 | },
358 | {
359 | "day": "Sat",
360 | "sex": "Male",
361 | "size": 2,
362 | "smoker": "No",
363 | "time": "Dinner",
364 | "tip": 3.27,
365 | "total_bill": 17.78
366 | },
367 | {
368 | "day": "Sat",
369 | "sex": "Male",
370 | "size": 3,
371 | "smoker": "No",
372 | "time": "Dinner",
373 | "tip": 3.6,
374 | "total_bill": 24.06
375 | },
376 | {
377 | "day": "Sat",
378 | "sex": "Male",
379 | "size": 3,
380 | "smoker": "No",
381 | "time": "Dinner",
382 | "tip": 2,
383 | "total_bill": 16.31
384 | },
385 | {
386 | "day": "Sat",
387 | "sex": "Female",
388 | "size": 3,
389 | "smoker": "No",
390 | "time": "Dinner",
391 | "tip": 3.07,
392 | "total_bill": 16.93
393 | },
394 | {
395 | "day": "Sat",
396 | "sex": "Male",
397 | "size": 3,
398 | "smoker": "No",
399 | "time": "Dinner",
400 | "tip": 2.31,
401 | "total_bill": 18.69
402 | },
403 | {
404 | "day": "Sat",
405 | "sex": "Male",
406 | "size": 3,
407 | "smoker": "No",
408 | "time": "Dinner",
409 | "tip": 5,
410 | "total_bill": 31.27
411 | },
412 | {
413 | "day": "Sat",
414 | "sex": "Male",
415 | "size": 3,
416 | "smoker": "No",
417 | "time": "Dinner",
418 | "tip": 2.24,
419 | "total_bill": 16.04
420 | },
421 | {
422 | "day": "Sun",
423 | "sex": "Male",
424 | "size": 2,
425 | "smoker": "No",
426 | "time": "Dinner",
427 | "tip": 2.54,
428 | "total_bill": 17.46
429 | },
430 | {
431 | "day": "Sun",
432 | "sex": "Male",
433 | "size": 2,
434 | "smoker": "No",
435 | "time": "Dinner",
436 | "tip": 3.06,
437 | "total_bill": 13.94
438 | },
439 | {
440 | "day": "Sun",
441 | "sex": "Male",
442 | "size": 2,
443 | "smoker": "No",
444 | "time": "Dinner",
445 | "tip": 1.32,
446 | "total_bill": 9.68
447 | },
448 | {
449 | "day": "Sun",
450 | "sex": "Male",
451 | "size": 4,
452 | "smoker": "No",
453 | "time": "Dinner",
454 | "tip": 5.6,
455 | "total_bill": 30.4
456 | },
457 | {
458 | "day": "Sun",
459 | "sex": "Male",
460 | "size": 2,
461 | "smoker": "No",
462 | "time": "Dinner",
463 | "tip": 3,
464 | "total_bill": 18.29
465 | },
466 | {
467 | "day": "Sun",
468 | "sex": "Male",
469 | "size": 2,
470 | "smoker": "No",
471 | "time": "Dinner",
472 | "tip": 5,
473 | "total_bill": 22.23
474 | },
475 | {
476 | "day": "Sun",
477 | "sex": "Male",
478 | "size": 4,
479 | "smoker": "No",
480 | "time": "Dinner",
481 | "tip": 6,
482 | "total_bill": 32.4
483 | },
484 | {
485 | "day": "Sun",
486 | "sex": "Male",
487 | "size": 3,
488 | "smoker": "No",
489 | "time": "Dinner",
490 | "tip": 2.05,
491 | "total_bill": 28.55
492 | },
493 | {
494 | "day": "Sun",
495 | "sex": "Male",
496 | "size": 2,
497 | "smoker": "No",
498 | "time": "Dinner",
499 | "tip": 3,
500 | "total_bill": 18.04
501 | },
502 | {
503 | "day": "Sun",
504 | "sex": "Male",
505 | "size": 2,
506 | "smoker": "No",
507 | "time": "Dinner",
508 | "tip": 2.5,
509 | "total_bill": 12.54
510 | },
511 | {
512 | "day": "Sun",
513 | "sex": "Female",
514 | "size": 2,
515 | "smoker": "No",
516 | "time": "Dinner",
517 | "tip": 2.6,
518 | "total_bill": 10.29
519 | },
520 | {
521 | "day": "Sun",
522 | "sex": "Female",
523 | "size": 4,
524 | "smoker": "No",
525 | "time": "Dinner",
526 | "tip": 5.2,
527 | "total_bill": 34.81
528 | },
529 | {
530 | "day": "Sun",
531 | "sex": "Male",
532 | "size": 2,
533 | "smoker": "No",
534 | "time": "Dinner",
535 | "tip": 1.56,
536 | "total_bill": 9.94
537 | },
538 | {
539 | "day": "Sun",
540 | "sex": "Male",
541 | "size": 4,
542 | "smoker": "No",
543 | "time": "Dinner",
544 | "tip": 4.34,
545 | "total_bill": 25.56
546 | },
547 | {
548 | "day": "Sun",
549 | "sex": "Male",
550 | "size": 2,
551 | "smoker": "No",
552 | "time": "Dinner",
553 | "tip": 3.51,
554 | "total_bill": 19.49
555 | },
556 | {
557 | "day": "Sat",
558 | "sex": "Male",
559 | "size": 4,
560 | "smoker": "Yes",
561 | "time": "Dinner",
562 | "tip": 3,
563 | "total_bill": 38.01
564 | },
565 | {
566 | "day": "Sat",
567 | "sex": "Female",
568 | "size": 2,
569 | "smoker": "No",
570 | "time": "Dinner",
571 | "tip": 1.5,
572 | "total_bill": 26.41
573 | },
574 | {
575 | "day": "Sat",
576 | "sex": "Male",
577 | "size": 2,
578 | "smoker": "Yes",
579 | "time": "Dinner",
580 | "tip": 1.76,
581 | "total_bill": 11.24
582 | },
583 | {
584 | "day": "Sat",
585 | "sex": "Male",
586 | "size": 4,
587 | "smoker": "No",
588 | "time": "Dinner",
589 | "tip": 6.73,
590 | "total_bill": 48.27
591 | },
592 | {
593 | "day": "Sat",
594 | "sex": "Male",
595 | "size": 2,
596 | "smoker": "Yes",
597 | "time": "Dinner",
598 | "tip": 3.21,
599 | "total_bill": 20.29
600 | },
601 | {
602 | "day": "Sat",
603 | "sex": "Male",
604 | "size": 2,
605 | "smoker": "Yes",
606 | "time": "Dinner",
607 | "tip": 2,
608 | "total_bill": 13.81
609 | },
610 | {
611 | "day": "Sat",
612 | "sex": "Male",
613 | "size": 2,
614 | "smoker": "Yes",
615 | "time": "Dinner",
616 | "tip": 1.98,
617 | "total_bill": 11.02
618 | },
619 | {
620 | "day": "Sat",
621 | "sex": "Male",
622 | "size": 4,
623 | "smoker": "Yes",
624 | "time": "Dinner",
625 | "tip": 3.76,
626 | "total_bill": 18.29
627 | },
628 | {
629 | "day": "Sat",
630 | "sex": "Male",
631 | "size": 3,
632 | "smoker": "No",
633 | "time": "Dinner",
634 | "tip": 2.64,
635 | "total_bill": 17.59
636 | },
637 | {
638 | "day": "Sat",
639 | "sex": "Male",
640 | "size": 3,
641 | "smoker": "No",
642 | "time": "Dinner",
643 | "tip": 3.15,
644 | "total_bill": 20.08
645 | },
646 | {
647 | "day": "Sat",
648 | "sex": "Female",
649 | "size": 2,
650 | "smoker": "No",
651 | "time": "Dinner",
652 | "tip": 2.47,
653 | "total_bill": 16.45
654 | },
655 | {
656 | "day": "Sat",
657 | "sex": "Female",
658 | "size": 1,
659 | "smoker": "Yes",
660 | "time": "Dinner",
661 | "tip": 1,
662 | "total_bill": 3.07
663 | },
664 | {
665 | "day": "Sat",
666 | "sex": "Male",
667 | "size": 2,
668 | "smoker": "No",
669 | "time": "Dinner",
670 | "tip": 2.01,
671 | "total_bill": 20.23
672 | },
673 | {
674 | "day": "Sat",
675 | "sex": "Male",
676 | "size": 2,
677 | "smoker": "Yes",
678 | "time": "Dinner",
679 | "tip": 2.09,
680 | "total_bill": 15.01
681 | },
682 | {
683 | "day": "Sat",
684 | "sex": "Male",
685 | "size": 2,
686 | "smoker": "No",
687 | "time": "Dinner",
688 | "tip": 1.97,
689 | "total_bill": 12.02
690 | },
691 | {
692 | "day": "Sat",
693 | "sex": "Female",
694 | "size": 3,
695 | "smoker": "No",
696 | "time": "Dinner",
697 | "tip": 3,
698 | "total_bill": 17.07
699 | },
700 | {
701 | "day": "Sat",
702 | "sex": "Female",
703 | "size": 2,
704 | "smoker": "Yes",
705 | "time": "Dinner",
706 | "tip": 3.14,
707 | "total_bill": 26.86
708 | },
709 | {
710 | "day": "Sat",
711 | "sex": "Female",
712 | "size": 2,
713 | "smoker": "Yes",
714 | "time": "Dinner",
715 | "tip": 5,
716 | "total_bill": 25.28
717 | },
718 | {
719 | "day": "Sat",
720 | "sex": "Female",
721 | "size": 2,
722 | "smoker": "No",
723 | "time": "Dinner",
724 | "tip": 2.2,
725 | "total_bill": 14.73
726 | },
727 | {
728 | "day": "Sat",
729 | "sex": "Male",
730 | "size": 2,
731 | "smoker": "No",
732 | "time": "Dinner",
733 | "tip": 1.25,
734 | "total_bill": 10.51
735 | },
736 | {
737 | "day": "Sat",
738 | "sex": "Male",
739 | "size": 2,
740 | "smoker": "Yes",
741 | "time": "Dinner",
742 | "tip": 3.08,
743 | "total_bill": 17.92
744 | },
745 | {
746 | "day": "Thur",
747 | "sex": "Male",
748 | "size": 4,
749 | "smoker": "No",
750 | "time": "Lunch",
751 | "tip": 4,
752 | "total_bill": 27.2
753 | },
754 | {
755 | "day": "Thur",
756 | "sex": "Male",
757 | "size": 2,
758 | "smoker": "No",
759 | "time": "Lunch",
760 | "tip": 3,
761 | "total_bill": 22.76
762 | },
763 | {
764 | "day": "Thur",
765 | "sex": "Male",
766 | "size": 2,
767 | "smoker": "No",
768 | "time": "Lunch",
769 | "tip": 2.71,
770 | "total_bill": 17.29
771 | },
772 | {
773 | "day": "Thur",
774 | "sex": "Male",
775 | "size": 2,
776 | "smoker": "Yes",
777 | "time": "Lunch",
778 | "tip": 3,
779 | "total_bill": 19.44
780 | },
781 | {
782 | "day": "Thur",
783 | "sex": "Male",
784 | "size": 2,
785 | "smoker": "No",
786 | "time": "Lunch",
787 | "tip": 3.4,
788 | "total_bill": 16.66
789 | },
790 | {
791 | "day": "Thur",
792 | "sex": "Female",
793 | "size": 1,
794 | "smoker": "No",
795 | "time": "Lunch",
796 | "tip": 1.83,
797 | "total_bill": 10.07
798 | },
799 | {
800 | "day": "Thur",
801 | "sex": "Male",
802 | "size": 2,
803 | "smoker": "Yes",
804 | "time": "Lunch",
805 | "tip": 5,
806 | "total_bill": 32.68
807 | },
808 | {
809 | "day": "Thur",
810 | "sex": "Male",
811 | "size": 2,
812 | "smoker": "No",
813 | "time": "Lunch",
814 | "tip": 2.03,
815 | "total_bill": 15.98
816 | },
817 | {
818 | "day": "Thur",
819 | "sex": "Female",
820 | "size": 4,
821 | "smoker": "No",
822 | "time": "Lunch",
823 | "tip": 5.17,
824 | "total_bill": 34.83
825 | },
826 | {
827 | "day": "Thur",
828 | "sex": "Male",
829 | "size": 2,
830 | "smoker": "No",
831 | "time": "Lunch",
832 | "tip": 2,
833 | "total_bill": 13.03
834 | },
835 | {
836 | "day": "Thur",
837 | "sex": "Male",
838 | "size": 2,
839 | "smoker": "No",
840 | "time": "Lunch",
841 | "tip": 4,
842 | "total_bill": 18.28
843 | },
844 | {
845 | "day": "Thur",
846 | "sex": "Male",
847 | "size": 2,
848 | "smoker": "No",
849 | "time": "Lunch",
850 | "tip": 5.85,
851 | "total_bill": 24.71
852 | },
853 | {
854 | "day": "Thur",
855 | "sex": "Male",
856 | "size": 2,
857 | "smoker": "No",
858 | "time": "Lunch",
859 | "tip": 3,
860 | "total_bill": 21.16
861 | },
862 | {
863 | "day": "Fri",
864 | "sex": "Male",
865 | "size": 2,
866 | "smoker": "Yes",
867 | "time": "Dinner",
868 | "tip": 3,
869 | "total_bill": 28.97
870 | },
871 | {
872 | "day": "Fri",
873 | "sex": "Male",
874 | "size": 2,
875 | "smoker": "No",
876 | "time": "Dinner",
877 | "tip": 3.5,
878 | "total_bill": 22.49
879 | },
880 | {
881 | "day": "Fri",
882 | "sex": "Female",
883 | "size": 2,
884 | "smoker": "Yes",
885 | "time": "Dinner",
886 | "tip": 1,
887 | "total_bill": 5.75
888 | },
889 | {
890 | "day": "Fri",
891 | "sex": "Female",
892 | "size": 2,
893 | "smoker": "Yes",
894 | "time": "Dinner",
895 | "tip": 4.3,
896 | "total_bill": 16.32
897 | },
898 | {
899 | "day": "Fri",
900 | "sex": "Female",
901 | "size": 2,
902 | "smoker": "No",
903 | "time": "Dinner",
904 | "tip": 3.25,
905 | "total_bill": 22.75
906 | },
907 | {
908 | "day": "Fri",
909 | "sex": "Male",
910 | "size": 4,
911 | "smoker": "Yes",
912 | "time": "Dinner",
913 | "tip": 4.73,
914 | "total_bill": 40.17
915 | },
916 | {
917 | "day": "Fri",
918 | "sex": "Male",
919 | "size": 2,
920 | "smoker": "Yes",
921 | "time": "Dinner",
922 | "tip": 4,
923 | "total_bill": 27.28
924 | },
925 | {
926 | "day": "Fri",
927 | "sex": "Male",
928 | "size": 2,
929 | "smoker": "Yes",
930 | "time": "Dinner",
931 | "tip": 1.5,
932 | "total_bill": 12.03
933 | },
934 | {
935 | "day": "Fri",
936 | "sex": "Male",
937 | "size": 2,
938 | "smoker": "Yes",
939 | "time": "Dinner",
940 | "tip": 3,
941 | "total_bill": 21.01
942 | },
943 | {
944 | "day": "Fri",
945 | "sex": "Male",
946 | "size": 2,
947 | "smoker": "No",
948 | "time": "Dinner",
949 | "tip": 1.5,
950 | "total_bill": 12.46
951 | },
952 | {
953 | "day": "Fri",
954 | "sex": "Female",
955 | "size": 2,
956 | "smoker": "Yes",
957 | "time": "Dinner",
958 | "tip": 2.5,
959 | "total_bill": 11.35
960 | },
961 | {
962 | "day": "Fri",
963 | "sex": "Female",
964 | "size": 2,
965 | "smoker": "Yes",
966 | "time": "Dinner",
967 | "tip": 3,
968 | "total_bill": 15.38
969 | },
970 | {
971 | "day": "Sat",
972 | "sex": "Female",
973 | "size": 3,
974 | "smoker": "Yes",
975 | "time": "Dinner",
976 | "tip": 2.5,
977 | "total_bill": 44.3
978 | },
979 | {
980 | "day": "Sat",
981 | "sex": "Female",
982 | "size": 2,
983 | "smoker": "Yes",
984 | "time": "Dinner",
985 | "tip": 3.48,
986 | "total_bill": 22.42
987 | },
988 | {
989 | "day": "Sat",
990 | "sex": "Female",
991 | "size": 2,
992 | "smoker": "No",
993 | "time": "Dinner",
994 | "tip": 4.08,
995 | "total_bill": 20.92
996 | },
997 | {
998 | "day": "Sat",
999 | "sex": "Male",
1000 | "size": 2,
1001 | "smoker": "Yes",
1002 | "time": "Dinner",
1003 | "tip": 1.64,
1004 | "total_bill": 15.36
1005 | },
1006 | {
1007 | "day": "Sat",
1008 | "sex": "Male",
1009 | "size": 2,
1010 | "smoker": "Yes",
1011 | "time": "Dinner",
1012 | "tip": 4.06,
1013 | "total_bill": 20.49
1014 | },
1015 | {
1016 | "day": "Sat",
1017 | "sex": "Male",
1018 | "size": 2,
1019 | "smoker": "Yes",
1020 | "time": "Dinner",
1021 | "tip": 4.29,
1022 | "total_bill": 25.21
1023 | },
1024 | {
1025 | "day": "Sat",
1026 | "sex": "Male",
1027 | "size": 2,
1028 | "smoker": "No",
1029 | "time": "Dinner",
1030 | "tip": 3.76,
1031 | "total_bill": 18.24
1032 | },
1033 | {
1034 | "day": "Sat",
1035 | "sex": "Female",
1036 | "size": 2,
1037 | "smoker": "Yes",
1038 | "time": "Dinner",
1039 | "tip": 4,
1040 | "total_bill": 14.31
1041 | },
1042 | {
1043 | "day": "Sat",
1044 | "sex": "Male",
1045 | "size": 2,
1046 | "smoker": "No",
1047 | "time": "Dinner",
1048 | "tip": 3,
1049 | "total_bill": 14
1050 | },
1051 | {
1052 | "day": "Sat",
1053 | "sex": "Female",
1054 | "size": 1,
1055 | "smoker": "No",
1056 | "time": "Dinner",
1057 | "tip": 1,
1058 | "total_bill": 7.25
1059 | },
1060 | {
1061 | "day": "Sun",
1062 | "sex": "Male",
1063 | "size": 3,
1064 | "smoker": "No",
1065 | "time": "Dinner",
1066 | "tip": 4,
1067 | "total_bill": 38.07
1068 | },
1069 | {
1070 | "day": "Sun",
1071 | "sex": "Male",
1072 | "size": 2,
1073 | "smoker": "No",
1074 | "time": "Dinner",
1075 | "tip": 2.55,
1076 | "total_bill": 23.95
1077 | },
1078 | {
1079 | "day": "Sun",
1080 | "sex": "Female",
1081 | "size": 3,
1082 | "smoker": "No",
1083 | "time": "Dinner",
1084 | "tip": 4,
1085 | "total_bill": 25.71
1086 | },
1087 | {
1088 | "day": "Sun",
1089 | "sex": "Female",
1090 | "size": 2,
1091 | "smoker": "No",
1092 | "time": "Dinner",
1093 | "tip": 3.5,
1094 | "total_bill": 17.31
1095 | },
1096 | {
1097 | "day": "Sun",
1098 | "sex": "Male",
1099 | "size": 4,
1100 | "smoker": "No",
1101 | "time": "Dinner",
1102 | "tip": 5.07,
1103 | "total_bill": 29.93
1104 | },
1105 | {
1106 | "day": "Thur",
1107 | "sex": "Female",
1108 | "size": 2,
1109 | "smoker": "No",
1110 | "time": "Lunch",
1111 | "tip": 1.5,
1112 | "total_bill": 10.65
1113 | },
1114 | {
1115 | "day": "Thur",
1116 | "sex": "Female",
1117 | "size": 2,
1118 | "smoker": "No",
1119 | "time": "Lunch",
1120 | "tip": 1.8,
1121 | "total_bill": 12.43
1122 | },
1123 | {
1124 | "day": "Thur",
1125 | "sex": "Female",
1126 | "size": 4,
1127 | "smoker": "No",
1128 | "time": "Lunch",
1129 | "tip": 2.92,
1130 | "total_bill": 24.08
1131 | },
1132 | {
1133 | "day": "Thur",
1134 | "sex": "Male",
1135 | "size": 2,
1136 | "smoker": "No",
1137 | "time": "Lunch",
1138 | "tip": 2.31,
1139 | "total_bill": 11.69
1140 | },
1141 | {
1142 | "day": "Thur",
1143 | "sex": "Female",
1144 | "size": 2,
1145 | "smoker": "No",
1146 | "time": "Lunch",
1147 | "tip": 1.68,
1148 | "total_bill": 13.42
1149 | },
1150 | {
1151 | "day": "Thur",
1152 | "sex": "Male",
1153 | "size": 2,
1154 | "smoker": "No",
1155 | "time": "Lunch",
1156 | "tip": 2.5,
1157 | "total_bill": 14.26
1158 | },
1159 | {
1160 | "day": "Thur",
1161 | "sex": "Male",
1162 | "size": 2,
1163 | "smoker": "No",
1164 | "time": "Lunch",
1165 | "tip": 2,
1166 | "total_bill": 15.95
1167 | },
1168 | {
1169 | "day": "Thur",
1170 | "sex": "Female",
1171 | "size": 2,
1172 | "smoker": "No",
1173 | "time": "Lunch",
1174 | "tip": 2.52,
1175 | "total_bill": 12.48
1176 | },
1177 | {
1178 | "day": "Thur",
1179 | "sex": "Female",
1180 | "size": 6,
1181 | "smoker": "No",
1182 | "time": "Lunch",
1183 | "tip": 4.2,
1184 | "total_bill": 29.8
1185 | },
1186 | {
1187 | "day": "Thur",
1188 | "sex": "Male",
1189 | "size": 2,
1190 | "smoker": "No",
1191 | "time": "Lunch",
1192 | "tip": 1.48,
1193 | "total_bill": 8.52
1194 | },
1195 | {
1196 | "day": "Thur",
1197 | "sex": "Female",
1198 | "size": 2,
1199 | "smoker": "No",
1200 | "time": "Lunch",
1201 | "tip": 2,
1202 | "total_bill": 14.52
1203 | },
1204 | {
1205 | "day": "Thur",
1206 | "sex": "Female",
1207 | "size": 2,
1208 | "smoker": "No",
1209 | "time": "Lunch",
1210 | "tip": 2,
1211 | "total_bill": 11.38
1212 | },
1213 | {
1214 | "day": "Thur",
1215 | "sex": "Male",
1216 | "size": 3,
1217 | "smoker": "No",
1218 | "time": "Lunch",
1219 | "tip": 2.18,
1220 | "total_bill": 22.82
1221 | },
1222 | {
1223 | "day": "Thur",
1224 | "sex": "Male",
1225 | "size": 2,
1226 | "smoker": "No",
1227 | "time": "Lunch",
1228 | "tip": 1.5,
1229 | "total_bill": 19.08
1230 | },
1231 | {
1232 | "day": "Thur",
1233 | "sex": "Female",
1234 | "size": 2,
1235 | "smoker": "No",
1236 | "time": "Lunch",
1237 | "tip": 2.83,
1238 | "total_bill": 20.27
1239 | },
1240 | {
1241 | "day": "Thur",
1242 | "sex": "Female",
1243 | "size": 2,
1244 | "smoker": "No",
1245 | "time": "Lunch",
1246 | "tip": 1.5,
1247 | "total_bill": 11.17
1248 | },
1249 | {
1250 | "day": "Thur",
1251 | "sex": "Female",
1252 | "size": 2,
1253 | "smoker": "No",
1254 | "time": "Lunch",
1255 | "tip": 2,
1256 | "total_bill": 12.26
1257 | },
1258 | {
1259 | "day": "Thur",
1260 | "sex": "Female",
1261 | "size": 2,
1262 | "smoker": "No",
1263 | "time": "Lunch",
1264 | "tip": 3.25,
1265 | "total_bill": 18.26
1266 | },
1267 | {
1268 | "day": "Thur",
1269 | "sex": "Female",
1270 | "size": 2,
1271 | "smoker": "No",
1272 | "time": "Lunch",
1273 | "tip": 1.25,
1274 | "total_bill": 8.51
1275 | },
1276 | {
1277 | "day": "Thur",
1278 | "sex": "Female",
1279 | "size": 2,
1280 | "smoker": "No",
1281 | "time": "Lunch",
1282 | "tip": 2,
1283 | "total_bill": 10.33
1284 | },
1285 | {
1286 | "day": "Thur",
1287 | "sex": "Female",
1288 | "size": 2,
1289 | "smoker": "No",
1290 | "time": "Lunch",
1291 | "tip": 2,
1292 | "total_bill": 14.15
1293 | },
1294 | {
1295 | "day": "Thur",
1296 | "sex": "Male",
1297 | "size": 2,
1298 | "smoker": "Yes",
1299 | "time": "Lunch",
1300 | "tip": 2,
1301 | "total_bill": 16
1302 | },
1303 | {
1304 | "day": "Thur",
1305 | "sex": "Female",
1306 | "size": 2,
1307 | "smoker": "No",
1308 | "time": "Lunch",
1309 | "tip": 2.75,
1310 | "total_bill": 13.16
1311 | },
1312 | {
1313 | "day": "Thur",
1314 | "sex": "Female",
1315 | "size": 2,
1316 | "smoker": "No",
1317 | "time": "Lunch",
1318 | "tip": 3.5,
1319 | "total_bill": 17.47
1320 | },
1321 | {
1322 | "day": "Thur",
1323 | "sex": "Male",
1324 | "size": 6,
1325 | "smoker": "No",
1326 | "time": "Lunch",
1327 | "tip": 6.7,
1328 | "total_bill": 34.3
1329 | },
1330 | {
1331 | "day": "Thur",
1332 | "sex": "Male",
1333 | "size": 5,
1334 | "smoker": "No",
1335 | "time": "Lunch",
1336 | "tip": 5,
1337 | "total_bill": 41.19
1338 | },
1339 | {
1340 | "day": "Thur",
1341 | "sex": "Female",
1342 | "size": 6,
1343 | "smoker": "No",
1344 | "time": "Lunch",
1345 | "tip": 5,
1346 | "total_bill": 27.05
1347 | },
1348 | {
1349 | "day": "Thur",
1350 | "sex": "Female",
1351 | "size": 2,
1352 | "smoker": "No",
1353 | "time": "Lunch",
1354 | "tip": 2.3,
1355 | "total_bill": 16.43
1356 | },
1357 | {
1358 | "day": "Thur",
1359 | "sex": "Female",
1360 | "size": 2,
1361 | "smoker": "No",
1362 | "time": "Lunch",
1363 | "tip": 1.5,
1364 | "total_bill": 8.35
1365 | },
1366 | {
1367 | "day": "Thur",
1368 | "sex": "Female",
1369 | "size": 3,
1370 | "smoker": "No",
1371 | "time": "Lunch",
1372 | "tip": 1.36,
1373 | "total_bill": 18.64
1374 | },
1375 | {
1376 | "day": "Thur",
1377 | "sex": "Female",
1378 | "size": 2,
1379 | "smoker": "No",
1380 | "time": "Lunch",
1381 | "tip": 1.63,
1382 | "total_bill": 11.87
1383 | },
1384 | {
1385 | "day": "Thur",
1386 | "sex": "Male",
1387 | "size": 2,
1388 | "smoker": "No",
1389 | "time": "Lunch",
1390 | "tip": 1.73,
1391 | "total_bill": 9.78
1392 | },
1393 | {
1394 | "day": "Thur",
1395 | "sex": "Male",
1396 | "size": 2,
1397 | "smoker": "No",
1398 | "time": "Lunch",
1399 | "tip": 2,
1400 | "total_bill": 7.51
1401 | },
1402 | {
1403 | "day": "Sun",
1404 | "sex": "Male",
1405 | "size": 2,
1406 | "smoker": "No",
1407 | "time": "Dinner",
1408 | "tip": 2.5,
1409 | "total_bill": 14.07
1410 | },
1411 | {
1412 | "day": "Sun",
1413 | "sex": "Male",
1414 | "size": 2,
1415 | "smoker": "No",
1416 | "time": "Dinner",
1417 | "tip": 2,
1418 | "total_bill": 13.13
1419 | },
1420 | {
1421 | "day": "Sun",
1422 | "sex": "Male",
1423 | "size": 3,
1424 | "smoker": "No",
1425 | "time": "Dinner",
1426 | "tip": 2.74,
1427 | "total_bill": 17.26
1428 | },
1429 | {
1430 | "day": "Sun",
1431 | "sex": "Male",
1432 | "size": 4,
1433 | "smoker": "No",
1434 | "time": "Dinner",
1435 | "tip": 2,
1436 | "total_bill": 24.55
1437 | },
1438 | {
1439 | "day": "Sun",
1440 | "sex": "Male",
1441 | "size": 4,
1442 | "smoker": "No",
1443 | "time": "Dinner",
1444 | "tip": 2,
1445 | "total_bill": 19.77
1446 | },
1447 | {
1448 | "day": "Sun",
1449 | "sex": "Female",
1450 | "size": 5,
1451 | "smoker": "No",
1452 | "time": "Dinner",
1453 | "tip": 5.14,
1454 | "total_bill": 29.85
1455 | },
1456 | {
1457 | "day": "Sun",
1458 | "sex": "Male",
1459 | "size": 6,
1460 | "smoker": "No",
1461 | "time": "Dinner",
1462 | "tip": 5,
1463 | "total_bill": 48.17
1464 | },
1465 | {
1466 | "day": "Sun",
1467 | "sex": "Female",
1468 | "size": 4,
1469 | "smoker": "No",
1470 | "time": "Dinner",
1471 | "tip": 3.75,
1472 | "total_bill": 25
1473 | },
1474 | {
1475 | "day": "Sun",
1476 | "sex": "Female",
1477 | "size": 2,
1478 | "smoker": "No",
1479 | "time": "Dinner",
1480 | "tip": 2.61,
1481 | "total_bill": 13.39
1482 | },
1483 | {
1484 | "day": "Sun",
1485 | "sex": "Male",
1486 | "size": 4,
1487 | "smoker": "No",
1488 | "time": "Dinner",
1489 | "tip": 2,
1490 | "total_bill": 16.49
1491 | },
1492 | {
1493 | "day": "Sun",
1494 | "sex": "Male",
1495 | "size": 4,
1496 | "smoker": "No",
1497 | "time": "Dinner",
1498 | "tip": 3.5,
1499 | "total_bill": 21.5
1500 | },
1501 | {
1502 | "day": "Sun",
1503 | "sex": "Male",
1504 | "size": 2,
1505 | "smoker": "No",
1506 | "time": "Dinner",
1507 | "tip": 2.5,
1508 | "total_bill": 12.66
1509 | },
1510 | {
1511 | "day": "Sun",
1512 | "sex": "Female",
1513 | "size": 3,
1514 | "smoker": "No",
1515 | "time": "Dinner",
1516 | "tip": 2,
1517 | "total_bill": 16.21
1518 | },
1519 | {
1520 | "day": "Sun",
1521 | "sex": "Male",
1522 | "size": 2,
1523 | "smoker": "No",
1524 | "time": "Dinner",
1525 | "tip": 2,
1526 | "total_bill": 13.81
1527 | },
1528 | {
1529 | "day": "Sun",
1530 | "sex": "Female",
1531 | "size": 2,
1532 | "smoker": "Yes",
1533 | "time": "Dinner",
1534 | "tip": 3,
1535 | "total_bill": 17.51
1536 | },
1537 | {
1538 | "day": "Sun",
1539 | "sex": "Male",
1540 | "size": 3,
1541 | "smoker": "No",
1542 | "time": "Dinner",
1543 | "tip": 3.48,
1544 | "total_bill": 24.52
1545 | },
1546 | {
1547 | "day": "Sun",
1548 | "sex": "Male",
1549 | "size": 2,
1550 | "smoker": "No",
1551 | "time": "Dinner",
1552 | "tip": 2.24,
1553 | "total_bill": 20.76
1554 | },
1555 | {
1556 | "day": "Sun",
1557 | "sex": "Male",
1558 | "size": 4,
1559 | "smoker": "No",
1560 | "time": "Dinner",
1561 | "tip": 4.5,
1562 | "total_bill": 31.71
1563 | },
1564 | {
1565 | "day": "Sat",
1566 | "sex": "Female",
1567 | "size": 2,
1568 | "smoker": "Yes",
1569 | "time": "Dinner",
1570 | "tip": 1.61,
1571 | "total_bill": 10.59
1572 | },
1573 | {
1574 | "day": "Sat",
1575 | "sex": "Female",
1576 | "size": 2,
1577 | "smoker": "Yes",
1578 | "time": "Dinner",
1579 | "tip": 2,
1580 | "total_bill": 10.63
1581 | },
1582 | {
1583 | "day": "Sat",
1584 | "sex": "Male",
1585 | "size": 3,
1586 | "smoker": "Yes",
1587 | "time": "Dinner",
1588 | "tip": 10,
1589 | "total_bill": 50.81
1590 | },
1591 | {
1592 | "day": "Sat",
1593 | "sex": "Male",
1594 | "size": 2,
1595 | "smoker": "Yes",
1596 | "time": "Dinner",
1597 | "tip": 3.16,
1598 | "total_bill": 15.81
1599 | },
1600 | {
1601 | "day": "Sun",
1602 | "sex": "Male",
1603 | "size": 2,
1604 | "smoker": "Yes",
1605 | "time": "Dinner",
1606 | "tip": 5.15,
1607 | "total_bill": 7.25
1608 | },
1609 | {
1610 | "day": "Sun",
1611 | "sex": "Male",
1612 | "size": 2,
1613 | "smoker": "Yes",
1614 | "time": "Dinner",
1615 | "tip": 3.18,
1616 | "total_bill": 31.85
1617 | },
1618 | {
1619 | "day": "Sun",
1620 | "sex": "Male",
1621 | "size": 2,
1622 | "smoker": "Yes",
1623 | "time": "Dinner",
1624 | "tip": 4,
1625 | "total_bill": 16.82
1626 | },
1627 | {
1628 | "day": "Sun",
1629 | "sex": "Male",
1630 | "size": 2,
1631 | "smoker": "Yes",
1632 | "time": "Dinner",
1633 | "tip": 3.11,
1634 | "total_bill": 32.9
1635 | },
1636 | {
1637 | "day": "Sun",
1638 | "sex": "Male",
1639 | "size": 2,
1640 | "smoker": "Yes",
1641 | "time": "Dinner",
1642 | "tip": 2,
1643 | "total_bill": 17.89
1644 | },
1645 | {
1646 | "day": "Sun",
1647 | "sex": "Male",
1648 | "size": 2,
1649 | "smoker": "Yes",
1650 | "time": "Dinner",
1651 | "tip": 2,
1652 | "total_bill": 14.48
1653 | },
1654 | {
1655 | "day": "Sun",
1656 | "sex": "Female",
1657 | "size": 2,
1658 | "smoker": "Yes",
1659 | "time": "Dinner",
1660 | "tip": 4,
1661 | "total_bill": 9.6
1662 | },
1663 | {
1664 | "day": "Sun",
1665 | "sex": "Male",
1666 | "size": 2,
1667 | "smoker": "Yes",
1668 | "time": "Dinner",
1669 | "tip": 3.55,
1670 | "total_bill": 34.63
1671 | },
1672 | {
1673 | "day": "Sun",
1674 | "sex": "Male",
1675 | "size": 4,
1676 | "smoker": "Yes",
1677 | "time": "Dinner",
1678 | "tip": 3.68,
1679 | "total_bill": 34.65
1680 | },
1681 | {
1682 | "day": "Sun",
1683 | "sex": "Male",
1684 | "size": 2,
1685 | "smoker": "Yes",
1686 | "time": "Dinner",
1687 | "tip": 5.65,
1688 | "total_bill": 23.33
1689 | },
1690 | {
1691 | "day": "Sun",
1692 | "sex": "Male",
1693 | "size": 3,
1694 | "smoker": "Yes",
1695 | "time": "Dinner",
1696 | "tip": 3.5,
1697 | "total_bill": 45.35
1698 | },
1699 | {
1700 | "day": "Sun",
1701 | "sex": "Male",
1702 | "size": 4,
1703 | "smoker": "Yes",
1704 | "time": "Dinner",
1705 | "tip": 6.5,
1706 | "total_bill": 23.17
1707 | },
1708 | {
1709 | "day": "Sun",
1710 | "sex": "Male",
1711 | "size": 2,
1712 | "smoker": "Yes",
1713 | "time": "Dinner",
1714 | "tip": 3,
1715 | "total_bill": 40.55
1716 | },
1717 | {
1718 | "day": "Sun",
1719 | "sex": "Male",
1720 | "size": 5,
1721 | "smoker": "No",
1722 | "time": "Dinner",
1723 | "tip": 5,
1724 | "total_bill": 20.69
1725 | },
1726 | {
1727 | "day": "Sun",
1728 | "sex": "Female",
1729 | "size": 3,
1730 | "smoker": "Yes",
1731 | "time": "Dinner",
1732 | "tip": 3.5,
1733 | "total_bill": 20.9
1734 | },
1735 | {
1736 | "day": "Sun",
1737 | "sex": "Male",
1738 | "size": 5,
1739 | "smoker": "Yes",
1740 | "time": "Dinner",
1741 | "tip": 2,
1742 | "total_bill": 30.46
1743 | },
1744 | {
1745 | "day": "Sun",
1746 | "sex": "Female",
1747 | "size": 3,
1748 | "smoker": "Yes",
1749 | "time": "Dinner",
1750 | "tip": 3.5,
1751 | "total_bill": 18.15
1752 | },
1753 | {
1754 | "day": "Sun",
1755 | "sex": "Male",
1756 | "size": 3,
1757 | "smoker": "Yes",
1758 | "time": "Dinner",
1759 | "tip": 4,
1760 | "total_bill": 23.1
1761 | },
1762 | {
1763 | "day": "Sun",
1764 | "sex": "Male",
1765 | "size": 2,
1766 | "smoker": "Yes",
1767 | "time": "Dinner",
1768 | "tip": 1.5,
1769 | "total_bill": 15.69
1770 | },
1771 | {
1772 | "day": "Thur",
1773 | "sex": "Female",
1774 | "size": 2,
1775 | "smoker": "Yes",
1776 | "time": "Lunch",
1777 | "tip": 4.19,
1778 | "total_bill": 19.81
1779 | },
1780 | {
1781 | "day": "Thur",
1782 | "sex": "Male",
1783 | "size": 2,
1784 | "smoker": "Yes",
1785 | "time": "Lunch",
1786 | "tip": 2.56,
1787 | "total_bill": 28.44
1788 | },
1789 | {
1790 | "day": "Thur",
1791 | "sex": "Male",
1792 | "size": 2,
1793 | "smoker": "Yes",
1794 | "time": "Lunch",
1795 | "tip": 2.02,
1796 | "total_bill": 15.48
1797 | },
1798 | {
1799 | "day": "Thur",
1800 | "sex": "Male",
1801 | "size": 2,
1802 | "smoker": "Yes",
1803 | "time": "Lunch",
1804 | "tip": 4,
1805 | "total_bill": 16.58
1806 | },
1807 | {
1808 | "day": "Thur",
1809 | "sex": "Male",
1810 | "size": 2,
1811 | "smoker": "No",
1812 | "time": "Lunch",
1813 | "tip": 1.44,
1814 | "total_bill": 7.56
1815 | },
1816 | {
1817 | "day": "Thur",
1818 | "sex": "Male",
1819 | "size": 2,
1820 | "smoker": "Yes",
1821 | "time": "Lunch",
1822 | "tip": 2,
1823 | "total_bill": 10.34
1824 | },
1825 | {
1826 | "day": "Thur",
1827 | "sex": "Female",
1828 | "size": 4,
1829 | "smoker": "Yes",
1830 | "time": "Lunch",
1831 | "tip": 5,
1832 | "total_bill": 43.11
1833 | },
1834 | {
1835 | "day": "Thur",
1836 | "sex": "Female",
1837 | "size": 2,
1838 | "smoker": "Yes",
1839 | "time": "Lunch",
1840 | "tip": 2,
1841 | "total_bill": 13
1842 | },
1843 | {
1844 | "day": "Thur",
1845 | "sex": "Male",
1846 | "size": 2,
1847 | "smoker": "Yes",
1848 | "time": "Lunch",
1849 | "tip": 2,
1850 | "total_bill": 13.51
1851 | },
1852 | {
1853 | "day": "Thur",
1854 | "sex": "Male",
1855 | "size": 3,
1856 | "smoker": "Yes",
1857 | "time": "Lunch",
1858 | "tip": 4,
1859 | "total_bill": 18.71
1860 | },
1861 | {
1862 | "day": "Thur",
1863 | "sex": "Female",
1864 | "size": 2,
1865 | "smoker": "Yes",
1866 | "time": "Lunch",
1867 | "tip": 2.01,
1868 | "total_bill": 12.74
1869 | },
1870 | {
1871 | "day": "Thur",
1872 | "sex": "Female",
1873 | "size": 2,
1874 | "smoker": "Yes",
1875 | "time": "Lunch",
1876 | "tip": 2,
1877 | "total_bill": 13
1878 | },
1879 | {
1880 | "day": "Thur",
1881 | "sex": "Female",
1882 | "size": 2,
1883 | "smoker": "Yes",
1884 | "time": "Lunch",
1885 | "tip": 2.5,
1886 | "total_bill": 16.4
1887 | },
1888 | {
1889 | "day": "Thur",
1890 | "sex": "Male",
1891 | "size": 4,
1892 | "smoker": "Yes",
1893 | "time": "Lunch",
1894 | "tip": 4,
1895 | "total_bill": 20.53
1896 | },
1897 | {
1898 | "day": "Thur",
1899 | "sex": "Female",
1900 | "size": 3,
1901 | "smoker": "Yes",
1902 | "time": "Lunch",
1903 | "tip": 3.23,
1904 | "total_bill": 16.47
1905 | },
1906 | {
1907 | "day": "Sat",
1908 | "sex": "Male",
1909 | "size": 3,
1910 | "smoker": "Yes",
1911 | "time": "Dinner",
1912 | "tip": 3.41,
1913 | "total_bill": 26.59
1914 | },
1915 | {
1916 | "day": "Sat",
1917 | "sex": "Male",
1918 | "size": 4,
1919 | "smoker": "Yes",
1920 | "time": "Dinner",
1921 | "tip": 3,
1922 | "total_bill": 38.73
1923 | },
1924 | {
1925 | "day": "Sat",
1926 | "sex": "Male",
1927 | "size": 2,
1928 | "smoker": "Yes",
1929 | "time": "Dinner",
1930 | "tip": 2.03,
1931 | "total_bill": 24.27
1932 | },
1933 | {
1934 | "day": "Sat",
1935 | "sex": "Female",
1936 | "size": 2,
1937 | "smoker": "Yes",
1938 | "time": "Dinner",
1939 | "tip": 2.23,
1940 | "total_bill": 12.76
1941 | },
1942 | {
1943 | "day": "Sat",
1944 | "sex": "Male",
1945 | "size": 3,
1946 | "smoker": "Yes",
1947 | "time": "Dinner",
1948 | "tip": 2,
1949 | "total_bill": 30.06
1950 | },
1951 | {
1952 | "day": "Sat",
1953 | "sex": "Male",
1954 | "size": 4,
1955 | "smoker": "Yes",
1956 | "time": "Dinner",
1957 | "tip": 5.16,
1958 | "total_bill": 25.89
1959 | },
1960 | {
1961 | "day": "Sat",
1962 | "sex": "Male",
1963 | "size": 4,
1964 | "smoker": "No",
1965 | "time": "Dinner",
1966 | "tip": 9,
1967 | "total_bill": 48.33
1968 | },
1969 | {
1970 | "day": "Sat",
1971 | "sex": "Female",
1972 | "size": 2,
1973 | "smoker": "Yes",
1974 | "time": "Dinner",
1975 | "tip": 2.5,
1976 | "total_bill": 13.27
1977 | },
1978 | {
1979 | "day": "Sat",
1980 | "sex": "Female",
1981 | "size": 3,
1982 | "smoker": "Yes",
1983 | "time": "Dinner",
1984 | "tip": 6.5,
1985 | "total_bill": 28.17
1986 | },
1987 | {
1988 | "day": "Sat",
1989 | "sex": "Female",
1990 | "size": 2,
1991 | "smoker": "Yes",
1992 | "time": "Dinner",
1993 | "tip": 1.1,
1994 | "total_bill": 12.9
1995 | },
1996 | {
1997 | "day": "Sat",
1998 | "sex": "Male",
1999 | "size": 5,
2000 | "smoker": "Yes",
2001 | "time": "Dinner",
2002 | "tip": 3,
2003 | "total_bill": 28.15
2004 | },
2005 | {
2006 | "day": "Sat",
2007 | "sex": "Male",
2008 | "size": 2,
2009 | "smoker": "Yes",
2010 | "time": "Dinner",
2011 | "tip": 1.5,
2012 | "total_bill": 11.59
2013 | },
2014 | {
2015 | "day": "Sat",
2016 | "sex": "Male",
2017 | "size": 2,
2018 | "smoker": "Yes",
2019 | "time": "Dinner",
2020 | "tip": 1.44,
2021 | "total_bill": 7.74
2022 | },
2023 | {
2024 | "day": "Sat",
2025 | "sex": "Female",
2026 | "size": 4,
2027 | "smoker": "Yes",
2028 | "time": "Dinner",
2029 | "tip": 3.09,
2030 | "total_bill": 30.14
2031 | },
2032 | {
2033 | "day": "Fri",
2034 | "sex": "Male",
2035 | "size": 2,
2036 | "smoker": "Yes",
2037 | "time": "Lunch",
2038 | "tip": 2.2,
2039 | "total_bill": 12.16
2040 | },
2041 | {
2042 | "day": "Fri",
2043 | "sex": "Female",
2044 | "size": 2,
2045 | "smoker": "Yes",
2046 | "time": "Lunch",
2047 | "tip": 3.48,
2048 | "total_bill": 13.42
2049 | },
2050 | {
2051 | "day": "Fri",
2052 | "sex": "Male",
2053 | "size": 1,
2054 | "smoker": "Yes",
2055 | "time": "Lunch",
2056 | "tip": 1.92,
2057 | "total_bill": 8.58
2058 | },
2059 | {
2060 | "day": "Fri",
2061 | "sex": "Female",
2062 | "size": 3,
2063 | "smoker": "No",
2064 | "time": "Lunch",
2065 | "tip": 3,
2066 | "total_bill": 15.98
2067 | },
2068 | {
2069 | "day": "Fri",
2070 | "sex": "Male",
2071 | "size": 2,
2072 | "smoker": "Yes",
2073 | "time": "Lunch",
2074 | "tip": 1.58,
2075 | "total_bill": 13.42
2076 | },
2077 | {
2078 | "day": "Fri",
2079 | "sex": "Female",
2080 | "size": 2,
2081 | "smoker": "Yes",
2082 | "time": "Lunch",
2083 | "tip": 2.5,
2084 | "total_bill": 16.27
2085 | },
2086 | {
2087 | "day": "Fri",
2088 | "sex": "Female",
2089 | "size": 2,
2090 | "smoker": "Yes",
2091 | "time": "Lunch",
2092 | "tip": 2,
2093 | "total_bill": 10.09
2094 | },
2095 | {
2096 | "day": "Sat",
2097 | "sex": "Male",
2098 | "size": 4,
2099 | "smoker": "No",
2100 | "time": "Dinner",
2101 | "tip": 3,
2102 | "total_bill": 20.45
2103 | },
2104 | {
2105 | "day": "Sat",
2106 | "sex": "Male",
2107 | "size": 2,
2108 | "smoker": "No",
2109 | "time": "Dinner",
2110 | "tip": 2.72,
2111 | "total_bill": 13.28
2112 | },
2113 | {
2114 | "day": "Sat",
2115 | "sex": "Female",
2116 | "size": 2,
2117 | "smoker": "Yes",
2118 | "time": "Dinner",
2119 | "tip": 2.88,
2120 | "total_bill": 22.12
2121 | },
2122 | {
2123 | "day": "Sat",
2124 | "sex": "Male",
2125 | "size": 4,
2126 | "smoker": "Yes",
2127 | "time": "Dinner",
2128 | "tip": 2,
2129 | "total_bill": 24.01
2130 | },
2131 | {
2132 | "day": "Sat",
2133 | "sex": "Male",
2134 | "size": 3,
2135 | "smoker": "Yes",
2136 | "time": "Dinner",
2137 | "tip": 3,
2138 | "total_bill": 15.69
2139 | },
2140 | {
2141 | "day": "Sat",
2142 | "sex": "Male",
2143 | "size": 2,
2144 | "smoker": "No",
2145 | "time": "Dinner",
2146 | "tip": 3.39,
2147 | "total_bill": 11.61
2148 | },
2149 | {
2150 | "day": "Sat",
2151 | "sex": "Male",
2152 | "size": 2,
2153 | "smoker": "No",
2154 | "time": "Dinner",
2155 | "tip": 1.47,
2156 | "total_bill": 10.77
2157 | },
2158 | {
2159 | "day": "Sat",
2160 | "sex": "Male",
2161 | "size": 2,
2162 | "smoker": "Yes",
2163 | "time": "Dinner",
2164 | "tip": 3,
2165 | "total_bill": 15.53
2166 | },
2167 | {
2168 | "day": "Sat",
2169 | "sex": "Male",
2170 | "size": 2,
2171 | "smoker": "No",
2172 | "time": "Dinner",
2173 | "tip": 1.25,
2174 | "total_bill": 10.07
2175 | },
2176 | {
2177 | "day": "Sat",
2178 | "sex": "Male",
2179 | "size": 2,
2180 | "smoker": "Yes",
2181 | "time": "Dinner",
2182 | "tip": 1,
2183 | "total_bill": 12.6
2184 | },
2185 | {
2186 | "day": "Sat",
2187 | "sex": "Male",
2188 | "size": 2,
2189 | "smoker": "Yes",
2190 | "time": "Dinner",
2191 | "tip": 1.17,
2192 | "total_bill": 32.83
2193 | },
2194 | {
2195 | "day": "Sat",
2196 | "sex": "Female",
2197 | "size": 3,
2198 | "smoker": "No",
2199 | "time": "Dinner",
2200 | "tip": 4.67,
2201 | "total_bill": 35.83
2202 | },
2203 | {
2204 | "day": "Sat",
2205 | "sex": "Male",
2206 | "size": 3,
2207 | "smoker": "No",
2208 | "time": "Dinner",
2209 | "tip": 5.92,
2210 | "total_bill": 29.03
2211 | },
2212 | {
2213 | "day": "Sat",
2214 | "sex": "Female",
2215 | "size": 2,
2216 | "smoker": "Yes",
2217 | "time": "Dinner",
2218 | "tip": 2,
2219 | "total_bill": 27.18
2220 | },
2221 | {
2222 | "day": "Sat",
2223 | "sex": "Male",
2224 | "size": 2,
2225 | "smoker": "Yes",
2226 | "time": "Dinner",
2227 | "tip": 2,
2228 | "total_bill": 22.67
2229 | },
2230 | {
2231 | "day": "Sat",
2232 | "sex": "Male",
2233 | "size": 2,
2234 | "smoker": "No",
2235 | "time": "Dinner",
2236 | "tip": 1.75,
2237 | "total_bill": 17.82
2238 | },
2239 | {
2240 | "day": "Thur",
2241 | "sex": "Female",
2242 | "size": 2,
2243 | "smoker": "No",
2244 | "time": "Dinner",
2245 | "tip": 3,
2246 | "total_bill": 18.78
2247 | }
2248 | ]
2249 | },
2250 | "encoding": {
2251 | "color": {
2252 | "value": "rgba(0,128,0,1.0)"
2253 | },
2254 | "x": {
2255 | "bin": true,
2256 | "field": "tip",
2257 | "type": "quantitative"
2258 | },
2259 | "y": {
2260 | "aggregate": "count",
2261 | "type": "quantitative"
2262 | }
2263 | },
2264 | "mark": "bar"
2265 | },
2266 | "image/png": "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",
2267 | "text/plain": [
2268 | "\n",
2269 | "\n",
2270 | "If you see this message, it means the renderer has not been properly enabled\n",
2271 | "for the frontend that you are using. For more information, see\n",
2272 | "https://altair-viz.github.io/user_guide/troubleshooting.html\n"
2273 | ]
2274 | },
2275 | "execution_count": 2,
2276 | "metadata": {},
2277 | "output_type": "execute_result"
2278 | }
2279 | ],
2280 | "source": [
2281 | "palt.hist('tip', data=tips, color=\"green\")"
2282 | ]
2283 | },
2284 | {
2285 | "cell_type": "code",
2286 | "execution_count": null,
2287 | "metadata": {},
2288 | "outputs": [],
2289 | "source": []
2290 | }
2291 | ],
2292 | "metadata": {
2293 | "kernelspec": {
2294 | "display_name": "Python 3",
2295 | "language": "python",
2296 | "name": "python3"
2297 | },
2298 | "language_info": {
2299 | "codemirror_mode": {
2300 | "name": "ipython",
2301 | "version": 3
2302 | },
2303 | "file_extension": ".py",
2304 | "mimetype": "text/x-python",
2305 | "name": "python",
2306 | "nbconvert_exporter": "python",
2307 | "pygments_lexer": "ipython3",
2308 | "version": "3.6.4"
2309 | }
2310 | },
2311 | "nbformat": 4,
2312 | "nbformat_minor": 2
2313 | }
2314 |
--------------------------------------------------------------------------------
/notebooks/pyplot-plot.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# seaborn_altair.pyplot.plot"
8 | ]
9 | },
10 | {
11 | "cell_type": "code",
12 | "execution_count": 1,
13 | "metadata": {},
14 | "outputs": [],
15 | "source": [
16 | "import seaborn as sns\n",
17 | "import seaborn_altair.pyplot as palt\n",
18 | "attend = sns.load_dataset(\"attention\")"
19 | ]
20 | },
21 | {
22 | "cell_type": "code",
23 | "execution_count": 2,
24 | "metadata": {},
25 | "outputs": [
26 | {
27 | "data": {
28 | "application/vnd.vegalite.v2+json": {
29 | "$schema": "https://vega.github.io/schema/vega-lite/v2.4.1.json",
30 | "config": {
31 | "range": {
32 | "category": [
33 | "rgba(31,119,180,1)",
34 | "rgba(255,127,14,1)",
35 | "rgba(44,160,44,1)",
36 | "rgba(214,39,40,1)",
37 | "rgba(148,103,189,1)",
38 | "rgba(140,86,75,1)",
39 | "rgba(227,119,194,1)",
40 | "rgba(127,127,127,1)",
41 | "rgba(188,189,34,1)",
42 | "rgba(23,190,207,1)"
43 | ]
44 | },
45 | "view": {
46 | "height": 300,
47 | "width": 400
48 | }
49 | },
50 | "data": {
51 | "values": [
52 | {
53 | "Unnamed: 0": 0,
54 | "attention": "divided",
55 | "score": 2,
56 | "solutions": 1,
57 | "subject": 1
58 | },
59 | {
60 | "Unnamed: 0": 20,
61 | "attention": "divided",
62 | "score": 4,
63 | "solutions": 2,
64 | "subject": 1
65 | },
66 | {
67 | "Unnamed: 0": 40,
68 | "attention": "divided",
69 | "score": 7,
70 | "solutions": 3,
71 | "subject": 1
72 | }
73 | ]
74 | },
75 | "encoding": {
76 | "color": {
77 | "value": "rgba(0,128,0,1.0)"
78 | },
79 | "x": {
80 | "field": "solutions",
81 | "type": "quantitative"
82 | },
83 | "y": {
84 | "field": "score",
85 | "type": "quantitative"
86 | }
87 | },
88 | "mark": "line",
89 | "selection": {
90 | "selector001": {
91 | "bind": "scales",
92 | "encodings": [
93 | "x",
94 | "y"
95 | ],
96 | "mark": {
97 | "fill": "#333",
98 | "fillOpacity": 0.125,
99 | "stroke": "white"
100 | },
101 | "on": "[mousedown, window:mouseup] > window:mousemove!",
102 | "resolve": "global",
103 | "translate": "[mousedown, window:mouseup] > window:mousemove!",
104 | "type": "interval",
105 | "zoom": "wheel!"
106 | }
107 | }
108 | },
109 | "image/png": "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",
110 | "text/plain": [
111 | "\n",
112 | "\n",
113 | "If you see this message, it means the renderer has not been properly enabled\n",
114 | "for the frontend that you are using. For more information, see\n",
115 | "https://altair-viz.github.io/user_guide/troubleshooting.html\n"
116 | ]
117 | },
118 | "execution_count": 2,
119 | "metadata": {},
120 | "output_type": "execute_result"
121 | }
122 | ],
123 | "source": [
124 | "palt.plot('solutions', 'score', color=\"green\", data=attend[attend['subject'] == 1]).interactive()"
125 | ]
126 | },
127 | {
128 | "cell_type": "code",
129 | "execution_count": null,
130 | "metadata": {},
131 | "outputs": [],
132 | "source": []
133 | }
134 | ],
135 | "metadata": {
136 | "kernelspec": {
137 | "display_name": "Python 3",
138 | "language": "python",
139 | "name": "python3"
140 | },
141 | "language_info": {
142 | "codemirror_mode": {
143 | "name": "ipython",
144 | "version": 3
145 | },
146 | "file_extension": ".py",
147 | "mimetype": "text/x-python",
148 | "name": "python",
149 | "nbconvert_exporter": "python",
150 | "pygments_lexer": "ipython3",
151 | "version": "3.6.4"
152 | }
153 | },
154 | "nbformat": 4,
155 | "nbformat_minor": 2
156 | }
157 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | altair
2 | seaborn
3 |
--------------------------------------------------------------------------------
/scripts/docgen.py:
--------------------------------------------------------------------------------
1 | import os
2 | import markdown
3 |
4 | index_content = """
5 |
6 |
7 |
8 |
9 |
10 |
11 |
28 |
29 |
30 | %s
31 |
32 |
33 | """
34 |
35 | with open('README.md') as readme:
36 | with open('docs/index.html', 'w') as index:
37 | md = readme.read().replace('```python', '```')
38 | index.write(index_content % markdown.markdown(md))
39 |
40 | for p in os.listdir('./notebooks'):
41 | sp = os.path.splitext(p)
42 | if sp[1] == '.ipynb':
43 | os.system('python scripts/nb2html.py notebooks/%s.ipynb docs/%s.html' % (sp[0], sp[0]))
44 |
--------------------------------------------------------------------------------
/scripts/nb2html.py:
--------------------------------------------------------------------------------
1 | import json
2 | import sys
3 | import markdown
4 |
5 | header = """
6 |
7 |
8 |
9 |
10 |
11 |
12 |
29 |
30 |
31 | """
32 |
33 | vis = """
34 |
35 |
40 | """
41 |
42 | footer = """
43 |
44 |
45 | """
46 |
47 | if __name__ == "__main__":
48 | with open(sys.argv[1]) as inp:
49 | nb = json.load(inp)
50 |
51 | if len(sys.argv) > 2:
52 | outf = sys.argv[2]
53 | else:
54 | outf = sys.argv[1].split(".")[0] + ".html"
55 |
56 | with open(outf, "w") as out:
57 | out.write(header)
58 | for i, c in enumerate(nb["cells"]):
59 | if c["cell_type"] == "markdown":
60 | out.write("%s
" % markdown.markdown("".join(c["source"])))
61 | if c["cell_type"] == "code":
62 | source = "".join(c["source"]).replace("&", "&").replace("<", "<").replace(">", ">")
63 | out.write("" % source)
64 | for o in c["outputs"]:
65 | if o.get("data"):
66 | vg = o["data"].get("application/vnd.vegalite.v2+json")
67 | img = o["data"].get("image/png")
68 | if vg:
69 | visid = "vis%s" % i
70 | out.write(vis % (visid, json.dumps(vg), visid))
71 | elif img:
72 | out.write("
" % img)
73 |
74 | out.write(footer)
75 |
76 |
--------------------------------------------------------------------------------
/seaborn_altair/__init__.py:
--------------------------------------------------------------------------------
1 | from .axisgrid import *
2 | from .categorical import *
3 | from .regression import *
4 | from .relational import *
5 |
--------------------------------------------------------------------------------
/seaborn_altair/axisgrid.py:
--------------------------------------------------------------------------------
1 | import altair as alt
2 | from IPython.display import display
3 | import pandas as pd
4 |
5 | from .util import size_chart
6 |
7 | __all__ = ["FacetGrid"]
8 |
9 | class FacetGrid():
10 | """Subplot grid for plotting conditional relationships."""
11 |
12 | def __init__(self, data, row=None, col=None, hue=None, palette=None, height=3, aspect=1):
13 | self.hue = hue
14 | self.palette = palette
15 | self.height = height
16 | self.aspect = aspect
17 |
18 | self.chart = alt.Chart(data).mark_point()
19 |
20 | facets = {}
21 | if col is not None:
22 | facets['column'] = col
23 | if row is not None:
24 | facets['row'] = row
25 | if len(facets):
26 | self.chart = self.chart.facet(**facets)
27 |
28 | def _ipython_display_(self):
29 | display(self.chart)
30 |
31 | def map(self, func, *args, **kwargs):
32 | raise ValueError('FacetGrid.map is not implemented. Use map_dataframe instead.')
33 |
34 | def map_dataframe(self, func, *args, **kwargs):
35 | plot_kwargs = dict(
36 | color=self.hue,
37 | palette=self.palette, size=self.height,
38 | aspect=self.aspect, data=self.chart.data
39 | )
40 | plot_kwargs.update(kwargs)
41 |
42 | single = func(*args, **plot_kwargs)
43 |
44 | if isinstance(self.chart, alt.FacetChart):
45 | if isinstance(single, alt.FacetChart):
46 | raise ValueError("Cannot facet a FacetChart")
47 | self.chart.config = single.config
48 | single.config = alt.Undefined
49 | single.data = alt.Undefined
50 | self.chart.spec = single
51 | else:
52 | self.chart = single
53 |
54 | return self
55 |
--------------------------------------------------------------------------------
/seaborn_altair/categorical.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import altair as alt
3 | import pandas as pd
4 | from .util import infer_orient, size_chart, vega_color, vega_palette
5 | from .axisgrid import FacetGrid
6 |
7 | __all__ = ["boxplot", "catplot", "stripplot", "pointplot", "barplot", "countplot"]
8 |
9 | def _bar_encodings(x, y, xs, ys, hue, dodge, estimator):
10 | encodings = {
11 | ys: alt.Y(field=y, aggregate=estimator, type="quantitative", axis={"title": y}),
12 | "color": alt.Color(field=x ,type="nominal", legend=None)
13 | }
14 |
15 | if hue:
16 | legend = None if dodge else alt.Undefined
17 | encodings["color"] = alt.Color(field=hue ,type="nominal", legend=legend)
18 |
19 | xf = hue if hue and dodge else x
20 | encodings[xs] = alt.X(field=xf, type="nominal")
21 | return encodings
22 |
23 | def _bar_ci_encodings(x, y, xs, ys, hue, dodge, estimator):
24 | encodings = {
25 | ys: alt.Y(field=y, aggregate="ci0", type="quantitative"),
26 | "%s2" % ys: alt.Y(field=y, aggregate="ci1", type="quantitative")
27 | }
28 |
29 | xf = hue if hue and dodge else x
30 | encodings[xs] = alt.X(field=xf, type="nominal")
31 | return encodings
32 |
33 | def _point_encodings(x, y, xs, ys, hue, estimator):
34 | encodings = {
35 | ys: alt.Y(field=y, aggregate=estimator, type="quantitative"),
36 | "color": alt.Color(field="___" ,type="nominal", legend=None)
37 | }
38 |
39 | if hue:
40 | encodings["color"] = alt.Color(field=hue ,type="nominal")
41 |
42 | encodings[xs] = alt.X(field=x, type="nominal")
43 | return encodings
44 |
45 | def _point_ci_encodings(x, y, xs, ys, hue, estimator):
46 | encodings = {
47 | ys: alt.Y(field=y, aggregate="ci0", type="quantitative"),
48 | "%s2" % ys: alt.Y(field=y, aggregate="ci1", type="quantitative")
49 | }
50 |
51 | encodings[xs] = alt.X(field=x, type="nominal")
52 | return encodings
53 |
54 | def _validate_estimator(estimator, ci):
55 | if estimator in ["mean", np.mean]:
56 | estimator = "mean"
57 | if ci != 95 and ci is not None:
58 | raise ValueError("ci must be 95 for mean")
59 | elif estimator in ["median", np.median]:
60 | estimator = "median"
61 | if ci is not None:
62 | raise ValueError("ci not available for median")
63 | elif estimator in ["count", len]:
64 | estimator = "count"
65 | if ci is not None:
66 | raise ValueError("ci not available for median")
67 | else:
68 | raise ValueError("estimator must be mean or median")
69 | return estimator
70 |
71 | def barplot(
72 | x=None, y=None, hue=None, data=None,
73 | estimator=np.mean, ci=95, size=None, aspect=1,
74 | orient=None, color=None, palette=None, saturation=.75, dodge=True
75 | ):
76 | xs, ys = "x", "y"
77 | orient = infer_orient(data[x], data[y], orient)
78 | if orient == "h":
79 | x, y = y, x
80 | xs, ys = ys, xs
81 |
82 | estimator = _validate_estimator(estimator, ci)
83 |
84 | encodings = _bar_encodings(x, y, xs, ys, hue, dodge, estimator)
85 | chart = alt.Chart(data).mark_bar().encode(**encodings)
86 |
87 | if ci:
88 | ci_encodings = _bar_ci_encodings(x, y, xs, ys, hue, dodge, estimator)
89 | ci_layer = alt.Chart().mark_rule().encode(**ci_encodings)
90 | chart.data = alt.Undefined
91 | chart = alt.LayerChart(data=data, layer=[chart, ci_layer])
92 |
93 | if hue and dodge:
94 | facet_dir = "column" if orient == "v" else "row"
95 | chart = chart.facet(**{facet_dir: "%s:N" % x})
96 |
97 | size_chart(chart, size, aspect)
98 |
99 | pal = vega_palette(palette, color, saturation)
100 | return chart.configure_range(category=pal)
101 |
102 | def countplot(
103 | x=None, y=None, hue=None, data=None, size=None, aspect=1,
104 | orient=None, color=None, palette=None, saturation=.75, dodge=True
105 | ):
106 | estimator = len
107 | ci = None
108 |
109 | if x is None and y is not None:
110 | orient = "h"
111 | x = y
112 | elif y is None and x is not None:
113 | orient = "v"
114 | y = x
115 | elif x is not None and y is not None:
116 | raise TypeError("Cannot pass values for both `x` and `y`")
117 | else:
118 | raise TypeError("Must pass values for either `x` or `y`")
119 |
120 | return barplot(
121 | x, y, hue, data,
122 | estimator=estimator, ci=ci, size=size, aspect=aspect,
123 | orient=orient, color=color, palette=palette, saturation=saturation,
124 | dodge=dodge,
125 | )
126 |
127 | def pointplot(
128 | x=None, y=None, hue=None, data=None,
129 | estimator=np.mean, ci=95, join=True, size=None, aspect=1,
130 | orient=None, color=None, palette=None, saturation=.75
131 | ):
132 | xs, ys = "x", "y"
133 | orient = infer_orient(data[x], data[y], orient)
134 | if orient == "h":
135 | x, y = y, x
136 | xs, ys = ys, xs
137 |
138 | estimator = _validate_estimator(estimator, ci)
139 |
140 | encodings = _point_encodings(x, y, xs, ys, hue, estimator)
141 | chart = alt.Chart(data).mark_circle().encode(**encodings)
142 | layers = [chart]
143 |
144 | if join:
145 | layers.append(alt.Chart().mark_line().encode(**encodings))
146 |
147 | if ci:
148 | ci_encodings = _point_ci_encodings(x, y, xs, ys, hue, estimator)
149 | cfield = hue if hue else "___"
150 | ci_encodings["color"] = alt.Color(field=cfield, type="nominal", legend=None)
151 | layers.append(alt.Chart().mark_rule().encode(**ci_encodings))
152 |
153 | if len(layers) > 1:
154 | chart.data = alt.Undefined
155 | chart = alt.LayerChart(data=data, layer=layers)
156 |
157 | size_chart(chart, size, aspect)
158 |
159 | pal = vega_palette(palette, color, saturation)
160 | return chart.configure_range(category=pal)
161 |
162 |
163 | def stripplot(
164 | x=None, y=None, hue=None, data=None, size=None, aspect=1,
165 | dodge=False, orient=None, color=None, palette=None, saturation=.75
166 | ):
167 | xs, ys = "x", "y"
168 | if data is None:
169 | data = pd.DataFrame({"x": x})
170 | x = "x"
171 | if y:
172 | data["y"] = y
173 | y = "y"
174 |
175 | if y:
176 | orient = infer_orient(data[x], data[y], orient)
177 | else:
178 | orient = "v"
179 |
180 | if orient == "h":
181 | x, y = y, x
182 | xs, ys = ys, xs
183 |
184 | encodings = {}
185 |
186 | if y:
187 | encodings[ys] = alt.Y(field=y, type="quantitative")
188 | xf = hue if hue and dodge else x
189 | encodings[xs] = alt.X(field=xf, type="nominal")
190 | encodings["color"] = alt.Color(field=x ,type="nominal", legend=None)
191 | else:
192 | encodings[xs] = alt.X(field=x, type="quantitative")
193 |
194 | if hue:
195 | legend = None if dodge else alt.Undefined
196 | encodings["color"] = alt.Color(field=hue ,type="nominal", legend=legend)
197 |
198 | chart = alt.Chart(data).mark_tick().encode(**encodings)
199 |
200 | if hue and dodge:
201 | facet_dir = "column" if orient == "v" else "row"
202 | chart = chart.facet(**{facet_dir: "%s:N" % x})
203 |
204 | size_chart(chart, size, aspect)
205 |
206 | pal = vega_palette(palette, color, saturation)
207 | return chart.configure_range(category=pal)
208 |
209 | def boxplot(
210 | x=None, y=None, hue=None, data=None, size=None, aspect=1,
211 | orient=None, color=None, palette=None, saturation=.75, dodge=True
212 | ):
213 | xs, ys = "x", "y"
214 | if data is None:
215 | data = pd.DataFrame({"x": x})
216 | x = "x"
217 | if y:
218 | data["y"] = y
219 | y = "y"
220 |
221 | if x is None and y is None:
222 | # Make a box plot for each numeric column
223 | numeric_cols = [c for c in data if data[c].dtype in [np.float32, np.float64]]
224 | col = []
225 | val = []
226 | for c in numeric_cols:
227 | for v in data[c]:
228 | col.append(c)
229 | val.append(v)
230 |
231 | data = pd.DataFrame({"column": col, "value": val})
232 | x = "column"
233 | y = "value"
234 | if orient == "h":
235 | x, y = y, x
236 |
237 | if y:
238 | orient = infer_orient(data[x], data[y], orient)
239 | elif orient is None:
240 | orient = "h"
241 |
242 | if orient == "h":
243 | x, y = y, x
244 | xs, ys = ys, xs
245 |
246 | xf = hue if hue and dodge else x
247 |
248 | # Main bar
249 | encodings = {
250 | ys: alt.Y(field=y, aggregate="q1", type="quantitative", axis={"title": y}),
251 | "%s2" % ys: alt.Y(field=y, aggregate="q3", type="quantitative"),
252 | "color": alt.Color(field=x ,type="nominal", legend=None),
253 | xs: alt.X(field=xf, type="nominal")
254 | }
255 | if x is None:
256 | del encodings["color"]
257 | del encodings[xs]
258 | if hue:
259 | legend = None if dodge else alt.Undefined
260 | encodings["color"] = alt.Color(field=hue ,type="nominal", legend=legend)
261 | bar_layer = alt.Chart().mark_bar().encode(**encodings)
262 |
263 | # Min/max range line
264 | range_encodings = {
265 | ys: alt.Y(field=y, aggregate="min", type="quantitative"),
266 | "%s2" % ys: alt.Y(field=y, aggregate="max", type="quantitative"),
267 | xs: alt.X(field=xf, type="nominal")
268 | }
269 | if x is None:
270 | del range_encodings[xs]
271 | range_layer = alt.Chart().mark_rule().encode(**range_encodings)
272 |
273 | # Median line
274 | median_encodings = {
275 | ys: alt.Y(field=y, aggregate="median", type="quantitative"),
276 | xs: alt.X(field=xf, type="nominal")
277 | }
278 | if x is None:
279 | del median_encodings[xs]
280 | median_layer = alt.Chart().mark_tick(size=18, color="black").encode(**median_encodings)
281 |
282 | chart = alt.LayerChart(data=data, layer=[range_layer, bar_layer, median_layer])
283 |
284 | if hue and dodge:
285 | facet_dir = "column" if orient == "v" else "row"
286 | chart = chart.facet(**{facet_dir: "%s:N" % x})
287 |
288 | size_chart(chart, size, aspect)
289 |
290 | pal = vega_palette(palette, color, saturation)
291 | return chart.configure_range(category=pal)
292 |
293 | def catplot(
294 | x=None, y=None, hue=None, data=None, row=None, col=None,
295 | estimator=np.mean, ci=95, kind="point", height=None, aspect=1,
296 | orient=None, color=None, palette=None, facet_kws=None, **kwargs
297 | ):
298 |
299 | # Determine the plotting function
300 | try:
301 | plot_func = globals()[kind + "plot"]
302 | except KeyError:
303 | err = "Plot kind '{}' is not recognized".format(kind)
304 | raise ValueError(err)
305 |
306 | # Determine keyword arguments for the facets
307 | facet_kws = {} if facet_kws is None else facet_kws
308 | facet_kws.update(data=data, row=row, col=col, height=height, aspect=aspect)
309 |
310 | # Determine keyword arguments for the plotting function
311 | plot_kws = kwargs
312 | plot_kws.update(orient=orient, color=color, palette=palette)
313 |
314 | if kind in ["bar", "point"]:
315 | plot_kws.update(estimator=estimator, ci=ci)
316 |
317 | # Initialize the facets
318 | g = FacetGrid(**facet_kws)
319 |
320 | # Draw the plot onto the facets
321 | g.map_dataframe(plot_func, x, y, hue, **plot_kws)
322 |
323 | return g
324 |
--------------------------------------------------------------------------------
/seaborn_altair/pyplot.py:
--------------------------------------------------------------------------------
1 | import altair as alt
2 | import numpy as np
3 | import pandas as pd
4 | import warnings
5 |
6 | from .util import build_dataframe, dtype_to_vega_type, size_chart, vega_color, vega_palette
7 |
8 |
9 | def fill_between(x, y1, y2, color=None, style=None, data=None, palette=None, saturation=1, size=None, aspect=1):
10 | if data is None:
11 | xname = x.name if isinstance(x, pd.Series) else "x"
12 | data = pd.DataFrame({xname: x})
13 | x = xname
14 | if y1 is not None:
15 | y1name = y1.name if isinstance(y1, pd.Series) else "y1"
16 | data[y1name] = y1
17 | y1 = y1name
18 | if y2 is not None:
19 | y2name = y2.name if isinstance(y2, pd.Series) else "y2"
20 | data[y2name] = y2
21 | y2 = y2name
22 |
23 | encodings = {
24 | "x": alt.X(field=x, type=dtype_to_vega_type(data[x].dtype)),
25 | "y": alt.Y(field=y1, type="quantitative"),
26 | "y2": alt.Y(field=y2, type="quantitative"),
27 | }
28 | if color:
29 | if color in list(data.columns):
30 | encodings["color"] = alt.Color(field=color, type="nominal")
31 | else:
32 | encodings["color"] = alt.Color(value=vega_color(color))
33 |
34 | chart = alt.Chart(data).mark_area().encode(**encodings)
35 | size_chart(chart, size, aspect)
36 | pal = vega_palette(palette, None, saturation)
37 | return chart.configure_range(category=pal)
38 |
39 | def hist(x, color=None, data=None, palette=None, saturation=1, size=None, aspect=1):
40 | if data is None:
41 | data = pd.DataFrame({"x": x})
42 | x = "x"
43 |
44 | encodings = {
45 | "x": alt.X(bin=True, field=x, type="quantitative"),
46 | "y": alt.Y(aggregate="count", type="quantitative"),
47 | }
48 | if color:
49 | if color in list(data.columns):
50 | encodings["color"] = alt.Color(field=color)
51 | else:
52 | encodings["color"] = alt.Color(value=vega_color(color))
53 |
54 | chart = alt.Chart(data).mark_bar().encode(**encodings)
55 | size_chart(chart, size, aspect)
56 | pal = vega_palette(palette, None, saturation)
57 | return chart.configure_range(category=pal)
58 |
59 | def scatter(x, y, s=None, color=None, style=None, size_by=None, sizes=None, x_type="quantitative", color_type="nominal", size_type="quantitative", data=None, palette=None, saturation=1, size=None, aspect=1):
60 | if data is None:
61 | data, fields = build_dataframe({"x": x, "y": y})
62 | x, y = fields["x"], fields["y"]
63 |
64 | encodings = {
65 | "x": alt.X(field=x, type=x_type, axis={"title": x}),
66 | "y": alt.Y(field=y, type="quantitative", axis={"title": y}),
67 | }
68 | if color:
69 | if isinstance(color, alt.Color):
70 | encodings["color"] = color
71 | elif color in list(data.columns):
72 | encodings["color"] = alt.Color(field=color, type=color_type)
73 | else:
74 | encodings["color"] = alt.Color(value=vega_color(color))
75 | if style:
76 | if style in list(data.columns):
77 | encodings["shape"] = alt.Shape(field=style, type="nominal")
78 | else:
79 | encodings["shape"] = alt.Shape(value=style)
80 | if size_by:
81 | if size_by in list(data.columns):
82 | size_enc = dict(field=size_by, type=size_type)
83 | if sizes:
84 | size_enc["scale"] = dict(range=sizes)
85 | else:
86 | size_enc = dict(value=size_by)
87 | encodings["size"] = size_enc
88 |
89 | chart = alt.Chart(data).mark_point(filled=True).encode(**encodings)
90 | size_chart(chart, size, aspect)
91 | pal = vega_palette(palette, None, saturation, vega_type=color_type)
92 | if color_type == "nominal":
93 | chart = chart.configure_range(category=pal)
94 | else:
95 | chart = chart.configure_range(ramp=pal)
96 | return chart
97 |
98 | def plot(x, y, s=None, color=None, data=None, palette=None, saturation=1, size=None, aspect=1):
99 | if data is None:
100 | data, fields = build_dataframe({"x": x, "y": y})
101 | x, y = fields["x"], fields["y"]
102 |
103 | encodings = {
104 | "x": alt.X(field=x, type=dtype_to_vega_type(data[x].dtype)),
105 | "y": alt.Y(field=y, type="quantitative"),
106 | }
107 | if color:
108 | if color in list(data.columns):
109 | encodings["color"] = alt.Color(field=color, type="nominal")
110 | else:
111 | encodings["color"] = alt.Color(value=vega_color(color))
112 |
113 | chart = alt.Chart(data).mark_line().encode(**encodings)
114 | size_chart(chart, size, aspect)
115 | pal = vega_palette(palette, None, saturation)
116 | return chart.configure_range(category=pal)
117 |
--------------------------------------------------------------------------------
/seaborn_altair/regression.py:
--------------------------------------------------------------------------------
1 | import altair as alt
2 | import pandas as pd
3 | import seaborn as sns
4 | import six
5 | from .util import build_dataframe, size_chart, vega_palette
6 | from .pyplot import fill_between, plot, scatter as pscatter
7 |
8 | __all__ = ["regplot", "lmplot"]
9 |
10 | def regplot(
11 | x, y, data=None, x_estimator=None, x_bins=None, x_ci="ci",
12 | x_range=None, y_range=None, truncate=False,
13 | scatter=True, fit_reg=True, ci=95, n_boot=1000, units=None,
14 | order=1, logistic=False, lowess=False, robust=False, logx=False,
15 | color=None, scatter_kws={}, line_kws={}, ax=None,
16 | palette=None, height=None, aspect=1, color_scale=None
17 | ):
18 |
19 | if data is None:
20 | data, names = build_dataframe({"x": x, "y": y})
21 | x, y = names["x"], names["y"]
22 |
23 | if x_range is None:
24 | x_raw_range = (data[x].min(), data[x].max())
25 | x_pad = 0.05*(x_raw_range[1] - x_raw_range[0])
26 | x_range = (x_raw_range[0] - x_pad, x_raw_range[1] + x_pad)
27 |
28 | def plot_regression(data, color):
29 | p = sns.regression._RegressionPlotter(
30 | data[x], data[y], x_estimator=x_estimator, x_bins=x_bins, x_ci=x_ci,
31 | n_boot=n_boot, units=units, ci=ci, truncate=truncate,
32 | order=order, logistic=logistic, lowess=lowess, robust=robust, logx=logx
33 | )
34 |
35 | layers = []
36 | grid, yhat, err_bands = p.fit_regression(x_range=x_range)
37 | layers.append(plot(grid, yhat, color=color, **line_kws))
38 | if err_bands is not None:
39 | area = fill_between(grid, *err_bands, color=color)
40 | area.encoding.opacity = alt.value(0.15)
41 | layers.append(area)
42 | return layers
43 |
44 | def plot_scatter(data, color):
45 | p = sns.regression._RegressionPlotter(
46 | data[x], data[y], x_estimator=x_estimator, x_bins=x_bins, x_ci=x_ci,
47 | n_boot=n_boot, units=units, ci=ci, truncate=truncate,
48 | order=order, logistic=logistic, lowess=lowess, robust=robust, logx=logx
49 | )
50 |
51 | layers = []
52 | if p.x_estimator is None:
53 | layers.append(pscatter(x, y, data=data, color=color, **scatter_kws))
54 | else:
55 | xs, ys, cis = p.estimate_data
56 | if [ci for ci in cis if ci is not None]:
57 | for xval, cci in zip(xs, cis):
58 | ci_df = pd.DataFrame({x: [xval, xval], y: cci})
59 | layers.append(plot(x, y, data=ci_df))
60 | estimate_df = pd.DataFrame({x: xs, y: ys})
61 | layers.append(pscatter(x, y, data=estimate_df, color=color, **scatter_kws))
62 | return layers
63 |
64 | if color and color in list(data.columns):
65 | if color_scale is None:
66 | val = data[color].unique()
67 | pal = sns.color_palette(palette)
68 | color_scale = alt.Scale(domain=list(val), range=vega_palette(pal))
69 | else:
70 | val = color_scale.domain
71 |
72 | color_map = {}
73 | for i in range(len(color_scale.domain)):
74 | color_map[color_scale.domain[i]] = color_scale.range[i % len(color_scale.range)]
75 |
76 | layers = []
77 | if scatter:
78 | for v in data[color].unique():
79 | part = data.loc[data[color] == v]
80 | layers += plot_scatter(part, color_map[v])
81 |
82 | if fit_reg:
83 | for v in data[color].unique():
84 | part = data.loc[data[color] == v]
85 | layers += plot_regression(part, color_map[v])
86 | else:
87 | layers = []
88 | if scatter:
89 | layers += plot_scatter(data, color)
90 |
91 | if fit_reg:
92 | layers += plot_regression(data, color)
93 |
94 | for layer in layers:
95 | if isinstance(layer.mark, six.string_types):
96 | layer.mark = dict(type=layer.mark, clip=True)
97 | else:
98 | layer.mark.clip = True
99 | layer.encoding.x.scale=alt.Scale(domain=x_range, nice=False)
100 | if y_range is not None:
101 | layer.encoding.y.scale=alt.Scale(domain=y_range, nice=False)
102 | layer.config = alt.Undefined
103 |
104 | chart = alt.LayerChart(layer=layers)
105 | return chart
106 |
107 |
108 | def lmplot(
109 | x, y, data, hue=None, col=None, row=None, palette=None,
110 | x_estimator=None, x_bins=None, x_ci="ci",
111 | col_wrap=None, height=5, aspect=1,
112 | hue_order=None, col_order=None, row_order=None,
113 | scatter=True, fit_reg=True, ci=95, n_boot=1000, truncate=False,
114 | units=None, order=1, logistic=False, lowess=False, robust=False,
115 | logx=False, scatter_kws={}, line_kws={}
116 | ):
117 |
118 | x_raw_range = (data[x].min(), data[x].max())
119 | x_pad = 0.05*(x_raw_range[1] - x_raw_range[0])
120 | x_range = (x_raw_range[0] - x_pad, x_raw_range[1] + x_pad)
121 |
122 | y_raw_range = (data[y].min(), data[y].max())
123 | y_pad = 0.05*(y_raw_range[1] - y_raw_range[0])
124 | y_range = (y_raw_range[0] - y_pad, y_raw_range[1] + y_pad)
125 |
126 | def unique_with_order(a, order):
127 | b = list(order) if order else []
128 | for i in a.unique():
129 | if i not in b:
130 | b.append(i)
131 | return b
132 |
133 | cols = unique_with_order(data[col], col_order) if col else [1]
134 | rows = unique_with_order(data[row], row_order) if row else [1]
135 | hues = unique_with_order(data[hue], hue_order) if hue else [1]
136 |
137 | pal = sns.color_palette(palette)
138 | color_scale = alt.Scale(domain=list(hues), range=vega_palette(pal))
139 |
140 | charts = []
141 | for r in rows:
142 | row_part = data.loc[data[row] == r] if row else data
143 | chart_row = []
144 | for c in cols:
145 | part = row_part.loc[row_part[col] == c] if col else row_part
146 | chart = regplot(
147 | data=part, x=x, y=y, color=hue, palette=palette, x_range=x_range, y_range=y_range,
148 | x_estimator=x_estimator, x_bins=x_bins, x_ci=x_ci,
149 | scatter=scatter, fit_reg=fit_reg, ci=ci, n_boot=n_boot, units=units, truncate=truncate,
150 | order=order, logistic=logistic, lowess=lowess, robust=robust, logx=logx,
151 | scatter_kws=scatter_kws, line_kws=line_kws, color_scale=color_scale,
152 | )
153 | size_chart(chart, height, aspect)
154 | chart.title = ("%s = %s" % (row, r) if row else "") + (" | " if row and col else "") + ("%s = %s" % (col, c) if col else "")
155 | chart_row.append(chart)
156 | if col_wrap is not None and len(chart_row) >= col_wrap:
157 | charts.append(chart_row)
158 | chart_row = []
159 |
160 | if len(chart_row) > 0:
161 | charts.append(chart_row)
162 |
163 | if len(charts) > 1 or len(charts[0]) > 1:
164 | chart_rows = []
165 | for chart_row in charts:
166 | chart_rows.append(alt.hconcat(*chart_row))
167 | facets = alt.vconcat(*chart_rows)
168 | else:
169 | facets = charts[0][0]
170 |
171 | return facets
172 |
--------------------------------------------------------------------------------
/seaborn_altair/relational.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import pandas as pd
3 | from .pyplot import scatter
4 | from .util import build_dataframe, dtype_to_vega_type
5 |
6 | __all__ = ["scatterplot"]
7 |
8 | def scatterplot(
9 | x=None, y=None, hue=None, style=None, size=None, data=None,
10 | palette=None, sizes=[10, 80]
11 | ):
12 | if data is None:
13 | data, names = build_dataframe({"x": x, "y": y, "hue": hue, "style": style, "size": size})
14 | x, y, hue, style, size = names["x"], names["y"], names["hue"], names["style"], names["size"]
15 |
16 | if x is None and y is None:
17 | plot_data = data.copy()
18 | x = getattr(data.index, "name", "x")
19 | plot_data.loc[:, x] = data.index
20 | plot_data = pd.melt(plot_data, x, var_name="series", value_name="y")
21 | y = "y"
22 | hue = "series"
23 | style = "series"
24 | data = plot_data
25 |
26 | x_type = dtype_to_vega_type(data[x].dtype)
27 | params = dict(x=x, x_type=x_type, y=y, color=hue, style=style, size_by=size, sizes=sizes, palette=palette, data=data)
28 | if hue is not None:
29 | params['color_type'] = dtype_to_vega_type(data[hue].dtype)
30 | if size is not None:
31 | params['size_type'] = dtype_to_vega_type(data[size].dtype)
32 | return scatter(**params)
33 |
--------------------------------------------------------------------------------
/seaborn_altair/util.py:
--------------------------------------------------------------------------------
1 | import altair as alt
2 | from matplotlib.colors import to_rgba
3 | import numpy as np
4 | import pandas as pd
5 | import seaborn as sns
6 | import matplotlib as mpl
7 | import six
8 |
9 | def build_dataframe(fields):
10 | field_names = {}
11 | data = pd.DataFrame()
12 | for name, field in six.iteritems(fields):
13 | if field is not None:
14 | if isinstance(field, pd.Series):
15 | fname = field.name
16 | else:
17 | fname = name
18 | data[fname] = field
19 | field_names[name] = fname
20 | else:
21 | field_names[name] = None
22 | return data, field_names
23 |
24 | def dtype_to_vega_type(t):
25 | if t == np.dtype('datetime64[ns]'):
26 | return 'temporal'
27 | if t == np.float64 or t == np.int64:
28 | return 'quantitative'
29 | return 'nominal'
30 |
31 | def size_chart(chart, size, aspect):
32 | dpi = mpl.rcParams['figure.dpi']
33 | if size:
34 | if isinstance(chart, alt.FacetChart):
35 | chart = chart.spec
36 | chart.height = size*dpi
37 | chart.width = aspect*size*dpi
38 |
39 | def vega_color(color):
40 | if isinstance(color, six.string_types) and (color.startswith('rgb(') or color.startswith('rgba(')):
41 | return color
42 | c = to_rgba(color)
43 | return "rgba(%s,%s,%s,%s)" % (int(c[0]*255), int(c[1]*255), int(c[2]*255), c[3])
44 |
45 | def vega_palette(palette, color=None, saturation=1, vega_type="nominal"):
46 | if palette:
47 | if isinstance(palette, mpl.colors.Colormap):
48 | pal = palette.colors
49 | else:
50 | pal = sns.color_palette(palette)
51 | elif color:
52 | pal = [color]
53 | elif vega_type == "nominal":
54 | pal = sns.color_palette()
55 | else:
56 | pal = sns.cubehelix_palette(0, as_cmap=True).colors
57 |
58 | if saturation < 1:
59 | pal = sns.color_palette(pal, desat=saturation)
60 |
61 | pal = sns.color_palette(pal)
62 | return [vega_color(c) for c in pal]
63 |
64 | def vega_semantic_type(data):
65 | try:
66 | float_data = data.astype(np.float)
67 | values = np.unique(float_data.dropna())
68 | if np.array_equal(values, np.array([0., 1.])):
69 | return "nominal"
70 | return "quantitative"
71 | except (ValueError, TypeError):
72 | return "nominal"
73 |
74 | # From seaborn.categorical
75 | def infer_orient(x, y, orient=None):
76 | """Determine how the plot should be oriented based on the data."""
77 | orient = str(orient)
78 |
79 | def is_categorical(s):
80 | try:
81 | # Correct way, but does not exist in older Pandas
82 | try:
83 | return pd.api.types.is_categorical_dtype(s)
84 | except AttributeError:
85 | return pd.core.common.is_categorical_dtype(s) # pylint: disable=E1101
86 | except AttributeError:
87 | # Also works, but feels hackier
88 | return str(s.dtype) == "categorical"
89 |
90 | def is_not_numeric(s):
91 | try:
92 | np.asarray(s, dtype=np.float)
93 | except ValueError:
94 | return True
95 | return False
96 |
97 | no_numeric = "Neither the `x` nor `y` variable appears to be numeric."
98 |
99 | if orient.startswith("v"):
100 | return "v"
101 | elif orient.startswith("h"):
102 | return "h"
103 | elif x is None:
104 | return "v"
105 | elif y is None:
106 | return "h"
107 | elif is_categorical(y):
108 | if is_categorical(x):
109 | raise ValueError(no_numeric)
110 | else:
111 | return "h"
112 | elif is_not_numeric(y):
113 | if is_not_numeric(x):
114 | raise ValueError(no_numeric)
115 | else:
116 | return "h"
117 | else:
118 | return "v"
119 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.md
3 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from distutils.core import setup
2 |
3 | setup(
4 | name='seaborn_altair',
5 | version='0.1.0',
6 | description='Seaborn-compatible API for interactive Vega-Lite plots via Altair',
7 | author='Kitware, Inc.',
8 | author_email='kitware@kitware.com',
9 | license='BSD (3-clause)',
10 | url='https://github.com/Kitware/seaborn_altair',
11 | packages=['seaborn_altair'],
12 | classifiers=[
13 | 'Development Status :: 3 - Alpha',
14 | 'Environment :: Web Environment',
15 | 'License :: OSI Approved :: BSD License',
16 | 'Operating System :: OS Independent',
17 | 'Programming Language :: Python',
18 | 'Programming Language :: Python :: 2',
19 | 'Programming Language :: Python :: 2.7'
20 | ],
21 | install_requires=[
22 | 'altair',
23 | 'seaborn'
24 | ]
25 | )
26 |
--------------------------------------------------------------------------------