Use Data struct

This commit is contained in:
kojix2 2020-08-15 22:17:44 +09:00
parent e887dc3f5a
commit ff7b0680aa

View File

@ -303,31 +303,34 @@ module Uplot
end end
end end
def get_lim(str)
str.split(/-|:|\.\./)[0..1].map(&:to_f)
end
def run def run
# Sometimes the input file does not end with a newline code. # Sometimes the input file does not end with a newline code.
while input = Kernel.gets(nil) while input = Kernel.gets(nil)
input.freeze input.freeze
@raw_inputs << input @raw_inputs << input
data, headers = preprocess(input) @data = Preprocess.input(input, @delimiter, @headers, @transpose)
pp input: input, data: data, headers: headers if @debug
case plot_type case plot_type
when :bar, :barplot when :bar, :barplot
barplot(data, headers) barplot(@data)
when :count, :c when :count, :c
@count = true @count = true
barplot(data, headers) barplot(@data)
when :hist, :histogram when :hist, :histogram
histogram(data, headers) histogram(@data)
when :line, :lineplot when :line, :lineplot
line(data, headers) line(@data)
when :lines, :lineplots when :lines, :lineplots
lines(data, headers) lines(@data)
when :scatter, :scatterplot when :scatter, :scatterplot
scatter(data, headers) scatter(@data)
when :density when :density
density(data, headers) density(@data)
when :box, :boxplot when :box, :boxplot
boxplot(data, headers) boxplot(@data)
else else
raise "unrecognized plot_type: #{plot_type}" raise "unrecognized plot_type: #{plot_type}"
end.render($stderr) end.render($stderr)
@ -336,90 +339,44 @@ module Uplot
end end
end end
# Transpose different sized ruby arrays def barplot(data)
# https://stackoverflow.com/q/26016632 headers = data.headers
def transpose2(arr) series = data.series
Array.new(arr.map(&:length).max) { |i| arr.map { |e| e[i] } } if @count
end series = Preprocess.count(series[0])
params.title = headers[0] if headers
def preprocess(input)
arr = CSV.parse(input, col_sep: @delimiter)
# Remove blank lines.
arr.delete([])
# Remove rows where all elements are nil
arr.delete_if { |i| i.all? nil }
p parsed_csv: arr if @debug
headers = get_headers(arr)
data = get_data(arr)
[data, headers]
end
def get_headers(data)
if @headers
if @transpose
data.map(&:first)
else
data[0]
end
end end
end
def get_data(data)
if @transpose
if @headers
data.map { |row| row[1..-1] }
else
data
end
else
if @headers
transpose2(data[1..-1])
else
transpose2(data)
end
end
end
def preprocess_count(data)
# tally was added in Ruby 2.7
if Enumerable.method_defined? :tally
data[0].tally
else
# https://github.com/marcandre/backports
data[0].each_with_object(Hash.new(0)) { |item, res| res[item] += 1 }
.tap { |h| h.default = nil }
end
.sort { |a, b| a[1] <=> b[1] }
.reverse
.transpose
end
def barplot(data, headers)
data = preprocess_count(data) if @count
params.title ||= headers[1] if headers params.title ||= headers[1] if headers
UnicodePlot.barplot(data[0], data[1].map(&:to_f), **params.to_hc) labels = series[0]
values = series[1].map(&:to_f)
UnicodePlot.barplot(labels, values, **params.to_hc)
end end
def histogram(data, headers) def histogram(data)
params.title ||= headers[0] if headers # labels? headers = data.headers
series = data[0].map(&:to_f) series = data.series
UnicodePlot.histogram(series, **params.to_hc) params.title ||= data.headers[0] if headers
values = series[0].map(&:to_f)
UnicodePlot.histogram(values, **params.to_hc)
end end
def get_lim(str) def line(data)
str.split(/-|:|\.\./)[0..1].map(&:to_f) headers = data.headers
end series = data.series
if series.size == 1
def line(data, headers) # If there is only one series, it is assumed to be sequential data.
if data.size == 1
params.ylabel ||= headers[0] if headers params.ylabel ||= headers[0] if headers
y = data[0].map(&:to_f) y = series[0].map(&:to_f)
UnicodePlot.lineplot(y, **params.to_hc) UnicodePlot.lineplot(y, **params.to_hc)
else else
params.xlabel ||= headers[0] if headers # If there are 2 or more series,
params.ylabel ||= headers[1] if headers # assume that the first 2 series are the x and y series respectively.
x = data[0].map(&:to_f) if headers
y = data[1].map(&:to_f) params.xlabel ||= headers[0]
params.ylabel ||= headers[1]
end
x = series[0].map(&:to_f)
y = series[1].map(&:to_f)
UnicodePlot.lineplot(x, y, **params.to_hc) UnicodePlot.lineplot(x, y, **params.to_hc)
end end
end end
@ -428,65 +385,73 @@ module Uplot
(method1.to_s + '!').to_sym (method1.to_s + '!').to_sym
end end
def xyy_plot(data, headers, method1) # improve method name def xyy_plot(data, method1)
headers = data.headers
series = data.series
method2 = get_method2(method1) method2 = get_method2(method1)
data.map! { |series| series.map(&:to_f) } series.map! { |s| s.map(&:to_f) }
params.name ||= headers[1] if headers if headers
params.xlabel ||= headers[0] if headers params.name ||= headers[1]
params.ylim ||= data[1..-1].flatten.minmax # need? params.xlabel ||= headers[0]
plot = UnicodePlot.public_send(method1, data[0], data[1], **params.to_hc) end
2.upto(data.size - 1) do |i| params.ylim ||= series[1..-1].flatten.minmax # why need?
UnicodePlot.public_send(method2, plot, data[0], data[i], name: headers[i]) plot = UnicodePlot.public_send(method1, series[0], series[1], **params.to_hc)
2.upto(series.size - 1) do |i|
UnicodePlot.public_send(method2, plot, series[0], series[i], name: headers&.[](i))
end end
plot plot
end end
def xyxy_plot(data, headers, method1) # improve method name def xyxy_plot(data, method1)
headers = data.headers
series = data.series
method2 = get_method2(method1) method2 = get_method2(method1)
data.map! { |series| series.map(&:to_f) } series.map! { |s| s.map(&:to_f) }
data = data.each_slice(2).to_a series = series.each_slice(2).to_a
params.name ||= headers[0] if headers params.name ||= headers[0] if headers
params.xlim = data.map(&:first).flatten.minmax params.xlim = series.map(&:first).flatten.minmax # why need?
params.ylim = data.map(&:last).flatten.minmax params.ylim = series.map(&:last).flatten.minmax # why need?
x1, y1 = data.shift x1, y1 = series.shift
plot = UnicodePlot.public_send(method1, x1, y1, **params.to_hc) plot = UnicodePlot.public_send(method1, x1, y1, **params.to_hc)
data.each_with_index do |(xi, yi), i| series.each_with_index do |(xi, yi), i|
UnicodePlot.public_send(method2, plot, xi, yi, name: headers[(i + 1) * 2]) UnicodePlot.public_send(method2, plot, xi, yi, name: headers&.[]((i + 1) * 2))
end end
plot plot
end end
def lines(data, headers) def lines(data)
case @fmt case @fmt
when 'xyy' when 'xyy'
xyy_plot(data, headers, :lineplot) xyy_plot(data, :lineplot)
when 'xyxy' when 'xyxy'
xyxy_plot(data, headers, :lineplot) xyxy_plot(data, :lineplot)
end end
end end
def scatter(data, headers) def scatter(data)
case @fmt case @fmt
when 'xyy' when 'xyy'
xyy_plot(data, headers, :scatterplot) xyy_plot(data, :scatterplot)
when 'xyxy' when 'xyxy'
xyxy_plot(data, headers, :scatterplot) xyxy_plot(data, :scatterplot)
end end
end end
def density(data, headers) def density(data)
case @fmt case @fmt
when 'xyy' when 'xyy'
xyy_plot(data, headers, :densityplot) xyy_plot(data, :densityplot)
when 'xyxy' when 'xyxy'
xyxy_plot(data, headers, :densityplot) xyxy_plot(data, :densityplot)
end end
end end
def boxplot(data, headers) def boxplot(data)
headers ||= (1..data.size).map(&:to_s) headers = data.headers
data.map! { |series| series.map(&:to_f) } series = data.series
UnicodePlot.boxplot(headers, data, params.to_hc) headers ||= (1..series.size).map(&:to_s)
series.map! { |s| s.map(&:to_f) }
UnicodePlot.boxplot(headers, series, params.to_hc)
end end
end end
end end