sycsvpro 0.1.13 → 0.2.0

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.
@@ -0,0 +1,523 @@
1
+ require_relative 'not_available'
2
+
3
+ # Operating csv files
4
+ module Sycsvpro
5
+
6
+ # A spread sheet is used to do column and row wise calculations between
7
+ # spread sheets. The calculations can be *, /, + and - where the operations
8
+ # are conducted between corresponding columns and rows. It is not as with
9
+ # matrix operations.
10
+ #
11
+ # Example:
12
+ # [0] [1] [0] [1]
13
+ # A = [0] 1 2 B = [0] 5 6
14
+ # [1] 3 4 [1] 7 8
15
+ #
16
+ # [0*0] [1*1]
17
+ # A * B = [0*0] 5 12
18
+ # [1*1] 21 32
19
+ #
20
+ # If spread sheets are not the same size then the operation is looping through
21
+ # the smaller spread sheets values
22
+ #
23
+ # Example:
24
+ # [0] [1] [0] [0]
25
+ # A = [0] 1 2 B = [0] 5 C = [0] 8
26
+ # [1] 3 4 [1] 7
27
+ #
28
+ # [0*0] [1*1]
29
+ # A * B = [0*0] 5 35
30
+ # [1*1] 21 28
31
+ #
32
+ # [0*0] [1*0]
33
+ # A * C = [0*0] 8 16
34
+ # [1*0] 24 32
35
+ class SpreadSheet
36
+
37
+ # rows of the spread sheet
38
+ attr_accessor :rows
39
+ # options of the spread sheet
40
+ attr_accessor :opts
41
+ # row labels
42
+ attr_accessor :row_labels
43
+ # column labels
44
+ attr_accessor :col_labels
45
+
46
+ # Creates a new spread sheet with rows and optional options.
47
+ #
48
+ # SpreadSheet.new([A,1,2], [B,3,4], r: true, c: false)
49
+ #
50
+ # rlabel: first column of the row contains labels if true
51
+ # clabel: first row are labels if true
52
+ #
53
+ # Creates a spread sheet with row labels 'A', 'B' and no column labels
54
+ # [0] [1]
55
+ # [A] 1 2
56
+ # [B] 3 4
57
+ #
58
+ # SpreadSheet.new(['One','Two'],['A',1,2],['B',3,4],
59
+ # r = true,
60
+ # c = true)
61
+ #
62
+ # Creates a spread sheet with row and column labels
63
+ #
64
+ # [One] [Two]
65
+ # [A] 1 2
66
+ # [B] 3 4
67
+ #
68
+ # It is also possible to specify row and column labels explicit
69
+ #
70
+ # SpreadSheet.new([1,2],[3,4], row_labels: ['A','B'],
71
+ # col_labels: ['One','Two'])
72
+ #
73
+ # Params
74
+ # ======
75
+ # r:: has row labels if true
76
+ # c:: has column labels if true
77
+ # row_labels:: explicitly provides row labels
78
+ # col_labels:: explicitly provides column labels
79
+ # values:: flat array with values
80
+ # rows:: indicates the row count in combination with values param
81
+ # cols:: indicates the col count in combination with values param
82
+ # file:: file that contains values to create spread sheet with
83
+ def initialize(*rows)
84
+ opts = rows.pop if rows.last.is_a?(::Hash)
85
+ @opts = opts || {}
86
+ rows = rows_from_params(@opts) if rows.empty?
87
+ check_validity_of(rows)
88
+ @row_labels, @col_labels = create_labels(rows)
89
+ @rows = rows
90
+ end
91
+
92
+ # Returns the dimension [rows, columns] of the spread sheet
93
+ # SpreadSheet.new([1,2,3], [4,5,6]).dim -> [2,3]
94
+ def dim
95
+ [nrows, ncols]
96
+ end
97
+
98
+ # Returns the size of the spread sheet, that is the count of elements
99
+ def size
100
+ nrows * ncols
101
+ end
102
+
103
+ # Returns the number of rows
104
+ def nrows
105
+ rows.size
106
+ end
107
+
108
+ # Returns the number of columns
109
+ def ncols
110
+ rows[0].size
111
+ end
112
+
113
+ # Swaps rows and columns and returns new spread sheet with result
114
+ def transpose
115
+ SpreadSheet.new(*rows.transpose, row_labels: col_labels,
116
+ col_labels: row_labels)
117
+ end
118
+
119
+ # Returns a subset of the spread sheet and returns a new spread sheet with
120
+ # the result and the corresponding row and column labels
121
+ def [](*range)
122
+ r, c = range
123
+ r ||= 0..(nrows-1)
124
+ c ||= 0..(ncols-1)
125
+
126
+ row_selection = rows.values_at(*r)
127
+ col_selection = []
128
+
129
+ if rows_are_arrays?(row_selection)
130
+ row_selection.each do |row|
131
+ values = row.values_at(*c)
132
+ col_selection << (values.respond_to?(:to_ary) ? values : [values])
133
+ end
134
+ else
135
+ col_selection << row_selection[*c]
136
+ end
137
+
138
+ SpreadSheet.new(*col_selection,
139
+ row_labels: row_labels.values_at(*r),
140
+ col_labels: col_labels.values_at(*c))
141
+ end
142
+
143
+ # Binds spread sheets column wise
144
+ #
145
+ # 1 2 3 10 20 30
146
+ # A = 4 5 6 B = 40 50 60
147
+ # 7 8 9 70 80 90
148
+ #
149
+ # C = SpeadSheet.bind_columns(A,B)
150
+ #
151
+ # 1 2 3 10 20 30
152
+ # C = 4 5 6 40 50 60
153
+ # 7 8 9 70 80 90
154
+ #
155
+ # If the spread sheets have different row sizes the columns of the spread
156
+ # sheet with fewer rows are filled with NotAvailable
157
+ #
158
+ # 1 2 3 10 20 30
159
+ # A = 4 5 6 B = 40 50 60
160
+ # 7 8 9
161
+ #
162
+ # C = SpeadSheet.bind_columns(A,B)
163
+ #
164
+ # 1 2 3 10 20 30
165
+ # C = 4 5 6 40 50 60
166
+ # 7 8 9 NA NA NA
167
+ #
168
+ # The column lables are also combined from the spread sheets and the row
169
+ # labels of the spread sheet with the higher row count are used
170
+ #
171
+ # Returns the result in a new spread sheet
172
+ def self.bind_columns(*sheets)
173
+ row_count = sheets.collect { |s| s.nrows }.max
174
+ binds = Array.new(row_count, [])
175
+ 0.upto(row_count - 1) do |r|
176
+ sheets.each do |sheet|
177
+ sheet_row = sheet.rows[r]
178
+ binds[r] += sheet_row.nil? ? [NotAvailable] * sheet.ncols : sheet_row
179
+ end
180
+ end
181
+ c_labels = sheets.collect { |s| s.col_labels }.inject(:+)
182
+ r_labels = sheets.collect { |s|
183
+ s.row_labels if s.row_labels.size == row_count
184
+ }.first
185
+ SpreadSheet.new(*binds, col_labels: c_labels, row_labels: r_labels)
186
+ end
187
+
188
+ # Binds spread sheets row wise
189
+ #
190
+ # 1 2 3 10 20 30
191
+ # A = 4 5 6 B = 40 50 60
192
+ # 7 8 9
193
+ #
194
+ # C = SpeadSheet.bind_rows(A,B)
195
+ #
196
+ # 1 2 3
197
+ # 4 5 6
198
+ # C = 7 8 9
199
+ # 10 20 30
200
+ # 40 50 60
201
+ #
202
+ # If the spread sheets have different column sizes the columns of the spread
203
+ # sheet with fewer columns are filled with NotAvailable
204
+ #
205
+ # 1 2 3 10 20
206
+ # A = 4 5 6 B = 40 50
207
+ # 7 8 9
208
+ #
209
+ # C = SpeadSheet.bind_rows(A,B)
210
+ #
211
+ # 1 2 3
212
+ # 4 5 6
213
+ # C = 7 8 9
214
+ # 10 20 NA
215
+ # 40 50 NA
216
+ #
217
+ # The row lables are also combined from the spread sheets and the column
218
+ # labels of the spread sheet with the higher column count are used
219
+ def self.bind_rows(*sheets)
220
+ col_count = sheets.collect { |s| s.ncols }.max
221
+ binds = []
222
+ sheets.each do |sheet|
223
+ binds << sheet.rows.collect { |r|
224
+ r + [NotAvailable] * ((col_count - r.size) % col_count)
225
+ }
226
+ end
227
+ r_labels = sheets.collect { |s| s.col_labels }.inject(:+)
228
+ c_labels = sheets.collect { |s| s.col_labels if s.ncols == col_count }.first
229
+ SpreadSheet.new(*binds.flatten(1),
230
+ row_labels: r_labels,
231
+ col_labels: c_labels)
232
+ end
233
+
234
+ # Returns the result in a new spread sheet
235
+ # Multiplies two spreadsheets column by column and returns a new spread
236
+ # sheet with the result
237
+ # 1 2 3 3 2 1 3 4 3
238
+ # 4 5 6 * 6 5 4 = 24 25 24
239
+ # 7 8 9 9 8 7 63 64 63
240
+ def *(s)
241
+ process("*", s)
242
+ end
243
+
244
+ # Divides two spreadsheets column by column and returns a new spread
245
+ # sheet with the result
246
+ # 1 2 3 3 2 1 1/3 1 3
247
+ # 4 5 6 / 6 5 4 = 2/3 1 6/4
248
+ # 7 8 9 9 8 7 7/9 1 9/7
249
+ def /(s)
250
+ process("/", s)
251
+ end
252
+
253
+ # Adds two spreadsheets column by column and returns a new spread
254
+ # sheet with the result
255
+ # 1 2 3 3 2 1 4 4 4
256
+ # 4 5 6 + 6 5 4 = 10 10 10
257
+ # 7 8 9 9 8 7 16 16 16
258
+ def +(s)
259
+ process("+", s)
260
+ end
261
+
262
+ # Subtracts two spreadsheets column by column and returns a new spread
263
+ # sheet with the result
264
+ # 1 2 3 3 2 1 -2 0 2
265
+ # 4 5 6 - 6 5 4 = -2 0 2
266
+ # 7 8 9 9 8 7 -2 0 2
267
+ def -(s)
268
+ process("-", s)
269
+ end
270
+
271
+ # Compares if two spread sheets are equal. Two spread sheets are equal
272
+ # if the spread sheets A and B are equal if Aij = Bij, that is elements at
273
+ # the same position are equal
274
+ def ==(other)
275
+ return false unless other.instance_of?(SpreadSheet)
276
+ return false unless dim == other.dim
277
+ row_count, col_count = dim
278
+ 0.upto(row_count - 1) do |r|
279
+ 0.upto(col_count - 1) do |c|
280
+ return false unless rows[r][c] == other.rows[r][c]
281
+ end
282
+ end
283
+ true
284
+ end
285
+
286
+ # Yields each column
287
+ def each_column
288
+ 0.upto(ncols-1) { |i| yield self[nil,i] }
289
+ end
290
+
291
+ # Collects the operation on each column and returns the result in an array
292
+ def column_collect(&block)
293
+ result = []
294
+ 0.upto(ncols-1) { |i| result << block.call(self[nil,i]) }
295
+ result
296
+ end
297
+
298
+ # Renames the row and column labels
299
+ #
300
+ # sheet.rename(rows: ['Row 1', 'Row 2'], cols: ['Col 1', 'Col 2'])
301
+ #
302
+ # If the provided rows and columns are larger than the spread sheet's rows
303
+ # and columns then only the respective row and column values are used. If
304
+ # the row and column labels are fewer than the respective row and column
305
+ # sizes the old labels are left untouched for the missing new labels
306
+ def rename(opts = {})
307
+ if opts[:rows]
308
+ opts[:rows] = opts[:rows][0,nrows]
309
+ opts[:rows] += row_labels[opts[:rows].size, nrows]
310
+ end
311
+
312
+ if opts[:cols]
313
+ opts[:cols] = opts[:cols][0,ncols]
314
+ opts[:cols] += col_labels[opts[:cols].size, ncols]
315
+ end
316
+
317
+ @row_labels = opts[:rows] if opts[:rows]
318
+ @col_labels = opts[:cols] if opts[:cols]
319
+ end
320
+
321
+ # Writes spread sheet to a file separated with ';'
322
+ def write(file)
323
+ File.open(file, 'w') do |out|
324
+ out.puts ";#{col_labels.join(';')}"
325
+ rows.each_with_index do |row, i|
326
+ out.puts "#{row_labels[i]};#{row.join(';')}"
327
+ end
328
+ end
329
+ end
330
+
331
+ # Prints a summary of the spread sheet
332
+ def summary
333
+ puts "\nSummary"
334
+ puts "-------\n"
335
+ puts "rows: #{nrows}, columns: #{ncols}, dimension: #{dim}, size: #{size}"
336
+ puts
337
+ puts "row labels:\n #{row_labels}"
338
+ puts "column labels:\n #{col_labels}\n"
339
+ end
340
+
341
+ # Prints the spread sheet in a matrix with column labels and row labels. If
342
+ # no labels are available the column number and row number is printed
343
+ def to_s
344
+ col_label_sizes = col_labels.collect { |c| c.to_s.size + 2 }
345
+ row_label_size = row_labels.collect { |r| r.to_s.size + 2 }.max
346
+
347
+ row_col_sizes = rows.transpose.collect { |r| r.collect { |c| c.to_s.size } }
348
+
349
+ i = -1
350
+ col_sizes = col_label_sizes.collect do |s|
351
+ i += 1
352
+ [row_col_sizes[i],s].flatten.max + 1
353
+ end
354
+
355
+ s = (sprintf("%#{row_label_size}s", " "))
356
+ col_labels.each_with_index { |l,i| s << (sprintf("%#{col_sizes[i]}s",
357
+ "[#{l}]")) }
358
+ s << "\n"
359
+
360
+ rows.each_with_index do |row, i|
361
+ s << (sprintf("%#{row_label_size}s", "[#{row_labels[i]}]"))
362
+ row.each_with_index { |c,j| s << (sprintf("%#{col_sizes[j]}s", c)) }
363
+ s << "\n"
364
+ end
365
+
366
+ s
367
+ end
368
+
369
+ private
370
+
371
+ # Creates rows from provided array or file. If array doesn't provide
372
+ # equal column sizes the array is extended with NotAvailable values
373
+ def rows_from_params(opts)
374
+ col_count = opts[:cols]
375
+ row_count = opts[:rows]
376
+
377
+ size = row_count * col_count if row_count && col_count
378
+
379
+ rows = []
380
+
381
+ if values = opts[:values]
382
+ if size
383
+ values += [NotAvailable] * (size - values.size)
384
+ elsif col_count
385
+ values += [NotAvailable] * ((col_count - values.size) % col_count)
386
+ elsif row_count
387
+ values += [NotAvailable] * ((row_count - values.size) % row_count)
388
+ col_count = values.size / row_count
389
+ else
390
+ col_count = Math.sqrt(values.size).ceil
391
+ values += [NotAvailable] * ((col_count - values.size) % col_count)
392
+ end
393
+ values.each_slice(col_count) { |row| rows << row }
394
+ elsif opts[:file]
395
+ File.readlines(opts[:file]).each do |line|
396
+ row = line.split(';')
397
+ rows << row.collect { |v|
398
+ v.strip.empty? ? NotAvailable : Float(v.chomp) rescue v.chomp
399
+ }
400
+ end
401
+ end
402
+
403
+ rows
404
+ end
405
+
406
+ # Checks whether the rows are valid, that is
407
+ # * same size
408
+ # * not nil
409
+ # * at least one row
410
+ def check_validity_of(rows)
411
+ raise "rows need to be arrays" if !rows_are_arrays?(rows)
412
+ raise "needs at least one row" if rows.empty?
413
+ raise "rows must be of same column size" if !same_column_size?(rows)
414
+ end
415
+
416
+ # Checks whether all rows have the same column size. Returns true if
417
+ # all columns have the same column size
418
+ def same_column_size?(rows)
419
+ offset = opts[:c] ? 1 : 0
420
+ return true if rows.size == 1 + offset
421
+ (0 + offset).upto(rows.size - 2) do |i|
422
+ return false unless rows[i].size == rows[i+1].size
423
+ end
424
+ true
425
+ end
426
+
427
+ # Checks whether the rows are provided as arrays. If a non array element
428
+ # is found false is returned otherwise true
429
+ def rows_are_arrays?(rows)
430
+ rows.each { |row| return false unless row.respond_to?(:to_ary) }
431
+ true
432
+ end
433
+
434
+ def create_labels(rows)
435
+ if opts[:c]
436
+ col_labels = extract_col_labels(rows)
437
+ end
438
+ if opts[:r]
439
+ row_labels = extract_row_labels(rows)
440
+ end
441
+
442
+ if opts[:row_labels]
443
+ row_labels = opts[:row_labels]
444
+ opts[:r] = true
445
+ end
446
+ if opts[:col_labels]
447
+ col_labels = opts[:col_labels]
448
+ opts[:c] = true
449
+ end
450
+
451
+ if opts[:c]
452
+ if col_labels.size > rows[0].size
453
+ col_labels = col_labels[col_labels.size - rows[0].size,
454
+ rows[0].size]
455
+ else
456
+ col_labels = col_labels + (0..rows[0].size-1).to_a[col_labels.size,
457
+ rows[0].size]
458
+ end
459
+ end
460
+
461
+ if opts[:r]
462
+ if row_labels.size > rows.size
463
+ row_labels = row_labels[row_labels.size - rows.size,
464
+ rows.size]
465
+ else
466
+ row_labels = row_labels + (0..rows.size-1).to_a[row_labels.size,
467
+ rows.size]
468
+ end
469
+ end
470
+
471
+ row_labels = (0..rows.size-1).to_a unless row_labels
472
+ col_labels = (0..rows[0].size-1).to_a unless col_labels
473
+ [row_labels, col_labels]
474
+ end
475
+
476
+ def extract_col_labels(rows)
477
+ col_labels = rows.shift
478
+ end
479
+
480
+ def extract_row_labels(rows)
481
+ row_labels = []
482
+ rows.each { |row| row_labels << row.shift }
483
+ row_labels
484
+ end
485
+
486
+ # Coerces a number or an array to a spread sheet
487
+ def coerce(value)
488
+ return SpreadSheet.new([value]) if value.is_a?(Numeric)
489
+ return SpreadSheet.new(value) if value.is_a?(Array)
490
+ end
491
+
492
+ # Conducts the calculation of this spread sheet with the provided value
493
+ # based on the operator. It s is a number or an array it is coerced into
494
+ # a spread sheet
495
+ def process(operator, s)
496
+ s = coerce(s) || s
497
+ raise "operand needs to be a SpreadSheet, Numeric or Array" unless s.is_a?(SpreadSheet)
498
+ result = []
499
+ rlabel = []
500
+ clabel = []
501
+ s1_row_count, s1_col_count = dim
502
+ s2_row_count, s2_col_count = s.dim
503
+ row_count = [s1_row_count, s2_row_count].max
504
+ col_count = [s1_col_count, s2_col_count].max
505
+ 0.upto(row_count - 1) do |r|
506
+ r1 = r % s1_row_count
507
+ r2 = r % s2_row_count
508
+ rlabel << "#{row_labels[r1]}#{operator}#{s.row_labels[r2]}"
509
+ element = []
510
+ 0.upto(col_count - 1) do |c|
511
+ c1 = c % s1_col_count
512
+ c2 = c % s2_col_count
513
+ clabel << "#{col_labels[c1]}#{operator}#{s.col_labels[c2]}"
514
+ element << rows[r1][c1].send(operator, s.rows[r2][c2])
515
+ end
516
+ result << element
517
+ end
518
+ SpreadSheet.new(*result, row_labels: rlabel, col_labels: clabel)
519
+ end
520
+
521
+ end
522
+
523
+ end
@@ -0,0 +1,104 @@
1
+ # Operating csv files
2
+ module Sycsvpro
3
+
4
+ # SpreadSheetBuilder is used in the command line interface of sycsvpro to
5
+ # use SpreadSheet from the command line
6
+ class SpreadSheetBuilder
7
+
8
+ # The result of the SpreadSheet operation is written to this file
9
+ attr_reader :outfile
10
+ # The operands, that is the spread sheets that are used in the arithmetic
11
+ # operation
12
+ attr_reader :operands
13
+ # The spread sheet operation where the operands are used
14
+ attr_reader :operation
15
+ # Indicates whether the result should be printed
16
+ attr_reader :print
17
+
18
+ # A spread sheet builder is doing arithmetic operations and can be called
19
+ # like this:
20
+ #
21
+ # SpreadSheetBuilder.new(outfile: "out.csv",
22
+ # files: "f1.csv,f2.csv",
23
+ # rlabels: "true,false",
24
+ # clabels: "false,true",
25
+ # aliases: "a,b",
26
+ # operation: "(a*b).transpose",
27
+ # print: "true").execute
28
+ #
29
+ # outfile: file where the result of the operation is written to
30
+ # files: files that hold the spread sheet data
31
+ # rlabels: indication whether the corresponding file has row labels
32
+ # clabels: indication whether the corresponding file has column labels
33
+ # aliases: symbols that correspond to the spread sheet created from the
34
+ # files. The symbols are used in the operation. The symbols have
35
+ # to be choosen carefully not to conflict with existing methods
36
+ # and variables
37
+ # operation: arithmetic operation on spread sheets using the aliases as
38
+ # place holders for the spread sheets. The last evaluated
39
+ # operation is returned as result and saved to outfile in case
40
+ # the result is a spread sheet. In all other cases the result can
41
+ # be printed with the print flag.
42
+ # print: print the result
43
+ def initialize(opts = {})
44
+ @print = opts[:print]
45
+ @operands = create_operands(opts)
46
+ @outfile = opts[:outfile]
47
+ @operation = opts[:operation]
48
+ end
49
+
50
+ # Returns the spread sheet operands when called in the arithmetic operation
51
+ def method_missing(name, *args, &block)
52
+ super unless operands.keys.index(name.to_s)
53
+ operands[name.to_s]
54
+ end
55
+
56
+ # Executes the operation and writes the result to the outfile
57
+ def execute
58
+ result = eval(operation)
59
+ if outfile
60
+ if result.is_a?(SpreadSheet)
61
+ result.write(outfile)
62
+ else
63
+ puts
64
+ puts "Warning: Result is no spread sheet and not written to file!"
65
+ puts " To view the result use -p flag" unless print
66
+ end
67
+ end
68
+
69
+ if print
70
+ puts
71
+ puts "Operation"
72
+ puts "---------"
73
+ operation.split(';').each { |o| puts o }
74
+ puts
75
+ puts "Result"
76
+ puts "------"
77
+ if result.nil? || result.empty?
78
+ puts result.inspect
79
+ else
80
+ puts result
81
+ end
82
+ puts
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ # Creates the spread sheet operands for the arithmetic operation
89
+ def create_operands(opts)
90
+ files = opts[:files].split(',')
91
+ rlabels = opts[:rlabels].split(',').collect { |l| l.upcase == "TRUE" }
92
+ clabels = opts[:clabels].split(',').collect { |l| l.upcase == "TRUE" }
93
+
94
+ operands = {}
95
+ opts[:aliases].split(',').each_with_index do |a,i|
96
+ operands[a] = SpreadSheet.new(file: files[i],
97
+ r: rlabels[i], c: clabels[i])
98
+ end
99
+
100
+ operands
101
+ end
102
+ end
103
+
104
+ end
@@ -6,22 +6,22 @@ module Sycsvpro
6
6
  # Example
7
7
  #
8
8
  # infile.csv
9
- # | Year | SP | RP | Total | SP-O | RP-O | O |
10
- # | ---- | -- | -- | ----- | ---- | ---- | --- |
11
- # | | 10 | 20 | 30 | 100 | 40 | 140 |
12
- # | 2008 | 5 | 10 | 15 | 10 | 20 | 10 |
13
- # | 2009 | 2 | 5 | 5 | 20 | 10 | 30 |
14
- # | 2010 | 3 | 5 | 10 | 70 | 10 | 100 |
9
+ # | Year | SP | RP | Total | SP-O | RP-O | O |
10
+ # | ---- | -- | -- | ----- | ---- | ---- | --- |
11
+ # | | 10 | 20 | 30 | 100 | 40 | 140 |
12
+ # | 2008 | 5 | 10 | 15 | 10 | 20 | 10 |
13
+ # | 2009 | 2 | 5 | 5 | 20 | 10 | 30 |
14
+ # | 2010 | 3 | 5 | 10 | 70 | 10 | 100 |
15
15
  #
16
16
  # outfile.csv
17
- # | Year | | 2008 | 2009 | 2010 |
18
- # | ----- | --- | ---- | ---- | ---- |
19
- # | SP | 10 | 5 | 5 | 3 |
20
- # | RP | 20 | 10 | 10 | 5 |
21
- # | Total | 30 | 15 | 15 | 10 |
22
- # | SP-O | 100 | 10 | 10 | 70 |
23
- # | RP-O | 40 | 20 | 20 | 10 |
24
- # | O | 140 | 10 | 30 | 100 |
17
+ # | Year | | 2008 | 2009 | 2010 |
18
+ # | ----- | --- | ---- | ---- | ---- |
19
+ # | SP | 10 | 5 | 5 | 3 |
20
+ # | RP | 20 | 10 | 10 | 5 |
21
+ # | Total | 30 | 15 | 15 | 10 |
22
+ # | SP-O | 100 | 10 | 10 | 70 |
23
+ # | RP-O | 40 | 20 | 20 | 10 |
24
+ # | O | 140 | 10 | 30 | 100 |
25
25
  #
26
26
  class Transposer
27
27
 
@@ -37,7 +37,6 @@ module Sycsvpro
37
37
  attr_reader :col_filter
38
38
 
39
39
  # Create a new Transpose
40
- # :call-seq:
41
40
  # Sycsvpro::Transpose(infile: "infile.csv",
42
41
  # outfile: "outfile.csv",
43
42
  # rows: "0,3-5",