ggsql: A Grammar of Graphics for SQL
Posted by thomasp85 20 hours ago
Comments
Comment by anentropic 19 hours ago
I was kind of guessing that it doesn't run in a database, that it's a SQL-like syntax for a visualisation DSL handled by front end chart library.
That appears to be what is described in https://ggsql.org/get_started/anatomy.html
But then https://ggsql.org/faq.html has a section, "Can I use SQL queries inside the VISUALISE clause," which says, "Some parts of the syntax are passed on directly to the database".
The homepage says "ggsql interfaces directly with your database"
But it's not shown how that happens AFAICT
confused
Comment by thomasp85 19 hours ago
ggsql connects directly with your database backend (if you wish - you can also run it with an in-memory DuckDB backend). Your visual query is translated into a SQL query for each layer of the visualisation and the resulting table is then used for rendering.
E.g.
VISUALISE page_views AS x FROM visits DRAW smooth
will create a SQL query that calculates a smoothing kernel over the data and returns points along that. Those points are then used to create the final line chart
Comment by georgestagg 19 hours ago
As an alpha, we support just a few readers today: duckdb, sqlite, and an experimental ODBC reader. We have largely been focusing development mainly around driving duckdb with local files, though duckdb has extensions to talk to some other types of database.
The idea is that ggsql takes your visualisation query, and then generates a selection of SQL queries to be executed on the database. It sends these queries using the reader, then builds the resulting visualisation with the returned data. That is how we can plot a histogram from very many rows of data, the statistics required to produce a histogram are converted into SQL queries, and only a few points are returned to us to draw bars of the correct height.
By default ggsql will connect to an in-memory duckDB database. If you are using the CLI, you can use the `--reader` argument to connect to files on-disk or an ODBC URI.
If you use Positron, you can do this a little easier through its dedicated "Connections" pane, and the ggsql Jupyter kernel has a magic SQL comment that can be issued to set up a particular reader. I plan to expand a little more on using ggsql with these external tools in the docs soon.
Comment by nojito 19 hours ago
Comment by chatmasta 16 hours ago
Comment by georgestagg 15 hours ago
Comment by chatmasta 12 hours ago
Comment by password4321 19 hours ago
Comment by thomasp85 19 hours ago
Comment by anentropic 17 hours ago
I eventually found this readme https://github.com/posit-dev/ggsql/tree/main/ggsql-python which tells me far more than anything I found on the website
Comment by tantalor 19 hours ago
"SQL" and "databases" are different things
SQL is a declarative language for data manipulation. You can use SQL to query a database, but there's nothing special about databases. You can also write SQL to query other non-database sources like flat files, data streams, or data in a program's memory.
Conversely, you can query a database without SQL.
Comment by philipallstar 12 hours ago
Comment by johnthescott 16 hours ago
fond memories of quel.
Comment by getnormality 19 hours ago
Or is the idea that SQL is such a great language to write in that a lot of people will be thrilled to do their ggplots in this SQL-like language?
EDIT: OK, after looking at almost all of the documentation, I think I've finally figured it out. It's a standalone visualization app with a SQL-like API that currently has backends for DuckDB and SQLite and renders plots with Vegalite. They plan to support more backends and renderers in the future. As a commenter below said, it's supposed to help SQL specialists who don't know Python or R make visualizations.
Comment by nchagnet 18 hours ago
In my experience, the only thing data fields share is SQL (analysts, scientists and engineers). As you said, you could do the same in R, but your project may not be written in R, or Python, but it likely uses an SQL database and some engine to access the data.
Also I've been using marimo notebooks a lot of analysis where it's so easy to write SQL cells using the background duckdb that plotting directly from SQL would be great.
And finally, I have found python APIs for plotting to be really difficult to remember/get used to. The amount of boilerplate for a simple scatterplot in matplotlib is ridiculous, even with a LLM. So a unified grammar within the unified query language would be pretty cool.
Comment by levocardia 13 hours ago
Comment by mbreese 14 hours ago
I mean, is it to avoid loading the full data into a dataframe/table in memory?
I just don't see what the pain point this solves is. ggplot solves quite a lot of this already, so I don't doubt that the authors know the domain well. I just don't see the why this.
Comment by nchagnet 4 hours ago
In a sense I really get your complaint. It's the xkcd standard thing all over, we now have a new competing standard.
I think for me it's not so much the ggplot connection, or the fact that I won't need a dataframe library.
It's that this might be the first piece of a standard way of plotting: no matter which backend (matplotlib, vega, ggplot), no matter how you are getting your data (dataframes, database), where you're doing this (Jupyter or marimo notebook, python script, R, heck lokkerstudio?). You could have just one way of defining a plot. That's something I've genuinely dreamt about.
And what makes this different from yet another library api to me is that it's integrated within SQL. SQL has already won the query standardisation battle, so this is a very promising idea for the visualization standardisation.
Comment by lioeters 2 hours ago
Thinking further, though, there might be value in extracting the specs of this "grammar of graphics" from SQL syntax and generalized, so other languages can implement the same interface.
Comment by philipallstar 12 hours ago
Comment by wonger_ 18 hours ago
Comment by epgui 16 hours ago
What makes it interesting is the interface (SQL) coupled with the formalism (GoG). The actual visualization or runtime is an implementation detail (albeit an important one).
Comment by nojito 19 hours ago
Comment by nchagnet 19 hours ago
Comment by oofbey 14 hours ago
So it’s something. But for most uses just prompting your favorite LLM to generate the matplotlib code is much easier.
Comment by mstr_anderson 3 hours ago
Comment by lmeyerov 17 hours ago
We reached a similar conclusion for GFQL (oss graph dataframe query language), where we needed an LLM-friendly interface to our visualization & analytics stack, especially without requiring a code sandbox. We realized we can do quite rich GPU visual analytics pipelines with some basic extensions to opencypher . Doing SQL for the tabular world makes a lot of sense for the same reasons!
For the GFQL version (OpenCypher), an example of data loading, shaping, algorithmic enrichment, visual encodings, and first-class pipelines:
- overall pipelines: https://pygraphistry.readthedocs.io/en/latest/gfql/benchmark...
- declarative visual encodings as simple calls: https://pygraphistry.readthedocs.io/en/latest/gfql/builtin_c...
Comment by JHonaker 15 hours ago
However, I don't see what the benefits of this are (other than having a simple DSL, but that creates the yet another DSL problme) over ggplot2. What do I gain by using this over ggplot2 in R?
The only problem, and the only reason I ever leave ggplot2 for visualizations, is how difficult it is to do anything "non-standard" that hasn't already had a geom created in the ggplot ecosystem. When you want to do something "different" it's way easier to drop into the primitive drawing operations of whatever you're using than it is to try to write the ggplot-friendly adapter.
Even wrapping common "partial specificiations" as a function (which should "just work" imo) is difficult depending on whether you're trying to wrap something that composes with the rest of the spec via `+` or via pipe (`|>`, the operator formerly known as `%>%`)
Comment by thomasp85 15 hours ago
ggsql is (partly) about reaching new audiences and putting powerful visualisation in new places. If you live in R most of the time I wouldn't expect you to be the prime audience for this (though you may have fun exploring it since it contains some pretty interesting things ggplot2 doesn't have)
Comment by JHonaker 8 hours ago
I really do think it’s a good idea to explore! Sometimes I feel crazy because I’m the only one in my department that prefers to just write SQL to deal with our DBs instead of fiddling with a python/R connector that always has its own quirks.
Comment by almostjazz 15 hours ago
Comment by sinnsro 12 hours ago
Usage:
lhs |> rhs
Arguments: lhs: expression producing a value.
rhs: a call expression.
Details:
[...] It is also possible to use a named argument with the placeholder
‘_’ in the ‘rhs’ call to specify where the ‘lhs’ is to be
inserted. The placeholder can only appear once on the ‘rhs’.Comment by Zedseayou 7 hours ago
Comment by asutekku 6 hours ago
You don't have to use R.
Comment by nicoritschel 17 hours ago
I devised a similar in spirit (inside SQL, very simplified vs GoG) approach that does degrade (but doesn't read as nice): https://sqlnb.com/spec
Comment by thomasp85 17 hours ago
Comment by nicoritschel 16 hours ago
# %%
foo = 1
# %%
print(foo)
Above is notebook with two "cells" & also a valid Python script. Perhaps it matters less with SQL vs Python, but it's a nice property.
Comment by thomasp85 16 hours ago
Comment by kasperset 19 hours ago
Comment by thomasp85 19 hours ago
The point of this is not to superseed ggplot2 in any way, but to provide a different approach which can do a lot of the things ggplot2 can, and some that it can't. But ggplot2 will remain more powerful for a lot of tasks in many years to come I predict
Comment by tomjakubowski 7 hours ago
Comment by tmoertel 15 hours ago
Feedback: A notable omission in the ggsql docs: I cannot find any mention of the possible outputs. Can I output a graphic in PDF? In SVG? PNG? How do I control things like output dimensions (e.g., width=8.5in, height=11in)?
The closest I got was finding these few lines of example code in the docs for the Python library:
# Display or save
chart.display() # In Jupyter
chart.save("chart.html") # Save to fileComment by georgestagg 15 hours ago
Our ggsql Jupyter kernel can use these vegalite specifications to output charts in a Quarto document, for example.
In the future we plan to create a new high performance writer module from scratch, avoiding this intermediate vegalite step, at which point we’ll have better answers for your questions!
Comment by RobinL 1 hour ago
Comment by efromvt 19 hours ago
Comment by refset 12 hours ago
Comment by semmulder 15 hours ago
What made it click for me was the following snippet from: https://ggsql.org/get_started/grammar.html
> We’ve tried to make the learning curve as easy as possible by keeping the grammar close to the SQL syntax that you’re already familiar with. You’ll start with a classic SELECT statement to get the data that you want. Then you’ll use VISUALIZE (or VISUALISE ) to switch from creating a table of data to creating a plot of that data. Then you’ll DRAW a layer that maps columns in your data to aesthetics (visual properties), like position, colour, and shape. Then you tweak the SCALEs, the mappings between the data and the visual properties, to make the plot easier to read. Then you FACET the plot to show how the relationships differ across subsets of the data. Finally you finish up by adding LABELs to explain your plot to others. This allows you to produce graphics using the same structured thinking that you already use to design a SQL query.
Comment by thomasp85 20 hours ago
Comment by zcw100 19 hours ago
Comment by thomasp85 20 hours ago
Comment by jorin 18 hours ago
Comment by tauroid 12 hours ago
I see the (a?) backend is polars, which is good as well.
This in CLI might be the quickest / tersest way to go from "have parquet / csv / other table format" to "see graph", though a keen polars / matplotlib user would also get there pretty quick.
Comment by gh5000 19 hours ago
Comment by thomasp85 19 hours ago
Comment by rustyconover 18 hours ago
Comment by jiehong 18 hours ago
It would be nice if it included a rendering engine.
Comment by thomasp85 17 hours ago
Comment by kasperset 20 hours ago
Comment by urams 15 hours ago
Comment by psadri 15 hours ago
Comment by urams 14 hours ago
In the context of a query used for a dashboard in prod, you're likely using a different viz environment so it's not useful at all there.
Comment by jiehong 18 hours ago
This can replace a lot of Excel in the end.
It makes so much sense now that it exists!
Comment by ericdfournier 14 hours ago
Comment by radarsat1 20 hours ago
Comment by data_ders 19 hours ago
my questions are less about the syntax, which i'm largely familiar with knowing both SQL and ggplot.
i'm more interested in the backend architecture. Looking at the Cargo.toml [1], I was surprised to not see a visualization dependency like D3 or Vega. Is this intentional?
I'm certainly going to take this for a spin and I think this could be incredible for agentic analytics. I'm mostly curious right now what "deployment" looks like both currently in a utopian future.
utopia is easier -- what if databases supported it directly?!? but even then I think I'd rather have databases spit out an intermediate representation (IR) that could be handed to a viz engine, similar to how vega works. or perhaps the SQL is the IR?!
another question that arises from the question of composability: how distinct would a ggplot IR be from a metrics layer spec? could i use ggsql to create an IR that I then use R's ggplot to render (or vise versa maybe?)
as for the deployment story today, I'll likely learn most by doing (with agents). My experiment will be to kick off an agent to do something like: extract this dataset to S3 using dlt [2], model it using dbt [3], then use ggsql to visualize.
p.s. @thomasp85, I was a big fan of tidygraph back in the day [4]. love how small our data world is.
[1]: https://github.com/posit-dev/ggsql/blob/main/Cargo.toml
[2]: https://github.com/dlt-hub/dlt
[3]: https://github.com/dbt-labs/dbt-fusion
[4]: https://stackoverflow.com/questions/46466351/how-to-hide-unc...
Comment by thomasp85 19 hours ago
ggsql is modular by design. It consists of various reader modules that takes care of connecting with different data backends (currently we have a DuckDB, an SQLite, and an ODBC reader), a central plot module, and various writer modules that take care of the rendering (currently only Vegalite but I plan to write my own renderer from scratch).
As for deployment I can only talk about a utopian future since this alpha-release doesn't provide much tangible in that area. The ggsql Jupyter kernel already allows you to execute ggsql queries in Jupyter and Quarto notebooks, so deployment of reports should kinda work already, though we are still looking at making it as easy as possible to move database credentials along with the deployment. I also envision deployment of single .ggsql files that result in embeddable visualisations you can reference on websites etc. Our focus in this area will be Posit Connect in the short term
I'm afraid I don't know what IR stands for - can you elaborate?
Comment by stevedh 17 hours ago
Comment by thomasp85 16 hours ago
Comment by persedes 15 hours ago
Comment by breakfastduck 18 hours ago
Comment by estetlinus 13 hours ago
Please, for the love of god and in the name of everything holy, kill the Jupyter Notebook.
Comment by hei-lima 19 hours ago
Comment by rvba 18 hours ago
2) how to make manual adjustments?
Comment by thomasp85 18 hours ago
1) No (unless you count 'render to image and insert that into your excel document') 2) This is not possible - manual adjustments are not reproducible and we live by that ethos
Comment by tonyarkles 15 hours ago
Just want to give you a high-five on that one. I've dealt with so many hand-adjusted plots in the past where they work until either the dataset changes just a little bit or the plot library itself gets upgraded... in both cases, the plots completely fall apart when you're not expecting it.
Comment by i000 14 hours ago
Comment by hadley 6 hours ago
Comment by dartharva 19 hours ago
Comment by mergisi 2 hours ago