code: private void svdImpute(double[][] raw, double[][] data) {
          SVD svd = Matrix.newInstance(data).svd();
  
          int d = data[0].length;
  
          for (int i = 0; i < raw.length; i++) {
              int missing = 0;
              for (int j = 0; j < d; j++) {
                  if (Double.isNaN(raw[i][j])) {
                      missing++;
                  } else {
                      data[i][j] = raw[i][j];
                  }
              }
  
              if (missing == 0) {
                  continue;
              }
  
              DenseMatrix A = Matrix.zeros(d - missing, k);
              double[] b = new double[d - missing];
  
              for (int j = 0, m = 0; j < d; j++) {
                  if (!Double.isNaN(raw[i][j])) {
                      for (int l = 0; l < k; l++) {
                          A.set(m, l, svd.getV().get(j, l));
                      }
                      b[m++] = raw[i][j];
                  }
              }
  
              double[] s = new double[k];
              QR qr = A.qr();
              qr.solve(b, s);
  
              for (int j = 0; j < d; j++) {
                  if (Double.isNaN(raw[i][j])) {
                      data[i][j] = 0;
                      for (int l = 0; l < k; l++) {
                          data[i][j] += s[l] * svd.getV().get(j, l);
                      }
                  }
              }
          }
      }
docs: Impute the missing values by SVD.
  @param raw the raw data with missing values.
  @param data the data with current imputations.