├── .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 | ![barplot](https://github.com/kitware/seaborn_altair/raw/master/img/visualization.png) 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 |

barplot

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 | 54 |

Categorical plots

55 | 65 |

Distribution plots

66 | 71 |

Regression plots

72 | 76 |

Matrix plots

77 | 81 |

Timeseries plots

82 | 85 |

Miscellaneous plots

86 | 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("
%s
" % 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 | --------------------------------------------------------------------------------