hastyplot 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,131 @@
1
+ Metadata-Version: 2.4
2
+ Name: hastyplot
3
+ Version: 0.1.0
4
+ Summary: Hasty plotting for Altair, inspired by ggplot2's qplot
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: altair>=5
8
+
9
+ # hastyplot
10
+
11
+ Hasty plotting for Altair, inspired by ggplot2's `qplot`.
12
+
13
+ ```python
14
+ from hastyplot import qplot
15
+
16
+ # Scatter plot
17
+ qplot(df, "x", "y")
18
+
19
+ # Histogram (x-only)
20
+ qplot(df, "x")
21
+
22
+ # With pipe
23
+ df.pipe(qplot, "x", "y", color="group")
24
+ ```
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ uv add hastyplot
30
+ ```
31
+
32
+ ## Features
33
+
34
+ - **Auto geom selection** -- x-only gives a histogram, x+y gives a scatter.
35
+ - **Geoms** -- `"scatter"`, `"circle"`, `"line"`, `"bar"`, `"boxplot"`, `"hist"`.
36
+ - **Aesthetics** -- `color`, `size`, `opacity`, `group`.
37
+ - **Smoothing** -- `smooth="loess"` overlays a trend line. Also supports `"linear"`, `"poly"`, `"log"`, `"exp"`, `"pow"`. Control loess wiggliness with `bandwidth`.
38
+ - **Faceting** -- `facet_col`, `facet_row` for grids. `facet_wrap` with `columns` for wrapped layouts.
39
+ - **Themes** -- `"default"`, `"clean"`, `"minimal"` (data journalism style).
40
+ - **Pipe-friendly** -- `data` is the first argument.
41
+
42
+ ## Examples
43
+
44
+ ```python
45
+ # Scatter with color and smooth
46
+ qplot(cars, "Horsepower", "Miles_per_Gallon",
47
+ color="Origin", smooth="loess",
48
+ title="HP vs MPG", theme="minimal")
49
+
50
+ # Boxplot
51
+ qplot(cars, "Origin", "Miles_per_Gallon", geom="boxplot")
52
+
53
+ # Faceted scatter
54
+ qplot(cars, "Horsepower", "Miles_per_Gallon",
55
+ facet_wrap="Cylinders", columns=3, width=200, height=150)
56
+
57
+ # Lines grouped without color
58
+ qplot(stocks, "date", "price", geom="line", group="symbol")
59
+
60
+ # Histogram with custom bins
61
+ qplot(cars, "Horsepower", bins=20, theme="clean")
62
+ ```
63
+
64
+ <!-- API_DOCS_START -->
65
+ ## `hastyplot.qplot.qplot`
66
+
67
+ > function
68
+
69
+ ```python
70
+ qplot(data, x: str, y: str | None = None, *, color: str | None = None, size: str | None = None, opacity: float | str = 0.7, group: str | None = None, geom: str = 'auto', smooth: str | None = None, bandwidth: float = 0.3, bins: int | None = None, facet_col: str | None = None, facet_row: str | None = None, facet_wrap: str | None = None, columns: int | None = None, width: int | None = None, height: int | None = None, title: str | None = None, subtitle: str | None = None, theme: str = 'default', actions: bool = False) -> altair.vegalite.v6.api.Chart
71
+ ```
72
+
73
+ Quick plot for Altair. Inspired by ggplot2's qplot.
74
+
75
+ `data` is the first argument so you can use `df.pipe(qplot, "x", "y")`.
76
+
77
+ **Data & axes**
78
+ - `data` — DataFrame to plot.
79
+ - `x` — column for the x-axis.
80
+ - `y` — column for the y-axis. Omit for a histogram.
81
+
82
+ **Aesthetics**
83
+ - `color` — column to map to color.
84
+ - `size` — column to map to point size.
85
+ - `opacity` — a fixed float (e.g. `0.5`) or a column name.
86
+ - `group` — column to group by *without* changing color.
87
+ Useful for separate lines per group in a uniform color.
88
+
89
+ **Geom & smoothing**
90
+ - `geom` — `"auto"` picks `"hist"` for x-only, `"scatter"` for x+y.
91
+ Options: `"scatter"`, `"circle"`, `"line"`, `"bar"`, `"boxplot"`, `"hist"`.
92
+ - `smooth` — overlay a trend line: `"loess"`, `"linear"`, `"poly"`,
93
+ `"log"`, `"exp"`, `"pow"`.
94
+ - `bandwidth` — loess bandwidth, 0 to 1 (default `0.3`). Lower = wigglier.
95
+ - `bins` — number of histogram bins. Omit for Altair's default.
96
+
97
+ **Faceting**
98
+ - `facet_col` / `facet_row` — column names for a facet grid.
99
+ - `facet_wrap` — single column, wraps into rows.
100
+ - `columns` — max columns before wrapping (default `3`).
101
+
102
+ **Layout & appearance**
103
+ - `width` / `height` — chart size in pixels (per panel when faceted).
104
+ - `title` / `subtitle` — chart title and subtitle.
105
+ - `theme` — `"default"`, `"clean"`, or `"minimal"`.
106
+ - `actions` — show the Vega-Lite export menu (default `False`).
107
+
108
+ | Name | Type | Default |
109
+ | --- | --- | --- |
110
+ | `data` | | |
111
+ | `x` | `str` | |
112
+ | `y` | `str | None` | `None` |
113
+ | `color` | `str | None` | `None` |
114
+ | `size` | `str | None` | `None` |
115
+ | `opacity` | `float | str` | `0.7` |
116
+ | `group` | `str | None` | `None` |
117
+ | `geom` | `str` | `'auto'` |
118
+ | `smooth` | `str | None` | `None` |
119
+ | `bandwidth` | `float` | `0.3` |
120
+ | `bins` | `int | None` | `None` |
121
+ | `facet_col` | `str | None` | `None` |
122
+ | `facet_row` | `str | None` | `None` |
123
+ | `facet_wrap` | `str | None` | `None` |
124
+ | `columns` | `int | None` | `None` |
125
+ | `width` | `int | None` | `None` |
126
+ | `height` | `int | None` | `None` |
127
+ | `title` | `str | None` | `None` |
128
+ | `subtitle` | `str | None` | `None` |
129
+ | `theme` | `str` | `'default'` |
130
+ | `actions` | `bool` | `False` |
131
+ <!-- API_DOCS_END -->
@@ -0,0 +1,123 @@
1
+ # hastyplot
2
+
3
+ Hasty plotting for Altair, inspired by ggplot2's `qplot`.
4
+
5
+ ```python
6
+ from hastyplot import qplot
7
+
8
+ # Scatter plot
9
+ qplot(df, "x", "y")
10
+
11
+ # Histogram (x-only)
12
+ qplot(df, "x")
13
+
14
+ # With pipe
15
+ df.pipe(qplot, "x", "y", color="group")
16
+ ```
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ uv add hastyplot
22
+ ```
23
+
24
+ ## Features
25
+
26
+ - **Auto geom selection** -- x-only gives a histogram, x+y gives a scatter.
27
+ - **Geoms** -- `"scatter"`, `"circle"`, `"line"`, `"bar"`, `"boxplot"`, `"hist"`.
28
+ - **Aesthetics** -- `color`, `size`, `opacity`, `group`.
29
+ - **Smoothing** -- `smooth="loess"` overlays a trend line. Also supports `"linear"`, `"poly"`, `"log"`, `"exp"`, `"pow"`. Control loess wiggliness with `bandwidth`.
30
+ - **Faceting** -- `facet_col`, `facet_row` for grids. `facet_wrap` with `columns` for wrapped layouts.
31
+ - **Themes** -- `"default"`, `"clean"`, `"minimal"` (data journalism style).
32
+ - **Pipe-friendly** -- `data` is the first argument.
33
+
34
+ ## Examples
35
+
36
+ ```python
37
+ # Scatter with color and smooth
38
+ qplot(cars, "Horsepower", "Miles_per_Gallon",
39
+ color="Origin", smooth="loess",
40
+ title="HP vs MPG", theme="minimal")
41
+
42
+ # Boxplot
43
+ qplot(cars, "Origin", "Miles_per_Gallon", geom="boxplot")
44
+
45
+ # Faceted scatter
46
+ qplot(cars, "Horsepower", "Miles_per_Gallon",
47
+ facet_wrap="Cylinders", columns=3, width=200, height=150)
48
+
49
+ # Lines grouped without color
50
+ qplot(stocks, "date", "price", geom="line", group="symbol")
51
+
52
+ # Histogram with custom bins
53
+ qplot(cars, "Horsepower", bins=20, theme="clean")
54
+ ```
55
+
56
+ <!-- API_DOCS_START -->
57
+ ## `hastyplot.qplot.qplot`
58
+
59
+ > function
60
+
61
+ ```python
62
+ qplot(data, x: str, y: str | None = None, *, color: str | None = None, size: str | None = None, opacity: float | str = 0.7, group: str | None = None, geom: str = 'auto', smooth: str | None = None, bandwidth: float = 0.3, bins: int | None = None, facet_col: str | None = None, facet_row: str | None = None, facet_wrap: str | None = None, columns: int | None = None, width: int | None = None, height: int | None = None, title: str | None = None, subtitle: str | None = None, theme: str = 'default', actions: bool = False) -> altair.vegalite.v6.api.Chart
63
+ ```
64
+
65
+ Quick plot for Altair. Inspired by ggplot2's qplot.
66
+
67
+ `data` is the first argument so you can use `df.pipe(qplot, "x", "y")`.
68
+
69
+ **Data & axes**
70
+ - `data` — DataFrame to plot.
71
+ - `x` — column for the x-axis.
72
+ - `y` — column for the y-axis. Omit for a histogram.
73
+
74
+ **Aesthetics**
75
+ - `color` — column to map to color.
76
+ - `size` — column to map to point size.
77
+ - `opacity` — a fixed float (e.g. `0.5`) or a column name.
78
+ - `group` — column to group by *without* changing color.
79
+ Useful for separate lines per group in a uniform color.
80
+
81
+ **Geom & smoothing**
82
+ - `geom` — `"auto"` picks `"hist"` for x-only, `"scatter"` for x+y.
83
+ Options: `"scatter"`, `"circle"`, `"line"`, `"bar"`, `"boxplot"`, `"hist"`.
84
+ - `smooth` — overlay a trend line: `"loess"`, `"linear"`, `"poly"`,
85
+ `"log"`, `"exp"`, `"pow"`.
86
+ - `bandwidth` — loess bandwidth, 0 to 1 (default `0.3`). Lower = wigglier.
87
+ - `bins` — number of histogram bins. Omit for Altair's default.
88
+
89
+ **Faceting**
90
+ - `facet_col` / `facet_row` — column names for a facet grid.
91
+ - `facet_wrap` — single column, wraps into rows.
92
+ - `columns` — max columns before wrapping (default `3`).
93
+
94
+ **Layout & appearance**
95
+ - `width` / `height` — chart size in pixels (per panel when faceted).
96
+ - `title` / `subtitle` — chart title and subtitle.
97
+ - `theme` — `"default"`, `"clean"`, or `"minimal"`.
98
+ - `actions` — show the Vega-Lite export menu (default `False`).
99
+
100
+ | Name | Type | Default |
101
+ | --- | --- | --- |
102
+ | `data` | | |
103
+ | `x` | `str` | |
104
+ | `y` | `str | None` | `None` |
105
+ | `color` | `str | None` | `None` |
106
+ | `size` | `str | None` | `None` |
107
+ | `opacity` | `float | str` | `0.7` |
108
+ | `group` | `str | None` | `None` |
109
+ | `geom` | `str` | `'auto'` |
110
+ | `smooth` | `str | None` | `None` |
111
+ | `bandwidth` | `float` | `0.3` |
112
+ | `bins` | `int | None` | `None` |
113
+ | `facet_col` | `str | None` | `None` |
114
+ | `facet_row` | `str | None` | `None` |
115
+ | `facet_wrap` | `str | None` | `None` |
116
+ | `columns` | `int | None` | `None` |
117
+ | `width` | `int | None` | `None` |
118
+ | `height` | `int | None` | `None` |
119
+ | `title` | `str | None` | `None` |
120
+ | `subtitle` | `str | None` | `None` |
121
+ | `theme` | `str` | `'default'` |
122
+ | `actions` | `bool` | `False` |
123
+ <!-- API_DOCS_END -->
@@ -0,0 +1,12 @@
1
+ [project]
2
+ name = "hastyplot"
3
+ version = "0.1.0"
4
+ description = "Hasty plotting for Altair, inspired by ggplot2's qplot"
5
+ readme = "README.md"
6
+ authors = []
7
+ requires-python = ">=3.12"
8
+ dependencies = ["altair>=5"]
9
+
10
+ [tool.pytest.ini_options]
11
+ pythonpath = ["src"]
12
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,251 @@
1
+ __all__ = ['qplot']
2
+
3
+
4
+ import altair as alt
5
+
6
+ def _clean_label(name):
7
+ """Lowercase and replace -/_ with spaces."""
8
+ return name.replace("_", " ").replace("-", " ").lower()
9
+
10
+
11
+ def qplot(
12
+ data,
13
+ x: str,
14
+ y: str | None = None,
15
+ *,
16
+ # Aesthetics
17
+ color: str | None = None,
18
+ size: str | None = None,
19
+ opacity: float | str = 0.7,
20
+ group: str | None = None,
21
+ # Geom & smoothing
22
+ geom: str = "auto",
23
+ smooth: str | None = None,
24
+ bandwidth: float = 0.3,
25
+ bins: int | None = None,
26
+ # Faceting
27
+ facet_col: str | None = None,
28
+ facet_row: str | None = None,
29
+ facet_wrap: str | None = None,
30
+ columns: int | None = None,
31
+ # Layout & appearance
32
+ width: int | None = None,
33
+ height: int | None = None,
34
+ title: str | None = None,
35
+ subtitle: str | None = None,
36
+ theme: str = "default",
37
+ actions: bool = False,
38
+ ) -> alt.Chart:
39
+ """Quick plot for Altair. Inspired by ggplot2's qplot.
40
+
41
+ `data` is the first argument so you can use `df.pipe(qplot, "x", "y")`.
42
+
43
+ **Data & axes**
44
+ - `data` — DataFrame to plot.
45
+ - `x` — column for the x-axis.
46
+ - `y` — column for the y-axis. Omit for a histogram.
47
+
48
+ **Aesthetics**
49
+ - `color` — column to map to color.
50
+ - `size` — column to map to point size.
51
+ - `opacity` — a fixed float (e.g. `0.5`) or a column name.
52
+ - `group` — column to group by *without* changing color.
53
+ Useful for separate lines per group in a uniform color.
54
+
55
+ **Geom & smoothing**
56
+ - `geom` — `"auto"` picks `"hist"` for x-only, `"scatter"` for x+y.
57
+ Options: `"scatter"`, `"circle"`, `"line"`, `"bar"`, `"boxplot"`, `"hist"`.
58
+ - `smooth` — overlay a trend line: `"loess"`, `"linear"`, `"poly"`,
59
+ `"log"`, `"exp"`, `"pow"`.
60
+ - `bandwidth` — loess bandwidth, 0 to 1 (default `0.3`). Lower = wigglier.
61
+ - `bins` — number of histogram bins. Omit for Altair's default.
62
+
63
+ **Faceting**
64
+ - `facet_col` / `facet_row` — column names for a facet grid.
65
+ - `facet_wrap` — single column, wraps into rows.
66
+ - `columns` — max columns before wrapping (default `3`).
67
+
68
+ **Layout & appearance**
69
+ - `width` / `height` — chart size in pixels (per panel when faceted).
70
+ - `title` / `subtitle` — chart title and subtitle.
71
+ - `theme` — `"default"`, `"clean"`, or `"minimal"`.
72
+ - `actions` — show the Vega-Lite export menu (default `False`).
73
+ """
74
+ chart = alt.Chart(data)
75
+
76
+ # Auto-select geom
77
+ if geom == "auto":
78
+ geom = "hist" if y is None else "scatter"
79
+
80
+ # Clean axis labels
81
+ x_enc = alt.X(x, bin=alt.Bin(maxbins=bins) if bins is not None else True, title=_clean_label(x)) if geom == "hist" else alt.X(x, title=_clean_label(x))
82
+ y_enc = alt.Y("count()", title="count") if geom == "hist" else (alt.Y(y, title=_clean_label(y)) if y else None)
83
+
84
+ # Build the mark + encoding
85
+ if geom == "scatter":
86
+ chart = chart.mark_point(filled=True, opacity=opacity if isinstance(opacity, (int, float)) else 0.7).encode(x=x_enc, y=y_enc)
87
+ elif geom == "circle":
88
+ chart = chart.mark_circle(opacity=opacity if isinstance(opacity, (int, float)) else 0.7).encode(x=x_enc, y=y_enc)
89
+ elif geom == "hist":
90
+ chart = chart.mark_bar().encode(x=x_enc, y=y_enc)
91
+ elif geom == "line":
92
+ chart = chart.mark_line(strokeWidth=2).encode(x=x_enc, y=y_enc)
93
+ elif geom == "bar":
94
+ chart = chart.mark_bar().encode(x=x_enc, y=y_enc)
95
+ elif geom == "boxplot":
96
+ chart = chart.mark_boxplot().encode(x=x_enc, y=y_enc)
97
+ else:
98
+ raise ValueError(f"Unknown geom: {geom}")
99
+
100
+ # Group: splits data by a column (separate marks) but no color distinction
101
+ if group is not None:
102
+ chart = chart.encode(detail=alt.Detail(group))
103
+
104
+ # Optional encodings
105
+ if color is not None:
106
+ chart = chart.encode(color=alt.Color(color, title=_clean_label(color)))
107
+ if size is not None:
108
+ chart = chart.encode(size=alt.Size(size, title=_clean_label(size)))
109
+ if isinstance(opacity, str):
110
+ chart = chart.encode(opacity=alt.Opacity(opacity, title=_clean_label(opacity)))
111
+
112
+ # Smooth: overlay a trend line
113
+ if smooth is not None and y is not None:
114
+ smooth_base = alt.Chart(data)
115
+ groupby = [color] if color is not None else []
116
+ if smooth == "loess":
117
+ trend = (
118
+ smooth_base
119
+ .transform_loess(x, y, groupby=groupby, bandwidth=bandwidth)
120
+ .mark_line(strokeWidth=3, opacity=0.9)
121
+ .encode(x=alt.X(x, title=_clean_label(x)), y=alt.Y(y, title=_clean_label(y)))
122
+ )
123
+ else:
124
+ trend = (
125
+ smooth_base
126
+ .transform_regression(x, y, method=smooth, groupby=groupby)
127
+ .mark_line(strokeWidth=3, opacity=0.9)
128
+ .encode(x=alt.X(x, title=_clean_label(x)), y=alt.Y(y, title=_clean_label(y)))
129
+ )
130
+ if color is not None:
131
+ trend = trend.encode(color=alt.Color(color, title=_clean_label(color)))
132
+ chart = chart + trend
133
+
134
+ # Width and height (applied per facet panel or to whole chart)
135
+ if width is not None or height is not None:
136
+ props = {}
137
+ if width is not None:
138
+ props["width"] = width
139
+ if height is not None:
140
+ props["height"] = height
141
+ chart = chart.properties(**props)
142
+
143
+ # Faceting
144
+ if facet_wrap is not None:
145
+ chart = chart.facet(
146
+ alt.Facet(facet_wrap, title=_clean_label(facet_wrap)),
147
+ columns=columns or 3,
148
+ )
149
+ elif facet_col is not None and facet_row is not None:
150
+ chart = chart.facet(column=alt.Column(facet_col, title=_clean_label(facet_col)),
151
+ row=alt.Row(facet_row, title=_clean_label(facet_row)))
152
+ elif facet_col is not None:
153
+ chart = chart.facet(column=alt.Column(facet_col, title=_clean_label(facet_col)))
154
+ elif facet_row is not None:
155
+ chart = chart.facet(row=alt.Row(facet_row, title=_clean_label(facet_row)))
156
+
157
+ # Title + subtitle
158
+ if title is not None:
159
+ title_obj = alt.TitleParams(text=title, subtitle=subtitle or "")
160
+ chart = chart.properties(title=title_obj)
161
+ elif subtitle is not None:
162
+ title_obj = alt.TitleParams(text="", subtitle=subtitle)
163
+ chart = chart.properties(title=title_obj)
164
+
165
+ # Embed options to control action menu
166
+ chart = chart.properties(
167
+ usermeta={"embedOptions": {"actions": actions}}
168
+ )
169
+
170
+ # Apply theme
171
+ chart = _apply_theme(chart, theme)
172
+
173
+ return chart
174
+
175
+
176
+ _TITLE_COMMON = dict(anchor="start", offset=10, dx=40)
177
+
178
+ def _apply_theme(chart, theme):
179
+ if theme == "default":
180
+ return chart
181
+ elif theme == "clean":
182
+ return (
183
+ chart
184
+ .configure_axis(
185
+ grid=False,
186
+ domainColor="#333",
187
+ tickColor="#333",
188
+ labelFontSize=11,
189
+ titleFontSize=12,
190
+ titleFont="system-ui",
191
+ labelFont="system-ui",
192
+ labelColor="#555",
193
+ titleColor="#333",
194
+ )
195
+ .configure_view(strokeWidth=0)
196
+ .configure_title(
197
+ fontSize=18, fontWeight="bold",
198
+ font="system-ui", subtitleFont="system-ui",
199
+ subtitleFontSize=13, subtitleColor="#666",
200
+ color="#1a1a1a",
201
+ **_TITLE_COMMON,
202
+ )
203
+ .configure_legend(
204
+ labelFont="system-ui", titleFont="system-ui",
205
+ labelFontSize=11, titleFontSize=11,
206
+ symbolSize=80,
207
+ )
208
+ )
209
+ elif theme == "minimal":
210
+ return (
211
+ chart
212
+ .configure_axis(
213
+ grid=True,
214
+ gridColor="#e5e5e5",
215
+ gridWidth=0.5,
216
+ domain=False,
217
+ tickSize=0,
218
+ labelFontSize=11,
219
+ titleFontSize=11,
220
+ titleFont="'Libre Franklin', 'Helvetica Neue', sans-serif",
221
+ labelFont="'Libre Franklin', 'Helvetica Neue', sans-serif",
222
+ labelColor="#666",
223
+ titleColor="#666",
224
+ titleFontWeight="normal",
225
+ labelPadding=8,
226
+ )
227
+ .configure_view(strokeWidth=0)
228
+ .configure_title(
229
+ fontSize=20, fontWeight=700,
230
+ font="'Libre Franklin', 'Helvetica Neue', sans-serif",
231
+ color="#1a1a1a",
232
+ subtitleFont="'Libre Franklin', 'Helvetica Neue', sans-serif",
233
+ subtitleFontSize=14, subtitleColor="#888",
234
+ subtitleFontWeight="normal",
235
+ subtitlePadding=4,
236
+ **_TITLE_COMMON,
237
+ )
238
+ .configure_legend(
239
+ labelFont="'Libre Franklin', 'Helvetica Neue', sans-serif",
240
+ titleFont="'Libre Franklin', 'Helvetica Neue', sans-serif",
241
+ labelFontSize=11, titleFontSize=11,
242
+ titleFontWeight="normal",
243
+ symbolSize=80, orient="bottom",
244
+ )
245
+ .configure_range(
246
+ category=["#e15759", "#4e79a7", "#f28e2b", "#76b7b2", "#59a14f",
247
+ "#edc948", "#b07aa1", "#ff9da7", "#9c755f", "#bab0ac"]
248
+ )
249
+ )
250
+ else:
251
+ raise ValueError(f"Unknown theme: {theme}")
@@ -0,0 +1,131 @@
1
+ Metadata-Version: 2.4
2
+ Name: hastyplot
3
+ Version: 0.1.0
4
+ Summary: Hasty plotting for Altair, inspired by ggplot2's qplot
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: altair>=5
8
+
9
+ # hastyplot
10
+
11
+ Hasty plotting for Altair, inspired by ggplot2's `qplot`.
12
+
13
+ ```python
14
+ from hastyplot import qplot
15
+
16
+ # Scatter plot
17
+ qplot(df, "x", "y")
18
+
19
+ # Histogram (x-only)
20
+ qplot(df, "x")
21
+
22
+ # With pipe
23
+ df.pipe(qplot, "x", "y", color="group")
24
+ ```
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ uv add hastyplot
30
+ ```
31
+
32
+ ## Features
33
+
34
+ - **Auto geom selection** -- x-only gives a histogram, x+y gives a scatter.
35
+ - **Geoms** -- `"scatter"`, `"circle"`, `"line"`, `"bar"`, `"boxplot"`, `"hist"`.
36
+ - **Aesthetics** -- `color`, `size`, `opacity`, `group`.
37
+ - **Smoothing** -- `smooth="loess"` overlays a trend line. Also supports `"linear"`, `"poly"`, `"log"`, `"exp"`, `"pow"`. Control loess wiggliness with `bandwidth`.
38
+ - **Faceting** -- `facet_col`, `facet_row` for grids. `facet_wrap` with `columns` for wrapped layouts.
39
+ - **Themes** -- `"default"`, `"clean"`, `"minimal"` (data journalism style).
40
+ - **Pipe-friendly** -- `data` is the first argument.
41
+
42
+ ## Examples
43
+
44
+ ```python
45
+ # Scatter with color and smooth
46
+ qplot(cars, "Horsepower", "Miles_per_Gallon",
47
+ color="Origin", smooth="loess",
48
+ title="HP vs MPG", theme="minimal")
49
+
50
+ # Boxplot
51
+ qplot(cars, "Origin", "Miles_per_Gallon", geom="boxplot")
52
+
53
+ # Faceted scatter
54
+ qplot(cars, "Horsepower", "Miles_per_Gallon",
55
+ facet_wrap="Cylinders", columns=3, width=200, height=150)
56
+
57
+ # Lines grouped without color
58
+ qplot(stocks, "date", "price", geom="line", group="symbol")
59
+
60
+ # Histogram with custom bins
61
+ qplot(cars, "Horsepower", bins=20, theme="clean")
62
+ ```
63
+
64
+ <!-- API_DOCS_START -->
65
+ ## `hastyplot.qplot.qplot`
66
+
67
+ > function
68
+
69
+ ```python
70
+ qplot(data, x: str, y: str | None = None, *, color: str | None = None, size: str | None = None, opacity: float | str = 0.7, group: str | None = None, geom: str = 'auto', smooth: str | None = None, bandwidth: float = 0.3, bins: int | None = None, facet_col: str | None = None, facet_row: str | None = None, facet_wrap: str | None = None, columns: int | None = None, width: int | None = None, height: int | None = None, title: str | None = None, subtitle: str | None = None, theme: str = 'default', actions: bool = False) -> altair.vegalite.v6.api.Chart
71
+ ```
72
+
73
+ Quick plot for Altair. Inspired by ggplot2's qplot.
74
+
75
+ `data` is the first argument so you can use `df.pipe(qplot, "x", "y")`.
76
+
77
+ **Data & axes**
78
+ - `data` — DataFrame to plot.
79
+ - `x` — column for the x-axis.
80
+ - `y` — column for the y-axis. Omit for a histogram.
81
+
82
+ **Aesthetics**
83
+ - `color` — column to map to color.
84
+ - `size` — column to map to point size.
85
+ - `opacity` — a fixed float (e.g. `0.5`) or a column name.
86
+ - `group` — column to group by *without* changing color.
87
+ Useful for separate lines per group in a uniform color.
88
+
89
+ **Geom & smoothing**
90
+ - `geom` — `"auto"` picks `"hist"` for x-only, `"scatter"` for x+y.
91
+ Options: `"scatter"`, `"circle"`, `"line"`, `"bar"`, `"boxplot"`, `"hist"`.
92
+ - `smooth` — overlay a trend line: `"loess"`, `"linear"`, `"poly"`,
93
+ `"log"`, `"exp"`, `"pow"`.
94
+ - `bandwidth` — loess bandwidth, 0 to 1 (default `0.3`). Lower = wigglier.
95
+ - `bins` — number of histogram bins. Omit for Altair's default.
96
+
97
+ **Faceting**
98
+ - `facet_col` / `facet_row` — column names for a facet grid.
99
+ - `facet_wrap` — single column, wraps into rows.
100
+ - `columns` — max columns before wrapping (default `3`).
101
+
102
+ **Layout & appearance**
103
+ - `width` / `height` — chart size in pixels (per panel when faceted).
104
+ - `title` / `subtitle` — chart title and subtitle.
105
+ - `theme` — `"default"`, `"clean"`, or `"minimal"`.
106
+ - `actions` — show the Vega-Lite export menu (default `False`).
107
+
108
+ | Name | Type | Default |
109
+ | --- | --- | --- |
110
+ | `data` | | |
111
+ | `x` | `str` | |
112
+ | `y` | `str | None` | `None` |
113
+ | `color` | `str | None` | `None` |
114
+ | `size` | `str | None` | `None` |
115
+ | `opacity` | `float | str` | `0.7` |
116
+ | `group` | `str | None` | `None` |
117
+ | `geom` | `str` | `'auto'` |
118
+ | `smooth` | `str | None` | `None` |
119
+ | `bandwidth` | `float` | `0.3` |
120
+ | `bins` | `int | None` | `None` |
121
+ | `facet_col` | `str | None` | `None` |
122
+ | `facet_row` | `str | None` | `None` |
123
+ | `facet_wrap` | `str | None` | `None` |
124
+ | `columns` | `int | None` | `None` |
125
+ | `width` | `int | None` | `None` |
126
+ | `height` | `int | None` | `None` |
127
+ | `title` | `str | None` | `None` |
128
+ | `subtitle` | `str | None` | `None` |
129
+ | `theme` | `str` | `'default'` |
130
+ | `actions` | `bool` | `False` |
131
+ <!-- API_DOCS_END -->
@@ -0,0 +1,9 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/hastyplot/qplot.py
4
+ src/hastyplot.egg-info/PKG-INFO
5
+ src/hastyplot.egg-info/SOURCES.txt
6
+ src/hastyplot.egg-info/dependency_links.txt
7
+ src/hastyplot.egg-info/requires.txt
8
+ src/hastyplot.egg-info/top_level.txt
9
+ tests/test_basic.py
@@ -0,0 +1 @@
1
+ altair>=5
@@ -0,0 +1 @@
1
+ hastyplot
@@ -0,0 +1,20 @@
1
+ from hastyplot import qplot
2
+ import altair as alt
3
+
4
+
5
+ def test_qplot_importable():
6
+ assert callable(qplot)
7
+
8
+
9
+ def test_scatter():
10
+ import pandas as pd
11
+ df = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]})
12
+ chart = qplot(df, "x", "y")
13
+ assert isinstance(chart, alt.Chart)
14
+
15
+
16
+ def test_histogram():
17
+ import pandas as pd
18
+ df = pd.DataFrame({"x": [1, 2, 3, 4, 5]})
19
+ chart = qplot(df, "x")
20
+ assert isinstance(chart, alt.Chart)