charty 0.2.5 → 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -1
- data/charty.gemspec +8 -5
- data/lib/charty.rb +2 -2
- data/lib/charty/backends/plotly.rb +197 -8
- data/lib/charty/backends/pyplot.rb +135 -44
- data/lib/charty/dash_pattern_generator.rb +57 -0
- data/lib/charty/index.rb +1 -1
- data/lib/charty/plot_methods.rb +73 -3
- data/lib/charty/plotters.rb +1 -0
- data/lib/charty/plotters/abstract_plotter.rb +69 -9
- data/lib/charty/plotters/line_plotter.rb +300 -0
- data/lib/charty/plotters/relational_plotter.rb +213 -96
- data/lib/charty/plotters/scatter_plotter.rb +7 -31
- data/lib/charty/statistics.rb +2 -2
- data/lib/charty/table.rb +124 -14
- data/lib/charty/table_adapters/base_adapter.rb +97 -0
- data/lib/charty/table_adapters/daru_adapter.rb +2 -0
- data/lib/charty/table_adapters/datasets_adapter.rb +7 -0
- data/lib/charty/table_adapters/hash_adapter.rb +13 -2
- data/lib/charty/table_adapters/pandas_adapter.rb +82 -0
- data/lib/charty/util.rb +8 -0
- data/lib/charty/vector_adapters.rb +5 -1
- data/lib/charty/vector_adapters/array_adapter.rb +2 -10
- data/lib/charty/vector_adapters/daru_adapter.rb +3 -11
- data/lib/charty/vector_adapters/narray_adapter.rb +1 -6
- data/lib/charty/vector_adapters/numpy_adapter.rb +1 -1
- data/lib/charty/vector_adapters/pandas_adapter.rb +0 -1
- data/lib/charty/version.rb +1 -1
- metadata +54 -25
- data/lib/charty/missing_value_support.rb +0 -14
@@ -6,7 +6,7 @@ module Charty
|
|
6
6
|
super(x, y, color, style, size, data: data, **options, &block)
|
7
7
|
end
|
8
8
|
|
9
|
-
attr_reader :alpha
|
9
|
+
attr_reader :alpha
|
10
10
|
|
11
11
|
def alpha=(val)
|
12
12
|
case val
|
@@ -25,18 +25,6 @@ module Charty
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def legend=(val)
|
29
|
-
case val
|
30
|
-
when :auto, :brief, :full, false
|
31
|
-
@legend = val
|
32
|
-
when "auto", "brief", "full"
|
33
|
-
@legend = val.to_sym
|
34
|
-
else
|
35
|
-
raise ArgumentError,
|
36
|
-
"invalid value of legend (%p for :auto, :brief, :full, or false)" % val
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
28
|
attr_reader :line_width, :edge_color
|
41
29
|
|
42
30
|
def line_width=(val)
|
@@ -55,7 +43,7 @@ module Charty
|
|
55
43
|
private def draw_points(backend)
|
56
44
|
map_color(palette: palette, order: color_order, norm: color_norm)
|
57
45
|
map_size(sizes: sizes, order: size_order, norm: size_norm)
|
58
|
-
map_style(markers: markers, order:
|
46
|
+
map_style(markers: markers, order: style_order)
|
59
47
|
|
60
48
|
data = @plot_data.drop_na
|
61
49
|
|
@@ -73,9 +61,12 @@ module Charty
|
|
73
61
|
x, y, @variables,
|
74
62
|
color: color, color_mapper: @color_mapper,
|
75
63
|
style: style, style_mapper: @style_mapper,
|
76
|
-
size: size, size_mapper: @size_mapper
|
77
|
-
legend: legend
|
64
|
+
size: size, size_mapper: @size_mapper
|
78
65
|
)
|
66
|
+
|
67
|
+
if legend
|
68
|
+
backend.add_scatter_plot_legend(@variables, @color_mapper, @size_mapper, @style_mapper, legend)
|
69
|
+
end
|
79
70
|
end
|
80
71
|
|
81
72
|
private def annotate_axes(backend)
|
@@ -83,21 +74,6 @@ module Charty
|
|
83
74
|
ylabel = self.variables[:y]
|
84
75
|
backend.set_xlabel(xlabel) unless xlabel.nil?
|
85
76
|
backend.set_ylabel(ylabel) unless ylabel.nil?
|
86
|
-
|
87
|
-
if legend
|
88
|
-
add_legend_data(backend)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
private def add_legend_data(backend)
|
93
|
-
# TODO: Legend Support
|
94
|
-
verbosity = legend
|
95
|
-
verbosity = :auto if verbosity == true
|
96
|
-
|
97
|
-
titles = Util.filter_map([:color, :size, :style]) do |v|
|
98
|
-
variables[v] if variables.key?(v)
|
99
|
-
end
|
100
|
-
legend_title = titles.length == 1 ? titles[0] : ""
|
101
77
|
end
|
102
78
|
end
|
103
79
|
end
|
data/lib/charty/statistics.rb
CHANGED
@@ -83,9 +83,9 @@ module Charty
|
|
83
83
|
"structured bootstrapping has not been supported yet"
|
84
84
|
end
|
85
85
|
|
86
|
-
def self.bootstrap_ci(*vectors,
|
86
|
+
def self.bootstrap_ci(*vectors, width, n_boot: 2000, func: :mean, units: nil, random: nil)
|
87
87
|
boot = bootstrap(*vectors, n_boot: n_boot, func: func, units: units, random: random)
|
88
|
-
q = [50 -
|
88
|
+
q = [50 - width / 2, 50 + width / 2]
|
89
89
|
if boot.respond_to?(:percentile)
|
90
90
|
boot.percentile(q)
|
91
91
|
else
|
data/lib/charty/table.rb
CHANGED
@@ -13,7 +13,6 @@ module Charty
|
|
13
13
|
|
14
14
|
class Table
|
15
15
|
extend Forwardable
|
16
|
-
include MissingValueSupport
|
17
16
|
|
18
17
|
def initialize(data, **kwargs)
|
19
18
|
adapter_class = TableAdapters.find_adapter_class(data)
|
@@ -34,6 +33,20 @@ module Charty
|
|
34
33
|
def_delegators :adapter, :index, :index=
|
35
34
|
|
36
35
|
def_delegator :@adapter, :column_names
|
36
|
+
|
37
|
+
def column?(name)
|
38
|
+
return true if column_names.include?(name)
|
39
|
+
|
40
|
+
case name
|
41
|
+
when String
|
42
|
+
column_names.include?(name.to_sym)
|
43
|
+
when Symbol
|
44
|
+
column_names.include?(name.to_s)
|
45
|
+
else
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
37
50
|
def_delegator :@adapter, :data, :raw_data
|
38
51
|
|
39
52
|
def ==(other)
|
@@ -65,6 +78,10 @@ module Charty
|
|
65
78
|
end
|
66
79
|
end
|
67
80
|
|
81
|
+
def group_by(grouper, sort: true, drop_na: true)
|
82
|
+
adapter.group_by(self, grouper, sort, drop_na)
|
83
|
+
end
|
84
|
+
|
68
85
|
def to_a(x=nil, y=nil, z=nil)
|
69
86
|
case
|
70
87
|
when defined?(Daru::DataFrame) && table.kind_of?(Daru::DataFrame)
|
@@ -99,21 +116,114 @@ module Charty
|
|
99
116
|
end
|
100
117
|
|
101
118
|
def drop_na
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
119
|
+
@adapter.drop_na || self
|
120
|
+
end
|
121
|
+
|
122
|
+
def_delegator :adapter, :sort_values
|
123
|
+
|
124
|
+
def_delegator :adapter, :reset_index
|
125
|
+
|
126
|
+
class GroupByBase
|
127
|
+
end
|
128
|
+
|
129
|
+
class HashGroupBy < GroupByBase
|
130
|
+
def initialize(table, grouper, sort, drop_na)
|
131
|
+
@table = table
|
132
|
+
@grouper = check_grouper(grouper)
|
133
|
+
init_groups(sort, drop_na)
|
134
|
+
end
|
135
|
+
|
136
|
+
private def check_grouper(grouper)
|
137
|
+
case grouper
|
138
|
+
when Symbol, String, Array
|
139
|
+
# TODO check column existence
|
140
|
+
return grouper
|
141
|
+
when Charty::Vector
|
142
|
+
if @table.length != grouper.length
|
143
|
+
raise ArgumentError,
|
144
|
+
"Wrong number of items in grouper array " +
|
145
|
+
"(%p for %p)" % [val.length, @table.length]
|
146
|
+
end
|
147
|
+
return grouper
|
148
|
+
when ->(x) { x.respond_to?(:call) }
|
149
|
+
raise NotImplementedError,
|
150
|
+
"A callable grouper is unsupported"
|
151
|
+
else
|
152
|
+
raise ArgumentError,
|
153
|
+
"Unable to recognize the value for `grouper`: %p" % val
|
106
154
|
end
|
107
155
|
end
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
156
|
+
|
157
|
+
private def init_groups(sort, drop_na)
|
158
|
+
case @grouper
|
159
|
+
when Symbol, String
|
160
|
+
column = @table[@grouper]
|
161
|
+
@indices = (0 ... @table.length).group_by do |i|
|
162
|
+
column.data[i]
|
163
|
+
end
|
164
|
+
when Array
|
165
|
+
@indices = (0 ... @table.length).group_by { |i|
|
166
|
+
@grouper.map {|j| @table[j].data[i] }
|
167
|
+
}
|
168
|
+
when Charty::Vector
|
169
|
+
@indices = (0 ... @table.length).group_by do |i|
|
170
|
+
@grouper.data[i]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
if drop_na
|
175
|
+
case @grouper
|
176
|
+
when Array
|
177
|
+
@indices.reject! {|key, | key.any? {|k| Util.missing?(k) } }
|
178
|
+
else
|
179
|
+
@indices.reject! {|key, | Util.missing?(key) }
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
if sort
|
184
|
+
@indices = @indices.sort_by {|key, | key }.to_h
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def indices
|
189
|
+
@indices.dup
|
190
|
+
end
|
191
|
+
|
192
|
+
def group_keys
|
193
|
+
@indices.keys
|
194
|
+
end
|
195
|
+
|
196
|
+
def each_group_key(&block)
|
197
|
+
@indices.each_key(&block)
|
198
|
+
end
|
199
|
+
|
200
|
+
def apply(*args, &block)
|
201
|
+
Charty::Table.new(
|
202
|
+
each_group.map { |_key, table|
|
203
|
+
block.call(table, *args)
|
204
|
+
},
|
205
|
+
index: Charty::Index.new(@indices.keys, name: @grouper)
|
206
|
+
)
|
207
|
+
end
|
208
|
+
|
209
|
+
def each_group
|
210
|
+
return enum_for(__method__) unless block_given?
|
211
|
+
|
212
|
+
@indices.each_key do |key|
|
213
|
+
yield(key, self[key])
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def [](key)
|
218
|
+
return nil unless @indices.key?(key)
|
219
|
+
|
220
|
+
index = @indices[key]
|
221
|
+
Charty::Table.new(
|
222
|
+
@table.column_names.map {|col|
|
223
|
+
[col, @table[col].values_at(*index)]
|
224
|
+
}.to_h,
|
225
|
+
index: index
|
226
|
+
)
|
117
227
|
end
|
118
228
|
end
|
119
229
|
end
|
@@ -33,6 +33,10 @@ module Charty
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
def group_by(table, grouper, sort, drop_na)
|
37
|
+
Table::HashGroupBy.new(table, grouper, sort, drop_na)
|
38
|
+
end
|
39
|
+
|
36
40
|
def compare_data_equality(other)
|
37
41
|
columns.each do |name|
|
38
42
|
if self[nil, name] != other[nil, name]
|
@@ -42,6 +46,99 @@ module Charty
|
|
42
46
|
true
|
43
47
|
end
|
44
48
|
|
49
|
+
def drop_na
|
50
|
+
# TODO: Must implement this method in each adapter
|
51
|
+
missing_index = index.select do |i|
|
52
|
+
column_names.any? do |key|
|
53
|
+
Util.missing?(self[i, key])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
if missing_index.empty?
|
57
|
+
nil
|
58
|
+
else
|
59
|
+
select_index = index.to_a - missing_index
|
60
|
+
new_data = column_names.map { |key|
|
61
|
+
vals = select_index.map {|i| self[i, key] }
|
62
|
+
[key, vals]
|
63
|
+
}.to_h
|
64
|
+
Charty::Table.new(new_data, index: select_index)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def sort_values(by, na_position: :last)
|
69
|
+
na_cmp_val = check_na_position(na_position)
|
70
|
+
case by
|
71
|
+
when String, Symbol
|
72
|
+
order = (0 ... length).sort do |i, j|
|
73
|
+
a = self[i, by]
|
74
|
+
b = self[j, by]
|
75
|
+
case
|
76
|
+
when Util.missing?(a) # missing > b
|
77
|
+
na_cmp_val
|
78
|
+
when Util.missing?(b) # a < missing
|
79
|
+
-na_cmp_val
|
80
|
+
else
|
81
|
+
cmp = a <=> b
|
82
|
+
if cmp == 0
|
83
|
+
i <=> j
|
84
|
+
else
|
85
|
+
cmp
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
when Array
|
90
|
+
order = (0 ... length).sort do |i, j|
|
91
|
+
cmp = 0
|
92
|
+
by.each do |key|
|
93
|
+
a = self[i, key]
|
94
|
+
b = self[j, key]
|
95
|
+
case
|
96
|
+
when Util.missing?(a) # missing > b
|
97
|
+
cmp = na_cmp_val
|
98
|
+
break
|
99
|
+
when Util.missing?(b) # a < missing
|
100
|
+
cmp = -na_cmp_val
|
101
|
+
break
|
102
|
+
else
|
103
|
+
cmp = a <=> b
|
104
|
+
break if cmp != 0
|
105
|
+
end
|
106
|
+
end
|
107
|
+
if cmp == 0
|
108
|
+
i <=> j
|
109
|
+
else
|
110
|
+
cmp
|
111
|
+
end
|
112
|
+
end
|
113
|
+
else
|
114
|
+
raise ArgumentError,
|
115
|
+
"%p is invalid value for `by`" % by
|
116
|
+
end
|
117
|
+
|
118
|
+
Charty::Table.new(
|
119
|
+
column_names.map { |name|
|
120
|
+
[
|
121
|
+
name,
|
122
|
+
self[nil, name].values_at(*order)
|
123
|
+
]
|
124
|
+
}.to_h,
|
125
|
+
index: index.to_a.values_at(*order)
|
126
|
+
)
|
127
|
+
end
|
128
|
+
|
129
|
+
private def check_na_position(val)
|
130
|
+
case val
|
131
|
+
when :first, "first"
|
132
|
+
-1
|
133
|
+
when :last, "last"
|
134
|
+
1
|
135
|
+
else
|
136
|
+
raise ArgumentError,
|
137
|
+
"`na_position` must be :first or :last",
|
138
|
+
caller
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
45
142
|
private def check_and_convert_index(values, name, expected_length)
|
46
143
|
case values
|
47
144
|
when Index, Range
|
@@ -13,12 +13,19 @@ module Charty
|
|
13
13
|
def initialize(dataset)
|
14
14
|
@table = dataset.to_table
|
15
15
|
@records = []
|
16
|
+
|
17
|
+
self.columns = self.column_names
|
18
|
+
self.index = 0 ... length
|
16
19
|
end
|
17
20
|
|
18
21
|
def data
|
19
22
|
@table
|
20
23
|
end
|
21
24
|
|
25
|
+
def column_length
|
26
|
+
column_names.length
|
27
|
+
end
|
28
|
+
|
22
29
|
def column_names
|
23
30
|
@table.column_names
|
24
31
|
end
|
@@ -27,6 +27,7 @@ module Charty
|
|
27
27
|
case data
|
28
28
|
when Charty::Vector
|
29
29
|
true
|
30
|
+
# TODO: Use vector adapter to detect them:
|
30
31
|
when Array, method(:daru_vector?), method(:narray_vector?), method(:nmatrix_vector?),
|
31
32
|
method(:numpy_vector?), method(:pandas_series?)
|
32
33
|
true
|
@@ -65,8 +66,13 @@ module Charty
|
|
65
66
|
when Numeric, String, Time, Date
|
66
67
|
arrays = [data]
|
67
68
|
when Hash
|
68
|
-
|
69
|
-
|
69
|
+
columns ||= data.map(&:keys).inject(&:|)
|
70
|
+
arrays = columns.map { [] }
|
71
|
+
data.each do |record|
|
72
|
+
columns.each_with_index do |key, i|
|
73
|
+
arrays[i] << record[key]
|
74
|
+
end
|
75
|
+
end
|
70
76
|
when self.class.method(:array?)
|
71
77
|
unsupported_data_format unless data.all?(&self.class.method(:array?))
|
72
78
|
arrays = data.map(&:to_a).transpose
|
@@ -196,6 +202,11 @@ module Charty
|
|
196
202
|
end
|
197
203
|
end
|
198
204
|
|
205
|
+
def reset_index
|
206
|
+
index_name = index.name || :index
|
207
|
+
Charty::Table.new({ index_name => index.to_a }.merge(data))
|
208
|
+
end
|
209
|
+
|
199
210
|
private def make_data_from_records(data, columns)
|
200
211
|
n_rows = data.length
|
201
212
|
n_columns = data.map(&:size).max
|
@@ -21,6 +21,10 @@ module Charty
|
|
21
21
|
|
22
22
|
attr_reader :data
|
23
23
|
|
24
|
+
def length
|
25
|
+
data.shape[0]
|
26
|
+
end
|
27
|
+
|
24
28
|
def columns
|
25
29
|
PandasIndex.new(data.columns)
|
26
30
|
end
|
@@ -29,8 +33,10 @@ module Charty
|
|
29
33
|
case new_columns
|
30
34
|
when PandasIndex
|
31
35
|
data.columns = new_columns.values
|
36
|
+
data.columns.name = new_columns.name
|
32
37
|
when Index
|
33
38
|
data.columns = new_columns.to_a
|
39
|
+
data.columns.name = new_columns.name
|
34
40
|
else
|
35
41
|
data.columns = new_columns
|
36
42
|
end
|
@@ -44,8 +50,10 @@ module Charty
|
|
44
50
|
case new_index
|
45
51
|
when PandasIndex
|
46
52
|
data.index = new_index.values
|
53
|
+
data.index.name = new_index.name
|
47
54
|
when Index
|
48
55
|
data.index = new_index.to_a
|
56
|
+
data.index.name = new_index.name
|
49
57
|
else
|
50
58
|
data.index = new_index
|
51
59
|
end
|
@@ -72,10 +80,84 @@ module Charty
|
|
72
80
|
end
|
73
81
|
end
|
74
82
|
|
83
|
+
def drop_na
|
84
|
+
Charty::Table.new(@data.dropna)
|
85
|
+
end
|
86
|
+
|
87
|
+
def sort_values(by, na_position: :last)
|
88
|
+
Charty::Table.new(@data.sort_values(by, na_position: na_position, kind: :mergesort))
|
89
|
+
end
|
90
|
+
|
75
91
|
private def check_type(type, data, name)
|
76
92
|
return data if data.is_a?(type)
|
77
93
|
raise TypeError, "#{name} must be a #{type}"
|
78
94
|
end
|
95
|
+
|
96
|
+
def group_by(_table, grouper, sort, drop_na)
|
97
|
+
GroupBy.new(@data.groupby(by: grouper, sort: sort, dropna: drop_na))
|
98
|
+
end
|
99
|
+
|
100
|
+
def reset_index
|
101
|
+
Charty::Table.new(data.reset_index)
|
102
|
+
end
|
103
|
+
|
104
|
+
class GroupBy < Charty::Table::GroupByBase
|
105
|
+
def initialize(groupby)
|
106
|
+
@groupby = groupby
|
107
|
+
end
|
108
|
+
|
109
|
+
def indices
|
110
|
+
@groupby.indices.map { |k, v|
|
111
|
+
[k, v.to_a]
|
112
|
+
}.to_h
|
113
|
+
end
|
114
|
+
|
115
|
+
def group_keys
|
116
|
+
each_group_key.to_a
|
117
|
+
end
|
118
|
+
|
119
|
+
def each_group_key
|
120
|
+
return enum_for(__method__) unless block_given?
|
121
|
+
|
122
|
+
if PyCall.respond_to?(:iterable)
|
123
|
+
PyCall.iterable(@groupby).each do |key, index|
|
124
|
+
if key.class == PyCall.builtins.tuple
|
125
|
+
key = key.to_a
|
126
|
+
end
|
127
|
+
yield key
|
128
|
+
end
|
129
|
+
else # TODO: Remove this clause after the new PyCall will be released
|
130
|
+
iter = @groupby.__iter__()
|
131
|
+
while true
|
132
|
+
begin
|
133
|
+
key, sub_data = iter.__next__
|
134
|
+
if key.class == PyCall.builtins.tuple
|
135
|
+
key = key.to_a
|
136
|
+
end
|
137
|
+
yield key
|
138
|
+
rescue PyCall::PyError => error
|
139
|
+
if error.type == PyCall.builtins.StopIteration
|
140
|
+
break
|
141
|
+
else
|
142
|
+
raise error
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def apply(*args, &block)
|
150
|
+
res = @groupby.apply(->(data) {
|
151
|
+
res = block.call(Charty::Table.new(data), *args)
|
152
|
+
Pandas::Series.new(data: res)
|
153
|
+
})
|
154
|
+
Charty::Table.new(res)
|
155
|
+
end
|
156
|
+
|
157
|
+
def [](key)
|
158
|
+
Charty::Table.new(@groupby.get_group(key))
|
159
|
+
end
|
160
|
+
end
|
79
161
|
end
|
80
162
|
end
|
81
163
|
end
|