@Override
    public Tuple apply(Tuple x) {
        StructType schema = x.schema();
        Neighbor<Tuple, Tuple>[] neighbors = knn.search(x, k);
        return new smile.data.AbstractTuple() {
            @Override
            public Object get(int i) {
                Object xi = x.get(i);
                if (!SimpleImputer.isMissing(xi)) {
                    return xi;
                } else {
                    StructField field = schema.field(i);
                    if (field.type.isBoolean()) {
                        int[] vector = MathEx.omit(
                                Arrays.stream(neighbors)
                                        .mapToInt(neighbor -> neighbor.key.getInt(i)).toArray(),
                                Integer.MIN_VALUE);
                        return vector.length == 0 ? null : MathEx.mode(vector) != 0;
                    } else if (field.type.isChar()) {
                        int[] vector = MathEx.omit(
                                Arrays.stream(neighbors)
                                        .mapToInt(neighbor -> neighbor.key.getInt(i)).toArray(),
                                Integer.MIN_VALUE);
                        return vector.length == 0 ? null : (char) MathEx.mode(vector);
                    } else if (field.measure instanceof NominalScale) {
                        int[] vector = MathEx.omit(
                                Arrays.stream(neighbors)
                                        .mapToInt(neighbor -> neighbor.key.getInt(i)).toArray(),
                                Integer.MIN_VALUE);
                        return vector.length == 0 ? null : MathEx.mode(vector);
                    } else if (field.type.isNumeric()) {
                        double[] vector = MathEx.omit(
                                Arrays.stream(neighbors)
                                        .mapToDouble(neighbor -> neighbor.key.getDouble(i)).toArray(),
                                Integer.MIN_VALUE);
                        return vector.length == 0 ? null : MathEx.mean(vector);
                    } else {
                        return null;
                    }
                }
            }

            @Override
            public StructType schema() {
                return schema;
            }
        };
    }
@Test
    public void test() throws Exception {
        System.out.println("KNNImputer");
        double[][] data = SyntheticControl.x;
        DataFrame df = DataFrame.of(data);
        KNNImputer knnImputer = new KNNImputer(df, 5);
        Function<double[][], double[][]> imputer = x -> knnImputer.apply(DataFrame.of(x)).toArray();

        impute(imputer, data, 0.01, 11.08);
        impute(imputer, data, 0.05, 12.22);
        impute(imputer, data, 0.10, 11.63);
    }