Class: Rumale::Manifold::ClassicalMDS

Inherits:
Base::Estimator show all
Includes:
Base::Transformer
Defined in:
rumale-manifold/lib/rumale/manifold/classical_mds.rb

Overview

ClassicalMDS is a class that implements classical multi-dimensional scaling.

Examples:

require 'rumale/manifold/classical_mds'

mds = Rumale::Manifold::ClassicalMDS.new(n_components: 2)
representations = mds.fit_transform(data)

Instance Attribute Summary collapse

Attributes inherited from Base::Estimator

#params

Instance Method Summary collapse

Constructor Details

#initialize(n_components: 2, metric: 'euclidean') ⇒ ClassicalMDS

Create a new transformer with Classical MDS.

Parameters:

  • n_components (Integer) (defaults to: 2)

    The number of dimensions on representation space.

  • metric (String) (defaults to: 'euclidean')

    The metric to calculate the distances in original space. If metric is ‘euclidean’, Euclidean distance is calculated for distance in original space. If metric is ‘precomputed’, the fit and fit_transform methods expect to be given a distance matrix.



32
33
34
35
36
37
38
# File 'rumale-manifold/lib/rumale/manifold/classical_mds.rb', line 32

def initialize(n_components: 2, metric: 'euclidean')
  super()
  @params = {
    n_components: n_components,
    metric: metric
  }
end

Instance Attribute Details

#embeddingNumo::DFloat (readonly)

Return the data in representation space.

Returns:

  • (Numo::DFloat)

    (shape: [n_samples, n_components])



24
25
26
# File 'rumale-manifold/lib/rumale/manifold/classical_mds.rb', line 24

def embedding
  @embedding
end

Instance Method Details

#fit(x) ⇒ ClassicalMDS

Fit the model with given training data.

Returns The learned transformer itself.

Parameters:

  • x (Numo::DFloat)

    (shape: [n_samples, n_features]) The training data to be used for fitting the model. If the metric is ‘precomputed’, x must be a square distance matrix (shape: [n_samples, n_samples]).

Returns:



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'rumale-manifold/lib/rumale/manifold/classical_mds.rb', line 46

def fit(x, _not_used = nil)
  raise 'ClassicalMDS#fit requires Numo::Linalg but that is not loaded' unless enable_linalg?(warning: false)

  x = ::Rumale::Validation.check_convert_sample_array(x)
  if @params[:metric] == 'precomputed' && x.shape[0] != x.shape[1]
    raise ArgumentError, 'Expect the input distance matrix to be square.'
  end

  n_samples = x.shape[0]
  distance_mat = @params[:metric] == 'precomputed' ? x : ::Rumale::PairwiseMetric.euclidean_distance(x)

  centering_mat = Numo::DFloat.eye(n_samples) - Numo::DFloat.new(n_samples, n_samples).fill(1.fdiv(n_samples))
  kernel_mat = -0.5 * centering_mat.dot(distance_mat * distance_mat).dot(centering_mat)
  eig_vals, eig_vecs = Numo::Linalg.eigh(kernel_mat, vals_range: (n_samples - @params[:n_components])...n_samples)
  eig_vals = eig_vals.reverse
  eig_vecs = eig_vecs.reverse(1)
  @embedding = eig_vecs.dot(Numo::NMath.sqrt(eig_vals.abs).diag)

  @embedding = @embedding.flatten.dup if @params[:n_components] == 1

  self
end

#fit_transform(x) ⇒ Numo::DFloat

Fit the model with training data, and then transform them with the learned model.

Returns (shape: [n_samples, n_components]) The transformed data.

Parameters:

  • x (Numo::DFloat)

    (shape: [n_samples, n_features]) The training data to be used for fitting the model. If the metric is ‘precomputed’, x must be a square distance matrix (shape: [n_samples, n_samples]).

Returns:

  • (Numo::DFloat)

    (shape: [n_samples, n_components]) The transformed data



75
76
77
78
79
80
81
# File 'rumale-manifold/lib/rumale/manifold/classical_mds.rb', line 75

def fit_transform(x, _not_used = nil)
  raise 'ClassicalMDS#fit_transform requires Numo::Linalg but that is not loaded' unless enable_linalg?(warning: false)

  x = ::Rumale::Validation.check_convert_sample_array(x)
  fit(x)
  @embedding.dup
end