Module: Polars::Plot

Included in:
DataFrame
Defined in:
lib/polars/plot.rb

Instance Method Summary collapse

Instance Method Details

#plot(x = nil, y = nil, type: nil, group: nil, stacked: nil) ⇒ Vega::LiteChart

Plot data.

Returns:

  • (Vega::LiteChart)

Raises:

  • (ArgumentError)


6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/polars/plot.rb', line 6

def plot(x = nil, y = nil, type: nil, group: nil, stacked: nil)
  require "vega"

  raise ArgumentError, "Must specify columns" if columns.size != 2 && (!x || !y)
  x ||= columns[0]
  y ||= columns[1]
  type ||= begin
    if self[x].numeric? && self[y].numeric?
      "scatter"
    elsif self[x].utf8? && self[y].numeric?
      "column"
    elsif (self[x].dtype == Date || self[x].dtype.is_a?(Datetime)) && self[y].numeric?
      "line"
    else
      raise "Cannot determine type. Use the type option."
    end
  end
  df = self[(group.nil? ? [x, y] : [x, y, group]).map(&:to_s).uniq]
  data = df.rows(named: true)

  case type
  when "line", "area"
    x_type =
      if df[x].numeric?
        "quantitative"
      elsif df[x].datelike?
        "temporal"
      else
        "nominal"
      end

    scale = x_type == "temporal" ? {type: "utc"} : {}
    encoding = {
      x: {field: x, type: x_type, scale: scale},
      y: {field: y, type: "quantitative"}
    }
    encoding[:color] = {field: group} if group

    Vega.lite
      .data(data)
      .mark(type: type, tooltip: true, interpolate: "cardinal", point: {size: 60})
      .encoding(encoding)
      .config(axis: {labelFontSize: 12})
  when "pie"
    raise ArgumentError, "Cannot use group option with pie chart" unless group.nil?

    Vega.lite
      .data(data)
      .mark(type: "arc", tooltip: true)
      .encoding(
        color: {field: x, type: "nominal", sort: "none", axis: {title: nil}, legend: {labelFontSize: 12}},
        theta: {field: y, type: "quantitative"}
      )
      .view(stroke: nil)
  when "column"
    encoding = {
      x: {field: x, type: "nominal", sort: "none", axis: {labelAngle: 0}},
      y: {field: y, type: "quantitative"}
    }
    if group
      encoding[:color] = {field: group}
      encoding[:xOffset] = {field: group} unless stacked
    end

    Vega.lite
      .data(data)
      .mark(type: "bar", tooltip: true)
      .encoding(encoding)
      .config(axis: {labelFontSize: 12})
  when "bar"
    encoding = {
      # TODO determine label angle
      y: {field: x, type: "nominal", sort: "none", axis: {labelAngle: 0}},
      x: {field: y, type: "quantitative"}
    }
    if group
      encoding[:color] = {field: group}
      encoding[:yOffset] = {field: group} unless stacked
    end

    Vega.lite
      .data(data)
      .mark(type: "bar", tooltip: true)
      .encoding(encoding)
      .config(axis: {labelFontSize: 12})
  when "scatter"
    encoding = {
      x: {field: x, type: "quantitative", scale: {zero: false}},
      y: {field: y, type: "quantitative", scale: {zero: false}},
      size: {value: 60}
    }
    encoding[:color] = {field: group} if group

    Vega.lite
      .data(data)
      .mark(type: "circle", tooltip: true)
      .encoding(encoding)
      .config(axis: {labelFontSize: 12})
  else
    raise ArgumentError, "Invalid type: #{type}"
  end
end