daru 0.1.5 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
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