rust 0.11 → 0.12
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.
- checksums.yaml +4 -4
- data/lib/rust/core/types/language.rb +1 -1
- data/lib/rust/external/ggplot2/core.rb +171 -0
- data/lib/rust/external/ggplot2/geoms.rb +83 -0
- data/lib/rust/external/ggplot2/helper.rb +122 -0
- data/lib/rust/external/ggplot2/plot_builder.rb +188 -0
- data/lib/rust/external/ggplot2/themes.rb +435 -0
- data/lib/rust/external/ggplot2.rb +5 -0
- data/lib/rust.rb +19 -0
- metadata +8 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 8eb6e3759ef38070603a941ef348ac46e4b6c08b4638c493edb2169acf16c793
         | 
| 4 | 
            +
              data.tar.gz: f855ca774695688ee64d7513ac94c8b98d3bae154bfae56b803ed1da567cb282
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: c281de698d8b4750832d77971dac857dbee2c31252b7950abf91777d37763bbb79577098ab48efeee6f8a5ef2a28949ef72816d33703eaad14887d588b4aac32
         | 
| 7 | 
            +
              data.tar.gz: d44c0532c19ff8eb2f505d4da62e82b4b4f32dada4b39a05cddf57679a3a725dd38ceecf63aaf2d551a8ae691841196bad7cc0694d3613b928a84a9b6291ef6a
         | 
| @@ -137,7 +137,7 @@ module Rust | |
| 137 137 | 
             
                    # Sets the +arguments+ (Arguments type) of the function.
         | 
| 138 138 |  | 
| 139 139 | 
             
                    def arguments=(arguments)
         | 
| 140 | 
            -
                        raise TypeError, "Expected Arguments" unless  | 
| 140 | 
            +
                        raise TypeError, "Expected Arguments" unless arguments.is_a?(Arguments)
         | 
| 141 141 |  | 
| 142 142 | 
             
                        @arguments = arguments
         | 
| 143 143 | 
             
                    end
         | 
| @@ -0,0 +1,171 @@ | |
| 1 | 
            +
            require_relative '../../../rust'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Rust.prerequisite("ggplot2")
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Rust::Plots::GGPlot
         | 
| 6 | 
            +
                def self.default_theme
         | 
| 7 | 
            +
                    @@theme
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
                
         | 
| 10 | 
            +
                def self.default_theme=(theme)
         | 
| 11 | 
            +
                    @@theme = theme.freeze
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
                
         | 
| 14 | 
            +
                class Layer
         | 
| 15 | 
            +
                    def initialize(function_name, **options)
         | 
| 16 | 
            +
                        @function_name = function_name
         | 
| 17 | 
            +
                        @arguments = Rust::Arguments.new
         | 
| 18 | 
            +
                        @options = Rust::Options.from_hash(options)
         | 
| 19 | 
            +
                    end
         | 
| 20 | 
            +
                    
         | 
| 21 | 
            +
                    def option(key, value)
         | 
| 22 | 
            +
                        @options[key] = value
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
                    
         | 
| 25 | 
            +
                    def to_R
         | 
| 26 | 
            +
                        if !block_given?
         | 
| 27 | 
            +
                            options, arguments = @options, @arguments
         | 
| 28 | 
            +
                        else
         | 
| 29 | 
            +
                            options, arguments = yield(@options, @arguments)
         | 
| 30 | 
            +
                        end
         | 
| 31 | 
            +
                        
         | 
| 32 | 
            +
                        options = Rust::Options.from_hash(options) unless options.is_a?(Rust::Options)
         | 
| 33 | 
            +
                        
         | 
| 34 | 
            +
                        function = Rust::Function.new(@function_name)
         | 
| 35 | 
            +
                        function.arguments = arguments if arguments
         | 
| 36 | 
            +
                        function.options = options if options
         | 
| 37 | 
            +
                        return function.to_R
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
                
         | 
| 41 | 
            +
                class Aes
         | 
| 42 | 
            +
                    def initialize(**options)
         | 
| 43 | 
            +
                        options = options.map { |k, v| [k, v.is_a?(Symbol) ? Rust::Variable.new(v) : v] }.to_h
         | 
| 44 | 
            +
                        @options = Rust::Options.from_hash(options)
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
                    
         | 
| 47 | 
            +
                    def to_R
         | 
| 48 | 
            +
                        function = Rust::Function.new("aes")
         | 
| 49 | 
            +
                        function.options = @options if @options
         | 
| 50 | 
            +
                        return function.to_R
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
                
         | 
| 54 | 
            +
                class Plot
         | 
| 55 | 
            +
                    attr_accessor   :theme
         | 
| 56 | 
            +
                    attr_accessor   :aes
         | 
| 57 | 
            +
                    
         | 
| 58 | 
            +
                    def initialize(data, aes = nil)
         | 
| 59 | 
            +
                        @layers = []
         | 
| 60 | 
            +
                        
         | 
| 61 | 
            +
                        @data = data
         | 
| 62 | 
            +
                        @aes = aes
         | 
| 63 | 
            +
                        @theme = Rust::Plots::GGPlot.default_theme
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
                    
         | 
| 66 | 
            +
                    def layer(layer)
         | 
| 67 | 
            +
                        @layers << layer
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
                    
         | 
| 70 | 
            +
                    def show()
         | 
| 71 | 
            +
                        Rust.exclusive do
         | 
| 72 | 
            +
                            dataset_name = nil
         | 
| 73 | 
            +
                            if @data
         | 
| 74 | 
            +
                                dataset_name = "ggplotter.data"
         | 
| 75 | 
            +
                                @data.load_in_r_as(dataset_name)
         | 
| 76 | 
            +
                            end
         | 
| 77 | 
            +
                            r = self.to_R(dataset_name)
         | 
| 78 | 
            +
                            Rust._eval(r)
         | 
| 79 | 
            +
                        end
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    def save(filename, **options)
         | 
| 83 | 
            +
                        Rust.exclusive do 
         | 
| 84 | 
            +
                            dataset_name = nil
         | 
| 85 | 
            +
                            if @data
         | 
| 86 | 
            +
                                dataset_name = "ggplotter.data"
         | 
| 87 | 
            +
                                @data.load_in_r_as(dataset_name)
         | 
| 88 | 
            +
                            end
         | 
| 89 | 
            +
                            r = self.to_R(dataset_name)
         | 
| 90 | 
            +
                            Rust._eval("ggplot.latest <- #{r}")
         | 
| 91 | 
            +
                            save = Rust::Function.new("ggsave")
         | 
| 92 | 
            +
                            save.arguments = Rust::Arguments.new([Rust::Variable.new("ggplot.latest")])
         | 
| 93 | 
            +
                            save.options = Rust::Options.from_hash({file: filename}.merge(options))
         | 
| 94 | 
            +
                            save.call
         | 
| 95 | 
            +
                        end
         | 
| 96 | 
            +
                    end
         | 
| 97 | 
            +
                    
         | 
| 98 | 
            +
                    def to_R(data_set_name="ggplotter.data")
         | 
| 99 | 
            +
                        function = Rust::Function.new("ggplot")
         | 
| 100 | 
            +
                        function.arguments = Rust::Arguments.new
         | 
| 101 | 
            +
                        function.arguments << (data_set_name ? Rust::Variable.new(data_set_name) : nil)
         | 
| 102 | 
            +
                        function.arguments << @aes if @aes
         | 
| 103 | 
            +
                        
         | 
| 104 | 
            +
                        result = function.to_R
         | 
| 105 | 
            +
                        result += " + " + @theme.to_R
         | 
| 106 | 
            +
                        @layers.each do |layer|
         | 
| 107 | 
            +
                            result += " + " + layer.to_R
         | 
| 108 | 
            +
                        end
         | 
| 109 | 
            +
                        
         | 
| 110 | 
            +
                        return result
         | 
| 111 | 
            +
                    end
         | 
| 112 | 
            +
                    
         | 
| 113 | 
            +
                    def <<(others)
         | 
| 114 | 
            +
                        if others.is_a?(Array) && others.all? { |o| o.is_a?(Layer) }
         | 
| 115 | 
            +
                            @layers += others
         | 
| 116 | 
            +
                        elsif others.is_a?(Layer)
         | 
| 117 | 
            +
                            @layers << others
         | 
| 118 | 
            +
                        else
         | 
| 119 | 
            +
                            raise ArgumentError, "Expected Layer or Array of Layers"
         | 
| 120 | 
            +
                        end
         | 
| 121 | 
            +
                        
         | 
| 122 | 
            +
                        return self
         | 
| 123 | 
            +
                    end
         | 
| 124 | 
            +
                    
         | 
| 125 | 
            +
                    def +(others)
         | 
| 126 | 
            +
                        copy = self.deep_clone
         | 
| 127 | 
            +
                        copy << others
         | 
| 128 | 
            +
                        return copy
         | 
| 129 | 
            +
                    end
         | 
| 130 | 
            +
                    
         | 
| 131 | 
            +
                    def inspect(show = true)
         | 
| 132 | 
            +
                        self.show if show
         | 
| 133 | 
            +
                        return super()
         | 
| 134 | 
            +
                    end
         | 
| 135 | 
            +
                end
         | 
| 136 | 
            +
                
         | 
| 137 | 
            +
                class Labels < Layer
         | 
| 138 | 
            +
                    def initialize(**options)
         | 
| 139 | 
            +
                        super("labs", **options)
         | 
| 140 | 
            +
                    end
         | 
| 141 | 
            +
                end
         | 
| 142 | 
            +
                
         | 
| 143 | 
            +
                class FlipCoordinates < Layer
         | 
| 144 | 
            +
                    def initialize(**options)
         | 
| 145 | 
            +
                        super("coord_flip", **options)
         | 
| 146 | 
            +
                    end
         | 
| 147 | 
            +
                end
         | 
| 148 | 
            +
            end
         | 
| 149 | 
            +
             | 
| 150 | 
            +
            module Rust::RBindings
         | 
| 151 | 
            +
                def ggplot(*arguments)
         | 
| 152 | 
            +
                    Rust::Plots::GGPlot::Plot.new(*arguments)
         | 
| 153 | 
            +
                end
         | 
| 154 | 
            +
                
         | 
| 155 | 
            +
                def aes(**options)
         | 
| 156 | 
            +
                    Rust::Plots::GGPlot::Aes.new(**options)
         | 
| 157 | 
            +
                end
         | 
| 158 | 
            +
                
         | 
| 159 | 
            +
                def labs(**options)
         | 
| 160 | 
            +
                    Rust::Plots::GGPlot::Labels.new(**options)
         | 
| 161 | 
            +
                end
         | 
| 162 | 
            +
                alias :labels :labs
         | 
| 163 | 
            +
                
         | 
| 164 | 
            +
                def coord_flip(**options)
         | 
| 165 | 
            +
                    Rust::Plots::GGPlot::FlipCoordinates.new(**options)
         | 
| 166 | 
            +
                end
         | 
| 167 | 
            +
            end
         | 
| 168 | 
            +
             | 
| 169 | 
            +
            def bind_ggplot!
         | 
| 170 | 
            +
                include Rust::Plots::GGPlot
         | 
| 171 | 
            +
            end
         | 
| @@ -0,0 +1,83 @@ | |
| 1 | 
            +
            require_relative 'core'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Rust::Plots::GGPlot
         | 
| 4 | 
            +
                class Geom < Layer
         | 
| 5 | 
            +
                    def initialize(type, arguments = [], **options)
         | 
| 6 | 
            +
                        super("geom_#{type}", **options)
         | 
| 7 | 
            +
                        @type = type
         | 
| 8 | 
            +
                        @arguments = Rust::Arguments.new(arguments)
         | 
| 9 | 
            +
                    end
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
                
         | 
| 12 | 
            +
                class GeomPoint < Geom
         | 
| 13 | 
            +
                    def initialize(arguments = [], **options)
         | 
| 14 | 
            +
                        super("point", arguments, **options)
         | 
| 15 | 
            +
                    end
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
                
         | 
| 18 | 
            +
                class GeomLine < Geom
         | 
| 19 | 
            +
                    def initialize(arguments = [], **options)
         | 
| 20 | 
            +
                        super("line", arguments, **options)
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
                
         | 
| 24 | 
            +
                class GeomCol < Geom
         | 
| 25 | 
            +
                    def initialize(arguments = [], **options)
         | 
| 26 | 
            +
                        super("col", arguments, **options)
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
                
         | 
| 30 | 
            +
                class GeomBoxplot < Geom
         | 
| 31 | 
            +
                    def initialize(arguments = [], **options)
         | 
| 32 | 
            +
                        super("boxplot", arguments, **options)
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
                
         | 
| 36 | 
            +
                class GeomBar < Geom
         | 
| 37 | 
            +
                    def initialize(arguments = [], **options)
         | 
| 38 | 
            +
                        super("bar", arguments, **options)
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
                
         | 
| 42 | 
            +
                class GeomHistogram < Geom
         | 
| 43 | 
            +
                    def initialize(arguments = [], **options)
         | 
| 44 | 
            +
                        super("histogram", arguments, **options)
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
                
         | 
| 48 | 
            +
                class GeomDensity < Geom
         | 
| 49 | 
            +
                    def initialize(arguments = [], **options)
         | 
| 50 | 
            +
                        super("density", arguments, **options)
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
            end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            module Rust::RBindings
         | 
| 56 | 
            +
                def geom_point(*arguments, **options)
         | 
| 57 | 
            +
                    return Rust::Plots::GGPlot::GeomPoint.new(*arguments, **options)
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
                
         | 
| 60 | 
            +
                def geom_line(*arguments, **options)
         | 
| 61 | 
            +
                    return Rust::Plots::GGPlot::GeomLine.new(*arguments, **options)
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
                
         | 
| 64 | 
            +
                def geom_col(*arguments, **options)
         | 
| 65 | 
            +
                    return Rust::Plots::GGPlot::GeomCol.new(*arguments, **options)
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
                
         | 
| 68 | 
            +
                def geom_bar(*arguments, **options)
         | 
| 69 | 
            +
                    return Rust::Plots::GGPlot::GeomBar.new(*arguments, **options)
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
                
         | 
| 72 | 
            +
                def geom_boxplot(*arguments, **options)
         | 
| 73 | 
            +
                    return Rust::Plots::GGPlot::GeomBoxplot.new(*arguments, **options)
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
                
         | 
| 76 | 
            +
                def geom_histogram(*arguments, **options)
         | 
| 77 | 
            +
                    return Rust::Plots::GGPlot::GeomHistogram.new(*arguments, **options)
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
                
         | 
| 80 | 
            +
                def geom_density(*arguments, **options)
         | 
| 81 | 
            +
                    return Rust::Plots::GGPlot::GeomDensity.new(*arguments, **options)
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
            end
         | 
| @@ -0,0 +1,122 @@ | |
| 1 | 
            +
            require_relative 'core'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            GGPLOT_EXAMPLES = {}
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            GGPLOT_EXAMPLES[["Quick introduction", /intro/]] = <<-EOS
         | 
| 6 | 
            +
            bind_ggplot! # Avoid using long module names to reach Rust::Plots::GGPlot (simply includes this module)
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            # Best with a dataframe, but not necessary. If you have it...
         | 
| 9 | 
            +
            df = Rust.toothgrowth
         | 
| 10 | 
            +
            plot = PlotBuilder.for_dataframe(df). # Use a dataframe (symbols will be variable names)
         | 
| 11 | 
            +
                    labeled("Example plot"). # "labeled" sets the label to the last set aesthetic item (x, y, or title, in this case)
         | 
| 12 | 
            +
                    with_x(:len).labeled("X data from df").  # Set all the aesthetics (x, y, ...)
         | 
| 13 | 
            +
                    with_y(:dose).labeled("Y data from df").
         | 
| 14 | 
            +
                    draw_points. # Set the geometries to plot (based on the plot type)
         | 
| 15 | 
            +
                    build # Returns the plot ready to use
         | 
| 16 | 
            +
            plot.show # Show the plot in a window
         | 
| 17 | 
            +
            plot.save("output.pdf", width: 5, height: 4) # Save the plot, width, height etc. are optional
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            # If you don't have a dataframe...
         | 
| 20 | 
            +
            plot2 = PlotBuilder.new.
         | 
| 21 | 
            +
                    with_x([1,2,3]).labeled("X data from df").
         | 
| 22 | 
            +
                    with_y([3,4,5]).labeled("Y data from df").
         | 
| 23 | 
            +
                    draw_points.
         | 
| 24 | 
            +
                    build
         | 
| 25 | 
            +
            plot2.show
         | 
| 26 | 
            +
            EOS
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            GGPLOT_EXAMPLES[["Scatter plots", /scatter/]] = <<-EOS
         | 
| 29 | 
            +
            bind_ggplot!
         | 
| 30 | 
            +
            df = Rust.toothgrowth
         | 
| 31 | 
            +
            plot = PlotBuilder.for_dataframe(df).
         | 
| 32 | 
            +
                    with_x(:len).labeled("X data").
         | 
| 33 | 
            +
                    with_y(:dose).labeled("Y data").
         | 
| 34 | 
            +
                    draw_points. # To draw points
         | 
| 35 | 
            +
                    draw_lines.  # To draw lines (keep both to draw both)
         | 
| 36 | 
            +
                    build
         | 
| 37 | 
            +
            plot.show
         | 
| 38 | 
            +
            EOS
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            GGPLOT_EXAMPLES[["Bar plots", /bar/]] = <<-EOS
         | 
| 41 | 
            +
            bind_ggplot!
         | 
| 42 | 
            +
            df = Rust.toothgrowth
         | 
| 43 | 
            +
            plot = PlotBuilder.for_dataframe(df).
         | 
| 44 | 
            +
                    with_x(:len).labeled("X data").
         | 
| 45 | 
            +
                    with_fill(:supp).labeled("Legend"). # Use with_fill or with_color for stacked plots
         | 
| 46 | 
            +
                    draw_bars. # To draw bars
         | 
| 47 | 
            +
                    build
         | 
| 48 | 
            +
            plot.show
         | 
| 49 | 
            +
            EOS
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            GGPLOT_EXAMPLES[["Box plots", /box/]] = <<-EOS
         | 
| 52 | 
            +
            bind_ggplot!
         | 
| 53 | 
            +
            df = Rust.toothgrowth
         | 
| 54 | 
            +
            plot = PlotBuilder.for_dataframe(df).
         | 
| 55 | 
            +
                    with_y(:len).labeled("Data to boxplot").
         | 
| 56 | 
            +
                    with_group(:supp).labeled("Groups"). # Groups to plot
         | 
| 57 | 
            +
                    draw_boxplot.
         | 
| 58 | 
            +
                    build
         | 
| 59 | 
            +
            plot.show
         | 
| 60 | 
            +
            EOS
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            GGPLOT_EXAMPLES[["Histograms", /hist/]] = <<-EOS
         | 
| 63 | 
            +
            bind_ggplot!
         | 
| 64 | 
            +
            df = Rust.toothgrowth
         | 
| 65 | 
            +
            plot = PlotBuilder.for_dataframe(df).
         | 
| 66 | 
            +
                    with_x(:len).labeled("Data to plot").
         | 
| 67 | 
            +
                    with_fill(:supp).labeled("Color"). # Use with_fill or with_color for multiple plots
         | 
| 68 | 
            +
                    draw_histogram.
         | 
| 69 | 
            +
                    build
         | 
| 70 | 
            +
            plot.show
         | 
| 71 | 
            +
            EOS
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            GGPLOT_EXAMPLES[["Themes", /them/]] = <<-EOS
         | 
| 74 | 
            +
            bind_ggplot!
         | 
| 75 | 
            +
            df = Rust.toothgrowth
         | 
| 76 | 
            +
            # The method with_theme allows to change theme options. The method can be called
         | 
| 77 | 
            +
            # several times, each time the argument does not overwrite the previous options,
         | 
| 78 | 
            +
            # unless they are specified again (in that case, the last specified ones win).
         | 
| 79 | 
            +
            plot = PlotBuilder.for_dataframe(df).
         | 
| 80 | 
            +
                    with_x(:len).labeled("X data").
         | 
| 81 | 
            +
                    with_y(:dose).labeled("Y data").
         | 
| 82 | 
            +
                    draw_points.
         | 
| 83 | 
            +
                    with_theme(
         | 
| 84 | 
            +
                        ThemeBuilder.new('bw').
         | 
| 85 | 
            +
                            title(face: 'bold', size: 12). # Each method sets the property for the related element
         | 
| 86 | 
            +
                            legend do |legend| # Legend and other parts can be set like this
         | 
| 87 | 
            +
                                legend.position(:left) # Puts the legend on the left
         | 
| 88 | 
            +
                            end.
         | 
| 89 | 
            +
                            axis do |axis| # Modifies the axes
         | 
| 90 | 
            +
                                axis.line(Theme::BlankElement.new) # Hides the lines for the axes
         | 
| 91 | 
            +
                                axis.text_x(size: 3) # X axis labels
         | 
| 92 | 
            +
                            end.
         | 
| 93 | 
            +
                            panel do |panel|
         | 
| 94 | 
            +
                                panel.grid_major(colour: 'grey70', size: 0.2) # Sets the major ticks grid
         | 
| 95 | 
            +
                                panel.grid_minor(Theme::BlankElement.new) # Hides the minor ticks grid
         | 
| 96 | 
            +
                            end.
         | 
| 97 | 
            +
                            build
         | 
| 98 | 
            +
                    ).build
         | 
| 99 | 
            +
            plot.show
         | 
| 100 | 
            +
            EOS
         | 
| 101 | 
            +
             | 
| 102 | 
            +
            module Rust::Plots::GGPlot
         | 
| 103 | 
            +
                def self.help!(topic = nil)
         | 
| 104 | 
            +
                    unless topic
         | 
| 105 | 
            +
                        puts "Topics:"
         | 
| 106 | 
            +
                        GGPLOT_EXAMPLES.keys.each do |key, matcher|
         | 
| 107 | 
            +
                            puts "- #{key}"
         | 
| 108 | 
            +
                        end
         | 
| 109 | 
            +
                        puts "Call again specifying the topic of interest."
         | 
| 110 | 
            +
                    else
         | 
| 111 | 
            +
                        GGPLOT_EXAMPLES.each do |key, value|
         | 
| 112 | 
            +
                            if topic.match(key[1])
         | 
| 113 | 
            +
                                puts "*** #{key[0]} ***"
         | 
| 114 | 
            +
                                puts value
         | 
| 115 | 
            +
                                return
         | 
| 116 | 
            +
                            end
         | 
| 117 | 
            +
                        end
         | 
| 118 | 
            +
                        
         | 
| 119 | 
            +
                        puts "Topic not found"
         | 
| 120 | 
            +
                    end
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
            end
         | 
| @@ -0,0 +1,188 @@ | |
| 1 | 
            +
            require_relative 'core'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Rust.prerequisite("ggplot2")
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Rust::Plots::GGPlot
         | 
| 6 | 
            +
                class PlotBuilder
         | 
| 7 | 
            +
                    def self.for_dataframe(data_frame)
         | 
| 8 | 
            +
                        return PlotBuilder.new(data_frame)
         | 
| 9 | 
            +
                    end
         | 
| 10 | 
            +
                    
         | 
| 11 | 
            +
                    def initialize(data=nil)
         | 
| 12 | 
            +
                        @data = data
         | 
| 13 | 
            +
                        
         | 
| 14 | 
            +
                        @aes_options   = {}
         | 
| 15 | 
            +
                        @label_options = {}
         | 
| 16 | 
            +
                        
         | 
| 17 | 
            +
                        @current_context = :title
         | 
| 18 | 
            +
                        
         | 
| 19 | 
            +
                        @layers = []
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                    
         | 
| 22 | 
            +
                    def with_x(variable, label = nil)
         | 
| 23 | 
            +
                        variable = variable.to_sym if variable.is_a?(String)
         | 
| 24 | 
            +
                        
         | 
| 25 | 
            +
                        @aes_options[:x] = variable
         | 
| 26 | 
            +
                        @current_context = :x
         | 
| 27 | 
            +
                        
         | 
| 28 | 
            +
                        return self
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                    
         | 
| 31 | 
            +
                    def with_y(variable)
         | 
| 32 | 
            +
                        variable = variable.to_sym if variable.is_a?(String)
         | 
| 33 | 
            +
                        
         | 
| 34 | 
            +
                        @aes_options[:y] = variable
         | 
| 35 | 
            +
                        @current_context = :y
         | 
| 36 | 
            +
                        
         | 
| 37 | 
            +
                        return self
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
                    
         | 
| 40 | 
            +
                    def with_group(variable)
         | 
| 41 | 
            +
                        variable = variable.to_sym if variable.is_a?(String)
         | 
| 42 | 
            +
                        
         | 
| 43 | 
            +
                        @aes_options[:group] = variable
         | 
| 44 | 
            +
                        @current_context = :group
         | 
| 45 | 
            +
                        
         | 
| 46 | 
            +
                        return self
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                    
         | 
| 49 | 
            +
                    def with_color(variable)
         | 
| 50 | 
            +
                        variable = variable.to_sym if variable.is_a?(String)
         | 
| 51 | 
            +
                        
         | 
| 52 | 
            +
                        @aes_options[:color] = variable
         | 
| 53 | 
            +
                        @current_context = :color
         | 
| 54 | 
            +
                        
         | 
| 55 | 
            +
                        return self
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                    
         | 
| 58 | 
            +
                    def with_fill(variable)
         | 
| 59 | 
            +
                        variable = variable.to_sym if variable.is_a?(String)
         | 
| 60 | 
            +
                        
         | 
| 61 | 
            +
                        @aes_options[:fill] = variable
         | 
| 62 | 
            +
                        @current_context = :fill
         | 
| 63 | 
            +
                        
         | 
| 64 | 
            +
                        return self
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
                    
         | 
| 67 | 
            +
                    def labeled(value)
         | 
| 68 | 
            +
                        raise "No context for assigning a label" unless @current_context
         | 
| 69 | 
            +
                        @label_options[@current_context] = value
         | 
| 70 | 
            +
                        @current_context = nil
         | 
| 71 | 
            +
                        
         | 
| 72 | 
            +
                        return self
         | 
| 73 | 
            +
                    end
         | 
| 74 | 
            +
                    
         | 
| 75 | 
            +
                    def with_x_label(value)
         | 
| 76 | 
            +
                        @label_options[:x] = value
         | 
| 77 | 
            +
                        
         | 
| 78 | 
            +
                        return self
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
                    
         | 
| 81 | 
            +
                    def with_y_label(value)
         | 
| 82 | 
            +
                        @label_options[:y] = value
         | 
| 83 | 
            +
                        
         | 
| 84 | 
            +
                        return self
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
                    
         | 
| 87 | 
            +
                    def with_color_label(value)
         | 
| 88 | 
            +
                        @label_options[:color] = value
         | 
| 89 | 
            +
                        
         | 
| 90 | 
            +
                        return self
         | 
| 91 | 
            +
                    end
         | 
| 92 | 
            +
                    
         | 
| 93 | 
            +
                    def with_title(value)
         | 
| 94 | 
            +
                        @label_options[:title] = value
         | 
| 95 | 
            +
                        
         | 
| 96 | 
            +
                        return self
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
                    
         | 
| 99 | 
            +
                    def draw_points(**options)
         | 
| 100 | 
            +
                        @layers << GeomPoint.new(**options)
         | 
| 101 | 
            +
                        
         | 
| 102 | 
            +
                        @current_context = nil
         | 
| 103 | 
            +
                        
         | 
| 104 | 
            +
                        return self
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
                    
         | 
| 107 | 
            +
                    def draw_lines(**options)
         | 
| 108 | 
            +
                        @layers << GeomLine.new(**options)
         | 
| 109 | 
            +
                        
         | 
| 110 | 
            +
                        @current_context = nil
         | 
| 111 | 
            +
                        
         | 
| 112 | 
            +
                        return self
         | 
| 113 | 
            +
                    end
         | 
| 114 | 
            +
                    
         | 
| 115 | 
            +
                    def draw_bars(**options)
         | 
| 116 | 
            +
                        @layers << GeomBar.new(**options)
         | 
| 117 | 
            +
                        
         | 
| 118 | 
            +
                        @current_context = nil
         | 
| 119 | 
            +
                        
         | 
| 120 | 
            +
                        return self
         | 
| 121 | 
            +
                    end
         | 
| 122 | 
            +
                    
         | 
| 123 | 
            +
                    def draw_cols(**options)
         | 
| 124 | 
            +
                        @layers << GeomCol.new(**options)
         | 
| 125 | 
            +
                        
         | 
| 126 | 
            +
                        @current_context = nil
         | 
| 127 | 
            +
                        
         | 
| 128 | 
            +
                        return self
         | 
| 129 | 
            +
                    end
         | 
| 130 | 
            +
                    
         | 
| 131 | 
            +
                    def draw_boxplot(**options)
         | 
| 132 | 
            +
                        @layers << GeomBoxplot.new(**options)
         | 
| 133 | 
            +
                        
         | 
| 134 | 
            +
                        @current_context = nil
         | 
| 135 | 
            +
                        
         | 
| 136 | 
            +
                        return self
         | 
| 137 | 
            +
                    end
         | 
| 138 | 
            +
                    
         | 
| 139 | 
            +
                    def draw_histogram(**options)
         | 
| 140 | 
            +
                        @layers << GeomHistogram.new(**options)
         | 
| 141 | 
            +
                        
         | 
| 142 | 
            +
                        @current_context = nil
         | 
| 143 | 
            +
                        
         | 
| 144 | 
            +
                        return self
         | 
| 145 | 
            +
                    end
         | 
| 146 | 
            +
                    
         | 
| 147 | 
            +
                    def draw_density(**options)
         | 
| 148 | 
            +
                        @layers << GeomDensity.new(**options)
         | 
| 149 | 
            +
                        
         | 
| 150 | 
            +
                        @current_context = nil
         | 
| 151 | 
            +
                        
         | 
| 152 | 
            +
                        return self
         | 
| 153 | 
            +
                    end
         | 
| 154 | 
            +
                    
         | 
| 155 | 
            +
                    def with_theme(theme)
         | 
| 156 | 
            +
                        @layers << theme
         | 
| 157 | 
            +
                        
         | 
| 158 | 
            +
                        @current_context = nil
         | 
| 159 | 
            +
                        
         | 
| 160 | 
            +
                        return self
         | 
| 161 | 
            +
                    end
         | 
| 162 | 
            +
                    
         | 
| 163 | 
            +
                    def flip_coordinates
         | 
| 164 | 
            +
                        @layers << FlipCoordinates.new
         | 
| 165 | 
            +
                        
         | 
| 166 | 
            +
                        @current_context = nil
         | 
| 167 | 
            +
                        
         | 
| 168 | 
            +
                        return self
         | 
| 169 | 
            +
                    end
         | 
| 170 | 
            +
                    
         | 
| 171 | 
            +
                    def build
         | 
| 172 | 
            +
                        plot = Plot.new(@data, Aes.new(**@aes_options))
         | 
| 173 | 
            +
                        plot.theme = @theme if @theme
         | 
| 174 | 
            +
                        plot << @layers if @layers.size > 0
         | 
| 175 | 
            +
                        if @label_options.size > 0
         | 
| 176 | 
            +
                            if @label_options.keys.include?(:group)
         | 
| 177 | 
            +
                                value = @label_options.delete(:group)
         | 
| 178 | 
            +
                                selected = [:x, :y] - @label_options.keys
         | 
| 179 | 
            +
                                @label_options[selected.first] = value if selected.size == 1
         | 
| 180 | 
            +
                            end
         | 
| 181 | 
            +
                            
         | 
| 182 | 
            +
                            plot << Labels.new(**@label_options)
         | 
| 183 | 
            +
                        end
         | 
| 184 | 
            +
                        
         | 
| 185 | 
            +
                        return plot
         | 
| 186 | 
            +
                    end
         | 
| 187 | 
            +
                end
         | 
| 188 | 
            +
            end
         | 
| @@ -0,0 +1,435 @@ | |
| 1 | 
            +
            require 'json'
         | 
| 2 | 
            +
            require_relative 'core'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Rust::Plots::GGPlot
         | 
| 5 | 
            +
                class Theme < Layer
         | 
| 6 | 
            +
                    def self.from_h(options)            
         | 
| 7 | 
            +
                        starting = options.delete('_starting')
         | 
| 8 | 
            +
                        options = options.map do |key, value|
         | 
| 9 | 
            +
                            if value.is_a?(Hash)
         | 
| 10 | 
            +
                                case value.delete('_type').split("::").last
         | 
| 11 | 
            +
                                when 'TextElement'
         | 
| 12 | 
            +
                                    [key, TextElement.new(**value)]
         | 
| 13 | 
            +
                                when 'RectElement'
         | 
| 14 | 
            +
                                    [key, RectElement.new(**value)]
         | 
| 15 | 
            +
                                when 'TextElement'
         | 
| 16 | 
            +
                                    [key, TextElement.new(**value)]
         | 
| 17 | 
            +
                                when 'LineElement'
         | 
| 18 | 
            +
                                    [key, LineElement.new(**value)]
         | 
| 19 | 
            +
                                end
         | 
| 20 | 
            +
                            else
         | 
| 21 | 
            +
                                [key, value]
         | 
| 22 | 
            +
                            end
         | 
| 23 | 
            +
                        end.to_h
         | 
| 24 | 
            +
                        
         | 
| 25 | 
            +
                        return Theme.new(starting, **options)
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
                    
         | 
| 28 | 
            +
                    def self.load(filename)
         | 
| 29 | 
            +
                        json = JSON.parse(File.read(filename))
         | 
| 30 | 
            +
                        return self.from_h(json.to_h)
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                    
         | 
| 33 | 
            +
                    def initialize(starting, **options)
         | 
| 34 | 
            +
                        super("theme", **options)
         | 
| 35 | 
            +
                        if starting
         | 
| 36 | 
            +
                            @starting = "theme_" + starting
         | 
| 37 | 
            +
                        end
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
                    
         | 
| 40 | 
            +
                    def to_R
         | 
| 41 | 
            +
                        result = super do |options, arguments| 
         | 
| 42 | 
            +
                            [
         | 
| 43 | 
            +
                                options.map { |k, v| [k.to_s.gsub("_", "."), v] }.to_h,
         | 
| 44 | 
            +
                                arguments
         | 
| 45 | 
            +
                            ] 
         | 
| 46 | 
            +
                        end
         | 
| 47 | 
            +
                        
         | 
| 48 | 
            +
                        result = Rust::Function.new(@starting).to_R + " + " +  result if @starting
         | 
| 49 | 
            +
                        
         | 
| 50 | 
            +
                        return result
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                    
         | 
| 53 | 
            +
                    def to_h
         | 
| 54 | 
            +
                        options = @options.clone
         | 
| 55 | 
            +
                        
         | 
| 56 | 
            +
                        options['_starting'] = @starting.sub("theme_", "")
         | 
| 57 | 
            +
                        options = options.map do |key, value|
         | 
| 58 | 
            +
                            [key, value.is_a?(Theme::Element) ? value.to_h : value]
         | 
| 59 | 
            +
                        end.to_h
         | 
| 60 | 
            +
                        
         | 
| 61 | 
            +
                        return options
         | 
| 62 | 
            +
                    end
         | 
| 63 | 
            +
                    
         | 
| 64 | 
            +
                    def save(path, force = false)
         | 
| 65 | 
            +
                        if !force && FileTest.exist?(path)
         | 
| 66 | 
            +
                            raise "File already existing."
         | 
| 67 | 
            +
                        end
         | 
| 68 | 
            +
                        
         | 
| 69 | 
            +
                        File.open(path, "w") do |f|
         | 
| 70 | 
            +
                            f.write(
         | 
| 71 | 
            +
                                JSON.pretty_generate(
         | 
| 72 | 
            +
                                    self.to_h
         | 
| 73 | 
            +
                                )
         | 
| 74 | 
            +
                            )
         | 
| 75 | 
            +
                        end
         | 
| 76 | 
            +
                        
         | 
| 77 | 
            +
                        return true
         | 
| 78 | 
            +
                    end
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
                
         | 
| 81 | 
            +
                class Theme::Element
         | 
| 82 | 
            +
                    attr_reader :options
         | 
| 83 | 
            +
                    
         | 
| 84 | 
            +
                    def initialize(**options)
         | 
| 85 | 
            +
                        @options = options
         | 
| 86 | 
            +
                    end
         | 
| 87 | 
            +
                    
         | 
| 88 | 
            +
                    def r_function
         | 
| 89 | 
            +
                        raise "Not implemented for generic theme element"
         | 
| 90 | 
            +
                    end
         | 
| 91 | 
            +
                    
         | 
| 92 | 
            +
                    def to_R
         | 
| 93 | 
            +
                        options = @options.map { |k, v| [k.to_s.gsub("_", "."), v] }.to_h
         | 
| 94 | 
            +
                        
         | 
| 95 | 
            +
                        function = Rust::Function.new(self.r_function)
         | 
| 96 | 
            +
                        function.options = Rust::Options.from_hash(options)
         | 
| 97 | 
            +
                        
         | 
| 98 | 
            +
                        return function.to_R
         | 
| 99 | 
            +
                    end
         | 
| 100 | 
            +
                    
         | 
| 101 | 
            +
                    def to_h
         | 
| 102 | 
            +
                        hash = @options.clone
         | 
| 103 | 
            +
                        hash['_type'] = self.class.name
         | 
| 104 | 
            +
                        return hash
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
                
         | 
| 108 | 
            +
                class Theme::TextElement < Theme::Element
         | 
| 109 | 
            +
                    def r_function
         | 
| 110 | 
            +
                        return "element_text"
         | 
| 111 | 
            +
                    end
         | 
| 112 | 
            +
                end
         | 
| 113 | 
            +
                
         | 
| 114 | 
            +
                class Theme::LineElement < Theme::Element
         | 
| 115 | 
            +
                    def r_function
         | 
| 116 | 
            +
                        return "element_line"
         | 
| 117 | 
            +
                    end
         | 
| 118 | 
            +
                end
         | 
| 119 | 
            +
                
         | 
| 120 | 
            +
                class Theme::RectElement < Theme::Element
         | 
| 121 | 
            +
                    def r_function
         | 
| 122 | 
            +
                        return "element_rect"
         | 
| 123 | 
            +
                    end
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
                
         | 
| 126 | 
            +
                class Theme::BlankElement < Theme::Element
         | 
| 127 | 
            +
                    def r_function
         | 
| 128 | 
            +
                        return "element_blank"
         | 
| 129 | 
            +
                    end
         | 
| 130 | 
            +
                end
         | 
| 131 | 
            +
                
         | 
| 132 | 
            +
                class ThemeComponentBuilder
         | 
| 133 | 
            +
                    def initialize(namespace=nil)
         | 
| 134 | 
            +
                        @namespace = namespace
         | 
| 135 | 
            +
                        @options = {}
         | 
| 136 | 
            +
                    end
         | 
| 137 | 
            +
                    
         | 
| 138 | 
            +
                    def option(key, value)
         | 
| 139 | 
            +
                        key = "#@namespace.#{key}" if @namespace
         | 
| 140 | 
            +
                        @options[key] = value
         | 
| 141 | 
            +
                        
         | 
| 142 | 
            +
                        return self
         | 
| 143 | 
            +
                    end
         | 
| 144 | 
            +
                    
         | 
| 145 | 
            +
                    def [](key)
         | 
| 146 | 
            +
                        key = "#@namespace.#{key}" if @namespace
         | 
| 147 | 
            +
                        return @options[key]
         | 
| 148 | 
            +
                    end
         | 
| 149 | 
            +
                    
         | 
| 150 | 
            +
                    def line_el(value)
         | 
| 151 | 
            +
                        if value.is_a?(Theme::LineElement) || value.is_a?(Theme::BlankElement)
         | 
| 152 | 
            +
                            return value
         | 
| 153 | 
            +
                        elsif value.is_a?(Hash)
         | 
| 154 | 
            +
                            return Theme::LineElement.new(**value)
         | 
| 155 | 
            +
                        else
         | 
| 156 | 
            +
                            raise "Expected line or hash"
         | 
| 157 | 
            +
                        end
         | 
| 158 | 
            +
                    end
         | 
| 159 | 
            +
                    
         | 
| 160 | 
            +
                    def rect_el(value)
         | 
| 161 | 
            +
                        if value.is_a?(Theme::RectElement) || value.is_a?(Theme::BlankElement)
         | 
| 162 | 
            +
                            return value
         | 
| 163 | 
            +
                        elsif value.is_a?(Hash)
         | 
| 164 | 
            +
                            return Theme::RectElement.new(**value)
         | 
| 165 | 
            +
                        else
         | 
| 166 | 
            +
                            raise "Expected rect or hash"
         | 
| 167 | 
            +
                        end
         | 
| 168 | 
            +
                    end
         | 
| 169 | 
            +
                    
         | 
| 170 | 
            +
                    def text_el(value)
         | 
| 171 | 
            +
                        if value.is_a?(Theme::TextElement) || value.is_a?(Theme::BlankElement)
         | 
| 172 | 
            +
                            return value
         | 
| 173 | 
            +
                        elsif value.is_a?(Hash)
         | 
| 174 | 
            +
                            return Theme::TextElement.new(**value)
         | 
| 175 | 
            +
                        else
         | 
| 176 | 
            +
                            raise "Expected text or hash"
         | 
| 177 | 
            +
                        end
         | 
| 178 | 
            +
                    end
         | 
| 179 | 
            +
                    
         | 
| 180 | 
            +
                    def unit_el(value)
         | 
| 181 | 
            +
                        numeric = nil
         | 
| 182 | 
            +
                        unit = nil
         | 
| 183 | 
            +
                        
         | 
| 184 | 
            +
                        if input.is_a?(String)
         | 
| 185 | 
            +
                            numeric, unit = *input.scan(/^([0-9.]+)([A-Za-z]+)/).flatten
         | 
| 186 | 
            +
                            
         | 
| 187 | 
            +
                            raise "Unclear numeric part in #{input}" unless numeric
         | 
| 188 | 
            +
                            raise "Unclear unit part in #{input}"    unless unit
         | 
| 189 | 
            +
                        elsif input.is_a?(Numeric)
         | 
| 190 | 
            +
                            numeric = input
         | 
| 191 | 
            +
                            unit = "npc"
         | 
| 192 | 
            +
                        end
         | 
| 193 | 
            +
                        
         | 
| 194 | 
            +
                        raise "Unable to handle #{input}" unless numeric && unit
         | 
| 195 | 
            +
                        
         | 
| 196 | 
            +
                        function = Rust::Function.new("units")
         | 
| 197 | 
            +
                        function.arguments = Rust::Arguments.new([numeric, unit])
         | 
| 198 | 
            +
                        
         | 
| 199 | 
            +
                        return function.to_R
         | 
| 200 | 
            +
                    end
         | 
| 201 | 
            +
                    
         | 
| 202 | 
            +
                    def alignment_el(value)
         | 
| 203 | 
            +
                        if value.is_a?(String) || value.is_a?(Symbol)
         | 
| 204 | 
            +
                            case value.to_s.downcase
         | 
| 205 | 
            +
                            when 'left'
         | 
| 206 | 
            +
                                value = 1
         | 
| 207 | 
            +
                            when 'right'
         | 
| 208 | 
            +
                                value = 0
         | 
| 209 | 
            +
                            else
         | 
| 210 | 
            +
                                value = 1
         | 
| 211 | 
            +
                            end
         | 
| 212 | 
            +
                        end
         | 
| 213 | 
            +
                        
         | 
| 214 | 
            +
                        return value
         | 
| 215 | 
            +
                    end
         | 
| 216 | 
            +
                    
         | 
| 217 | 
            +
                    def numeric_el(value)
         | 
| 218 | 
            +
                        raise "Expected number" unless value.is_a?(Numeric)
         | 
| 219 | 
            +
                        return value
         | 
| 220 | 
            +
                    end
         | 
| 221 | 
            +
                    
         | 
| 222 | 
            +
                    def build
         | 
| 223 | 
            +
                        @options
         | 
| 224 | 
            +
                    end
         | 
| 225 | 
            +
                end
         | 
| 226 | 
            +
                
         | 
| 227 | 
            +
                class ThemeBuilder < ThemeComponentBuilder
         | 
| 228 | 
            +
                    def initialize(starting = 'bw')
         | 
| 229 | 
            +
                        super("plot")
         | 
| 230 | 
            +
                        @starting = starting
         | 
| 231 | 
            +
                    end
         | 
| 232 | 
            +
                    
         | 
| 233 | 
            +
                    def background(value)
         | 
| 234 | 
            +
                        self.option('background', rect_el(value))
         | 
| 235 | 
            +
                    end
         | 
| 236 | 
            +
                    
         | 
| 237 | 
            +
                    def title(value)
         | 
| 238 | 
            +
                        self.option('title', text_el(value))
         | 
| 239 | 
            +
                    end
         | 
| 240 | 
            +
                    
         | 
| 241 | 
            +
                    def axis
         | 
| 242 | 
            +
                        builder = ThemeAxisBuilder.new
         | 
| 243 | 
            +
                        yield builder
         | 
| 244 | 
            +
                        
         | 
| 245 | 
            +
                        @options.merge!(builder.build)
         | 
| 246 | 
            +
                        return self
         | 
| 247 | 
            +
                    end
         | 
| 248 | 
            +
                    
         | 
| 249 | 
            +
                    def legend
         | 
| 250 | 
            +
                        builder = ThemeLegendBuilder.new
         | 
| 251 | 
            +
                        yield builder
         | 
| 252 | 
            +
                        
         | 
| 253 | 
            +
                        @options.merge!(builder.build)
         | 
| 254 | 
            +
                        return self
         | 
| 255 | 
            +
                    end
         | 
| 256 | 
            +
                    
         | 
| 257 | 
            +
                    def panel
         | 
| 258 | 
            +
                        builder = ThemePanelBuilder.new
         | 
| 259 | 
            +
                        yield builder
         | 
| 260 | 
            +
                        
         | 
| 261 | 
            +
                        @options.merge!(builder.build)
         | 
| 262 | 
            +
                        return self
         | 
| 263 | 
            +
                    end
         | 
| 264 | 
            +
                    
         | 
| 265 | 
            +
                    def build
         | 
| 266 | 
            +
                        return Theme.new(@starting, **@options)
         | 
| 267 | 
            +
                    end
         | 
| 268 | 
            +
                end
         | 
| 269 | 
            +
                
         | 
| 270 | 
            +
                class ThemeAxisBuilder < ThemeComponentBuilder
         | 
| 271 | 
            +
                    def initialize
         | 
| 272 | 
            +
                        super("axis")
         | 
| 273 | 
            +
                    end
         | 
| 274 | 
            +
                    
         | 
| 275 | 
            +
                    def line(value)
         | 
| 276 | 
            +
                        self.option('line', line_el(value))
         | 
| 277 | 
            +
                    end
         | 
| 278 | 
            +
                    
         | 
| 279 | 
            +
                    def text(value)
         | 
| 280 | 
            +
                        self.option('text', text_el(value))
         | 
| 281 | 
            +
                    end
         | 
| 282 | 
            +
                    
         | 
| 283 | 
            +
                    def text_x(value)
         | 
| 284 | 
            +
                        self.option('text.x', text_el(value))
         | 
| 285 | 
            +
                    end
         | 
| 286 | 
            +
                    
         | 
| 287 | 
            +
                    def text_y(value)
         | 
| 288 | 
            +
                        self.option('text.y', text_el(value))
         | 
| 289 | 
            +
                    end
         | 
| 290 | 
            +
                    
         | 
| 291 | 
            +
                    def title(value)
         | 
| 292 | 
            +
                        self.option('title', text_el(value))
         | 
| 293 | 
            +
                    end
         | 
| 294 | 
            +
                    
         | 
| 295 | 
            +
                    def title_x(value)
         | 
| 296 | 
            +
                        self.option('title.x', text_el(value))
         | 
| 297 | 
            +
                    end
         | 
| 298 | 
            +
                    
         | 
| 299 | 
            +
                    def title_y(value)
         | 
| 300 | 
            +
                        self.option('title.y', text_el(value))
         | 
| 301 | 
            +
                    end
         | 
| 302 | 
            +
                    
         | 
| 303 | 
            +
                    def ticks(value)
         | 
| 304 | 
            +
                        self.option('ticks', line_el(value))
         | 
| 305 | 
            +
                    end
         | 
| 306 | 
            +
                    
         | 
| 307 | 
            +
                    def ticks_length(value)
         | 
| 308 | 
            +
                        self.option('ticks.length', unit_el(value))
         | 
| 309 | 
            +
                    end
         | 
| 310 | 
            +
                end
         | 
| 311 | 
            +
                
         | 
| 312 | 
            +
                class ThemeLegendBuilder < ThemeComponentBuilder
         | 
| 313 | 
            +
                    def initialize
         | 
| 314 | 
            +
                        super("legend")
         | 
| 315 | 
            +
                    end
         | 
| 316 | 
            +
                    
         | 
| 317 | 
            +
                    def position(value)
         | 
| 318 | 
            +
                        self.option('position', value)
         | 
| 319 | 
            +
                    end
         | 
| 320 | 
            +
                    
         | 
| 321 | 
            +
                    def justification(value)
         | 
| 322 | 
            +
                        self.option('justification', value)
         | 
| 323 | 
            +
                    end
         | 
| 324 | 
            +
                    
         | 
| 325 | 
            +
                    def background(value)
         | 
| 326 | 
            +
                        self.option('background', rect_el(value))
         | 
| 327 | 
            +
                    end
         | 
| 328 | 
            +
                    
         | 
| 329 | 
            +
                    def key_background(value)
         | 
| 330 | 
            +
                        self.option('key', rect_el(value))
         | 
| 331 | 
            +
                    end
         | 
| 332 | 
            +
                    
         | 
| 333 | 
            +
                    def key_size(value)
         | 
| 334 | 
            +
                        self.option('key.size', unit_el(value))
         | 
| 335 | 
            +
                    end
         | 
| 336 | 
            +
                    
         | 
| 337 | 
            +
                    def key_height(value)
         | 
| 338 | 
            +
                        self.option('key.height', unit_el(value))
         | 
| 339 | 
            +
                    end
         | 
| 340 | 
            +
                    
         | 
| 341 | 
            +
                    def key_width(value)
         | 
| 342 | 
            +
                        self.option('key.width', unit_el(value))
         | 
| 343 | 
            +
                    end
         | 
| 344 | 
            +
                    
         | 
| 345 | 
            +
                    def margin(value)
         | 
| 346 | 
            +
                        self.option('margin', unit_el(value))
         | 
| 347 | 
            +
                    end
         | 
| 348 | 
            +
                    
         | 
| 349 | 
            +
                    def text(value)
         | 
| 350 | 
            +
                        self.option('text', text_el(value))
         | 
| 351 | 
            +
                    end
         | 
| 352 | 
            +
                    
         | 
| 353 | 
            +
                    def text_align(value)
         | 
| 354 | 
            +
                        self.option('text.align', alignment_el(value))
         | 
| 355 | 
            +
                    end
         | 
| 356 | 
            +
                    
         | 
| 357 | 
            +
                    def title(value)
         | 
| 358 | 
            +
                        self.option('title', text_el(value))
         | 
| 359 | 
            +
                    end
         | 
| 360 | 
            +
                    
         | 
| 361 | 
            +
                    def title_align(value)
         | 
| 362 | 
            +
                        self.option('key.size', alignment_el(value))
         | 
| 363 | 
            +
                    end
         | 
| 364 | 
            +
                end
         | 
| 365 | 
            +
                
         | 
| 366 | 
            +
                class ThemePanelBuilder < ThemeComponentBuilder
         | 
| 367 | 
            +
                    def initialize
         | 
| 368 | 
            +
                        super("panel")
         | 
| 369 | 
            +
                    end
         | 
| 370 | 
            +
                    
         | 
| 371 | 
            +
                    def background(value)
         | 
| 372 | 
            +
                        self.option('background', rect_el(value))
         | 
| 373 | 
            +
                    end
         | 
| 374 | 
            +
                    
         | 
| 375 | 
            +
                    def border(value)
         | 
| 376 | 
            +
                        self.option('border', rect_el(value))
         | 
| 377 | 
            +
                    end
         | 
| 378 | 
            +
                    
         | 
| 379 | 
            +
                    def grid_major(value)
         | 
| 380 | 
            +
                        self.option('grid.major', line_el(value))
         | 
| 381 | 
            +
                    end
         | 
| 382 | 
            +
                    
         | 
| 383 | 
            +
                    def grid_major_x(value)
         | 
| 384 | 
            +
                        self.option('grid.major.x', line_el(value))
         | 
| 385 | 
            +
                    end
         | 
| 386 | 
            +
                    
         | 
| 387 | 
            +
                    def grid_major_y(value)
         | 
| 388 | 
            +
                        self.option('grid.major.y', line_el(value))
         | 
| 389 | 
            +
                    end
         | 
| 390 | 
            +
                    
         | 
| 391 | 
            +
                    def grid_minor(value)
         | 
| 392 | 
            +
                        self.option('grid.minor', line_el(value))
         | 
| 393 | 
            +
                    end
         | 
| 394 | 
            +
                    
         | 
| 395 | 
            +
                    def grid_minor_x(value)
         | 
| 396 | 
            +
                        self.option('grid.minor.x', line_el(value))
         | 
| 397 | 
            +
                    end
         | 
| 398 | 
            +
                    
         | 
| 399 | 
            +
                    def grid_minor_y(value)
         | 
| 400 | 
            +
                        self.option('grid.minor.y', line_el(value))
         | 
| 401 | 
            +
                    end
         | 
| 402 | 
            +
                    
         | 
| 403 | 
            +
                    def aspect_ratio(value)
         | 
| 404 | 
            +
                        self.option('aspect.ratio', numeric_el(value))
         | 
| 405 | 
            +
                    end
         | 
| 406 | 
            +
                    
         | 
| 407 | 
            +
                    def margin(value)
         | 
| 408 | 
            +
                        self.option('margin', unit_el(value))
         | 
| 409 | 
            +
                    end
         | 
| 410 | 
            +
                    
         | 
| 411 | 
            +
                    def margin_x(value)
         | 
| 412 | 
            +
                        self.option('margin.x', unit_el(value))
         | 
| 413 | 
            +
                    end
         | 
| 414 | 
            +
                    
         | 
| 415 | 
            +
                    def margin_y(value)
         | 
| 416 | 
            +
                        self.option('margin.y', unit_el(value))
         | 
| 417 | 
            +
                    end
         | 
| 418 | 
            +
                end
         | 
| 419 | 
            +
                
         | 
| 420 | 
            +
                self.default_theme = ThemeBuilder.new.
         | 
| 421 | 
            +
                        title(face: 'bold', size: 12).
         | 
| 422 | 
            +
                        legend do |legend|
         | 
| 423 | 
            +
                            legend.background(fill: 'white', size: 4, colour: 'white')
         | 
| 424 | 
            +
                            legend.position([0, 1])
         | 
| 425 | 
            +
                            legend.justification([0, 1])
         | 
| 426 | 
            +
                        end.
         | 
| 427 | 
            +
                        axis do |axis| 
         | 
| 428 | 
            +
                            axis.ticks(colour: 'grey70', size: 0.2)
         | 
| 429 | 
            +
                        end.
         | 
| 430 | 
            +
                        panel do |panel|
         | 
| 431 | 
            +
                            panel.grid_major(colour: 'grey70', size: 0.2)
         | 
| 432 | 
            +
                            panel.grid_minor(Theme::BlankElement.new)
         | 
| 433 | 
            +
                        end.
         | 
| 434 | 
            +
                        build
         | 
| 435 | 
            +
            end
         | 
    
        data/lib/rust.rb
    CHANGED
    
    | @@ -2,3 +2,22 @@ require_relative 'rust/core' | |
| 2 2 | 
             
            require_relative 'rust/models/all'
         | 
| 3 3 | 
             
            require_relative 'rust/plots/all'
         | 
| 4 4 | 
             
            require_relative 'rust/stats/all'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Rust
         | 
| 7 | 
            +
                @@datasets = {}
         | 
| 8 | 
            +
                
         | 
| 9 | 
            +
                def self.toothgrowth
         | 
| 10 | 
            +
                    @@datasets[:ToothGrowth] = Rust.exclusive { Rust['ToothGrowth'] } unless @@datasets[:ToothGrowth]
         | 
| 11 | 
            +
                    return @@datasets[:ToothGrowth]
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
                
         | 
| 14 | 
            +
                def self.cars
         | 
| 15 | 
            +
                    @@datasets[:cars] = Rust.exclusive { Rust['cars'] } unless @@datasets[:cars]
         | 
| 16 | 
            +
                    return @@datasets[:cars]
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
                
         | 
| 19 | 
            +
                def self.iris
         | 
| 20 | 
            +
                    @@datasets[:iris] = Rust.exclusive { Rust['iris'] } unless @@datasets[:iris]
         | 
| 21 | 
            +
                    return @@datasets[:iris]
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: rust
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: '0. | 
| 4 | 
            +
              version: '0.12'
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Simone Scalabrino
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2022-08- | 
| 11 | 
            +
            date: 2022-08-17 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: rinruby
         | 
| @@ -71,6 +71,12 @@ files: | |
| 71 71 | 
             
            - lib/rust/core/types/matrix.rb
         | 
| 72 72 | 
             
            - lib/rust/core/types/s4class.rb
         | 
| 73 73 | 
             
            - lib/rust/core/types/utils.rb
         | 
| 74 | 
            +
            - lib/rust/external/ggplot2.rb
         | 
| 75 | 
            +
            - lib/rust/external/ggplot2/core.rb
         | 
| 76 | 
            +
            - lib/rust/external/ggplot2/geoms.rb
         | 
| 77 | 
            +
            - lib/rust/external/ggplot2/helper.rb
         | 
| 78 | 
            +
            - lib/rust/external/ggplot2/plot_builder.rb
         | 
| 79 | 
            +
            - lib/rust/external/ggplot2/themes.rb
         | 
| 74 80 | 
             
            - lib/rust/external/robustbase.rb
         | 
| 75 81 | 
             
            - lib/rust/models/all.rb
         | 
| 76 82 | 
             
            - lib/rust/models/anova.rb
         |