daru_lite 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE.md +18 -0
  3. data/.github/workflows/ci.yml +33 -0
  4. data/.gitignore +10 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +27 -0
  7. data/.rubocop_todo.yml +137 -0
  8. data/CONTRIBUTING.md +47 -0
  9. data/Gemfile +2 -0
  10. data/History.md +4 -0
  11. data/LICENSE +24 -0
  12. data/README.md +218 -0
  13. data/Rakefile +69 -0
  14. data/ReleasePolicy.md +20 -0
  15. data/benchmarks/TradeoffData.csv +65 -0
  16. data/benchmarks/csv_reading.rb +22 -0
  17. data/benchmarks/dataframe_creation.rb +39 -0
  18. data/benchmarks/db_loading.rb +34 -0
  19. data/benchmarks/duplicating.rb +45 -0
  20. data/benchmarks/group_by.rb +32 -0
  21. data/benchmarks/joining.rb +52 -0
  22. data/benchmarks/row_access.rb +41 -0
  23. data/benchmarks/row_assign.rb +36 -0
  24. data/benchmarks/sorting.rb +51 -0
  25. data/benchmarks/statistics.rb +28 -0
  26. data/benchmarks/vector_access.rb +31 -0
  27. data/benchmarks/vector_assign.rb +42 -0
  28. data/benchmarks/where_clause.rb +48 -0
  29. data/benchmarks/where_vs_filter.rb +28 -0
  30. data/daru_lite.gemspec +55 -0
  31. data/images/README.md +5 -0
  32. data/images/con0.png +0 -0
  33. data/images/con1.png +0 -0
  34. data/images/init0.png +0 -0
  35. data/images/init1.png +0 -0
  36. data/images/man0.png +0 -0
  37. data/images/man1.png +0 -0
  38. data/images/man2.png +0 -0
  39. data/images/man3.png +0 -0
  40. data/images/man4.png +0 -0
  41. data/images/man5.png +0 -0
  42. data/images/man6.png +0 -0
  43. data/lib/daru_lite/accessors/array_wrapper.rb +109 -0
  44. data/lib/daru_lite/accessors/dataframe_by_row.rb +25 -0
  45. data/lib/daru_lite/accessors/mdarray_wrapper.rb +7 -0
  46. data/lib/daru_lite/category.rb +929 -0
  47. data/lib/daru_lite/configuration.rb +34 -0
  48. data/lib/daru_lite/core/group_by.rb +403 -0
  49. data/lib/daru_lite/core/merge.rb +270 -0
  50. data/lib/daru_lite/core/query.rb +109 -0
  51. data/lib/daru_lite/dataframe.rb +3080 -0
  52. data/lib/daru_lite/date_time/index.rb +569 -0
  53. data/lib/daru_lite/date_time/offsets.rb +397 -0
  54. data/lib/daru_lite/exceptions.rb +2 -0
  55. data/lib/daru_lite/extensions/which_dsl.rb +53 -0
  56. data/lib/daru_lite/formatters/table.rb +52 -0
  57. data/lib/daru_lite/helpers/array.rb +53 -0
  58. data/lib/daru_lite/index/categorical_index.rb +201 -0
  59. data/lib/daru_lite/index/index.rb +374 -0
  60. data/lib/daru_lite/index/multi_index.rb +374 -0
  61. data/lib/daru_lite/io/csv/converters.rb +21 -0
  62. data/lib/daru_lite/io/io.rb +294 -0
  63. data/lib/daru_lite/io/sql_data_source.rb +97 -0
  64. data/lib/daru_lite/iruby/helpers.rb +38 -0
  65. data/lib/daru_lite/iruby/templates/dataframe.html.erb +5 -0
  66. data/lib/daru_lite/iruby/templates/dataframe_mi.html.erb +5 -0
  67. data/lib/daru_lite/iruby/templates/dataframe_mi_tbody.html.erb +35 -0
  68. data/lib/daru_lite/iruby/templates/dataframe_mi_thead.html.erb +21 -0
  69. data/lib/daru_lite/iruby/templates/dataframe_tbody.html.erb +28 -0
  70. data/lib/daru_lite/iruby/templates/dataframe_thead.html.erb +21 -0
  71. data/lib/daru_lite/iruby/templates/multi_index.html.erb +12 -0
  72. data/lib/daru_lite/iruby/templates/vector.html.erb +5 -0
  73. data/lib/daru_lite/iruby/templates/vector_mi.html.erb +5 -0
  74. data/lib/daru_lite/iruby/templates/vector_mi_tbody.html.erb +26 -0
  75. data/lib/daru_lite/iruby/templates/vector_mi_thead.html.erb +8 -0
  76. data/lib/daru_lite/iruby/templates/vector_tbody.html.erb +17 -0
  77. data/lib/daru_lite/iruby/templates/vector_thead.html.erb +8 -0
  78. data/lib/daru_lite/maths/arithmetic/dataframe.rb +91 -0
  79. data/lib/daru_lite/maths/arithmetic/vector.rb +117 -0
  80. data/lib/daru_lite/maths/statistics/dataframe.rb +202 -0
  81. data/lib/daru_lite/maths/statistics/vector.rb +1019 -0
  82. data/lib/daru_lite/monkeys.rb +56 -0
  83. data/lib/daru_lite/vector.rb +1678 -0
  84. data/lib/daru_lite/version.rb +3 -0
  85. data/lib/daru_lite.rb +99 -0
  86. data/profile/_base.rb +23 -0
  87. data/profile/df_to_a.rb +10 -0
  88. data/profile/filter.rb +13 -0
  89. data/profile/joining.rb +13 -0
  90. data/profile/sorting.rb +12 -0
  91. data/profile/vector_each_with_index.rb +9 -0
  92. data/profile/vector_new.rb +9 -0
  93. data/spec/accessors/array_wrapper_spec.rb +3 -0
  94. data/spec/category_spec.rb +1741 -0
  95. data/spec/core/group_by_spec.rb +655 -0
  96. data/spec/core/merge_spec.rb +179 -0
  97. data/spec/core/query_spec.rb +347 -0
  98. data/spec/daru_lite_spec.rb +22 -0
  99. data/spec/dataframe_spec.rb +4330 -0
  100. data/spec/date_time/data_spec.rb +197 -0
  101. data/spec/date_time/date_time_index_helper_spec.rb +72 -0
  102. data/spec/date_time/index_spec.rb +588 -0
  103. data/spec/date_time/offsets_spec.rb +465 -0
  104. data/spec/extensions/which_dsl_spec.rb +38 -0
  105. data/spec/fixtures/bank2.dat +200 -0
  106. data/spec/fixtures/boolean_converter_test.csv +5 -0
  107. data/spec/fixtures/countries.json +7794 -0
  108. data/spec/fixtures/duplicates.csv +32 -0
  109. data/spec/fixtures/eciresults.html +394 -0
  110. data/spec/fixtures/empties.dat +2 -0
  111. data/spec/fixtures/empty_rows_test.csv +17 -0
  112. data/spec/fixtures/macau.html +3691 -0
  113. data/spec/fixtures/macd_data.csv +150 -0
  114. data/spec/fixtures/matrix_test.csv +100 -0
  115. data/spec/fixtures/moneycontrol.html +6812 -0
  116. data/spec/fixtures/music_data.tsv +2501 -0
  117. data/spec/fixtures/repeated_fields.csv +7 -0
  118. data/spec/fixtures/sales-funnel.csv +18 -0
  119. data/spec/fixtures/scientific_notation.csv +4 -0
  120. data/spec/fixtures/string_converter_test.csv +5 -0
  121. data/spec/fixtures/strings.dat +2 -0
  122. data/spec/fixtures/test_xls.xls +0 -0
  123. data/spec/fixtures/test_xls_2.xls +0 -0
  124. data/spec/fixtures/url_test.txt~ +0 -0
  125. data/spec/fixtures/valid_markup.html +62 -0
  126. data/spec/fixtures/wiki_climate.html +1243 -0
  127. data/spec/fixtures/wiki_table_info.html +631 -0
  128. data/spec/formatters/table_formatter_spec.rb +137 -0
  129. data/spec/helpers_spec.rb +8 -0
  130. data/spec/index/categorical_index_spec.rb +170 -0
  131. data/spec/index/index_spec.rb +417 -0
  132. data/spec/index/multi_index_spec.rb +680 -0
  133. data/spec/io/io_spec.rb +373 -0
  134. data/spec/io/sql_data_source_spec.rb +56 -0
  135. data/spec/iruby/dataframe_spec.rb +170 -0
  136. data/spec/iruby/helpers_spec.rb +49 -0
  137. data/spec/iruby/multi_index_spec.rb +37 -0
  138. data/spec/iruby/vector_spec.rb +105 -0
  139. data/spec/maths/arithmetic/dataframe_spec.rb +148 -0
  140. data/spec/maths/arithmetic/vector_spec.rb +165 -0
  141. data/spec/maths/statistics/dataframe_spec.rb +178 -0
  142. data/spec/maths/statistics/vector_spec.rb +756 -0
  143. data/spec/monkeys_spec.rb +42 -0
  144. data/spec/shared/vector_display_spec.rb +213 -0
  145. data/spec/spec_helper.rb +87 -0
  146. data/spec/support/database_helper.rb +30 -0
  147. data/spec/support/matchers.rb +5 -0
  148. data/spec/vector_spec.rb +2293 -0
  149. metadata +571 -0
@@ -0,0 +1,97 @@
1
+ module DaruLite
2
+ module IO
3
+ class SqlDataSource
4
+ # @private
5
+ class Adapter
6
+ def initialize(conn, query)
7
+ @conn = conn
8
+ @query = query
9
+ end
10
+
11
+ def result_hash
12
+ column_names
13
+ .map(&:to_sym)
14
+ .zip(rows.transpose)
15
+ .to_h
16
+ end
17
+ end
18
+
19
+ # Private adapter class for DBI::DatabaseHandle
20
+ # @private
21
+ class DbiAdapter < Adapter
22
+ private
23
+
24
+ def column_names
25
+ result.column_names
26
+ end
27
+
28
+ def rows
29
+ result.to_a.map(&:to_a)
30
+ end
31
+
32
+ def result
33
+ @result ||= @conn.execute(@query)
34
+ end
35
+ end
36
+
37
+ # Private adapter class for connections of ActiveRecord
38
+ # @private
39
+ class ActiveRecordConnectionAdapter < Adapter
40
+ private
41
+
42
+ def column_names
43
+ result.columns
44
+ end
45
+
46
+ def rows
47
+ result.cast_values
48
+ end
49
+
50
+ def result
51
+ @result ||= @conn.exec_query(@query)
52
+ end
53
+ end
54
+
55
+ private_constant :DbiAdapter
56
+ private_constant :ActiveRecordConnectionAdapter
57
+
58
+ def self.make_dataframe(db, query)
59
+ new(db, query).make_dataframe
60
+ end
61
+
62
+ def initialize(db, query)
63
+ @adapter = init_adapter(db, query)
64
+ end
65
+
66
+ def make_dataframe
67
+ DaruLite::DataFrame.new(@adapter.result_hash).tap(&:update)
68
+ end
69
+
70
+ private
71
+
72
+ def init_adapter(db, query)
73
+ query = String.try_convert(query) or
74
+ raise ArgumentError, "Query must be a string, #{query.class} received"
75
+
76
+ db = attempt_sqlite3_connection(db) if db.is_a?(String) && Pathname(db).exist?
77
+
78
+ case db
79
+ when DBI::DatabaseHandle
80
+ DbiAdapter.new(db, query)
81
+ when ActiveRecord::ConnectionAdapters::AbstractAdapter
82
+ ActiveRecordConnectionAdapter.new(db, query)
83
+ else
84
+ raise ArgumentError, "Unknown database adapter type #{db.class}"
85
+ end
86
+ end
87
+
88
+ def attempt_sqlite3_connection(db)
89
+ DBI.connect("DBI:SQLite3:#{db}")
90
+ rescue SQLite3::NotADatabaseException
91
+ raise ArgumentError, "Expected #{db} to point to a SQLite3 database"
92
+ rescue NameError
93
+ raise NameError, "In order to establish a connection to #{db}, please require 'dbi'"
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,38 @@
1
+ module DaruLite
2
+ # @private
3
+ module IRuby
4
+ module Helpers
5
+ module_function
6
+
7
+ def tuples_with_rowspans(index)
8
+ index.sparse_tuples.transpose
9
+ .map { |r| nils_counted(r) }
10
+ .transpose.map(&:compact)
11
+ end
12
+
13
+ def tuples_with_colspans(index)
14
+ index.sparse_tuples.transpose
15
+ .map { |r| nils_counted(r) }
16
+ .map(&:compact)
17
+ end
18
+
19
+ # It is complicated, but the only algo I could think of.
20
+ # It does [:a, nil, nil, :b, nil, :c] # =>
21
+ # [[:a,3], nil, nil, [:b,2], nil, :c]
22
+ # Needed by tuples_with_colspans/rowspans, which we need for pretty HTML
23
+ def nils_counted(array)
24
+ grouped = [[array.first]]
25
+ array[1..].each do |val|
26
+ if val
27
+ grouped << [val]
28
+ else
29
+ grouped.last << val
30
+ end
31
+ end
32
+ grouped.flat_map do |items|
33
+ [[items.first, items.count], *Array.new(items.count - 1)]
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,5 @@
1
+ <b> DaruLite::DataFrame<%= name ? ": #{name} " : ''%>(<%=nrows%>x<%=ncols%>) </b>
2
+ <table border="1" class="dataframe">
3
+ <%= table_thead %>
4
+ <%= table_tbody %>
5
+ </table>
@@ -0,0 +1,5 @@
1
+ <b> DaruLite::DataFrame<%= name ? ": #{name} " : ''%>(<%=nrows%>x<%=ncols%>) </b>
2
+ <table>
3
+ <%= table_thead %>
4
+ <%= table_tbody %>
5
+ </table>
@@ -0,0 +1,35 @@
1
+ <tbody>
2
+ <% DaruLite::IRuby::Helpers.tuples_with_rowspans(@index).first(threshold).zip(@index.to_a).each do |tuple, index| %>
3
+ <tr>
4
+ <% tuple.each do |idx, span| %>
5
+ <th rowspan="<%= span %>"><%= idx %></th>
6
+ <% end %>
7
+ <% row[index].each do |element| %>
8
+ <td><%= element.to_s %></td>
9
+ <% end %>
10
+ </tr>
11
+ <% end %>
12
+
13
+ <% if nrows > threshold %>
14
+ <tr>
15
+ <% index.width.times do %>
16
+ <th>...</th>
17
+ <% end %>
18
+ <% @vectors.size.times do %>
19
+ <td>...</td>
20
+ <% end %>
21
+ </tr>
22
+
23
+ <% last_index = @index.to_a.last
24
+ last_row = row[last_index] %>
25
+
26
+ <tr>
27
+ <% last_index.each do |idx| %>
28
+ <th><%= idx %></td>
29
+ <% end %>
30
+ <% last_row.each do |element| %>
31
+ <td><%= element.to_s %></td>
32
+ <% end %>
33
+ </tr>
34
+ <% end %>
35
+ </tbody>
@@ -0,0 +1,21 @@
1
+ <thead>
2
+ <% if @vectors.is_a? MultiIndex %>
3
+ <% DaruLite::IRuby::Helpers.tuples_with_colspans(@vectors).each_with_index do |tuple, idx| %>
4
+ <tr>
5
+ <% if idx.zero? %>
6
+ <th colspan="<%= index.width %>" rowspan="<%= @vectors.width %>"></th>
7
+ <% end %>
8
+ <% tuple.each do |idx, span| %>
9
+ <th colspan="<%= span %>"><%= idx %></th>
10
+ <% end %>
11
+ </tr>
12
+ <% end %>
13
+ <% else %>
14
+ <tr>
15
+ <th colspan="<%= index.width %>"></th>
16
+ <% @vectors.each do |vector| %>
17
+ <th><%=vector%></th>
18
+ <% end %>
19
+ </tr>
20
+ <% end %>
21
+ </thead>
@@ -0,0 +1,28 @@
1
+ <tbody>
2
+ <% @index.first(threshold).each_with_index do |index, pos| %>
3
+ <tr>
4
+ <td><%= index %></td>
5
+ <% row.at(pos).each do |element| %>
6
+ <td><%= element.to_s %></td>
7
+ <% end %>
8
+ </tr>
9
+ <% end %>
10
+
11
+ <% if nrows > threshold %>
12
+ <tr>
13
+ <% (@vectors.size + 1).times do %>
14
+ <td>...</td>
15
+ <% end %>
16
+ </tr>
17
+
18
+ <% last_index = @index.to_a.last
19
+ last_row = row_at @index.size-1 %>
20
+
21
+ <tr>
22
+ <td><%= last_index %></td>
23
+ <% last_row.each do |element| %>
24
+ <td><%= element.to_s %></td>
25
+ <% end %>
26
+ </tr>
27
+ <% end %>
28
+ </tbody>
@@ -0,0 +1,21 @@
1
+ <thead>
2
+ <% if @vectors.is_a? MultiIndex %>
3
+ <% DaruLite::IRuby::Helpers.tuples_with_colspans(@vectors).each_with_index do |tuple, idx| %>
4
+ <tr>
5
+ <% if idx.zero? %>
6
+ <th rowspan="<%= @vectors.width %>"></th>
7
+ <% end %>
8
+ <% tuple.each do |idx, span| %>
9
+ <th colspan="<%= span %>"><%= idx %></th>
10
+ <% end %>
11
+ </tr>
12
+ <% end %>
13
+ <% else %>
14
+ <tr>
15
+ <th></th>
16
+ <% @vectors.each do |vector| %>
17
+ <th><%=vector%></th>
18
+ <% end %>
19
+ </tr>
20
+ <% end %>
21
+ </thead>
@@ -0,0 +1,12 @@
1
+ <table>
2
+ <tr>
3
+ <th colspan="<%= width %>">DaruLite::MultiIndex(<%= size %>x<%= width %>)</th>
4
+ </tr>
5
+ <% DaruLite::IRuby::Helpers.tuples_with_rowspans(self).each do |row| %>
6
+ <tr>
7
+ <% row.each do |val, span| %>
8
+ <th rowspan="<%= span %>"><%= val %></th>
9
+ <% end %>
10
+ </tr>
11
+ <% end %>
12
+ </table>
@@ -0,0 +1,5 @@
1
+ <b> DaruLite::Vector(<%= size %>)<%= ':category' if category? %> </b>
2
+ <table>
3
+ <%= table_thead %>
4
+ <%= table_tbody %>
5
+ </table>
@@ -0,0 +1,5 @@
1
+ <b> DaruLite::Vector(<%= size %>)<%= ':category' if category? %> </b>
2
+ <table>
3
+ <%= table_thead %>
4
+ <%= table_tbody %>
5
+ </table>
@@ -0,0 +1,26 @@
1
+ <tbody>
2
+ <% DaruLite::IRuby::Helpers.tuples_with_rowspans(@index).first(threshold).zip(to_a).each do |tuple, value| %>
3
+ <tr>
4
+ <% tuple.each do |idx, span| %>
5
+ <th rowspan="<%= span %>"><%= idx %></th>
6
+ <% end %>
7
+ <td><%= value %></td>
8
+ </tr>
9
+ <% end %>
10
+
11
+ <% if size > threshold %>
12
+ <% last_index = @index.to_a.last %>
13
+ <tr>
14
+ <% last_index.size.times do %>
15
+ <th>...</th>
16
+ <% end %>
17
+ <td>...</td>
18
+ </tr>
19
+ <tr>
20
+ <% last_index.each do |idx| %>
21
+ <th><%= idx %></td>
22
+ <% end %>
23
+ <td><%= self[last_index] %></td>
24
+ </tr>
25
+ <% end %>
26
+ </tbody>
@@ -0,0 +1,8 @@
1
+ <thead>
2
+ <% if name %>
3
+ <tr>
4
+ <th colspan="<%= index.width %>"> </th>
5
+ <th><%= name %></th>
6
+ </tr>
7
+ <% end %>
8
+ </thead>
@@ -0,0 +1,17 @@
1
+ <tbody>
2
+ <% @index.each_with_index.first(threshold).each do |index, pos| %>
3
+ <tr>
4
+ <td><%= index %></td>
5
+ <td><%= self.at(pos) %></td>
6
+ </tr>
7
+ <% end %>
8
+
9
+ <% if size > threshold %>
10
+ <% last_index = @index.size-1 %>
11
+ <tr><td>...</td><td>...</td></tr>
12
+ <tr>
13
+ <td><%= last_index %></td>
14
+ <td><%= self.at last_index %></td>
15
+ </tr>
16
+ <% end %>
17
+ </tbody>
@@ -0,0 +1,8 @@
1
+ <thead>
2
+ <% if name %>
3
+ <tr>
4
+ <th> </th>
5
+ <th><%= name %></th>
6
+ </tr>
7
+ <% end %>
8
+ </thead>
@@ -0,0 +1,91 @@
1
+ module DaruLite
2
+ module Maths
3
+ # Module encapsulating all aritmetic methods on DataFrame.
4
+ module Arithmetic
5
+ module DataFrame
6
+ # Add a scalar or another DataFrame
7
+ def +(other)
8
+ binary_operation :+, other
9
+ end
10
+
11
+ # Subtract a scalar or another DataFrame.
12
+ def -(other)
13
+ binary_operation :-, other
14
+ end
15
+
16
+ # Multiply a scalar or another DataFrame.
17
+ def *(other)
18
+ binary_operation :*, other
19
+ end
20
+
21
+ # Divide a scalar or another DataFrame.
22
+ def /(other)
23
+ binary_operation :/, other
24
+ end
25
+
26
+ # Modulus with a scalar or another DataFrame.
27
+ def %(other)
28
+ binary_operation :%, other
29
+ end
30
+
31
+ # Exponent with a scalar or another DataFrame.
32
+ def **(other)
33
+ binary_operation :**, other
34
+ end
35
+
36
+ # Calculate exponenential of all vectors with numeric values.
37
+ def exp
38
+ only_numerics(clone: false).recode(&:exp)
39
+ end
40
+
41
+ # Calcuate square root of numeric vectors.
42
+ def sqrt
43
+ only_numerics(clone: false).recode(&:sqrt)
44
+ end
45
+
46
+ def round(precision = 0)
47
+ only_numerics(clone: false).recode { |v| v.round(precision) }
48
+ end
49
+
50
+ private
51
+
52
+ def binary_operation(operation, other)
53
+ case other
54
+ when DaruLite::DataFrame
55
+ dataframe_binary_operation operation, other
56
+ else
57
+ scalar_binary_operation operation, other
58
+ end
59
+ end
60
+
61
+ def dataframe_binary_operation(operation, other)
62
+ all_vectors = (vectors.to_a | other.vectors.to_a).sort
63
+ all_indexes = (index.to_a | other.index.to_a).sort
64
+
65
+ hsh =
66
+ all_vectors.to_h do |vector_name|
67
+ vector = dataframe_binary_operation_on_vectors other, vector_name, operation, all_indexes
68
+
69
+ [vector_name, vector]
70
+ end
71
+
72
+ DaruLite::DataFrame.new(hsh, index: all_indexes, name: @name, dtype: @dtype)
73
+ end
74
+
75
+ def dataframe_binary_operation_on_vectors(other, name, operation, indexes)
76
+ if has_vector?(name) && other.has_vector?(name)
77
+ self[name].send(operation, other[name])
78
+ else
79
+ DaruLite::Vector.new([], index: indexes, name: name)
80
+ end
81
+ end
82
+
83
+ def scalar_binary_operation(operation, other)
84
+ dup.map_vectors! do |vector|
85
+ vector.numeric? ? vector.send(operation, other) : vector
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,117 @@
1
+ module DaruLite
2
+ module Maths
3
+ module Arithmetic
4
+ module Vector
5
+ def +(other)
6
+ binary_op :+, other
7
+ end
8
+
9
+ def -(other)
10
+ binary_op :-, other
11
+ end
12
+
13
+ def *(other)
14
+ binary_op :*, other
15
+ end
16
+
17
+ def /(other)
18
+ binary_op :/, other
19
+ end
20
+
21
+ def %(other)
22
+ binary_op :%, other
23
+ end
24
+
25
+ def **(other)
26
+ binary_op :**, other
27
+ end
28
+
29
+ def exp
30
+ math_unary_op :exp
31
+ end
32
+
33
+ def sqrt
34
+ math_unary_op :sqrt
35
+ end
36
+
37
+ def abs
38
+ recode { |e| e&.abs }
39
+ end
40
+
41
+ def round(precision = 0)
42
+ recode { |e| e&.round(precision) }
43
+ end
44
+
45
+ # Add specified vector.
46
+ #
47
+ # @param other [DaruLite::Vector] The vector thats added to this.
48
+ # @param opts [Boolean] :skipnil if true treats nils as 0.
49
+ #
50
+ # @example
51
+ #
52
+ # v0 = DaruLite::Vector.new [1, 2, nil, nil]
53
+ # v1 = DaruLite::Vector.new [2, 1, 3, nil]
54
+ #
55
+ # irb> v0.add v1
56
+ # => #<DaruLite::Vector(4)>
57
+ # 0 3
58
+ # 1 3
59
+ # 2 nil
60
+ # 3 nil
61
+ #
62
+ # irb> v0.add v1, skipnil: true
63
+ # => #<DaruLite::Vector(4)>
64
+ # 0 3
65
+ # 1 3
66
+ # 2 3
67
+ # 3 0
68
+ #
69
+ def add(other, opts = {})
70
+ v2v_binary :+, other, skipnil: opts.fetch(:skipnil, false)
71
+ end
72
+
73
+ private
74
+
75
+ def math_unary_op(operation)
76
+ recode { |e| Math.send(operation, e) unless e.nil? }
77
+ end
78
+
79
+ def binary_op(operation, other)
80
+ case other
81
+ when DaruLite::Vector
82
+ v2v_binary operation, other
83
+ else
84
+ v2o_binary operation, other
85
+ end
86
+ end
87
+
88
+ def v2o_binary(operation, other)
89
+ DaruLite::Vector.new map { |e| e&.send(operation, other) },
90
+ name: @name, index: @index
91
+ end
92
+
93
+ def v2v_binary(operation, other, opts = {})
94
+ # FIXME: why the sorting?.. - zverok, 2016-05-18
95
+ index = ArrayHelper.sort_composite_data(@index.to_a | other.index.to_a)
96
+
97
+ elements = index.map do |idx|
98
+ this = self.index.include?(idx) ? self[idx] : nil
99
+ that = other.index.include?(idx) ? other[idx] : nil
100
+ this, that = zero_nil_args(this, that, opts.fetch(:skipnil, false))
101
+ this && that ? this.send(operation, that) : nil
102
+ end
103
+
104
+ DaruLite::Vector.new(elements, name: @name, index: index)
105
+ end
106
+
107
+ def zero_nil_args(this, that, skipnil)
108
+ if skipnil
109
+ this = 0 if this.nil?
110
+ that = 0 if that.nil?
111
+ end
112
+ [this, that]
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end