(1) Overview

Introduction

Julia [] is a programming language that achieves high performance, stellar modularity and easy composability by making use of multiple dispatch and just-in-time compilation. This comes at the cost of increased latency for new function calls, as the language compiles new machine-code the first time any function is called on new types of arguments. This first call compilation time is a notorious issue for packages that call a large part of their codebase in the first call, such as plotting packages. It even coined the term time to first plot (TTFP) as a phrase for Julia’s start-up latency. Indeed, the Julia language survey 2020 [] identified “it takes too long to generate the first plot” as the biggest problem faced by Julia users.

Package authors try to minimize loading time by reducing the number of dependencies, in particular those that themselves have long loading times. Because depending on a plotting package drastically increases startup time, authors are faced with a challenge if they want to define new plotting functionality for their packages, e.g. if a package for differential equations wishes to define a specialized plotting method for differential equation solutions, to make it easy for users to investigate them visually. Furthermore, depending on a plotting package limits users to plotting with that particular package. If a project imports multiple packages that depend on different plotting packages, this may lead to conflicts and namespace clashes. As a consequence, depending on a plotting package is rarely seen in the Julia ecosystem. Plots.jl has solved this problem by introducing the concept of plot recipes, which allows package authors to define new types of plots while only depending on a very lightweight package RecipesBase.jl. For package developers this makes it easy and unproblematic to define Plots.jl recipes for any new types of objects they define in their package. This recipe will automatically support any Plots.jl backend without running the risk of package conflicts. From the users point of view, the support for multiple backends considerably lowers the threshold for use, as learning syntax and concepts of a new plotting framework is a significant time investment that many users are reluctant to make. With Plots.jl, the backend plotting package can be switched with a single function call, while the syntax and usage stays the same. Thus Plots.jl offers a unified and powerful API with a convenient way for package authors to support visualizations with multiple plotting packages, without increasing the loading time of their package, while at the same time offering users more choice and less cognitive load. An example of the convenient composability of Plots.jl and third party packages is given in Listing 9.

Development

Plots.jl was created by Tom Breloff between September 2015 and 2017, with the stated goal of creating a plotting API for the Julia [] language that was powerful, intuitive, concise, flexible, consistent, lightweight and smart. In particular the recipe system helped the package gain traction within the community, as the latency of loading large dependencies was generally recognized as one of the major factors limiting the uptake of Julia.

With time Tom moved on, and the development of Plots.jl was continued by Michael K. Borregaard, Daniel Schwabeneder and Simon Christ (cf. Figures 6, 7 and Table 1). The maintenance of the project is now a joint effort of the Julia community. The package has reached a very high uptake in the ecosystem. In the Julia Language Survey of both 2019 [] and 2020 [], Plots.jl was identified as the Julia community’s favorite package across the entire ecosystem with 47 percent of all participants listing it among their favorite packages.

Table 1

Contributors sorted by number of commits.


NAMEAFFILIATIONROLEORCID

Tom BreloffHeadlands TechnologiesCreatormissing

Daniel SchwabenederTU WienProjectLeader0000-0002-0412-0777

Michael Krabbe BorregaardGLOBE Institute, University of CopenhagenProjectLeader0000-0002-8146-8435

Simon ChristLeibniz Universität HannoverProjectLeader0000-0002-5866-1472

Josef HeinenForschungszentrum JülichProjectMember0000-0001-6509-1925

YuvalmissingOthermissing

Andrew PalugniokmissingProjectMembermissing

Simon Danisch@beacon-biosignalsOthermissing

Pietro VertechiVeos Digital (https://veos.digital/)ProjectMembermissing

Zhanibek OmarovKorea Advanced Inst. of Science and Technology (KAIST)ProjectMember0000-0002-8783-8791

Thatcher ChamberlinmissingOthermissing

@ma-laforgemissingProjectMembermissing

Christopher RackauckasMassachusetts Institute of TechnologyOther0000-0001-5850-0663

Oliver SchulzMax Planck Institute for PhysicsOthermissing

Sebastian Pfitzner@JuliaComputingOthermissing

Takafumi ArakakimissingOthermissing

Amin YahyaabadiUniversity of ManitobaOthermissing

Jack DevinemissingOthermissing

Sebastian PechmissingOthermissing

Patrick Kofod Mogensen@JuliaComputingOther0000-0002-4910-1932

Samuel S. WatsonmissingOthermissing

Naoki SaitoUC DavisOther0000-0001-5234-4719

Benoit PasquierUniversity of Southern California (USC)Other0000-0002-3838-5976

Ronny BergmannNTNU TrondheimOther0000-0001-8342-7218

Andy NowackiUniversity of LeedsOther0000-0001-7669-7383

Ian ButterworthmissingOthermissing

David GustavssonLund UniversityOther0000-0002-0195-475X

Anshul SinghviColumbia UniversityOther0000-0001-6055-1291

david-macmahonmissingOthermissing

Fredrik EkremissingOthermissing

Maaz Bin Tahir SaeedmissingOthermissing

Kristoffer CarlssonmissingOthermissing

Will KearneymissingOthermissing

Niklas KorsbomissingOthermissing

Miles LucasmissingOthermissing

@GodisemomissingOthermissing

Florian OswaldmissingOthermissing

Diego Javier ZeamissingOthermissing

@WillRammissingOthermissing

Fedor BezrukovmissingOthermissing

Spencer LyonmissingOthermissing

Darwin DarakanandamissingOthermissing

Lukas HauertmannmissingOthermissing

Huckleberry FebbomissingOthermissing

@H-M-HmissingOthermissing

Josh DaymissingOthermissing

@wfgramissingOthermissing

Sheehan OlvermissingOthermissing

Jerry LingmissingOthermissing

Jks LiumissingOthermissing

Seth AxenmissingOthermissing

@o01egmissingOthermissing

Sebastian Micluța-CâmpeanumissingOthermissing

Tim HolymissingOthermissing

Tony KelmanmissingOthermissing

Antoine LevittmissingOthermissing

Iblis LinmissingOthermissing

Harry ScholesmissingOthermissing

@djsegalmissingOthermissing

Goran NakerstmissingOthermissing

Felix HagemannmissingOthermissing

Matthieu GomezmissingOthermissing

@biggsbiggsbymissingOthermissing

Jonathan AndersonmissingOthermissing

Michael KrausmissingOthermissing

Carlo LucibellomissingOthermissing

Robin DeitsmissingOthermissing

Misha MkhasenkomissingOthermissing

Benoît LegatmissingOthermissing

Steven G. JohnsonmissingOthermissing

John VerzanimissingOthermissing

Mattias FältmissingOthermissing

Rashika KarkimissingOthermissing

Morten PiibelehtmissingOthermissing

Filippo VicentinimissingOthermissing

David AnthoffmissingOthermissing

Leon WabekemissingOthermissing

Yusuke KominamimissingOthermissing

Oscar DowsonmissingOthermissing

Max GmissingOthermissing

Fabian GreimelmissingOthermissing

JérémymissingOthermissing

Pearl LimissingOthermissing

David P. SandersmissingOthermissing

Asbjørn Nilsen RisethmissingOthermissing

Jan WeidnermissingOthermissing

@jakkor2missingOthermissing

Pablo ZubietamissingOthermissing

Hamza Yusuf ÇakırmissingOthermissing

John RinehartmissingOthermissing

Martin BielmissingOthermissing

Moritz SchauermissingOthermissing

Mosè GiodanomissingOthermissing

@olegshtchmissingOthermissing

Leon ShenmissingOthermissing

Jeff FesslermissingOthermissing

@hustfmissingOthermissing

Asim H DarmissingOthermissing

@8uurgmissingOthermissing

Abel SiqueiramissingOthermissing

Adrian DawidmissingOthermissing

Alberto LusianimissingOthermissing

Balázs MezeimissingOthermissing

Ben IdemissingOthermissing

Benjamin LungwitzmissingOthermissing

Bernd RiedererUniversity of GrazOther0000-0001-8390-0087

Christina LeemissingOthermissing

Christof StockermissingOthermissing

Christoph FinkensiepmissingOthermissing

@Cornelius-GmissingOthermissing

Daniel HøeghmissingOthermissing

Denny BiasiollimissingOthermissing

Dieter CastelmissingOthermissing

Elliot SabamissingOthermissing

Fengyang WangmissingOthermissing

Fons van der PlasmissingOthermissing

Fredrik Bagge CarlsonmissingOthermissing

Graham SmithmissingOthermissing

Hayato IkomamissingOthermissing

Hessam MehrmissingOthermissing

@InfiniteChaimissingOthermissing

Jack DunnmissingOthermissing

Jeff BezansonmissingOthermissing

Jeff EldredgemissingOthermissing

Jinay JainmissingOthermissing

Johan BlåbäckmissingOthermissing

@jmertmissingOthermissing

Lakshya KhatrimissingOthermissing

Lia SiegelmannmissingOthermissing

@marekkukan-twmissingOthermissing

Mauro WerderETH ZurichOther0000-0003-0137-9377

Maxim GrechkinmissingOthermissing

Michael CawtemissingOthermissing

@milesfrainmissingOthermissing

Nicholas BauermissingOthermissing

Nicolau Leal WerneckmissingOthermissing

@nilshgmissingOthermissing

Oliver EvansmissingOthermissing

Peter GagarinovmissingOthermissing

Páll HaraldssonmissingOthermissing

Rik HuijzermissingOthermissing

Romain FranconvillemissingOthermissing

Ronan PigottmissingOthermissing

Roshan ShariffmissingOthermissing

Scott ThomasmissingOthermissing

Sebastian RollénmissingOthermissing

Seth BrombergermissingOthermissing

Siva SwaminathanmissingOthermissing

Tim DuBoismissingOthermissing

Travis DePratomissingOthermissing

Will ThompsonmissingOthermissing

Yakir Luc GagnonmissingOthermissing

Benjamin ChislettmissingOthermissing

@hhaenselmissingOthermissing

@improbable22missingOthermissing

Johannes FleckmissingOthermissing

Peter CzabanmissingOthermissing

@innerleemissingOthermissing

Mats CronqvistmissingOthermissing

Shi PengchengmissingOthermissing

@wg030missingOthermissing

Will TebbuttUniversity of CambridgeOthermissing

@t-bltgmissingOthermissing

Fred CallawaymissingOthermissing

Jan Thorben SchneidermissingOthermissing

Lee PhillipsAlogus Research CorporationOther0000-0003-4102-2460

Tom GillammissingOthermissing

Usage

Plots.jl is used for visualizations in scientific publications from different fields, such as numerics [, , , , , ], mathematics [], biology [, ], ecology [] and geology [, ] as well as for teaching purposes [, ].

Many packages in the Julia ecosystem as well as non-packaged code (e.g. for scientific projects and publications) contain Plots.jl recipes. According to recent download statistics [] Plots.jl has between 500 and 2000 downloads per day, and over 300 published packages in the general package registry of Julia currently have recipes for Plots.jl defined.

Comparison

Plots.jl achieves its functionality by leveraging the multiple dispatch paradigm of julia, which allows the user to define multiple methods for the same function, with the compiler selecting the appropriate method based on the types of the input arguments. Because of the close connection to Julia’s multiple dispatch paradigm its approach to plotting is fairly unique.

In python, the library unified-plotting [] shares the aim of providing a unified API for multiple packages, in this case matplotlib [], pyplot and javascript libraries including d3.js []. However, unified-plotting is still in the beta phase and not widely used.

The authors are not aware of other package ecosystems that have a recipe system akin to that of Plots.jl, though a recipe system inspired by that of Plots.jl is presently being implemented for the Julia library Makie.jl [].

Implementation and architecture

One-function API

A central design goal of Plots.jl is that the user should rarely have to consult the documentation while plotting. This is achieved by having a tightly unified syntax that has few function names to remember, and by having a great deal of flexibility in the inputs of those functions through aliases and redundancy.

Plots.jl’s main interface is simply the plot function, which creates a new plot object. Additionally, there is a plot! function that modifies an existing plot object, e.g. by changing axes limits or adding new elements. Any type of predefined plot (e.g. a histogram, a bar plot, a scatter plot, a heatmap, an image, a geographical map, etc.) may be created by a call to plot. The exact type is defined by the keyword argument seriestype and the input arguments (type and number). New seriestypes can be created with recipes (see below).

For convenience, plots.jl also exports shorthand functions named after the seriestypes (see examples in Listing 1).

Listing 1 

Examples of shorthands. Full list available at https://docs.juliaplots/stable/api/#Plot-specification.

All aspects of the plot are controlled by a set of plot attributes, that are controlled by keyword arguments []. plots.jl distinguishes four hierarchical levels of attributes: plot attributes, subplot attributes, axis attributes and series attributes (cf. Figure 1). There is one additional special type of attributes called magic attributes. They allow to set multiple attributes that belong to a common element in a single keyword. E.g. plot(1:2; line = (5, :dash)) is equivalent to plot(1:2; linewidth = 5, linestyle = :dash).

Figure 1 

Example plot of the iris dataset [] to illustrate the use of different attribute types (cf. Listing 2).

Listing 2 

Code corresponding to Figure 1.

A series in the context of plots.jl syntax is an individual plot element, such as a continuous line or a set of scatter points. For example, the left plot in Figure 1 has three series, distinguished by different types of markers. A plot may contain multiple series, e.g. when adding a trend line to a scatter plot. Multiple series may be added in the same plot call by concatenating the data as columns in a row matrix (see below), or added sequentially with the plot! function.

Input arguments can have many different forms, such as:

Calling the plot function returns a plot object. The plot object is essentially a big nested dictionary holding the plot attributes for the layout, subplots, series, segments, etc. and their associated data values. The plot object is automatically rendered in the surrounding context when returned to an interactive session, or can be displayed explicitly by calling the display function on the object. This delayed rendering means that plot calls can be combined without unnecessary intermediate rendering.

Pipeline

The plotting pipeline has two main stages (cf. Figure 2): construction of the plot using plot/plot! calls and creation of the output via savefig/display/gui calls. These calls are often called implicitly in environments like the Julia REPL, notebooks or IDEs.

Figure 2 

Plotting pipeline in Plots.jl. The separation of construction and output production enables the flexible use of different backends in the same session and helps to avoid unnecessary intermediate calculation. Created using mermaid [].

The very first step upon construction is to convert all inputs to form the list of plot attributes that constitute the plot specification. As shown in Listing 3 plots.jl is very flexible about possible input values. The conversion step involves defining values for every attribute based on the provided keyword arguments. This includes replacing aliases of attributes (which are multiple alternatively spelled keywords, such as ‘c’ or ‘color’, encoding the same attribute), handling of missing and nothing values in the input data and attribute values, and determining the final values based on the set of defaults. The default values are organized in a hierarchical framework, based on the values of other attributes; e.g. linecolor, fillcolor and markercolor will default to seriescolor under most seriestypes. But, for instance, under the bar seriestype, linecolor will default to :black, giving bars a black border by default. This allows the construction of appropriate plots with a minimum of user specification, an input paradigm that contrasts with that of e.g. matplotlib, where every aspect of the plot is usually defined manually by the user. This paradigm allows for quick and simple visualisation as part of e.g. data analysis.

Listing 3 

Examples of input preprocessing steps in Plots.jl. All these calls are equivalent.

After input and attribute conversion, recipes are applied recursively and the plot and Subplot objects are initialized. Recipes will be explained in detail in the next section.

When an output is to be produced the layout will be computed and the backend-specific code will be executed to produce the result.

Recipes

As mentioned in the introduction, recipes are the key mechanism in the plots.jl pipeline to allow composable definition of visualizations across Julia packages. A recipe works as a template that allows the users and package developers to define custom plotting routines while only depending on the lightweight package RecipesBase.jl (instead of on plots.jl). Recipes are applied recursively, which makes it easy to compose or combine multiple recipes. This means that any recipe may call other recipes until the plot consists of a seriestype defined internally in plots.jl. This composability, is a major improvement to ecosystem support, as it gives a combinatory reduction in the amount of code required for downstream libraries to add native plotting support for their types.

plots.jl distinguishes four types of recipes: user recipes, type recipes, plot recipes and series recipes [] (cf. Listing 4). By far the most commonly used types are User recipes, which define how to plot objects of a certain type, and series recipes, which define a new seriestype. All four types can be constructed with the @recipe macro which acts on a function definition and creates a new method for the RecipesBase.apply_recipe function. The recipe type is determined by the signature of the function definition, utilizing the multiple dispatch capabilities of the Julia programming language.

Listing 4 

Recipe signatures.

It is sufficient to depend on the RecipesBase.jl package, a small and lightweight dependency to define a recipe. The aim of RecipesBase.jl is to make specialized syntax available for the code author to define visualizations; it has no effect until the package end user loads plots.jl directly.

How is this an improvement over other approaches to defining new visualizations? In most plotting libraries such as matplotlib [], a downstream ODE solver library can add a new function plotsolution that will plot an ODE solution. However, the primary technological advance of the plots.jl recipe system is that the application of recipes is recursive and extendable via multiple dispatch. This solves a combinatory problem for downstream support: it is possible to combine and chain recipes to support plotting on new combinations of input types without ever defining a recipe for that specific combination.

To illustrate this, consider the example of combining recipes defined by the Julia packages DifferentialEquations.jl [] and Measurements.jl [] (cf. Figure 3 and Listing 9). In this example, a user solves a differential equation with uncertain initial conditions specified by Measurements.Measurement objects. The uncertainty encoded in the Measurement objects are automatically propagated through the ODE solver, as multiple methods for this type have been defined for arithmetic functions. The resulting ODE solution Sol will then also be specified in terms of Measurements.Measurements. When calling plot(sol), the recipe for ODE solvers will transform the ODESolution object into an array of arrays, each representing a time series to plot, using techniques like dense output to produce a continuous looking solution. This array of arrays contains number types matching the state of the solution, in this case Measurements.Measurements. Successive applications of the user recipe defined in Measurements.jl then take each state value and assign the uncertainty part of the state to the yerror attribute and pass the value part of the state to the next recipe. When used with the initial seriestype :scatter this results in a scatter plot with proper error bars as seen in Figure 3.

Figure 3 

Showcase of composing recipes. Plotting a ODESolution object from DifferentialEquations.jl containing Measurements from Measurements.jl will apply the recipe of DifferentialEquations.jl which will return vectors of Measurements, which will apply the recipe from Measurements.jl; yielding the solutions of the Lotka-Volterra system [] with correct error bounds without the user having to change the callsite. Neither of these packages has code in their recipes for handling types of the other package. Full code available in Listing 9.

Notably, the two packages have not been developed to work together and are not aware of each other. Yet, multiple dispatch allows to efficiently combine functionality from both packages, and the plots.jl recipe system allows the combined visualization to work automatically.

The recipe of Measurements.jl is an example of a particularly short recipe (cf. Listing 5). A Measurements.Measurement is represented as a type with two fields: value and uncertainty. It can be conveniently constructed with the Unicode infix operator ±. Thus, the object a ± b has a as the value and b as the uncertainty. An array of measurement values can be converted into an array of floating point values to plot, along with having the uncertainties as error bars, via the recipe defined in Listing 5:

Listing 5 

Measurements.jl recipe.

Structure and interfaces

The code for plots.jl is not located in one repository, but split into a few packages, to enhance reuse of more general parts of the code by other packages (cf. Figure 4). In the following the different packages and their use cases will be described.

Plots.jl: The main user facing package; Defines all default values and holds the code for layouting, conversion of input arguments, output generation, all backend code and the default recipes. This is the repository with the highest rate of change.

Figure 4 

Overview of the Plots.jl ecosystem and its interfaces with other Julia packages. The numbers of dependents are taken from juliahub [].

StatsPlots.jl: A drop-in replacement for plots.jl, meaning it loads and reexports all of plots.jl and adds recipes that are specially targeted at visualisation of statistical data; It aims to be integrated with Julia’s statistical package ecosystem under the JuliaStats organisation. Therefore it has more dependencies than plots.jl, which increases the loading time. Since not all plots.jl users need this functionality it is separated in its own repository.

PlotUtils.jl: Provides general utility routines, such as handling colors, optimizing ticks or function sampling; This package is also used by e.g. the newer plotting package Makie.jl.

RecipesBase.jl: A package with zero 3rd-party dependencies, that can be used by other packages to define recipes for their own types without needing to depend on plots.jl.

RecipesPipeline.jl: Another lightweight package that defines an API such that other plotting packages can consume recipes from RecipesBase.jl without needing to become a backend of plots.jl.

GraphRecipes.jl: A package that provides recipes for visualisation of graphs in the sense of graph theory; These are also split out because they have some heavy dependencies.

PlotThemes.jl: Provides different themes for plots.jl.

PlotDocs.jl: Hosts the documentation of plots.jl.

Backends

plots.jl currently supports seven plotting frameworks as backends. These backends are the libraries that do the actual rendering, either on the screen or to a file. The code in plots.jl translates the user code to backend code (cf. Listings 6 to 8 and Figure 5).

Listing 6 

Plots.jl code corresponding to Figure 5.

Listing 7 

PyPlot.jl code roughly corresponding to using Plots; pyplot() in line 4 of Listing .

Listing 8 

PGFPlotsX.jl code roughly corresponding to using Plots;pgfplotsx() in line 4 of Listing .

Figure 5 

An example figure generated from the code shown in Listing 6. Listings 7 and 8 illustrate how the generated backend code could look like for different backends. The actual backend code is more verbose and likely uses more low-level functions. These listings also show how Plots.jl provides a unified API for its backend packages, since the translation between Listings 7 and 8 is not straightforward.

The backend packages are independently developed by different people and organizations. Typically these plotting frameworks themselves have different graphic libraries as backends to support different output types. They differ in their area of expertise and have different trade-offs.

GR: The default backend; It uses the GR framework [] and is among the fastest backends with a good coverage of functionality.

Plotly/PlotlyJS: The backend with the most interactivity and best web support using the plotly javascript library []; One use case is to create interactive plots in documentation [] or notebooks. The plotly backend is a version with minimal dependencies, which doesn’t require the user to load any other Julia package and displays its graphics in the browser, while plotlyJS requires the user to load plotlyJS.jl, but offers display of plots in a standalone window.

PyPlot:PyPlot.jl is the Julia wrapper of matplotlib [] and covers a lot of functionality at moderate speed.

PGFPlotsX: Uses the pgfplots LATEXpackage []; Thus, it is the slowest of the backends, but integrates very good with LATEXdocuments.

InspectDR: Fast backend with GUI and some interactivity; It does good for 2D and handles large datasets and high refresh rates [].

UnicodePlots: A backend that allows plotting in the terminal with unicode characters; It can be used in a terminal also on headless machines []. Therefore it lacks a lot of functionality compared to the other backends.

HDF5: A backend that can be used to save the plot object along with the data in a hdf5-file using HDF5.jl [], such that it can be recovered with any backend; It potentially allows interfacing with plots.jl from other programming languages.

Furthermore, there are six deprecated backends that were used in the earlier stages of plots.jl, but which are no longer maintained as well as the Gaston.jl backend which is in an early experimental stage. Gaston.jl is a Julia interface for gnuplot []. This shows that plots.jl can be sustained even if a maintainer of backend code leaves. Either the backend will be maintained by the community or it will be replaced by another backend.

The backend code of these backends, that is the glue code that translates plots.jl objects and attributes into calls and objects of the backend library, is located in the src/backends folder and, apart from the default backend, are only loaded when the backend gets activated.

A shortcoming of the backend design is that, in terms of maintenance, this part of the codebase is the biggest challenge, since it requires knowledge of plots.jl as well as of the target backend library. Maintainers of those libraries are usually working to capacity on their library, while users of plots.jl are often using plots.jl because they don’t want to learn the usage of one or even several different backends. That is why feature coverage between backends typically varies, though a good amount of these holes can be covered by recipes. Sometimes these even provide features that the backend library is missing or gives at least easier access in terms of syntax. On the other hand there are some features that are present in one backend library, but not in others. Some examples are layouts, information on hover, shared or split legends, hexagonal bins and more. In these cases one either has to find workarounds leveraging other aspects of the backend code, not support that feature at all, or only support it partially.

Quality control

plots.jl runs unit tests of all backends, as well as visual regression tests of the default backend, against the latest version of macOS, Ubuntu and Windows, using the current stable version of Julia, the long term support version and the nightly version, on every pull request and pushes to the default branch of plots.jl. Furthermore, benchmarks are run to detect performance regressions. Lastly, building the documentation creates a suite of example plots for every backend, which also sometimes highlight hard-to-detect errors.

However, the size and flexibility of this project also creates a large surface area that is hard to cover by tests in its entirety. And while it is continually worked on to increase the coverage, there is probably always something missing.

(2) Availability

Operating system

plots.jl is tested on Windows, Linux and macOS.

Programming language

plots.jl v1.13.2 runs on Julia 1.5 and later.

Additional system requirements

Dependencies

plots.jl has the following direct dependencies:

Contour.jl v0.5

FFMPEG.jl v0.2 – v0.4

FixedPointNumbers v0.6 – v0.8

GR.jl v0.46 – v0.55, v0.57

GeometryBasics.jl v0.2, v0.3.1 – v0.3

JSON.jl v0.21, v1

Latexify.jl v0.14 – v0.15

Measures.jl v0.3

NaNMath.jl v0.3

PlotThemes.jl v2

PlotUtils.jl v1

RecipesBase.jl v1

RecipesPipeline.jl v0.3

Reexport.jl v0.2, v1

Requires.jl v1

Scratch.jl v1

Showoff.jl v0.3.1 – v0.3, v1

StatsBase.jl v0.32 – v0.33

In addition, plots.jl has 125 indirect dependencies all of which can be seen at JuliaHub []. Thanks to Julia’s excellent package manager Pkg.jl, BinaryBuilder.jl, semantic versioning and required upper bounds for the general registry, handling of dependencies is relatively painless for users.

List of contributors

The plots.jl project lives from the many contsibutions of its community. Table 1 lists all contributors of plots.jl and Figures 6 and 7 illustrate the distribution of code over the different contributors. The code for creating Table 1 and figures is publicly available at https://gitlab.uni-hannover.de/comp-bio/manuscripts/plots-paper.

Figure 6 

Lines of code alive of the top ten contributors of the Plots.jl repository over time. Data created with hercules [].

Figure 7 

Lines of code alive of the top ten contributors of the Plots.jl ecosystem (Figure 4) over time. Data created with hercules [].

Software location

Code repository Github

Name: JuliaPlots/Plots.jl

Persistent identifier:https://doi.org/10.5281/zenodo.4725318

Licence: MIT

Version published: 1.13.2

Date published: 28/04/2021

The first version of plots.jl was published on github at 11/09/2015.

Language

julia

(3) Reuse potential

plots.jl can be used by people working in all fields for data visualization. In particular, it is possible to define backend agnostic recipes for their domain specific data structures with minimal dependencies. These can be shared, reused and extended by peers with ease by including these recipes in their packages or published scripts. Moreover, it is possible for other plotting software with Julia bindings to take advantage of the recipe system either by contributing backend code to plots.jl or by using RecipesPipeline.jl to become an independent consumer of RecipesBase.jl’s recipes. Plotting software without Julia bindings could potentially use the HDF5 backend to consume fully processed and serialized recipe data.

People interested in modifying, extending or maintaining plots.jl can get in contact either via the github issue tracker, the Julia discourse forum or the Julia slack and zulip spaces. There are quarterly maintenance calls that can be joined on request.

Code examples

Listing 9 

Recipes showcase.