diff --git a/moderne_visualizations_misc/dependency_usage_violin_jackson.ipynb b/moderne_visualizations_misc/dependency_usage_violin_jackson.ipynb
new file mode 100644
index 0000000..0f92d4e
--- /dev/null
+++ b/moderne_visualizations_misc/dependency_usage_violin_jackson.ipynb
@@ -0,0 +1,162 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from code_data_science import data_table as dt\n",
+ "\n",
+ "df = dt.read_csv(\"../samples/dependency_usage_violin.csv\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df = df[[\"artifactId\", \"version\", \"depth\"]]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from code_data_science.versions import index as index_versions\n",
+ "\n",
+ "# make sure version is a string\n",
+ "df[\"version\"] = df[\"version\"].astype(str)\n",
+ "\n",
+ "vmap = index_versions(df.version)\n",
+ "df[\"nVersion\"] = list(map(lambda v: vmap[v], df.version))\n",
+ "\n",
+ "\n",
+ "def index_ga(groupartifacts):\n",
+ " sorted_ga = sorted(list(set(groupartifacts)))\n",
+ " return {ga: sorted_ga.index(ga) for ga in sorted_ga}\n",
+ "\n",
+ "\n",
+ "gmap = index_ga(df.artifactId)\n",
+ "df[\"nArtifactId\"] = list(map(lambda g: gmap[g], df.artifactId))\n",
+ "\n",
+ "df = df.sort_values(by=[\"nVersion\", \"nArtifactId\"])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import plotly.graph_objects as go\n",
+ "import code_data_science.palette as palette\n",
+ "\n",
+ "colors = palette.colors_by_weight(500)\n",
+ "\n",
+ "fig = go.Figure()\n",
+ "\n",
+ "# Add a trace to the plot for each category\n",
+ "for i, category in enumerate(df[\"nArtifactId\"].unique()):\n",
+ " category_data = df[df[\"nArtifactId\"] == category]\n",
+ "\n",
+ " # Calculate counts for each dependency and version combination\n",
+ " counts = (\n",
+ " category_data.groupby(\"nVersion\")[\"nArtifactId\"]\n",
+ " .count()\n",
+ " .reset_index(name=\"count\")\n",
+ " )\n",
+ "\n",
+ " category_data_with_counts = category_data.merge(counts, on=\"nVersion\")\n",
+ "\n",
+ " # Generate hover text including the count information\n",
+ " hover_text = category_data_with_counts.apply(\n",
+ " lambda row: f'Artifact: {row[\"artifactId\"]}
Version: {row[\"version\"]}
Count: {row[\"count\"]}',\n",
+ " axis=1,\n",
+ " )\n",
+ "\n",
+ " fig.add_trace(\n",
+ " go.Scatter(\n",
+ " x=category_data[\"nArtifactId\"],\n",
+ " y=category_data[\"nVersion\"],\n",
+ " mode=\"markers\",\n",
+ " marker=dict(color=colors[i % len(colors)], size=8),\n",
+ " showlegend=False,\n",
+ " name=\"\",\n",
+ " text=hover_text,\n",
+ " hoverinfo=\"text\",\n",
+ " hoverlabel=dict(font=dict(size=18)),\n",
+ " )\n",
+ " )\n",
+ "\n",
+ " fig.add_trace(\n",
+ " go.Violin(\n",
+ " x=category_data[\"nArtifactId\"],\n",
+ " y=category_data[\"nVersion\"],\n",
+ " fillcolor=\"black\",\n",
+ " opacity=0.15,\n",
+ " line_color=\"black\",\n",
+ " showlegend=False,\n",
+ " width=0.7,\n",
+ " bandwidth=0.4,\n",
+ " hoverinfo=\"none\",\n",
+ " hoveron=\"points\",\n",
+ " )\n",
+ " )\n",
+ "\n",
+ "num_versions = df[\"nVersion\"].nunique()\n",
+ "height_per_version = 32\n",
+ "width_per_dependency = 80\n",
+ "fig_height = max(num_versions * height_per_version, 900)\n",
+ "fig_width = max(len(list(gmap.values())) * width_per_dependency, 900)\n",
+ "tick_font_size = 13\n",
+ "# Customizing the layout\n",
+ "fig.update_layout(\n",
+ " title=\"Artifact versions in use\",\n",
+ " xaxis_title=\"Artifacts\",\n",
+ " yaxis_title=\"Versions\",\n",
+ " height=fig_height,\n",
+ " width=fig_width,\n",
+ " xaxis=dict(\n",
+ " tickfont=dict(size=tick_font_size),\n",
+ " tickmode=\"array\",\n",
+ " tickvals=list(gmap.values()),\n",
+ " ticktext=list(gmap.keys()),\n",
+ " ),\n",
+ " yaxis=dict(\n",
+ " tickfont=dict(size=tick_font_size),\n",
+ " tickmode=\"array\",\n",
+ " tickvals=list(vmap.values()),\n",
+ " ticktext=list(vmap.keys()),\n",
+ " ),\n",
+ ")\n",
+ "\n",
+ "fig.show()"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/moderne_visualizations_misc/images/dependency_usage_violin_jackson.300.png b/moderne_visualizations_misc/images/dependency_usage_violin_jackson.300.png
new file mode 100644
index 0000000..74af32d
Binary files /dev/null and b/moderne_visualizations_misc/images/dependency_usage_violin_jackson.300.png differ
diff --git a/moderne_visualizations_misc/specs/dependency_usage_violin_jackson.yml b/moderne_visualizations_misc/specs/dependency_usage_violin_jackson.yml
new file mode 100644
index 0000000..e73a6aa
--- /dev/null
+++ b/moderne_visualizations_misc/specs/dependency_usage_violin_jackson.yml
@@ -0,0 +1,11 @@
+---
+type: specs.moderne.io/v1beta/visualization
+name: io.moderne.DependencyUsageViolin
+displayName: Jackson usage
+description: >
+ See the distribution of dependencies by version.
+recipe:
+ org.openrewrite.java.dependencies.DependencyInsight:
+ groupIdPattern: com.fasterxml.jackson.*
+ artifactIdPattern: "*"
+dataTable: org.openrewrite.maven.table.DependenciesInUse