def sort! vector_order, opts={}
      raise ArgumentError, 'Required atleast one vector name' if vector_order.empty?

      # To enable sorting with categorical data,
      # map categories to integers preserving their order
      old = convert_categorical_vectors vector_order
      block = sort_prepare_block vector_order, opts

      order = @index.size.times.sort(&block)
      new_index = @index.reorder order

      # To reverse map mapping of categorical data to integers
      restore_categorical_vectors old

      @data.each do |vector|
        vector.reorder! order
      end

      self.index = new_index

      self
    end
Sorts a dataframe (ascending/descending) in the given pripority sequence of
vectors, with or without a block.

@param vector_order [Array] The order of vector names in which the DataFrame
should be sorted.
@param opts [Hash] opts The options to sort with.
@option opts [TrueClass,FalseClass,Array] :ascending (true) Sort in ascending
or descending order. Specify Array corresponding to *order* for multiple
sort orders.
@option opts [Hash] :by (lambda{|a| a }) Specify attributes of objects to
to be used for sorting, for each vector name in *order* as a hash of
vector name and lambda expressions. In case a lambda for a vector is not
specified, the default will be used.
@option opts [TrueClass,FalseClass,Array] :handle_nils (false) Handle nils
automatically or not when a block is provided.
If set to True, nils will appear at top after sorting.

@example Sort a dataframe with a vector sequence.


df = Daru::DataFrame.new({a: [1,2,1,2,3], b: [5,4,3,2,1]})

df.sort [:a, :b]
# =>
# <Daru::DataFrame:30604000 @name = d6a9294e-2c09-418f-b646-aa9244653444 @size = 5>
#                   a          b
#        2          1          3
#        0          1          5
#        3          2          2
#        1          2          4
#        4          3          1

@example Sort a dataframe without a block. Here nils will be handled automatically.

df = Daru::DataFrame.new({a: [-3,nil,-1,nil,5], b: [4,3,2,1,4]})

df.sort([:a])
# =>
# <Daru::DataFrame:14810920 @name = c07fb5c7-2201-458d-b679-6a1f7ebfe49f @size = 5>
#                    a          b
#         1        nil          3
#         3        nil          1
#         0         -3          4
#         2         -1          2
#         4          5          4

@example Sort a dataframe with a block with nils handled automatically.

df = Daru::DataFrame.new({a: [nil,-1,1,nil,-1,1], b: ['aaa','aa',nil,'baaa','x',nil] })

df.sort [:b], by: {b: lambda { |a| a.length } }
# NoMethodError: undefined method `length' for nil:NilClass
# from (pry):8:in `block in __pry__'

df.sort [:b], by: {b: lambda { |a| a.length } }, handle_nils: true

# =>
# <Daru::DataFrame:28469540 @name = 5f986508-556f-468b-be0c-88cc3534445c @size = 6>
#                    a          b
#         2          1        nil
#         5          1        nil
#         4         -1          x
#         1         -1         aa
#         0        nil        aaa
#         3        nil       baaa

@example Sort a dataframe with a block with nils handled manually.

df = Daru::DataFrame.new({a: [nil,-1,1,nil,-1,1], b: ['aaa','aa',nil,'baaa','x',nil] })

# To print nils at the bottom one can use lambda { |a| (a.nil?)[1]:[0,a.length] }
df.sort [:b], by: {b: lambda { |a| (a.nil?)?[1]:[0,a.length] } }, handle_nils: true

# =>
#<Daru::DataFrame:22214180 @name = cd7703c7-1dca-4560-840b-5ea51a852ef9 @size = 6>
#                 a          b
#      4         -1          x
#      1         -1         aa
#      0        nil        aaa
#      3        nil       baaa
#      2          1        nil
#      5          1        nil