daru 0.1.5 → 0.3

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 (106) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE.md +18 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +21 -7
  5. data/.travis.yml +10 -5
  6. data/CONTRIBUTING.md +15 -10
  7. data/History.md +124 -2
  8. data/README.md +37 -9
  9. data/ReleasePolicy.md +20 -0
  10. data/benchmarks/db_loading.rb +34 -0
  11. data/benchmarks/statistics.rb +6 -6
  12. data/benchmarks/where_clause.rb +1 -1
  13. data/benchmarks/where_vs_filter.rb +1 -1
  14. data/daru.gemspec +17 -41
  15. data/lib/daru.rb +10 -13
  16. data/lib/daru/accessors/gsl_wrapper.rb +1 -1
  17. data/lib/daru/accessors/nmatrix_wrapper.rb +2 -0
  18. data/lib/daru/category.rb +29 -15
  19. data/lib/daru/configuration.rb +34 -0
  20. data/lib/daru/core/group_by.rb +158 -77
  21. data/lib/daru/core/merge.rb +12 -3
  22. data/lib/daru/core/query.rb +20 -4
  23. data/lib/daru/dataframe.rb +692 -118
  24. data/lib/daru/date_time/index.rb +14 -11
  25. data/lib/daru/date_time/offsets.rb +9 -1
  26. data/lib/daru/extensions/which_dsl.rb +55 -0
  27. data/lib/daru/formatters/table.rb +3 -5
  28. data/lib/daru/index/categorical_index.rb +4 -4
  29. data/lib/daru/index/index.rb +131 -42
  30. data/lib/daru/index/multi_index.rb +118 -10
  31. data/lib/daru/io/csv/converters.rb +21 -0
  32. data/lib/daru/io/io.rb +105 -33
  33. data/lib/daru/io/sql_data_source.rb +10 -0
  34. data/lib/daru/iruby/templates/dataframe.html.erb +4 -51
  35. data/lib/daru/iruby/templates/dataframe_mi.html.erb +3 -56
  36. data/lib/daru/iruby/templates/dataframe_mi_tbody.html.erb +35 -0
  37. data/lib/daru/iruby/templates/dataframe_mi_thead.html.erb +21 -0
  38. data/lib/daru/iruby/templates/dataframe_tbody.html.erb +28 -0
  39. data/lib/daru/iruby/templates/dataframe_thead.html.erb +21 -0
  40. data/lib/daru/iruby/templates/vector.html.erb +3 -25
  41. data/lib/daru/iruby/templates/vector_mi.html.erb +3 -34
  42. data/lib/daru/iruby/templates/vector_mi_tbody.html.erb +26 -0
  43. data/lib/daru/iruby/templates/vector_mi_thead.html.erb +8 -0
  44. data/lib/daru/iruby/templates/vector_tbody.html.erb +17 -0
  45. data/lib/daru/iruby/templates/vector_thead.html.erb +8 -0
  46. data/lib/daru/maths/arithmetic/vector.rb +38 -2
  47. data/lib/daru/maths/statistics/dataframe.rb +28 -30
  48. data/lib/daru/maths/statistics/vector.rb +295 -41
  49. data/lib/daru/plotting/gruff/dataframe.rb +13 -15
  50. data/lib/daru/plotting/nyaplot/category.rb +1 -1
  51. data/lib/daru/plotting/nyaplot/dataframe.rb +15 -4
  52. data/lib/daru/plotting/nyaplot/vector.rb +1 -2
  53. data/lib/daru/vector.rb +308 -96
  54. data/lib/daru/version.rb +1 -1
  55. data/profile/vector_new.rb +9 -0
  56. data/spec/accessors/gsl_wrapper_spec.rb +38 -35
  57. data/spec/accessors/nmatrix_wrapper_spec.rb +25 -22
  58. data/spec/category_spec.rb +24 -20
  59. data/spec/core/group_by_spec.rb +238 -4
  60. data/spec/core/merge_spec.rb +1 -1
  61. data/spec/core/query_spec.rb +65 -50
  62. data/spec/daru_spec.rb +22 -0
  63. data/spec/dataframe_spec.rb +473 -16
  64. data/spec/date_time/date_time_index_helper_spec.rb +72 -0
  65. data/spec/date_time/index_spec.rb +34 -16
  66. data/spec/date_time/offsets_spec.rb +14 -0
  67. data/spec/extensions/rserve_spec.rb +1 -1
  68. data/spec/extensions/which_dsl_spec.rb +38 -0
  69. data/spec/fixtures/boolean_converter_test.csv +5 -0
  70. data/spec/fixtures/duplicates.csv +32 -0
  71. data/spec/fixtures/eciresults.html +394 -0
  72. data/spec/fixtures/empty_rows_test.csv +17 -0
  73. data/spec/fixtures/macau.html +3691 -0
  74. data/spec/fixtures/macd_data.csv +150 -0
  75. data/spec/fixtures/matrix_test.csv +55 -55
  76. data/spec/fixtures/moneycontrol.html +6812 -0
  77. data/spec/fixtures/string_converter_test.csv +5 -0
  78. data/spec/fixtures/test_xls.xls +0 -0
  79. data/spec/fixtures/test_xls_2.xls +0 -0
  80. data/spec/fixtures/url_test.txt~ +0 -0
  81. data/spec/fixtures/valid_markup.html +62 -0
  82. data/spec/fixtures/wiki_climate.html +1243 -0
  83. data/spec/fixtures/wiki_table_info.html +631 -0
  84. data/spec/formatters/table_formatter_spec.rb +29 -0
  85. data/spec/index/categorical_index_spec.rb +33 -33
  86. data/spec/index/index_spec.rb +160 -41
  87. data/spec/index/multi_index_spec.rb +143 -33
  88. data/spec/io/io_spec.rb +246 -2
  89. data/spec/io/sql_data_source_spec.rb +31 -41
  90. data/spec/iruby/dataframe_spec.rb +17 -19
  91. data/spec/iruby/vector_spec.rb +26 -28
  92. data/spec/maths/arithmetic/dataframe_spec.rb +1 -1
  93. data/spec/maths/arithmetic/vector_spec.rb +18 -0
  94. data/spec/maths/statistics/vector_spec.rb +153 -15
  95. data/spec/plotting/gruff/category_spec.rb +3 -3
  96. data/spec/plotting/gruff/dataframe_spec.rb +14 -4
  97. data/spec/plotting/gruff/vector_spec.rb +9 -9
  98. data/spec/plotting/nyaplot/category_spec.rb +5 -9
  99. data/spec/plotting/nyaplot/dataframe_spec.rb +95 -47
  100. data/spec/plotting/nyaplot/vector_spec.rb +5 -11
  101. data/spec/shared/vector_display_spec.rb +12 -14
  102. data/spec/spec_helper.rb +30 -7
  103. data/spec/support/matchers.rb +5 -0
  104. data/spec/vector_spec.rb +306 -72
  105. metadata +96 -55
  106. data/spec/fixtures/stock_data.csv +0 -500
@@ -93,11 +93,12 @@ module Daru
93
93
  def infer_offset data
94
94
  diffs = data.each_cons(2).map { |d1, d2| d2 - d1 }
95
95
 
96
- if diffs.uniq.count == 1
97
- TIME_INTERVALS[diffs.first].new
98
- else
99
- nil
100
- end
96
+ return nil unless diffs.uniq.count == 1
97
+
98
+ return TIME_INTERVALS[diffs.first].new if TIME_INTERVALS.include?(diffs.first)
99
+
100
+ number_of_seconds = diffs.first / Daru::Offsets::Second.new.multiplier
101
+ Daru::Offsets::Second.new(number_of_seconds.numerator) if number_of_seconds.denominator == 1
101
102
  end
102
103
 
103
104
  def find_index_of_date data, date_time
@@ -226,7 +227,7 @@ module Daru
226
227
  to_a.each(&block)
227
228
  end
228
229
 
229
- attr_reader :frequency, :offset, :periods
230
+ attr_reader :frequency, :offset, :periods, :keys
230
231
 
231
232
  # Create a DateTimeIndex with or without a frequency in data. The constructor
232
233
  # should be used for creating DateTimeIndex by directly passing in DateTime
@@ -253,6 +254,7 @@ module Daru
253
254
  # DateTime.new(2012,4,11), DateTime.new(2012,4,12)], freq: :infer)
254
255
  # #=>#<DateTimeIndex:84198340 offset=D periods=8 data=[2012-04-05T00:00:00+00:00...2012-04-12T00:00:00+00:00]>
255
256
  def initialize data, opts={freq: nil}
257
+ super data
256
258
  Helper.possibly_convert_to_date_time data
257
259
 
258
260
  @offset =
@@ -343,7 +345,7 @@ module Daru
343
345
 
344
346
  # Retreive a slice or a an individual index number from the index.
345
347
  #
346
- # @param [String, DateTime] Specify a date partially (as a String) or
348
+ # @param key [String, DateTime] Specify a date partially (as a String) or
347
349
  # completely to retrieve.
348
350
  def [] *key
349
351
  return slice(*key) if key.size != 1
@@ -405,7 +407,7 @@ module Daru
405
407
  @data
406
408
  else
407
409
  @data.sort_by(&:last)
408
- end.transpose.first
410
+ end.transpose.first || []
409
411
  end
410
412
 
411
413
  # Size of index.
@@ -419,6 +421,7 @@ module Daru
419
421
 
420
422
  def inspect
421
423
  meta = [@periods, @frequency ? "frequency=#{@frequency}" : nil].compact.join(', ')
424
+ return "#<#{self.class}(#{meta})>" if @data.empty?
422
425
  "#<#{self.class}(#{meta}) " \
423
426
  "#{@data.first[0]}...#{@data.last[0]}>"
424
427
  end
@@ -490,7 +493,7 @@ module Daru
490
493
  # @return [Array<Integer>] Array containing minutes of each index.
491
494
  # @!method sec
492
495
  # @return [Array<Integer>] Array containing seconds of each index.
493
- [:year, :month, :day, :hour, :min, :sec].each do |meth|
496
+ %i[year month day hour min sec].each do |meth|
494
497
  define_method(meth) do
495
498
  each_with_object([]) do |d, arr|
496
499
  arr << d.send(meth)
@@ -528,7 +531,7 @@ module Daru
528
531
  slice first, last
529
532
  end
530
533
 
531
- def slice_between_dates first, last # rubocop:disable Metrics/AbcSize
534
+ def slice_between_dates first, last # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity
532
535
  # about that ^ disable: I'm waiting for cleaner understanding
533
536
  # of offsets logic. Reference: https://github.com/v0dro/daru/commit/7e1c34aec9516a9ba33037b4a1daaaaf1de0726a#diff-a95ef410a8e1f4ea3cc48d231bb880faR250
534
537
  start = @data.bsearch { |d| d[0] >= first }
@@ -542,7 +545,7 @@ module Daru
542
545
  st = @data.index(start)
543
546
  en = after_en ? @data.index(after_en) - 1 : Helper.last_date(@data)[1]
544
547
  return start[1] if st == en
545
- DateTimeIndex.new(@data[st..en].transpose[0])
548
+ DateTimeIndex.new(@data[st..en].transpose[0] || []) # empty slice guard
546
549
  end
547
550
  end
548
551
 
@@ -86,7 +86,7 @@ module Daru
86
86
 
87
87
  module Offsets
88
88
  class DateOffsetType < DateOffset
89
- # @method initialize
89
+ # @!method initialize(n)
90
90
  # Initialize one of the subclasses of DateOffsetType with the number of the times
91
91
  # the offset should be applied, which is the supplied as the argument.
92
92
  #
@@ -111,6 +111,14 @@ module Daru
111
111
  def - date_time
112
112
  date_time - @n*multiplier
113
113
  end
114
+
115
+ def ==(other_obj)
116
+ other_obj.is_a?(Tick) && period == other_obj.period
117
+ end
118
+
119
+ def period
120
+ @n * multiplier
121
+ end
114
122
  end
115
123
 
116
124
  # Create a seconds offset
@@ -0,0 +1,55 @@
1
+ # Support for a simple query DSL for accessing where(), inspired by gem "squeel"
2
+
3
+ module Daru
4
+ class DataFrame
5
+ # a simple query DSL for accessing where(), inspired by gem "squeel"
6
+ # e.g.:
7
+ # df.which{ `FamilySize` == `FamilySize`.max }
8
+ # equals
9
+ # df.where( df['FamilySize'].eq( df['FamilySize'].max ) )
10
+ #
11
+ # e.g.:
12
+ # df.which{ (`NameTitle` == 'Dr') & (`Sex` == 'female') }
13
+ # equals
14
+ # df.where( df['NameTitle'].eq('Dr') & df['Sex'].eq('female') )
15
+ def which(&block)
16
+ WhichQuery.new(self, &block).exec
17
+ end
18
+ end
19
+
20
+ class WhichQuery
21
+ def initialize(df, &condition)
22
+ @df = df
23
+ @condition = condition
24
+ end
25
+
26
+ # executes a block of DSL code
27
+ def exec
28
+ query = instance_eval(&@condition)
29
+ @df.where(query)
30
+ end
31
+
32
+ def `(vector_name)
33
+ if !@df.has_vector?(vector_name) && @df.has_vector?(vector_name.to_sym)
34
+ vector_name = vector_name.to_sym
35
+ end
36
+ VectorWrapper.new(@df[vector_name])
37
+ end
38
+
39
+ class VectorWrapper < SimpleDelegator
40
+ {
41
+ :== => :eq,
42
+ :!= => :not_eq,
43
+ :< => :lt,
44
+ :<= => :lteq,
45
+ :> => :mt,
46
+ :>= => :mteq,
47
+ :=~ => :in
48
+ }.each do |opt, method|
49
+ define_method opt do |*args|
50
+ send(method, *args)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -10,15 +10,13 @@ module Daru
10
10
  @data = data || []
11
11
  @headers = (headers || []).to_a
12
12
  @row_headers = (row_headers || []).to_a
13
+ @row_headers = [''] * @data.to_a.size if @row_headers.empty?
13
14
  end
14
15
 
15
- DEFAULT_SPACING = 10
16
- DEFAULT_THRESHOLD = 15
17
-
18
16
  def format threshold=nil, spacing=nil
19
- rows = build_rows(threshold || DEFAULT_THRESHOLD)
17
+ rows = build_rows(threshold || Daru.max_rows)
20
18
 
21
- formatter = construct_formatter rows, spacing || DEFAULT_SPACING
19
+ formatter = construct_formatter rows, spacing || Daru.spacing
22
20
 
23
21
  rows.map { |r| formatter % r }.join("\n")
24
22
  end
@@ -45,7 +45,7 @@ module Daru
45
45
  # Returns positions given categories or positions
46
46
  # @note If the argument does not a valid category it treats it as position
47
47
  # value and return it as it is.
48
- # @param [Array<object>] *indexes categories or positions
48
+ # @param indexes [Array<object>] categories or positions
49
49
  # @example
50
50
  # x = Daru::CategoricalIndex.new [:a, 1, :a, 1, :c]
51
51
  # x.pos :a, 1
@@ -145,7 +145,7 @@ module Daru
145
145
  end
146
146
 
147
147
  # Return subset given categories or positions
148
- # @param [Array<object>] *indexes categories or positions
148
+ # @param indexes [Array<object>] categories or positions
149
149
  # @return [Daru::CategoricalIndex] subset of the self containing the
150
150
  # mentioned categories or positions
151
151
  # @example
@@ -161,7 +161,7 @@ module Daru
161
161
 
162
162
  # Takes positional values and returns subset of the self
163
163
  # capturing the categories at mentioned positions
164
- # @param [Array<Integer>] positional values
164
+ # @param positions [Array<Integer>] positional values
165
165
  # @return [object] index object
166
166
  # @example
167
167
  # idx = Daru::CategoricalIndex.new [:a, :b, :a, :b, :c]
@@ -178,7 +178,7 @@ module Daru
178
178
  end
179
179
 
180
180
  # Add specified index values to the index object
181
- # @param [Array<object>] *indexes index values to add
181
+ # @param indexes [Array<object>] index values to add
182
182
  # @return [Daru::CategoricalIndex] index object with added values
183
183
  # @example
184
184
  # idx = Daru::CategoricalIndex.new [:a, :b, :a, :b, :c]
@@ -1,5 +1,5 @@
1
1
  module Daru
2
- class Index
2
+ class Index # rubocop:disable Metrics/ClassLength
3
3
  include Enumerable
4
4
  # It so happens that over riding the .new method in a super class also
5
5
  # tampers with the default .new method for class that inherit from the
@@ -44,30 +44,37 @@ module Daru
44
44
  end
45
45
 
46
46
  attr_reader :relation_hash, :size
47
+ attr_accessor :name
47
48
 
48
- def initialize index
49
- index =
50
- case index
51
- when nil
52
- []
53
- when Integer
54
- index.times.to_a
55
- when Enumerable
56
- index.to_a
57
- else
58
- raise ArgumentError,
59
- "Cannot create index from #{index.class} #{index.inspect}"
60
- end
61
-
49
+ # @example
50
+ #
51
+ # idx = Daru::Index.new [:one, 'one', 1, 2, :two]
52
+ # => #<Daru::Index(5): {one, one, 1, 2, two}>
53
+ #
54
+ # # set the name
55
+ #
56
+ # idx.name = "index_name"
57
+ # => "index_name"
58
+ #
59
+ # idx
60
+ # => #<Daru::Index(5): index_name {one, one, 1, 2, two}>
61
+ #
62
+ # # set the name during initialization
63
+ #
64
+ # idx = Daru::Index.new [:one, 'one', 1, 2, :two], name: "index_name"
65
+ # => #<Daru::Index(5): index_name {one, one, 1, 2, two}>
66
+ def initialize index, opts={}
67
+ index = guess_index index
62
68
  @relation_hash = index.each_with_index.to_h.freeze
63
69
  @keys = @relation_hash.keys
64
70
  @size = @relation_hash.size
71
+ @name = opts[:name]
65
72
  end
66
73
 
67
74
  def ==(other)
68
75
  return false if self.class != other.class || other.size != @size
69
76
 
70
- @relation_hash.keys == other.to_a &&
77
+ @keys == other.to_a &&
71
78
  @relation_hash.values == other.relation_hash.values
72
79
  end
73
80
 
@@ -83,7 +90,7 @@ module Daru
83
90
  end
84
91
 
85
92
  # Returns true if all arguments are either a valid category or position
86
- # @param [Array<object>] *indexes categories or positions
93
+ # @param indexes [Array<object>] categories or positions
87
94
  # @return [true, false]
88
95
  # @example
89
96
  # idx.valid? :a, 2
@@ -97,7 +104,7 @@ module Daru
97
104
  # Returns positions given indexes or positions
98
105
  # @note If the arugent is both a valid index and a valid position,
99
106
  # it will treated as valid index
100
- # @param [Array<object>] *indexes indexes or positions
107
+ # @param indexes [Array<object>] indexes or positions
101
108
  # @example
102
109
  # x = Daru::Index.new [:a, :b, :c]
103
110
  # x.pos :a, 1
@@ -106,27 +113,30 @@ module Daru
106
113
  indexes = preprocess_range(indexes.first) if indexes.first.is_a? Range
107
114
 
108
115
  if indexes.size == 1
109
- self[indexes.first]
116
+ numeric_pos indexes.first
110
117
  else
111
- indexes.map { |index| by_single_key index }
118
+ indexes.map { |index| numeric_pos index }
112
119
  end
113
120
  end
114
121
 
115
122
  def subset *indexes
116
123
  if indexes.first.is_a? Range
117
- slice indexes.first.begin, indexes.first.end
124
+ start = indexes.first.begin
125
+ en = indexes.first.end
126
+
127
+ subset_slice start, en
118
128
  elsif include? indexes.first
119
129
  # Assume 'indexes' contain indexes not positions
120
130
  Daru::Index.new indexes
121
131
  else
122
132
  # Assume 'indexes' contain positions not indexes
123
- Daru::Index.new indexes.map { |k| key k }
133
+ Daru::Index.new(indexes.map { |k| key k })
124
134
  end
125
135
  end
126
136
 
127
137
  # Takes positional values and returns subset of the self
128
138
  # capturing the indexes at mentioned positions
129
- # @param [Array<Integer>] positional values
139
+ # @param positions [Array<Integer>] positional values
130
140
  # @return [object] index object
131
141
  # @example
132
142
  # idx = Daru::Index.new [:a, :b, :c]
@@ -143,10 +153,11 @@ module Daru
143
153
  end
144
154
 
145
155
  def inspect threshold=20
156
+ name_part = @name ? "#{@name} " : ''
146
157
  if size <= threshold
147
- "#<#{self.class}(#{size}): {#{to_a.join(', ')}}>"
158
+ "#<#{self.class}(#{size}): #{name_part}{#{to_a.join(', ')}}>"
148
159
  else
149
- "#<#{self.class}(#{size}): {#{to_a.first(threshold).join(', ')} ... #{to_a.last}}>"
160
+ "#<#{self.class}(#{size}): #{name_part}{#{to_a.first(threshold).join(', ')} ... #{to_a.last}}>"
150
161
  end
151
162
  end
152
163
 
@@ -154,12 +165,27 @@ module Daru
154
165
  start = args[0]
155
166
  en = args[1]
156
167
 
168
+ start_idx = @relation_hash[start]
169
+ en_idx = @relation_hash[en]
170
+
171
+ if start_idx.nil?
172
+ nil
173
+ elsif en_idx.nil?
174
+ Array(start_idx..size-1)
175
+ else
176
+ Array(start_idx..en_idx)
177
+ end
178
+ end
179
+
180
+ def subset_slice *args
181
+ start = args[0]
182
+ en = args[1]
183
+
157
184
  if start.is_a?(Integer) && en.is_a?(Integer)
158
185
  Index.new @keys[start..en]
159
186
  else
160
187
  start_idx = @relation_hash[start]
161
188
  en_idx = @relation_hash[en]
162
-
163
189
  Index.new @keys[start_idx..en_idx]
164
190
  end
165
191
  end
@@ -175,7 +201,7 @@ module Daru
175
201
  end
176
202
 
177
203
  def to_a
178
- @relation_hash.keys
204
+ @keys
179
205
  end
180
206
 
181
207
  def key(value)
@@ -187,12 +213,33 @@ module Daru
187
213
  @relation_hash.key? index
188
214
  end
189
215
 
216
+ # @note Do not use it to check for Float::NAN as
217
+ # Float::NAN == Float::NAN is false
218
+ # Return vector of booleans with value at ith position is either
219
+ # true or false depending upon whether index value at position i is equal to
220
+ # any of the values passed in the argument or not
221
+ # @param indexes [Array] values to equate with
222
+ # @return [Daru::Vector] vector of boolean values
223
+ # @example
224
+ # dv = Daru::Index.new [1, 2, 3, :one, 'one']
225
+ # dv.is_values 1, 'one'
226
+ # # => #<Daru::Vector(5)>
227
+ # # 0 true
228
+ # # 1 false
229
+ # # 2 false
230
+ # # 3 false
231
+ # # 4 true
232
+ def is_values(*indexes) # rubocop:disable Style/PredicateName
233
+ bool_array = @keys.map { |r| indexes.include?(r) }
234
+ Daru::Vector.new(bool_array)
235
+ end
236
+
190
237
  def empty?
191
- @relation_hash.empty?
238
+ @size.zero?
192
239
  end
193
240
 
194
241
  def dup
195
- Daru::Index.new @relation_hash.keys
242
+ Daru::Index.new @keys, name: @name
196
243
  end
197
244
 
198
245
  def add *indexes
@@ -211,7 +258,7 @@ module Daru
211
258
 
212
259
  # Provide an Index for sub vector produced
213
260
  #
214
- # @param input_indexes [Array] the input by user to index the vector
261
+ # @option * [Array] the input by user to index the vector
215
262
  # @return [Object] the Index object for sub vector produced
216
263
  def conform(*)
217
264
  self
@@ -222,8 +269,49 @@ module Daru
222
269
  self.class.new(new_order.map { |i| from[i] })
223
270
  end
224
271
 
272
+ # Sorts a `Index`, according to its values. Defaults to ascending order
273
+ # sorting.
274
+ #
275
+ # @param [Hash] opts the options for sort method.
276
+ # @option opts [Boolean] :ascending False, to get descending order.
277
+ #
278
+ # @return [Index] sorted `Index` according to its values.
279
+ #
280
+ # @example
281
+ # di = Daru::Index.new [100, 99, 101, 1, 2]
282
+ # # Say you want to sort in descending order
283
+ # di.sort(ascending: false) #=> Daru::Index.new [101, 100, 99, 2, 1]
284
+ # # Say you want to sort in ascending order
285
+ # di.sort #=> Daru::Index.new [1, 2, 99, 100, 101]
286
+ def sort opts={}
287
+ opts = {ascending: true}.merge(opts)
288
+
289
+ new_index = @keys.sort
290
+ new_index = new_index.reverse unless opts[:ascending]
291
+
292
+ self.class.new(new_index)
293
+ end
294
+
295
+ def to_df
296
+ Daru::DataFrame.new(name => to_a)
297
+ end
298
+
225
299
  private
226
300
 
301
+ def guess_index index
302
+ case index
303
+ when nil
304
+ []
305
+ when Integer
306
+ index.times.to_a
307
+ when Enumerable
308
+ index.to_a
309
+ else
310
+ raise ArgumentError,
311
+ "Cannot create index from #{index.class} #{index.inspect}"
312
+ end
313
+ end
314
+
227
315
  def preprocess_range rng
228
316
  start = rng.begin
229
317
  en = rng.end
@@ -243,30 +331,21 @@ module Daru
243
331
  end
244
332
 
245
333
  def by_multi_key *key
246
- if include? key[0]
247
- Daru::Index.new key.map { |k| k }
248
- else
249
- # Assume the user is specifing values for index not keys
250
- # Return index object having keys corresponding to values provided
251
- Daru::Index.new key.map { |k| key k }
252
- end
334
+ key.map { |k| by_single_key k }
253
335
  end
254
336
 
255
337
  def by_single_key key
256
338
  if @relation_hash.key?(key)
257
339
  @relation_hash[key]
258
- elsif key.is_a?(Numeric) && key < size
259
- key
260
340
  else
261
- raise IndexError, "Specified index #{key.inspect} does not exist"
341
+ nil
262
342
  end
263
343
  end
264
344
 
265
345
  # Raises IndexError when one of the positions is an invalid position
266
346
  def validate_positions *positions
267
- positions = [positions] if positions.is_a? Integer
268
347
  positions.each do |pos|
269
- raise IndexError, "#{pos} is not a valid position." if pos >= size
348
+ raise IndexError, "#{pos} is not a valid position." if pos >= size || pos < -size
270
349
  end
271
350
  end
272
351
 
@@ -285,5 +364,15 @@ module Daru
285
364
  positions
286
365
  end
287
366
  end
367
+
368
+ def numeric_pos key
369
+ if @relation_hash.key?(key)
370
+ @relation_hash[key]
371
+ elsif key.is_a?(Numeric) && (key < size && key >= -size)
372
+ key
373
+ else
374
+ raise IndexError, "Specified index #{key.inspect} does not exist"
375
+ end
376
+ end
288
377
  end
289
378
  end