daru_lite 0.1

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.
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