Top Level Namespace
Defined Under Namespace
Classes: DstatPlot
Constant Summary collapse
- Y_DEFAULT =
105.0
Instance Method Summary collapse
- #analyze_header_create_plot_title(prefix, smooth, inversion, csv_header) ⇒ Object
-
#average(data, slice_size) ⇒ Object
- Calculate the average of groups of values from data Params: +data
- Array containing the data +slice_size
-
number of values each group of data should contain.
-
#create_gnuplot_dataset(timecode, values, no_plot_key, smooth, file) ⇒ Object
Create the GnuplotDataSet that is going to be printed.
-
#data_preprocessing(dataset_container, slice_size) ⇒ Object
- Preprocesses the data contained in all datasets in the dataset_container Groups of values are averaged with respect to timecode and actual data Params: +dataset_container
- Hash that holds the datasets and further information +slice_size
-
size of the group that averages are supposed to be calculated from.
- #generate_filename(output, column, category, field, target_dir) ⇒ Object
- #plot(dataset_container, category, field, dry, filename) ⇒ Object
-
#read_data_from_csv(files, category, field, column, no_plot_key, y_max, inversion, title, smooth) ⇒ Object
returns the values from a headerless csv file.
- #read_options_and_arguments ⇒ Object
- #translate_to_column(category, field, csv) ⇒ Object
Instance Method Details
#analyze_header_create_plot_title(prefix, smooth, inversion, csv_header) ⇒ Object
105 106 107 108 109 110 111 112 113 |
# File 'lib/dstat_plot.rb', line 105 def analyze_header_create_plot_title(prefix, smooth, inversion, csv_header) plot_title = "#{prefix} over time" if smooth then plot_title += " (smoothing: #{smooth})" end if csv_header[2].index("Host:") plot_title += '\n' + "(Host: #{csv_header[2][1]} User: #{csv_header[2][6]} Date: #{csv_header[3].last})" end if inversion then plot_title += '\n(inverted)' end plot_title end |
#average(data, slice_size) ⇒ Object
Calculate the average of groups of values from data Params:
- +data
-
Array containing the data
- +slice_size
-
number of values each group of data should contain
61 62 63 64 65 66 67 |
# File 'lib/dstat_plot.rb', line 61 def average(data, slice_size) reduced_data = [] data.each_slice(slice_size) do |slice| reduced_data.push(slice.reduce(:+) / slice.size) end reduced_data end |
#create_gnuplot_dataset(timecode, values, no_plot_key, smooth, file) ⇒ Object
Create the GnuplotDataSet that is going to be printed. Params:
- +timecode
-
Array containing the timestamps
- +values
-
Array containing the actual values
- +no_plot_key
-
boolean to de-/activate plotkey
- +smooth
-
nil or smoothing algorithm
- +file
-
file
93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/dstat_plot.rb', line 93 def create_gnuplot_dataset(timecode, values, no_plot_key, smooth, file) Gnuplot::DataSet.new([timecode, values]) do |gp_dataset| gp_dataset.with = "lines" if no_plot_key then gp_dataset.notitle else gp_dataset.title = (File.basename file).gsub('_', '\\_') end gp_dataset.smooth = smooth unless smooth.nil? end end |
#data_preprocessing(dataset_container, slice_size) ⇒ Object
Preprocesses the data contained in all datasets in the dataset_container Groups of values are averaged with respect to timecode and actual data Params:
- +dataset_container
-
Hash that holds the datasets and further information
- +slice_size
-
size of the group that averages are supposed to be calculated from
74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/dstat_plot.rb', line 74 def data_preprocessing(dataset_container, slice_size) dataset_container[:datasets].each do |dataset| timecode = dataset.data[0] reduced_timecode = average(timecode, slice_size) values = dataset.data[1].map { |value| value.to_f } reduced_values = average(values, slice_size) dataset.data = [reduced_timecode, reduced_values] end end |
#generate_filename(output, column, category, field, target_dir) ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/dstat_plot.rb', line 37 def generate_filename(output, column, category, field, target_dir) if output.nil? || File.directory?(output) # if an output file is not explicitly stated or if it's a directory # generate filename if column generated_filename = "dstat-column#{column}.png" else generated_filename = "#{category}-#{field}.png".sub("/", "_") end # add directory portion if output.nil? filename = File.join(target_dir, generated_filename) elsif File.directory?(output) filename = File.join(output, generated_filename) end else # specific path+file is given so just use that filename = output end end |
#plot(dataset_container, category, field, dry, filename) ⇒ Object
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/dstat_plot.rb', line 15 def plot(dataset_container, category, field, dry, filename) Gnuplot.open do |gp| Gnuplot::Plot.new(gp) do |plot| plot.title dataset_container[:plot_title].gsub('_', '\\\\\\\\_') plot.xlabel "Time in seconds" plot.ylabel "#{category}: #{field}" plot.yrange "[0:#{dataset_container[:y_max] * 1.05}]" if dataset_container[:autoscale] then plot.set "autoscale" end plot.key "out vert right top" unless dry format = filename.split('.')[-1] plot.terminal format + ' size 1600,800 enhanced font "Helvetica,11"' plot.output filename puts "Saving plot to '#{filename}'" end plot.data = dataset_container[:datasets] end end end |
#read_data_from_csv(files, category, field, column, no_plot_key, y_max, inversion, title, smooth) ⇒ Object
returns the values from a headerless csv file
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/dstat_plot.rb', line 135 def read_data_from_csv(files, category, field, column, no_plot_key, y_max, inversion, title, smooth) plot_title = nil datasets = [] autoscale = false overall_max = y_max.nil? ? Y_DEFAULT : y_max files.each do |file| csv = CSV.read(file) if column if $verbose then puts "Reading from csv to get column #{column}." end prefix = "dstat-column #{column}" else if $verbose then puts "Reading from csv to get #{category}-#{field}." end column = translate_to_column(category, field, csv) prefix = "#{category}-#{field}" end if plot_title.nil? # this only needs to be done for the first file if title plot_title = title else plot_title = analyze_header_create_plot_title(prefix, smooth, inversion != 0.0, csv[0..6]) end end if csv[2].index "Host:" csv = csv.drop(7) end begin csv = csv.transpose rescue IndexError => e puts 'ERROR: It appears that your csv file is malformed. Check for incomplete lines, empty lines etc.' puts e.backtrace[0] + e. exit end timecode = csv[0].map { || .to_f - csv[0].first.to_f } values = csv[column] if inversion != 0.0 values.map! { |value| (value.to_f - inversion).abs } overall_max = inversion end if y_max.nil? local_maximum = values.max { |a, b| a.to_f <=> b.to_f }.to_f if local_maximum > overall_max then overall_max = local_maximum end end dataset = create_gnuplot_dataset(timecode, values, no_plot_key, smooth, file) datasets.push dataset end if $verbose then puts "datasets: #{datasets.count} \nplot_title: #{plot_title} \ny_max: #{y_max} \nautoscale: #{autoscale}" end dataset_container = { :datasets => datasets, :plot_title => plot_title, :y_max => overall_max, :autoscale => autoscale } end |
#read_options_and_arguments ⇒ Object
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/dstat_plot.rb', line 194 def opts = {} # Hash that holds all the options optparse = OptionParser.new do |parser| # banner that is displayed at the top parser. = "Usage: \b dstat_plot.rb [options] -c CATEGORY -f FIELD [directory | file1 file2 ...] or \b dstat_plot.rb [options] -l COLUMN [directory | file1 file2 ...]\n\n" ### options and what they do parser.on('-v', '--verbose', 'Output more information') do $verbose = true end opts[:inversion] = 0.0 parser.on('-i', '--invert [VALUE]', Float, 'Invert the graph such that inverted(x) = VALUE - f(x),', 'default is 100.') do |value| opts[:inversion] = value.nil? ? 100.0 : value end opts[:no_plot_key] = false parser.on('-n', '--no-key', 'No plot key is printed.') do opts[:no_plot_key] = true end opts[:dry] = false parser.on('-d', '--dry', 'Dry run. Plot is not saved to file but instead displayed with gnuplot.') do opts[:dry] = true end opts[:output] = nil parser.on('-o','--output FILE|DIR', 'File or Directory that plot should be saved to. ' \ 'If a directory is given', 'the filename will be generated. Default is csv file directory.') do |path| opts[:output] = path end opts[:y_max] parser.on('-y', '--y-range RANGE', Float, 'Sets the y-axis range. Default is 105. ' \ 'If a value exceeds this range,', '"autoscale" is enabled.') do |range| opts[:y_max] = range end opts[:title] = nil parser.on('-t', '--title TITLE', 'Override the default title of the plot.') do |title| opts[:title] = title end opts[:smooth] = nil parser.on('-s', '--smoothing ALGORITHM', 'Smoothes the graph using the given algorithm.') do |algorithm| algorithms = [ 'unique', 'frequency', 'cumulative', 'cnormal', 'kdensity', 'unwrap', 'csplines', 'acsplines', 'mcsplines', 'bezier', 'sbezier' ] if algorithms.index(algorithm) opts[:smooth] = algorithm else puts "#{algorithm} is not a valid option as an algorithm." exit end end opts[:slice_size] = nil parser.on('-a', '--average-over SLICE_SIZE', Integer, 'Calculates the everage for slice_size large groups of values.',"\n") do |slice_size| opts[:slice_size] = slice_size end opts[:category] = nil parser.on('-c', '--category CATEGORY', 'Select the category.') do |category| opts[:category] = category end opts[:field] = nil parser.on('-f', '--field FIELD' , 'Select the field.') do |field| opts[:field] = field end opts[:column] = nil parser.on('-l', '--column COLUMN', 'Select the desired column directly.', "\n") do |column| unless opts[:category] && opts[:field] # -c and -f override -l opts[:column] = column.to_i end end # This displays the help screen parser.on_tail('-h', '--help', 'Display this screen.' ) do puts parser exit end end # there are two forms of the parse method. 'parse' # simply parses ARGV, while 'parse!' parses ARGV # and removes all options and parameters found. What's # left is the list of files optparse.parse! if $verbose then puts "opts: #{opts.inspect}" end if opts[:category].nil? || opts[:category].nil? if opts[:column].nil? puts "[Error] (-c CATEGORY and -f FIELD) or (-l COLUMN) are mandatory parameters.\n\n" puts optparse exit end end # if ARGV is empty at this point no directory or file(s) is specified # and the current working directory is used if ARGV.empty? then ARGV.push "." end files = [] if File.directory?(ARGV.last) then opts[:target_dir] = ARGV.last.chomp("/") # cuts of "/" from the end if present files = Dir.glob "#{opts[:target_dir]}/*.csv" files = files.sort else opts[:target_dir] = File.dirname ARGV.first ARGV.each do |filename| files.push filename end end puts "Plotting data from #{files.count} file(s)." opts[:files] = files if $verbose then puts "files: #{files.count} #{files.inspect}" end # opts = { :inversion, :no_plot_key, :dry, :output, :y_max, :title, :category, :field, :column, :target_dir, :files } opts end |
#translate_to_column(category, field, csv) ⇒ Object
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/dstat_plot.rb', line 115 def translate_to_column(category, field, csv) category_index = csv[5].index category if category_index.nil? puts "'#{category}' is not a valid parameter for 'category'." puts "Allowed categories: #{csv[5].reject{ |elem| elem == nil }.inspect}" exit 0 end field_index = csv[6].drop(category_index).index field if field_index.nil? puts "'#{field}' is not a valid parameter for 'field'." puts "Allowed fields: #{csv[6].reject{ |elem| elem == nil }.inspect}" exit 0 end if $verbose then puts "'#{category}-#{field}' was translated to #{category_index + field_index}." end column = category_index + field_index end |