class_docstr: Search structure which gives speedup at slight loss of recall.
  
  Uses cluster pruning structure as defined in:
  http://nlp.stanford.edu/IR-book/html/htmledition/cluster-pruning-1.html
  
  tldr - searching for a document in an index of K documents is naievely
      O(K). However you can create a tree structure where the first level
      is O(sqrt(K)) and each of the leaves are also O(sqrt(K)).
  
      You randomly pick sqrt(K) items to be in the top level. Then for
      the K doccuments you assign it to the closest neighbor in the top
      level.
  
      This breaks up one O(K) search into O(2 * sqrt(K)) searches which
      is much much faster when K is big.
  
      This generalizes to h levels. The runtime becomes:
          O(h * h_root(K))
class_docstr_tok: ["Search","structure","which","gives","speedup","at","slight","loss","of","recall",".","Uses","cluster","pruning","structure","as","defined","in",":","http",":","\/\/nlp.stanford.edu\/IR-book\/html\/htmledition\/cluster-pruning-1.html","tldr","-","searching","for","a","document","in","an","index","of","K","documents","is","naievely","O","(","K",")",".","However","you","can","create","a","tree","structure","where","the","first","level","is","O","(","sqrt","(","K",")",")","and","each","of","the","leaves","are","also","O","(","sqrt","(","K",")",")",".","You","randomly","pick","sqrt","(","K",")","items","to","be","in","the","top","level",".","Then","for","the","K","doccuments","you","assign","it","to","the","closest","neighbor","in","the","top","level",".","This","breaks","up","one","O","(","K",")","search","into","O","(","2","*","sqrt","(","K",")",")","searches","which","is","much","much","faster","when","K","is","big",".","This","generalizes","to","h","levels",".","The","runtime","becomes",":","O","(","h","*","h_root","(","K",")",")"]
docstr: Create a search index composed of recursively defined
  matricies. Does recursive KNN search. See class docstring for a
  description of the method.
  
  Args:
      features: A csr_matrix with rows that represent records
          (corresponding to the elements in records_data) and columns
          that describe a point in space for each row.
      records_data: Data to return when a doc is matched. Index of
          corresponds to records_features.
      distance_type: Class that defines the distance measure to use.
      matrix_size: Ideal size for matrix multiplication. This controls
          the depth of the tree. Defaults to 2 levels (approx). Highly
          reccomended that the default value is used.
docstr_tok: ["Create","a","search","index","composed","of","recursively","defined","matricies",".","Does","recursive","KNN","search",".","See","class","docstring","for","a","description","of","the","method",".","Args",":","features",":","A","csr_matrix","with","rows","that","represent","records","(","corresponding","to","the","elements","in","records_data",")","and","columns","that","describe","a","point","in","space","for","each","row",".","records_data",":","Data","to","return","when","a","doc","is","matched",".","Index","of","corresponds","to","records_features",".","distance_type",":","Class","that","defines","the","distance","measure","to","use",".","matrix_size",":","Ideal","size","for","matrix","multiplication",".","This","controls","the","depth","of","the","tree",".","Defaults","to","2","levels","(","approx",")",".","Highly","reccomended","that","the","default","value","is","used","."]
code: def __init__(
      self,
      features,
      records_data,
      distance_type=pysparnn.matrix_distance.CosineDistance,
      matrix_size=None,
      parent=None,
  ):
      """Create a search index composed of recursively defined
      matricies. Does recursive KNN search. See class docstring for a
      description of the method.
  
      Args:
          features: A csr_matrix with rows that represent records
              (corresponding to the elements in records_data) and columns
              that describe a point in space for each row.
          records_data: Data to return when a doc is matched. Index of
              corresponds to records_features.
          distance_type: Class that defines the distance measure to use.
          matrix_size: Ideal size for matrix multiplication. This controls
              the depth of the tree. Defaults to 2 levels (approx). Highly
              reccomended that the default value is used.
      """
  
      self.is_terminal = False
      self.parent = parent
      self.distance_type = distance_type
      self.desired_matrix_size = matrix_size
      features = distance_type.features_to_matrix(features)
      num_records = features.shape[0]
  
      if matrix_size is None:
          matrix_size = max(int(_np.sqrt(num_records)), 1000)
      else:
          matrix_size = int(matrix_size)
  
      self.matrix_size = matrix_size
  
      num_levels = _np.log(num_records) / _np.log(self.matrix_size)
  
      if num_levels <= 1.4:
          self.is_terminal = True
          self.root = distance_type(features, records_data)
      else:
          self.is_terminal = False
          records_data = _np.array(records_data)
  
          records_index = list(_np.arange(features.shape[0]))
          clusters_size = min(self.matrix_size, num_records)
          clusters_selection = _random.sample(
              records_index, clusters_size
          )
          clusters_selection = features[clusters_selection]
  
          item_to_clusters = _collections.defaultdict(list)
  
          root = distance_type(
              clusters_selection,
              list(_np.arange(clusters_selection.shape[0])),
          )
  
          root.remove_near_duplicates()
          root = distance_type(
              root.matrix, list(_np.arange(root.matrix.shape[0]))
          )
  
          rng_step = self.matrix_size
          for rng in range(0, features.shape[0], rng_step):
              max_rng = min(rng + rng_step, features.shape[0])
              records_rng = features[rng:max_rng]
              for i, clstrs in enumerate(
                  root.nearest_search(records_rng)
              ):
                  _random.shuffle(clstrs)
                  for _, cluster in _k_best(clstrs, k=1):
                      item_to_clusters[cluster].append(i + rng)
  
          clusters = []
          cluster_keeps = []
          for k, clust_sel in enumerate(clusters_selection):
              clustr = item_to_clusters[k]
              if len(clustr) > 0:
                  index = ClusterIndex(
                      self.distance_type.vstack(features[clustr]),
                      records_data[clustr],
                      distance_type=distance_type,
                      matrix_size=self.matrix_size,
                      parent=self,
                  )
  
                  clusters.append(index)
                  cluster_keeps.append(clust_sel)
  
          cluster_keeps = self.distance_type.vstack(cluster_keeps)
          clusters = _np.array(clusters)
  
          self.root = distance_type(cluster_keeps, clusters)
  
imports: from __future__ import absolute_import, division, print_function, unicode_literals
  import collections
  import random
  import numpy
  import pysparnn.matrix_distance
label_desc: Use image node_id 1 to create a new ClusterIndex object from inherited base classes: object with example: obj = ClusterIndex(features, records_data, distance_type, matrix_size, parent)