test__procrustes.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. from __future__ import absolute_import, division, print_function
  2. import numpy as np
  3. from numpy.testing import assert_allclose, assert_equal, assert_almost_equal
  4. from pytest import raises as assert_raises
  5. from scipy.spatial import procrustes
  6. class TestProcrustes(object):
  7. def setup_method(self):
  8. """creates inputs"""
  9. # an L
  10. self.data1 = np.array([[1, 3], [1, 2], [1, 1], [2, 1]], 'd')
  11. # a larger, shifted, mirrored L
  12. self.data2 = np.array([[4, -2], [4, -4], [4, -6], [2, -6]], 'd')
  13. # an L shifted up 1, right 1, and with point 4 shifted an extra .5
  14. # to the right
  15. # pointwise distance disparity with data1: 3*(2) + (1 + 1.5^2)
  16. self.data3 = np.array([[2, 4], [2, 3], [2, 2], [3, 2.5]], 'd')
  17. # data4, data5 are standardized (trace(A*A') = 1).
  18. # procrustes should return an identical copy if they are used
  19. # as the first matrix argument.
  20. shiftangle = np.pi / 8
  21. self.data4 = np.array([[1, 0], [0, 1], [-1, 0],
  22. [0, -1]], 'd') / np.sqrt(4)
  23. self.data5 = np.array([[np.cos(shiftangle), np.sin(shiftangle)],
  24. [np.cos(np.pi / 2 - shiftangle),
  25. np.sin(np.pi / 2 - shiftangle)],
  26. [-np.cos(shiftangle),
  27. -np.sin(shiftangle)],
  28. [-np.cos(np.pi / 2 - shiftangle),
  29. -np.sin(np.pi / 2 - shiftangle)]],
  30. 'd') / np.sqrt(4)
  31. def test_procrustes(self):
  32. # tests procrustes' ability to match two matrices.
  33. #
  34. # the second matrix is a rotated, shifted, scaled, and mirrored version
  35. # of the first, in two dimensions only
  36. #
  37. # can shift, mirror, and scale an 'L'?
  38. a, b, disparity = procrustes(self.data1, self.data2)
  39. assert_allclose(b, a)
  40. assert_almost_equal(disparity, 0.)
  41. # if first mtx is standardized, leaves first mtx unchanged?
  42. m4, m5, disp45 = procrustes(self.data4, self.data5)
  43. assert_equal(m4, self.data4)
  44. # at worst, data3 is an 'L' with one point off by .5
  45. m1, m3, disp13 = procrustes(self.data1, self.data3)
  46. #assert_(disp13 < 0.5 ** 2)
  47. def test_procrustes2(self):
  48. # procrustes disparity should not depend on order of matrices
  49. m1, m3, disp13 = procrustes(self.data1, self.data3)
  50. m3_2, m1_2, disp31 = procrustes(self.data3, self.data1)
  51. assert_almost_equal(disp13, disp31)
  52. # try with 3d, 8 pts per
  53. rand1 = np.array([[2.61955202, 0.30522265, 0.55515826],
  54. [0.41124708, -0.03966978, -0.31854548],
  55. [0.91910318, 1.39451809, -0.15295084],
  56. [2.00452023, 0.50150048, 0.29485268],
  57. [0.09453595, 0.67528885, 0.03283872],
  58. [0.07015232, 2.18892599, -1.67266852],
  59. [0.65029688, 1.60551637, 0.80013549],
  60. [-0.6607528, 0.53644208, 0.17033891]])
  61. rand3 = np.array([[0.0809969, 0.09731461, -0.173442],
  62. [-1.84888465, -0.92589646, -1.29335743],
  63. [0.67031855, -1.35957463, 0.41938621],
  64. [0.73967209, -0.20230757, 0.52418027],
  65. [0.17752796, 0.09065607, 0.29827466],
  66. [0.47999368, -0.88455717, -0.57547934],
  67. [-0.11486344, -0.12608506, -0.3395779],
  68. [-0.86106154, -0.28687488, 0.9644429]])
  69. res1, res3, disp13 = procrustes(rand1, rand3)
  70. res3_2, res1_2, disp31 = procrustes(rand3, rand1)
  71. assert_almost_equal(disp13, disp31)
  72. def test_procrustes_shape_mismatch(self):
  73. assert_raises(ValueError, procrustes,
  74. np.array([[1, 2], [3, 4]]),
  75. np.array([[5, 6, 7], [8, 9, 10]]))
  76. def test_procrustes_empty_rows_or_cols(self):
  77. empty = np.array([[]])
  78. assert_raises(ValueError, procrustes, empty, empty)
  79. def test_procrustes_no_variation(self):
  80. assert_raises(ValueError, procrustes,
  81. np.array([[42, 42], [42, 42]]),
  82. np.array([[45, 45], [45, 45]]))
  83. def test_procrustes_bad_number_of_dimensions(self):
  84. # fewer dimensions in one dataset
  85. assert_raises(ValueError, procrustes,
  86. np.array([1, 1, 2, 3, 5, 8]),
  87. np.array([[1, 2], [3, 4]]))
  88. # fewer dimensions in both datasets
  89. assert_raises(ValueError, procrustes,
  90. np.array([1, 1, 2, 3, 5, 8]),
  91. np.array([1, 1, 2, 3, 5, 8]))
  92. # zero dimensions
  93. assert_raises(ValueError, procrustes, np.array(7), np.array(11))
  94. # extra dimensions
  95. assert_raises(ValueError, procrustes,
  96. np.array([[[11], [7]]]),
  97. np.array([[[5, 13]]]))