nyaplot 0.1.6 → 0.2.0.rc1
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/.gitignore +1 -0
- data/.gitmodules +4 -3
- data/examples/notebook/3DPlot.ipynb +398 -455
- data/examples/notebook/Interaction_with_DataFrame.ipynb +572 -650
- data/examples/notebook/Introduction.ipynb +452 -525
- data/examples/notebook/Mapnya.ipynb +441 -212
- data/examples/notebook/Nyaplotv2.ipynb +307 -0
- data/lib/nyaplot.rb +7 -3
- data/lib/nyaplot/base.rb +80 -46
- data/lib/nyaplot/core.rb +45 -5
- data/lib/nyaplot/data.rb +220 -172
- data/lib/nyaplot/glyph.rb +227 -0
- data/lib/nyaplot/interactive.rb +94 -0
- data/lib/nyaplot/pane.rb +43 -0
- data/lib/nyaplot/plot.rb +173 -124
- data/lib/nyaplot/position.rb +7 -0
- data/lib/nyaplot/scale.rb +19 -0
- data/lib/nyaplot/sheet.rb +83 -0
- data/lib/nyaplot/simple/stage.rb +29 -0
- data/lib/nyaplot/stage2d.rb +56 -0
- data/lib/nyaplot/templates/init.js.erb +1 -1
- data/lib/nyaplot/templates/init_interactive.js +77 -0
- data/lib/nyaplot/templates/iruby.erb +2 -9
- data/lib/nyaplot/version.rb +1 -1
- data/lib/nyaplot3d/core.rb +1 -1
- data/nyaplot.gemspec +1 -26
- metadata +29 -10
- data/lib/mapnya/datasets/geo-boundaries-world-110m/README.md +0 -3
- data/lib/mapnya/datasets/geo-boundaries-world-110m/countries.geojson +0 -360
- data/lib/mapnya/datasets/geo-boundaries-world-110m/datapackage.json +0 -35
- data/lib/nyaplot/database.rb +0 -22
- data/lib/nyaplot/diagram.rb +0 -341
- data/lib/nyaplot/frame.rb +0 -70
data/lib/nyaplot/core.rb
CHANGED
@@ -2,10 +2,7 @@ require 'erb'
|
|
2
2
|
|
3
3
|
module Nyaplot
|
4
4
|
|
5
|
-
@@dep_libraries = {
|
6
|
-
d3:'https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min',
|
7
|
-
downloadable: 'http://cdn.rawgit.com/domitry/d3-downloadable/master/d3-downloadable'
|
8
|
-
}
|
5
|
+
@@dep_libraries = {d3:'http://d3js.org/d3.v3.min'}
|
9
6
|
@@additional_libraries = {}
|
10
7
|
@@extension_lists = []
|
11
8
|
|
@@ -39,11 +36,54 @@ module Nyaplot
|
|
39
36
|
js
|
40
37
|
end
|
41
38
|
|
39
|
+
def self.start_debug(port=9996)
|
40
|
+
require 'webrick'
|
41
|
+
path = File.expand_path("../../../nyaplotjs/release", __FILE__)
|
42
|
+
`ruby -e httpd #{path} -p #{port}`
|
43
|
+
|
44
|
+
js = self.generate_init_code
|
45
|
+
js.gsub!("http.+nyaplot.js", "http://localhost:" + port.to_s + "/nyaplot.js")
|
46
|
+
IRuby.display(IRuby.javascript(js))
|
47
|
+
end
|
48
|
+
|
42
49
|
# Enable to show plots on IRuby notebook
|
43
50
|
def self.init_iruby
|
44
51
|
js = self.generate_init_code
|
45
52
|
IRuby.display(IRuby.javascript(js))
|
46
53
|
end
|
47
54
|
|
48
|
-
|
55
|
+
# Create multi-column layout
|
56
|
+
# @example
|
57
|
+
# include Nyaplot
|
58
|
+
# p1 = Plot.add(:scatter, x1, y1)
|
59
|
+
# p2 = Plot.add(:line, x2, y2)
|
60
|
+
# columns(p1, p2).draw
|
61
|
+
#
|
62
|
+
def columns(*plots)
|
63
|
+
panes = plots.map{|p| p.pane}
|
64
|
+
plot = Plot.new
|
65
|
+
plot.pane = Pane.columns(*panes)
|
66
|
+
plot
|
67
|
+
end
|
68
|
+
|
69
|
+
# Create multi-row layout
|
70
|
+
# @example
|
71
|
+
# include Nyaplot
|
72
|
+
# p1 = Plot.add(:scatter, x1, y1)
|
73
|
+
# p2 = Plot.add(:line, x2, y2)
|
74
|
+
# p3 = Plot.add(:bar, x3, y3)
|
75
|
+
# rows(columns(p1, p2), p3).draw
|
76
|
+
#
|
77
|
+
def rows(*plots)
|
78
|
+
panes = plots.map{|p| p.pane}
|
79
|
+
plot = Plot.new
|
80
|
+
plot.pane = Pane.rows(*panes)
|
81
|
+
plot
|
82
|
+
end
|
83
|
+
|
84
|
+
if $DEBUG_NYAPLOT == true
|
85
|
+
start_debug
|
86
|
+
else
|
87
|
+
init_iruby if defined? IRuby
|
88
|
+
end
|
49
89
|
end
|
data/lib/nyaplot/data.rb
CHANGED
@@ -4,229 +4,277 @@ require 'json'
|
|
4
4
|
require 'csv'
|
5
5
|
|
6
6
|
module Nyaplot
|
7
|
+
begin
|
8
|
+
require "mikon"
|
9
|
+
rescue
|
10
|
+
end
|
7
11
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
:
|
12
|
-
:
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@name = name
|
21
|
-
@rows = []
|
22
|
-
case
|
23
|
-
when source.is_a?(Array)
|
24
|
-
# like [{a:10, b:10},{a:20,b:20}]
|
25
|
-
@rows = source
|
26
|
-
when source.is_a?(Hash)
|
27
|
-
# like {a:[10,20], b:[10, 20]}
|
28
|
-
keys = source.keys
|
29
|
-
len = source[keys[0]].length
|
30
|
-
(0..len-1).each do |i|
|
31
|
-
hash = {}
|
32
|
-
keys.each{|key| hash[key] = source[key][i]}
|
33
|
-
@rows.push(hash)
|
12
|
+
if defined? Mikon
|
13
|
+
class DataFrame
|
14
|
+
include Nyaplot::Base
|
15
|
+
type :data
|
16
|
+
required_args :data
|
17
|
+
|
18
|
+
def initialize(*args)
|
19
|
+
super()
|
20
|
+
if args.length == 1 && args.first.is_a?(Mikon::DataFrame)
|
21
|
+
@df = args.first
|
22
|
+
else
|
23
|
+
@df = Mikon::DataFrame.new(*args)
|
34
24
|
end
|
25
|
+
data(@df)
|
35
26
|
end
|
36
27
|
|
37
|
-
|
38
|
-
|
39
|
-
@
|
40
|
-
|
41
|
-
|
42
|
-
|
28
|
+
def select!(*args, &block)
|
29
|
+
@df = @df.select(*args, &block)
|
30
|
+
data(@df)
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_html
|
35
|
+
@df.to_html
|
36
|
+
end
|
37
|
+
|
38
|
+
def method_missing(name, *args, &block)
|
39
|
+
if @df.respond_to? name
|
40
|
+
ret = @df.send(name, *args, &block)
|
41
|
+
if ret.is_a?(Mikon::DataFrame)
|
42
|
+
self.class.new(ret)
|
43
|
+
else
|
44
|
+
ret
|
43
45
|
end
|
46
|
+
else
|
47
|
+
super
|
44
48
|
end
|
45
49
|
end
|
46
50
|
end
|
51
|
+
else
|
52
|
+
# Ruby DataFrame for plotting
|
53
|
+
class DataFrame
|
54
|
+
include Nyaplot::Base
|
55
|
+
type :data
|
56
|
+
required_args :data
|
57
|
+
|
58
|
+
DEFAULT_OPTS = {
|
59
|
+
:col_sep => ',',
|
60
|
+
:headers => true,
|
61
|
+
:converters => :numeric,
|
62
|
+
:header_converters => :symbol
|
63
|
+
}
|
64
|
+
|
65
|
+
attr_reader :rows
|
66
|
+
|
67
|
+
def initialize(source)
|
68
|
+
super()
|
69
|
+
@rows = []
|
70
|
+
case
|
71
|
+
when source.is_a?(Array)
|
72
|
+
# like [{a:10, b:10},{a:20,b:20}]
|
73
|
+
@rows = source
|
74
|
+
when source.is_a?(Hash)
|
75
|
+
# like {a:[10,20], b:[10, 20]}
|
76
|
+
keys = source.keys
|
77
|
+
len = source[keys[0]].length
|
78
|
+
(0..len-1).each do |i|
|
79
|
+
hash = {}
|
80
|
+
keys.each{|key| hash[key] = source[key][i]}
|
81
|
+
@rows.push(hash)
|
82
|
+
end
|
83
|
+
end
|
47
84
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
85
|
+
# transform String to Symbol as a key
|
86
|
+
unless @rows.all? {|row| row.keys.all? {|el| el.is_a?(Symbol)}}
|
87
|
+
@rows.map! do |row|
|
88
|
+
row.inject({}) do |hash, (key, val)|
|
89
|
+
hash[key.to_sym]=val
|
90
|
+
hash
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
57
94
|
end
|
58
95
|
|
59
|
-
|
60
|
-
|
96
|
+
def self.from_csv(*args)
|
97
|
+
path = args.shift
|
61
98
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
99
|
+
opts = DEFAULT_OPTS
|
100
|
+
if args.size > 0 && args.first.is_a?(Hash)
|
101
|
+
opts = opts.merge(args.shift)
|
102
|
+
else
|
103
|
+
opts[:col_sep] = args.shift if args.size > 0
|
104
|
+
opts[:headers] = args.shift if args.size > 0
|
68
105
|
end
|
69
|
-
rows << hash
|
70
|
-
end
|
71
|
-
self.new(rows)
|
72
|
-
end
|
73
106
|
|
74
|
-
|
75
|
-
|
76
|
-
# new_df = df.filter{|row| row[:a] %2 == 0}
|
77
|
-
def filter(&block)
|
78
|
-
DataFrame.new(@rows.select(&block))
|
79
|
-
end
|
107
|
+
csv = CSV.open(path, "r", opts)
|
108
|
+
yield csv if block_given?
|
80
109
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
110
|
+
rows = []
|
111
|
+
csv.each do |row|
|
112
|
+
hash = {}
|
113
|
+
row.each_with_index do |el,i|
|
114
|
+
next if el[0].nil? && el[1].nil?
|
115
|
+
hash[el[0].to_sym] = el[1]
|
116
|
+
end
|
117
|
+
rows << hash
|
118
|
+
end
|
119
|
+
self.new(rows)
|
120
|
+
end
|
85
121
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
122
|
+
# Filtering row out using recieved block
|
123
|
+
# @example
|
124
|
+
# new_df = df.filter{|row| row[:a] %2 == 0}
|
125
|
+
def filter(&block)
|
126
|
+
DataFrame.new(@rows.select(&block))
|
127
|
+
end
|
90
128
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
129
|
+
# destructive version of DataFrame#filter
|
130
|
+
def filter!(&block)
|
131
|
+
@rows.select!(&block)
|
132
|
+
end
|
95
133
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
row.delete(name)
|
134
|
+
# @return [String] the name of dataframe. If not specified when initializing, uuid v4 will be set.
|
135
|
+
def name
|
136
|
+
@uuid
|
100
137
|
end
|
101
|
-
end
|
102
138
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
return Series.new(name, column)
|
108
|
-
end
|
139
|
+
def insert_column(name, arr)
|
140
|
+
name = name.is_a?(Symbol) ? name : name.to_sym
|
141
|
+
arr.each_with_index{|val, i| @rows[i][name]=val}
|
142
|
+
end
|
109
143
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
144
|
+
def delete_column(name)
|
145
|
+
name = name.is_a?(Symbol) ? name : name.to_sym
|
146
|
+
@rows.each do |row|
|
147
|
+
row.delete(name)
|
148
|
+
end
|
149
|
+
end
|
116
150
|
|
117
|
-
|
118
|
-
|
119
|
-
|
151
|
+
# Access column using its label
|
152
|
+
def column(name)
|
153
|
+
id = name.is_a?(Symbol) ? name : name.to_sym
|
154
|
+
column = @rows.map{|row| row[id]}
|
155
|
+
return Series.new(name, column)
|
156
|
+
end
|
120
157
|
|
121
|
-
|
122
|
-
@
|
123
|
-
|
158
|
+
# Insert row using index
|
159
|
+
# @param [Hash] row row to insert
|
160
|
+
# @param [Numeric] index if not specified, the row will be inserted to the end
|
161
|
+
def insert_row(row, index=@rows.length)
|
162
|
+
@rows.insert(index, row)
|
163
|
+
end
|
124
164
|
|
125
|
-
|
126
|
-
|
127
|
-
block.call(column(label).to_a)
|
165
|
+
def row(index)
|
166
|
+
@rows[index]
|
128
167
|
end
|
129
|
-
end
|
130
168
|
|
131
|
-
|
132
|
-
|
133
|
-
block.call(row)
|
169
|
+
def before_to_json
|
170
|
+
data(@rows)
|
134
171
|
end
|
135
|
-
end
|
136
172
|
|
137
|
-
|
138
|
-
|
173
|
+
def each_column(&block)
|
174
|
+
self.column_labels.each do |label|
|
175
|
+
block.call(column(label).to_a)
|
176
|
+
end
|
177
|
+
end
|
139
178
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
179
|
+
def each_row(&block)
|
180
|
+
@rows.each do |row|
|
181
|
+
block.call(row)
|
182
|
+
end
|
144
183
|
end
|
145
184
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
html += '</tr>'
|
151
|
-
if i == threshold
|
185
|
+
def to_html(threshold = 15)
|
186
|
+
html = '<table>'
|
187
|
+
|
188
|
+
unless @rows[0].nil?
|
152
189
|
html += '<tr>'
|
153
|
-
|
190
|
+
@rows[0].each {|key, val| html.concat('<th>' + key.to_s + '</th>')}
|
154
191
|
html += '</tr>'
|
155
192
|
end
|
193
|
+
|
194
|
+
@rows.each_with_index do |row, i|
|
195
|
+
next if i > threshold && i < @rows.length-1
|
196
|
+
html += '<tr>'
|
197
|
+
row.each{|key, val| html.concat('<td>' + val.to_s + '</td>')}
|
198
|
+
html += '</tr>'
|
199
|
+
if i == threshold
|
200
|
+
html += '<tr>'
|
201
|
+
row.length.times {html.concat('<td>...</td>')}
|
202
|
+
html += '</tr>'
|
203
|
+
end
|
204
|
+
end
|
205
|
+
html += '</table>'
|
156
206
|
end
|
157
|
-
html += '</table>'
|
158
|
-
end
|
159
207
|
|
160
|
-
|
161
|
-
|
162
|
-
|
208
|
+
def to_s
|
209
|
+
to_html
|
210
|
+
end
|
163
211
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
212
|
+
# The alias method for DataFrame#column
|
213
|
+
def [](name)
|
214
|
+
return self.column(name)
|
215
|
+
end
|
168
216
|
|
169
|
-
|
170
|
-
|
171
|
-
|
217
|
+
def column_labels
|
218
|
+
@rows[0].keys
|
219
|
+
end
|
172
220
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
221
|
+
def method_missing(name, *args, &block)
|
222
|
+
if md = name.match(/(.+)\=/)
|
223
|
+
self.insert_column(name[/(.+)\=/].delete("="), args[0])
|
224
|
+
return
|
225
|
+
elsif column_labels.include?(name)
|
226
|
+
return self.column(name)
|
227
|
+
else
|
228
|
+
super(name, *args, &block)
|
229
|
+
end
|
181
230
|
end
|
182
231
|
end
|
183
|
-
end
|
184
232
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
233
|
+
class Series
|
234
|
+
include Enumerable
|
235
|
+
def each(&block)
|
236
|
+
@arr.each(&block)
|
237
|
+
end
|
190
238
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
239
|
+
def initialize(label, arr)
|
240
|
+
@arr = arr
|
241
|
+
@label = label
|
242
|
+
end
|
195
243
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
244
|
+
def to_html(threshold=15)
|
245
|
+
html = '<table><tr><th>' + label.to_s + '</th></tr>>'
|
246
|
+
@arr.each_with_index do |el,i|
|
247
|
+
next if threshold < i && i < @arr.length-1
|
248
|
+
content = i == threshold ? '...' : el.to_s
|
249
|
+
html.concat('<tr><td>' + content + '</td></tr>')
|
250
|
+
end
|
251
|
+
html += '</table>'
|
202
252
|
end
|
203
|
-
html += '</table>'
|
204
|
-
end
|
205
253
|
|
206
|
-
|
207
|
-
|
208
|
-
|
254
|
+
def to_json(*args)
|
255
|
+
@arr.to_json
|
256
|
+
end
|
209
257
|
|
210
|
-
|
211
|
-
|
212
|
-
|
258
|
+
def to_a
|
259
|
+
@arr
|
260
|
+
end
|
213
261
|
|
214
|
-
|
215
|
-
|
216
|
-
|
262
|
+
def label
|
263
|
+
@label
|
264
|
+
end
|
217
265
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
266
|
+
def method_missing(meth, *args, &block)
|
267
|
+
if @arr.respond_to?(meth)
|
268
|
+
@arr.send(meth, *args, &block)
|
269
|
+
else
|
270
|
+
super(meth, *args, &block)
|
271
|
+
end
|
223
272
|
end
|
224
|
-
end
|
225
273
|
|
226
|
-
|
227
|
-
|
228
|
-
|
274
|
+
def respond_to?(meth)
|
275
|
+
return true if @arr.respond_to?(meth)
|
276
|
+
super(meth)
|
277
|
+
end
|
229
278
|
end
|
230
|
-
|
231
279
|
end
|
232
280
|
end
|