def build_node(individuals_ids, y_hat)
      # General loss function value for the node
      individuals_count = individuals_ids.size
      return label_node(y_hat, individuals_ids) if individuals_count < @node_min_size
      node_loss_function = Nimbus::LossFunctions.quadratic_loss individuals_ids, @id_to_fenotype, y_hat

      # Finding the SNP that minimizes loss function
      snps = snps_random_sample
      min_loss, min_SNP, split, split_type, means = node_loss_function, nil, nil, nil, nil

      snps.each do |snp|
        individuals_split_by_snp_value, node_split_type = split_by_snp_avegare_value individuals_ids, snp
        mean_0 = Nimbus::LossFunctions.average individuals_split_by_snp_value[0], @id_to_fenotype
        mean_1 = Nimbus::LossFunctions.average individuals_split_by_snp_value[1], @id_to_fenotype
        loss_0 = Nimbus::LossFunctions.mean_squared_error individuals_split_by_snp_value[0], @id_to_fenotype, mean_0
        loss_1 = Nimbus::LossFunctions.mean_squared_error individuals_split_by_snp_value[1], @id_to_fenotype, mean_1
        loss_snp = (loss_0 + loss_1) / individuals_count

        min_loss, min_SNP, split, split_type, means = loss_snp, snp, individuals_split_by_snp_value, node_split_type, [mean_0, mean_1] if loss_snp < min_loss
      end

      return build_branch(min_SNP, split, split_type, means, y_hat) if min_loss < node_loss_function
      return label_node(y_hat, individuals_ids)
    end
def build_node(individuals_ids, y_hat)
      # General loss function value for the node
      individuals_count = individuals_ids.size
      return label_node(y_hat, individuals_ids) if individuals_count < @node_min_size
      node_loss_function = Nimbus::LossFunctions.quadratic_loss individuals_ids, @id_to_fenotype, y_hat

      # Finding the SNP that minimizes loss function
      snps = snps_random_sample
      min_loss, min_SNP, split, split_type, means = node_loss_function, nil, nil, nil, nil

      snps.each do |snp|
        individuals_split_by_snp_value, node_split_type = split_by_snp_avegare_value individuals_ids, snp
        mean_0 = Nimbus::LossFunctions.average individuals_split_by_snp_value[0], @id_to_fenotype
        mean_1 = Nimbus::LossFunctions.average individuals_split_by_snp_value[1], @id_to_fenotype
        loss_0 = Nimbus::LossFunctions.mean_squared_error individuals_split_by_snp_value[0], @id_to_fenotype, mean_0
        loss_1 = Nimbus::LossFunctions.mean_squared_error individuals_split_by_snp_value[1], @id_to_fenotype, mean_1
        loss_snp = (loss_0 + loss_1) / individuals_count

        min_loss, min_SNP, split, split_type, means = loss_snp, snp, individuals_split_by_snp_value, node_split_type, [mean_0, mean_1] if loss_snp < min_loss
      end

      return build_branch(min_SNP, split, split_type, means, y_hat) if min_loss < node_loss_function
      return label_node(y_hat, individuals_ids)
    end
Creates a node by taking a random sample of the SNPs and computing the loss function for every split by SNP of that sample.

 * If SNP_min is the SNP with smaller loss function and it is < the loss function of the node, it splits the individuals sample in two:
 (the average of the 0,1,2 values for the SNP_min in the individuals is computed, and they are splitted in [<=avg], [>avg]) then it builds these 2 new nodes.
 * Otherwise every individual in the node gets labeled with the average of the fenotype values of all of them.