AlgebraOfGraphics.jl

repository·master·Indexed 19 days ago

https://github.com/makieorg/algebraofgraphics.jl

A Makie-powered algebraic grammar of graphics for Julia. Inspired by ggplot2, it allows users to build complex visualizations by composing simple building blocks using algebraic operators (+ and *). The library supports a wide range of visuals including scatter plots, lines, BoxPlot, Violin, QQPlot, and geographic Choropleth maps, utilizing the Makie.jl ecosystem for rendering.

Tokens
43.5K
Snippets
163
Records
172
Agent score
68%

What's inside AlgebraOfGraphics.jl

  1. Access the underlying Makie Figure from an AoG plot

    master

    When you call draw(spec) in AlgebraOfGraphics, it returns a figuregrid object. This object contains two primary fields:

    • figure: The Makie Figure object containing all visual elements.
    • grid: An object containing AxisEntries for facet layouts.

    You can access the figure to perform global modifications like changing the background color or resizing the canvas.

    figuregrid = draw(spec)
    figure = figuregrid.figure
    
    # Change background color
    figure.scene.backgroundcolor = Makie.to_color(:gray90)
    
    # Resize the figure
    resize!(figure, 500, 300)
  2. Use dodge_x and dodge_y for generic dodging

    master

    Instead of the native :dodge attribute, you can use the generic dodge_x or dodge_y mappings.

    • On plot types with native :dodge support (like BarPlot), dodge_x is automatically routed to the native attribute if the direction matches (e.g., using dodge_x on a vertical barplot), causing the bars to narrow accordingly.
    • On "width-less" plot types (like Scatter or Errorbars) that do not have a native dodge attribute, dodge_x/dodge_y allows you to apply dodging logic anyway. This is particularly useful for sharing a single mapping across multiple layers of different plot types, as AlgebraOfGraphics will attempt to align the widths automatically.
    # Example of sharing a dodge mapping across different plot types
    shared = mapping(:x, :y, dodge_x = :group)
    plt = data(df) * (
        shared * mapping(color = :group) * visual(BarPlot) +
        shared * mapping(:err, group = :group) * visual(Errorbars)
    )
    draw(plt)
  3. Understand how mapping arguments work

    master

    The mapping function translates data columns into visual attributes of a plot. It accepts two types of arguments:

    1. Positional arguments: Passed in order to the plotting function. For example, in Scatter, the first argument maps to the x-axis and the second to the y-axis.
    2. Named arguments: Map data to specific visual attributes (keywords) like color, markersize, or linestyle. The available keywords depend on the specific plotting function being used.

    To discover which arguments and aesthetics a specific plotting function supports, use show_aesthetics(PlotType).

    mapping(:weight, :height)  # weight → x-axis, height → y-axis
    mapping(:weight, :height, color = :age, markersize = :age)
    
    # Check supported aesthetics for Scatter
    show_aesthetics(Scatter)
  4. Handle continuous and categorical data in mappings

    master

    AlgebraOfGraphics automatically treats numeric columns as continuous and other types as categorical.

    • Continuous data: Mapping a numeric column to an aesthetic like color will automatically generate a Colorbar.
    • Categorical data: Mapping a non-numeric column (e.g., :species) will automatically generate a Legend.

    You can merge new mappings into an existing layer using the * operator.

    # Continuous mapping
    color_layer_continuous = layer * mapping(color = :body_mass_g)
    draw(color_layer_continuous)
    
    # Categorical mapping
    color_layer_categorical = layer * mapping(color = :species)
    draw(color_layer_categorical)
  5. Use dodging to avoid overlapping plots

    master

    Dodging shifts plots on a categorical scale to prevent overlap (e.g., placing bars side-by-side instead of on top of each other).

    • Generic Dodging: Use dodge_x or dodge_y in mapping for plot types that don't have built-in dodging logic (like Scatter or Rangebars).
    • Built-in Dodging: Some plot types like BarPlot have their own dodge keyword because they need to adjust visual properties (like bar width) alongside the position.

    For plot types without inherent width (like Scatter), you can manually specify the dodging width using scales(DodgeX = (; width = value)) during the draw! call.

    # Using generic dodging for Scatter and Rangebars
    plt = data(df) * (
       mapping(:x, :y, dodge_x = :dodge, color = :dodge) * visual(Scatter) +
       mapping(:x, :ylow, :yhigh, dodge_x = :dodge, color = :dodge) * visual(Rangebars)
    )
    
    # Manually setting the dodge width
    draw!(f, plt, scales(DodgeX = (; width = 0.75)))
  6. Manage categories across multiple datasets

    master

    When combining multiple datasets (e.g., using data(df1) + data(df2)), you can manage the unified categorical scale in two ways:

    1. Apply the same renamer to multiple mappings in the expression.
    2. Set the global ordering and labeling in one place using the categories keyword in the draw call.
    # Combining datasets
    plt = (data(df1) + data(df2)) * mapping(:x, :y) * visual(BoxPlot)
    
    # Setting the unified scale order/labels in one place
    draw(plt, scales(X = (; 
        categories = ["one", "two", "three", "four"]
    ))
  7. How to compose visualizations in AlgebraOfGraphics

    master

    AlgebraOfGraphics uses algebraic operators to build complex plots from simple components:

    • * (Multiplication): Used to combine a data specification with mappings or transformations (e.g., data(df) * mapping(...) or spec * mapping(col = :var) for faceting).
    • + (Addition): Used to combine layers or visual properties within a specification (e.g., linear() + visual(alpha = 0.3)).

    This allows for a highly modular approach to building plots, where you can start with a base specification and incrementally add color, facets, or statistical layers.

    # Example of composition
    spec = data(penguins) * mapping(:bill_length_mm, :bill_depth_mm)
    
    # Adding color via multiplication
    by_color = spec * mapping(color = :species)
    
    # Adding a layer and visual property via addition
    with_regression = by_color * (linear() + visual(alpha = 0.3))
  8. Understand the building blocks of AlgebraOfGraphics layers

    master

    In AlgebraOfGraphics, layers are the fundamental building blocks. A layer is constructed by combining four elementary objects using algebraic operations:

    • Data: The dataset being encoded.
    • Mapping: The association of variables from the dataset to specific plot attributes (e.g., mapping a column to the x-axis).
    • Visual: Data-independent plot information (e.g., setting a fixed color or line width regardless of the data).
    • Analyses: Transformations applied to the data before it is plotted (e.g., statistical transformations).

    You can combine these objects using algebraic operations to form complex layers, which can then be visualized.

  9. Create facet layouts with layout, row, and col mappings

    master

    Faceting breaks visualizations into multiple axes (facets) to reduce overplotting. AlgebraOfGraphics provides three primary mappings for this:

    • layout: Creates a wrapped layout that attempts an approximately square configuration of facets.
    • col: Distributes groups along the columns of a grid.
    • row: Distributes groups along the rows of a grid.

    You can combine row and col to create a grid layout.

    Note: You cannot combine layout with row or col facetting in the same specification.

    # Wrapped layout
    spec_layout = data(df) * mapping(:x, :y) * mapping(layout = :category)
    
    # Grid layout (rows and columns)
    spec_grid = data(df) * mapping(:x, :y) * mapping(row = :sex, col = :species)
  10. Multiply individual layers to combine properties

    master

    In AlgebraOfGraphics, you can use the multiplication operator * on individual Layer objects. This is an associative operation used to combine partially defined layers. When you multiply two layers, the resulting layer is a composition where:

    • Datasets can be replaced.
    • Mappings can be merged.
    • Transformations can be concatenated.

    This is primarily useful for building complex layers from smaller, reusable components.

    # Example concept (not a literal code snippet from source, but representing the logic)
    layer_a * layer_b
  11. Why mapping transformations are element-wise

    master

    Transformations within a mapping, such as mapping(:x => log => "log(x)"), are applied element-wise.

    Whole-column operations are intentionally not supported to prevent errors when data is grouped or when multiple datasets are used.

    If you require column-wise transformations (like calculating a density), you should:

    1. Implement a custom analysis (e.g., density()) that accepts the whole data as input.
    2. Apply the transformation directly to your data before passing it to AlgebraOfGraphics.
  12. Understand Aesthetics and mapping to Makie arguments

    master

    Aesthetics are abstractions of visual properties (like X, Y, Color, or Marker) that tell AlgebraOfGraphics how to handle labels and legends.

    Because Makie plotting functions use different positional and keyword arguments, AlgebraOfGraphics uses an internal aesthetic mapping to determine which argument corresponds to which aesthetic.

    • Use show_aesthetics(VisualType) to inspect how a specific Makie visual maps to AlgebraOfGraphics aesthetics.
    • Some aesthetics are context-dependent. For example, in Violin, the mapping of positional arguments to X and Y changes based on the orientation attribute.
    # Inspect aesthetics for a Scatter plot
    show_aesthetics(Scatter)
    
    # Observe how orientation changes axis mapping for Violin
    violin_layer = data(penguins) * mapping(:species, :bill_length_mm) * visual(Violin)
    draw(violin_layer)
    
    # Switch orientation
    draw(violin_layer * visual(orientation = :horizontal))