### Quickstart: Variable Selection with Knockoffs Source: https://github.com/amspector100/knockpy/blob/master/docs/_sources/installation.rst.txt Perform variable selection using knockoffs with the cross-validated lasso feature statistic and Gaussian knockoff sampler. This example demonstrates generating synthetic data and applying the KnockoffFilter. ```python import knockpy as kpy from knockpy.knockoff_filter import KnockoffFilter # Generate synthetic data from a Gaussian linear model data_gen_process = kpy.dgp.DGP() data_gen_process.sample_data( n=1500, # Number of datapoints p=500, # Dimensionality sparsity=0.1, x_dist='gaussian', ) X = data_gen_process.X y = data_gen_process.y Sigma=data_gen_process.Sigma # Run model-X knockoffs kfilter = KnockoffFilter( fstat='lasso', ksampler='gaussian', ) rejections = kfilter.forward(X=X, y=y, Sigma=Sigma) ``` -------------------------------- ### Install Choldate for Faster Knockoff Computation Source: https://github.com/amspector100/knockpy/blob/master/README.md Installs the choldate package from GitHub, which is used to speed up minimum reconstructability knockoffs. This is an optional step. ```bash pip install git+https://github.com/jcrudy/choldate.git ``` -------------------------------- ### Install Cython for Faster Knockoff Computation Source: https://github.com/amspector100/knockpy/blob/master/README.md Installs Cython, a prerequisite for speeding up minimum reconstructability knockoffs. If this fails, consider using Anaconda or precompiled binaries. ```bash pip install cython>=0.29.14 ``` -------------------------------- ### Initial Parameters and Scaling Source: https://github.com/amspector100/knockpy/blob/master/docs/_modules/knockpy/smatrix.html Initializes parameters and scales the Sigma matrix to a correlation matrix. This is a common setup for various knockoff methods. ```python p = Sigma.shape[0] if groups is None: groups = np.arange(1, p + 1, 1) # Scale to correlation matrix scale = np.sqrt(np.diag(Sigma)) scale_matrix = np.outer(scale, scale) Sigma = Sigma / scale_matrix ``` -------------------------------- ### Install Knockpy with Fast Dependencies Source: https://github.com/amspector100/knockpy/blob/master/README.md Installs knockpy along with optional dependencies for faster computation. Ensure pip is up-to-date. ```bash pip install knockpy[fast] ``` -------------------------------- ### Build and Install Knockpy Distribution Source: https://github.com/amspector100/knockpy/blob/master/README.md Builds and installs the knockpy distribution locally. This command is used after making changes to the project. ```bash make install ``` -------------------------------- ### Quickstart: Generate Data and Run Knockoffs Source: https://github.com/amspector100/knockpy/blob/master/docs/installation.html Demonstrates generating synthetic data from a Gaussian linear model and applying the knockpy library with a cross-validated lasso feature statistic to perform variable selection. ```python import knockpy as kpy from knockpy.knockoff_filter import KnockoffFilter # Generate synthetic data from a Gaussian linear model data_gen_process = kpy.dgp.DGP() data_gen_process.sample_data( n=1500, # Number of datapoints p=500, # Dimensionality sparsity=0.1, x_dist='gaussian', ) X = data_gen_process.X y = data_gen_process.y Sigma=data_gen_process.Sigma # Run model-X knockoffs kfilter = KnockoffFilter( fstat='lasso', ksampler='gaussian', ) rejections = kfilter.forward(X=X, y=y, Sigma=Sigma) ``` -------------------------------- ### Install choldate Source: https://github.com/amspector100/knockpy/blob/master/docs/_sources/installation.rst.txt Install the choldate package using pip. This is a prerequisite for installing knockpy. ```bash pip install git+git://github.com/jcrudy/choldate.git ``` -------------------------------- ### Quickstart: Variable Selection with Knockpy Source: https://github.com/amspector100/knockpy/blob/master/README.md Demonstrates using knockpy to perform variable selection with a cross-validated lasso feature statistic and Gaussian knockoffs. Requires synthetic data generation. ```python import knockpy as kpy from knockpy.knockoff_filter import KnockoffFilter # Generate synthetic data from a Gaussian linear model data_gen_process = kpy.dgp.DGP() data_gen_process.sample_data( n=1500, # Number of datapoints p=500, # Dimensionality sparsity=0.1, x_dist='gaussian', ) X = data_gen_process.X y = data_gen_process.y Sigma=data_gen_process.Sigma # Run model-X knockoffs kfilter = KnockoffFilter( fstat='lasso', ksampler='gaussian', ) rejections = kfilter.forward(X=X, y=y, Sigma=Sigma) ``` -------------------------------- ### Install Lightweight Knockpy Source: https://github.com/amspector100/knockpy/blob/master/README.md Installs a lightweight version of knockpy without optional dependencies. This is useful if the full installation fails, especially on non-Linux environments. ```bash pip install knockpy ``` -------------------------------- ### Install Knockpy with Fast Computation Source: https://context7.com/amspector100/knockpy/llms.txt Install knockpy with the fast computation option using pip. For optional deep learning features, install PyTorch separately and then install knockpy. ```bash pip install knockpy[fast] ``` ```bash pip install torch pip install knockpy[fast] ``` -------------------------------- ### Initialize S-matrix and QR Decomposition Source: https://github.com/amspector100/knockpy/blob/master/docs/_modules/knockpy/mrc.html Initializes the S-matrix using a solver and computes the initial QR decomposition for the matrix 2*Sigma - S. This setup is crucial for the iterative optimization steps. ```python S = mac.solve_equicorrelated(Sigma, groups) / 2 Q, R = np.linalg.qr(2 * Sigma - S + smoothing * np.eye(p)) ``` -------------------------------- ### Example: Fit KnockoffGGM with Fake Data Source: https://github.com/amspector100/knockpy/blob/master/docs/_modules/knockpy/ggm.html Demonstrates fitting KnockoffGGM under the global null hypothesis using fake data. It utilizes the LCD statistic with FX knockoffs. ```python # Fake data-generating process for Gaussian graphical model import numpy as np X = np.random.randn(300, 30) # LCD statistic with FX knockoffs from knockpy.ggm import KnockoffGGM gkf = KnockoffGGM( fstat='lcd', knockoff_kwargs={"method":"mvr"}, ) edges = gkf.forward(X=X, verbose=True) ``` -------------------------------- ### Instantiate KnockoffFilter with LCD and Gaussian MX knockoffs Source: https://github.com/amspector100/knockpy/blob/master/docs/kfilter.html Instantiates the KnockoffFilter using the LCD statistic and Gaussian Model-X knockoffs. This example uses LedoitWolf covariance estimation by default and requires the 'mvr' method for knockoff_kwargs. ```python from knockpy.knockoff_filter import KnockoffFilter kfilter = KnockoffFilter( fstat='lcd', ksampler='gaussian', knockoff_kwargs={"method":"mvr"}, ) ``` -------------------------------- ### Install Pre-commit Hooks for Knockpy Development Source: https://github.com/amspector100/knockpy/blob/master/README.md Installs pre-commit hooks for the knockpy project. These hooks help maintain code quality and consistency during development. ```bash make install-pre-commit ``` -------------------------------- ### Initialize KnockoffFilter with Random Forest Feature Statistics Source: https://github.com/amspector100/knockpy/blob/master/docs/usage.html Illustrates initializing a KnockoffFilter with the 'gaussian' sampler and 'randomforest' feature statistic. This setup is suitable for using random forest-based feature importances. ```python kfilter1 = KnockoffFilter(ksampler="gaussian", fstat="randomforest") ``` -------------------------------- ### Generate Gaussian Data and Run SDP Knockoffs Source: https://github.com/amspector100/knockpy/blob/master/docs/source/mrcknock.ipynb Generates synthetic Gaussian data with 50% feature correlation and applies the SDP knockoff filter. This example highlights the poor performance of SDP knockoffs in such scenarios. ```python import numpy as np import knockpy as kpy from knockpy.knockoff_filter import KnockoffFilter p = 300 n = 600 np.random.seed(110) # Covariance matrix of X rho = 0.5 Sigma = (1 - rho) * np.eye(p) + rho * np.ones((p, p)) X = np.random.multivariate_normal(np.zeros(p), cov=Sigma, size=(n,)) # Sample y given X beta = kpy.dgp.create_sparse_coefficients( p=p, sparsity=0.2, coeff_size=1, coeff_dist="uniform" ) y = X @ beta + np.random.randn(n) # SDP knockoff filter kfilter_sdp = KnockoffFilter( fstat="lasso", ksampler="gaussian", knockoff_kwargs={\"method\": \"sdp\"} ) selections_sdp = kfilter_sdp.forward(X=X, y=y, Sigma=Sigma, fdr=0.1) print(f"SDP knockoffs made {selections_sdp.sum()} discoveries!") ``` ```text Output: SDP knockoffs made 0.0 discoveries! ``` -------------------------------- ### Run Fixed-X Knockoffs Source: https://context7.com/amspector100/knockpy/llms.txt This example demonstrates how to use Fixed-X knockoffs, which are suitable when the design matrix X is treated as fixed. This method requires n >= 2p and is often used in controlled experiments. The code generates data, initializes KnockoffFilter with the 'fx' sampler, and prints true and false positive counts. ```python import numpy as np from knockpy.knockoff_filter import KnockoffFilter # Generate data with n >= 2p (required for FX knockoffs) n, p = 500, 100 X = np.random.randn(n, p) beta = np.zeros(p) beta[:10] = 3.0 # First 10 features are non-null y = X @ beta + np.random.randn(n) # Use Fixed-X knockoffs kfilter = KnockoffFilter( fstat='lasso', ksampler='fx', # Fixed-X knockoffs knockoff_kwargs={"method": "mvr"}, ) rejections = kfilter.forward(X=X, y=y, fdr=0.1) print(f"True positives: {rejections[:10].sum()}") print(f"False positives: {rejections[10:].sum()}") ``` -------------------------------- ### Solve Group SDP with DSDP Source: https://github.com/amspector100/knockpy/blob/master/docs/_modules/knockpy/mac.html Uses the DSDP solver for generating knockoffs. Requires DSDP to be installed. Adjusts tolerance to prevent infeasibility. ```python if not DSDP_AVAILABLE: return solve_group_SDP( Sigma=Sigma, verbose=verbose, num_iter=num_iter, tol=tol, ) # Constants TestIfCorrMatrix(Sigma) p = Sigma.shape[0] maxtol = utilities.calc_mineig(Sigma) / 10 if tol > maxtol and verbose: warnings.warn( f"Reducing SDP tol from {tol} to {maxtol}, otherwise SDP would be infeasible" ) tol = min(maxtol, tol) # Construct C (-b + vec(F0) from above) # Note the tolerance here prevents the min. val # of S from being too small. Cl1 = np.diag(-1 * tol * np.ones(p)).reshape(1, p**2) Cl2 = np.diag(np.ones(p)).reshape(1, p**2) Cs = np.reshape(2 * Sigma, [1, p * p]) C = np.concatenate([Cl1, Cl2, Cs], axis=1) # Construct A rows = [] cols = [] data = [] for j in range(p): rows.append(j) cols.append((p + 1) * j) data.append(-1) Al1 = sp.sparse.csr_matrix((data, (rows, cols))) Al2 = -1 * Al1.copy() As = Al2.copy() A = sp.sparse.hstack([Al1, Al2, As]) # Construct b b = np.ones([p, 1]) # Options K = {} K["s"] = [p, p, p] OPTIONS = { "gaptol": 1e-6, "maxit": 1000, "logsummary": 1 if verbose else 0, "outputstats": 1 if verbose else 0, "print": 1 if verbose else 0, } # Solve warnings.filterwarnings("ignore") result = dsdp(A, b, C, K, OPTIONS=OPTIONS) warnings.resetwarnings() # Raise an error if unsolvable status = result["STATS"]["stype"] if status != "PDFeasible": raise ValueError(f"DSDP solver returned status {status}, should be PDFeasible") S = np.diag(result["y"]) # Scale to make this PSD using binary search S, gamma = utilities.scale_until_PSD(Sigma, S, tol, num_iter) if verbose: mineig = utilities.calc_mineig(2 * Sigma - S) print(f"After SDP, mineig is {mineig} after {num_iter} line search iters.") return S ``` -------------------------------- ### Initialize KnockoffFilter with Gaussian MX Knockoffs Source: https://github.com/amspector100/knockpy/blob/master/docs/apiref.html Instantiate the KnockoffFilter using the 'lcd' feature statistic and 'gaussian' knockoff sampler with specific knockoff keyword arguments. This setup is suitable for Gaussian linear models and uses Ledoit-Wolf covariance estimation by default. ```python import knockpy as kpy dgprocess = kpy.dgp.DGP() dgprocess.sample_data(n=500, p=500, sparsity=0.1) from knockpy.knockoff_filter import KnockoffFilter kfilter = KnockoffFilter( fstat='lcd', ksampler='gaussian', knockoff_kwargs={"method":"mvr"}, ) rejections = kfilter.forward(X=dgprocess.X, y=dgprocess.y) ``` -------------------------------- ### Run Model-X Knockoffs with Lasso Source: https://context7.com/amspector100/knockpy/llms.txt This example demonstrates how to use the KnockoffFilter class to perform variable selection using Gaussian Model-X knockoffs with the lasso feature statistic. It includes data generation, filter initialization, and accessing results like rejections and feature statistics. ```python import knockpy as kpy from knockpy.knockoff_filter import KnockoffFilter # Generate synthetic data from a Gaussian linear model dgp = kpy.dgp.DGP() dgp.sample_data( n=1500, # Number of datapoints p=500, # Dimensionality sparsity=0.1, # 10% of features are truly important x_dist='gaussian', ) X = dgp.X y = dgp.y Sigma = dgp.Sigma # Run model-X knockoffs with lasso feature statistic kfilter = KnockoffFilter( fstat='lasso', # Use cross-validated lasso coefficients ksampler='gaussian', # Gaussian Model-X knockoffs knockoff_kwargs={"method": "mvr"}, # Minimum variance reconstructability ) # Run the filter and get rejections (boolean array) rejections = kfilter.forward(X=X, y=y, Sigma=Sigma, fdr=0.1) # Access results print(f"Number of discoveries: {rejections.sum()}") print(f"Discovered features: {np.where(rejections)[0]}") print(f"Feature statistics (W): {kfilter.W[:10]}") # First 10 W statistics print(f"Threshold: {kfilter.threshold}") ``` -------------------------------- ### Metropolized Knockoff Sampler Implementation Source: https://github.com/amspector100/knockpy/blob/master/docs/_modules/knockpy/metro.html This code implements the core logic for a metropolized knockoff sampler, including proposal generation, acceptance probability calculation, and knockoff storage. It utilizes dynamic programming and caches intermediate results for performance. Ensure 'tqdm' is installed for verbose progress bars. ```python if self.metro_verbose: if self.cache: print( f"Metro will use memory expensive caching for 2-3x speedup, storing {num_params} params" ) else: print("Metro will not cache cond_means to save a lot of memory") # Dynamic programming approach: store acceptance probs # as well as Fj values (see page 33) self.acc_dicts = [{} for j in range(self.p)] self.F_dicts = [{} for j in range(self.p)] # Locate previous terms affected by variable j self.affected_vars = [[ ] for k in range(self.p)] for j, j2 in itertools.product(range(self.p), range(self.p)): if j in self.active_frontier[j2]: self.affected_vars[j] += [j2] # Store pattern of TRUE acceptances / rejections self.acceptances = np.zeros((self.n, self.p)).astype(bool) self.final_acc_probs = np.zeros((self.n, self.p)) # Proposals self.X_prop = np.zeros((self.n, self.p)).astype(np.float32) self.X_prop[:] = np.nan # Start to store knockoffs self.Xk = np.zeros((self.n, self.p)).astype(np.float32) self.Xk[:] = np.nan # Decide whether or not to log if self.metro_verbose: print("Metro beginning to compute proposals...") j_iter = tqdm(range(self.p)) else: j_iter = range(self.p) # Loop across variables to sample proposals for j in j_iter: # Sample proposal self.X_prop[:, j] = self.sample_proposals( X=self.X, prev_proposals=self.X_prop[:, 0:j] ).astype(np.float32) # Cache the conditional proposal params self.cache_conditional_proposal_params( verbose=self.metro_verbose, expensive_cache=self.cache ) # Loop across variables to compute acc ratios if self.metro_verbose: print("Metro computing acceptance probabilities...") j_iter = tqdm(range(self.p)) else: j_iter = range(self.p) for j in j_iter: # Cache which knockoff we are sampling self.step = j # Compute acceptance probability, which is an n-length vector acc_prob = self.compute_acc_prob( x_flags=np.zeros((self.n, self.p)), j=j, ) self.final_acc_probs[:, j] = acc_prob # Sample to get actual acceptances self.acceptances[:, j] = np.random.binomial(1, acc_prob).astype(bool) # Store knockoffs mask = self.acceptances[:, j] == 1 self.Xk[:, j][mask] = self.X_prop[:, j][mask] self.Xk[:, j][~mask] = self.X[:, j][~mask] # Delete cache to save memory if self.cache: if self.metro_verbose: print("Deleting cache to save memory...") del self.cached_mean_obs_eq_obs del self.cached_mean_obs_eq_prop # Return re-sorted return self.Xk[:, self.inv_order] ``` -------------------------------- ### Initialize KnockoffFilter with Random Forest Statistics Source: https://github.com/amspector100/knockpy/blob/master/docs/_sources/usage.ipynb.txt Use built-in feature statistics by setting the 'fstat' argument. This example initializes filters for standard random forest statistics and for swap integral importances. ```python # Random forest statistics with swap importances kfilter1 = KnockoffFilter(ksampler="gaussian", fstat="randomforest") # Random forest with swap integral importances kfilter2 = KnockoffFilter( ksampler="gaussian", fstat="randomforest", fstat_kwargs={"feature_importance": "swapint"}, ) ``` -------------------------------- ### Initialize and Run KnockoffFilter Source: https://github.com/amspector100/knockpy/blob/master/docs/source/apiref.md Demonstrates initializing the KnockoffFilter with 'lcd' statistic and 'gaussian' knockoffs, then running it on sampled data. Requires prior data sampling using DGP. ```python # Fake data-generating process for Gaussian linear model import knockpy as kpy dgprocess = kpy.dgp.DGP() dgprocess.sample_data(n=500, p=500, sparsity=0.1) # LCD statistic with Gaussian MX knockoffs # This uses LedoitWolf covariance estimation by default. from knockpy.knockoff_filter import KnockoffFilter kfilter = KnockoffFilter( fstat='lcd', ksampler='gaussian', knockoff_kwargs={"method":"mvr"}, ) rejections = kfilter.forward(X=dgprocess.X, y=dgprocess.y) ``` -------------------------------- ### Output Knockpy Make Options Source: https://github.com/amspector100/knockpy/blob/master/README.md Displays a help message listing all available make commands and their descriptions for the knockpy project. ```bash make help ``` -------------------------------- ### Initialize Knockoff Filters with Different Samplers Source: https://github.com/amspector100/knockpy/blob/master/docs/_sources/usage.ipynb.txt Demonstrates initializing KnockoffFilter with different knockoff sampler types ('gaussian', 'fx', 'artk') and their associated methods ('maxent', 'sdp', 'mvr'). ```python # This uses gaussian maxent knockoffs kfilter1 = KnockoffFilter(ksampler="gaussian", knockoff_kwargs={"method": "maxent"}) # This uses fixed-X SDP knockoffs kfilter2 = KnockoffFilter(ksampler="fx", knockoff_kwargs={"method": "sdp"}) # Metropolized sampler for heavy-tailed t markov chain using MVR-guided proposals kfilter3 = KnockoffFilter(ksampler="artk", knockoff_kwargs={"method": "mvr"}) # The 'method' options include: equicorrelated, sdp, mvr, maxent, and ci. ``` -------------------------------- ### Install Cython using Anaconda Source: https://github.com/amspector100/knockpy/blob/master/README.md Installs Cython using the Anaconda package manager, which includes a C compiler and can resolve installation issues on various platforms. ```bash install cython ``` -------------------------------- ### KnockoffFilter Initialization and Usage Source: https://github.com/amspector100/knockpy/blob/master/docs/apiref.html Demonstrates how to initialize the KnockoffFilter with different feature statistics and knockoff samplers, and how to use the forward method to perform feature selection. ```APIDOC ## KnockoffFilter Class ### Description The KnockoffFilter class is used for feature selection using the knockoff filter method. It allows for various feature statistics and knockoff samplers to be configured. ### Attributes * **fstat** (knockpy.knockoff_stats.FeatureStatistic): The feature statistics to use for inference. * **ksampler** (knockpy.knockoffs.KnockoffSampler): The knockoff sampler to use during inference. * **fstat_kwargs** (dict): Dictionary of kwargs to pass to the `fit` call of `self.fstat`. * **knockoff_kwargs** (dict): If `ksampler` is not yet initialized, kwargs to pass to `ksampler`. * **Z** (np.ndarray): A `2p`-dimensional array of feature and knockoff importances. * **W** (np.ndarray): An array of feature statistics. * **S** (np.ndarray): The `(p, p)`-shaped knockoff S-matrix used to generate knockoffs. * **X** (np.ndarray): The `(n, p)`-shaped design matrix. * **Xk** (np.ndarray): The `(n, p)`-shaped matrix of knockoffs. * **groups** (np.ndarray): For group knockoffs, a p-length array of integers from 1 to num_groups. * **rejections** (np.ndarray): A `(p,)`-shaped boolean array indicating rejected features. * **G** (np.ndarray): The `(2p, 2p)`-shaped feature-knockoff covariance matrix. * **threshold** (float): The knockoff data-dependent threshold used to select variables. ### Methods * [`forward`](#knockpy.knockoff_filter.KnockoffFilter.forward): Runs the knockoff filter and returns feature rejections. * [`make_selections`](#knockpy.knockoff_filter.KnockoffFilter.make_selections): Calculates threshold and selections. * [`sample_knockoffs`](#knockpy.knockoff_filter.KnockoffFilter.sample_knockoffs): Samples knockoffs during `forward`. * [`seqstep_plot`](#knockpy.knockoff_filter.KnockoffFilter.seqstep_plot): Visualizes the knockoff filter. ## POST /api/knockoff_filter/forward ### Description Runs the knockoff filter to determine which features are significant based on their importance scores compared to knockoff scores. ### Method POST ### Endpoint `/api/knockoff_filter/forward` ### Parameters #### Request Body * **X** (np.ndarray) - Required - `(n, p)`-shaped design matrix. * **y** (np.ndarray) - Required - `(n,)`-shaped response vector. * **Xk** (np.ndarray) - Optional - `(n, p)`-shaped knockoff matrix. If `None`, knockoffs are constructed using `self.ksampler`. * **mu** (np.ndarray) - Optional - `(p, )`-shaped mean of the features. Defaults to empirical mean. * **Sigma** (np.ndarray) - Optional - `(p, p)`-shaped covariance matrix of the features. Estimated using `shrinkage` if `None`. Ignored for fixed-X knockoffs. * **groups** (np.ndarray) - Optional - For group knockoffs, a p-length array of integers from 1 to num_groups. * **fdr** (float) - Optional - False discovery rate, defaults to 0.1. * **fstat_kwargs** (dict) - Optional - Kwargs to pass to the feature statistic `fit` function. * **knockoff_kwargs** (dict) - Optional - Kwargs for instantiating the knockoff sampler if `ksampler` is a string identifier. * **shrinkage** (str) - Optional - Covariance estimation method, defaults to 'ledoitwolf'. * **num_factors** (int) - Optional - Number of factors for factor models. * **recycle_up_to** (int) - Optional - Parameter for recycling computations. ### Response #### Success Response (200) * **rejections** (np.ndarray) - A `(p,)`-shaped boolean array where `rejections[j] == 1` iff the knockoff filter rejects the jth feature. ### Request Example ```json { "X": [[1.0, 2.0], [3.0, 4.0]], "y": [1.0, 2.0], "fdr": 0.05 } ``` ### Response Example ```json { "rejections": [1, 0] } ``` ``` -------------------------------- ### Install Cython using Conda Source: https://github.com/amspector100/knockpy/blob/master/docs/_sources/installation.rst.txt Install Cython using the conda package manager. This is an alternative if pip installation fails due to C compiler configuration issues. ```bash conda install cython ``` -------------------------------- ### Initialize KnockoffFilter with Different Methods Source: https://github.com/amspector100/knockpy/blob/master/docs/usage.html Demonstrates initializing KnockoffFilter with various knockoff generation methods for 'gaussian' and 'fx' types. The 'method' option within 'knockoff_kwargs' specifies the algorithm. ```python kfilter1 = KnockoffFilter(ksampler="gaussian", knockoff_kwargs={"method": "maxent"}) ``` ```python kfilter2 = KnockoffFilter(ksampler="fx", knockoff_kwargs={"method": "sdp"}) ``` ```python kfilter3 = KnockoffFilter(ksampler="artk", knockoff_kwargs={"method": "mvr"}) ``` -------------------------------- ### Initialize GibbsGridSampler Source: https://github.com/amspector100/knockpy/blob/master/docs/_modules/knockpy/metro.html Initializes the GibbsGridSampler with data, parameters, and configuration for divide-and-conquer knockoff sampling. Requires pre-computed covariance matrices and graph structures. ```python self.dc_keys.append(f"{div_type}-{trans}") rand_inds = np.arange(self.n) np.random.shuffle(rand_inds) self.X_ninds = {key: [] for key in self.dc_keys} for i, j in enumerate(rand_inds): key = self.dc_keys[j % len(self.dc_keys)] self.X_ninds[key].append(i) # Structure of self.divconq_info # (1) Dictionary takes a divide-and-conquery key # (translation + row/col) # (2) This maps to a list of dictionaries. Each # dictionary corresponds to one of the blocks # in the divide and conquer procedure. # (3) Each dictionary takes three keys: inds, # cliques, lps. # - inds is the list of ORIGINAL coordinates # of the block of variables. E.g. if block 1 # corresponds to columns 1,5,6, in X, # then inds = [1,5,6] # - cliques is a list of cliques in the NEW coordinates # of the block. So for example, if 1,5 was a clique in # the prior example, then (0,1) would be in this list. # - lps are the log-potentials corresponding to # the cliques above. self.divconq_info = {} # This maps divconq key # to a list of separators (e.g. knockoffs = features) # for these indices. self.separators = {} for dc_key in self.dc_keys: div_type = dc_key.split("-")[0] trans = int(dc_key.split("-")[-1]) seps, dict_list = self._divide_variables( dc_key=dc_key, translation=trans, max_width=max_width, div_type=div_type, ) self.separators[dc_key] = seps self.divconq_info[dc_key] = dict_list # Initialize samplers. Each sampler will only sample # a subset of variables for a subset of the data # Structure: maps divconq key to list of n_inds, p_inds, sampler self.samplers = {key: [] for key in self.dc_keys} for key in self.dc_keys: # Fetch indicies n_inds = np.array(self.X_ninds[key]) sep_inds = self.separators[key] # Helper for conditional cov matrices V11 = V[sep_inds][:, sep_inds] # s x s V11_inv = utilities.chol2inv(V11) for div_group_dict in self.divconq_info[key]: p_inds = div_group_dict["inds"] # Find conditional covariance matrix V # for p_inds given # the conditioned-on-separators V22 = V[p_inds][:, p_inds] # p_i x p_i V21 = V[p_inds][:, sep_inds] # p_i x s Vcond = V22 - np.dot( V21, np.dot(V11_inv, V21.T), ) sampler = MetropolizedKnockoffSampler( lf=None, X=X[n_inds][:, p_inds], mu=mu[p_inds], Sigma=Vcond, undir_graph=gibbs_graph[p_inds][:, p_inds] != 0, cliques=div_group_dict["cliques"], log_potentials=div_group_dict["lps"], buckets=self.buckets, S=None, **kwargs, ) if sampler.width > max_width: warnings.warn( f"Treewidth heuristic inaccurate during divide/conquer, sampler for {p_inds} has width {sampler.width} > max_width {max_width}" ) self.samplers[key].append((n_inds, p_inds, sampler)) ``` -------------------------------- ### knockpy.utilities.preprocess_groups Source: https://github.com/amspector100/knockpy/blob/master/docs/dgp.html Maps unique group identifiers to a contiguous range of integers starting from 1. ```APIDOC ## knockpy.utilities.preprocess_groups ### Description Maps the m unique elements of a 1D "groups" array to the integers from 1 to m. ### Parameters - **groups** (np.ndarray) - (p, )-shaped array which takes m integer values from 1 to m. If `groups[i] == j`, this indicates that coordinate `i` belongs to group `j`. ### Returns - **processed_groups** (np.ndarray) - An array where unique group identifiers are mapped to integers from 1 to m. ``` -------------------------------- ### KnockoffFilter Class Source: https://github.com/amspector100/knockpy/blob/master/docs/_modules/knockpy/knockoff_filter.html The KnockoffFilter class performs knockoff-based inference from start to finish, wrapping KnockoffSampler and FeatureStatistic. ```APIDOC ## KnockoffFilter Class ### Description Performs knockoff-based inference, from start to finish. This wraps both the `knockoffs.KnockoffSampler` and `knockoff_stats.FeatureStatistic` classes. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **fstat** (str or knockpy.knockoff_stats.FeatureStatistic) - Required - The feature statistic to use in the knockoff filter. Defaults to a lasso statistic. This may also be a string identifier, including: - 'lasso' or 'lcd': cross-validated lasso coefficients differences - 'mlr': the masked likelihood ratio (MLR) statistic, which has provable optimality properties for knockoffs - 'lsm': signed maximum of the lasso path statistic as in Barber and Candes 2015 - 'dlasso': Cross-validated debiased lasso coefficients - 'ridge': Cross validated ridge coefficients - 'ols': Ordinary least squares coefficients - 'margcorr': marginal correlations between features and response - 'deeppink': The deepPINK statistic as in Lu et al. 2018 - 'mlrspline' or 'spline': The MLR statistic applied to regression spline basis functions. - 'randomforest': A random forest with swap importances Note that when using FX knockoffs, the `lcd` statistic does not use cross-valdilidation and sets `alphas = sigma2 * sqrt(log(p) / n)`. - **ksampler** (str or knockpy.knockoffs.KnockoffSampler) - Required - The knockoff sampler to use in the knockoff filter. This may also be a string identifier, including: - 'gaussian': Gaussian Model-X knockoffs - 'fx': Fixed-X knockoffs. - 'metro': Generic metropolized knockoff sampler - 'artk': t-tailed Markov chain. - 'blockt': Blocks of t-distributed - 'gibbs_grid': Discrete gibbs grid An alternative to specifying the ksampler is to simply pass in a knockoff matrix during the `forward` call. - **fstat_kwargs** (dict) - Optional - Kwargs to pass to the feature statistic `fit` function, excluding the required arguments, defaults to {} - **knockoff_kwargs** (dict) - Optional - Kwargs for instantiating the knockoff sampler argument if the ksampler argument is a string identifier. This can be the empty dict for some identifiers such as "gaussian" or "fx", but additional keyword arguments are required for complex samplers such as the "metro" identifier. Defaults to {} ### Attributes - **fstat** (knockpy.knockoff_stats.FeatureStatistic) - The feature statistics to use for inference. This inherits from `knockoff_stats.FeatureStatistic`. - **ksampler** (knockpy.knockoffs.KnockoffSampler) - The knockoff sampler to use during inference. This eventually inherits from `knockoffs.KnockoffSampler`. - **fstat_kwargs** (dict) - Dictionary of kwargs to pass to the `fit` call of `self.fstat`. - **knockoff_kwargs** (dict) - If `ksampler` is not yet initialized, kwargs to pass to `ksampler`. - **Z** (np.ndarray) - a `2p`-dimsional array of feature and knockoff importances. The first p coordinates correspond to features, the last p correspond to knockoffs. - **W** (np.ndarray) - an array of feature statistics. This is `(p,)`-dimensional for regular knockoffs and `(num_groups,)`-dimensional for group knockoffs. - **S** (np.ndarray) - the `(p, p)`-shaped knockoff S-matrix used to generate knockoffs. - **X** (np.ndarray) - the `(n, p)`-shaped design matrix - **Xk** (np.ndarray) - the `(n, p)`-shaped matrix of knockoffs - **groups** (np.ndarray) - For group knockoffs, a p-length array of integers from 1 to num_groups such that `groups[j] == i` indicates that variable `j` is a member of group `i`. Defaults to None (regular knockoffs). - **rejections** (np.ndarray) - a `(p,)`-shaped boolean array where `rejections[j] == 1` iff the the knockoff filter rejects the jth feature. - **G** (np.ndarray) - the `(2p, 2p)`-shaped feature-knockoff covariance matrix - **threshold** (float) - the knockoff data-dependent threshold used to select variables ### Request Example ```json { "example": "request body" } ``` ### Response #### Success Response (200) - **field1** (type) - Description #### Response Example ```json { "example": "response body" } ``` ``` -------------------------------- ### Initialize PSGDSolver Source: https://github.com/amspector100/knockpy/blob/master/docs/_modules/knockpy/kpytorch/mrcgrad.html Initializes the PSGDSolver with Sigma, groups, and various optimization parameters. It sets up the loss calculator and caches the initial optimal solution. ```python def __init__(self, Sigma, groups, losscalc=None, lr=1e-2, verbose=False, max_epochs=100, tol=1e-5, line_search_iter=10, convergence_tol=1e-1, **kwargs, ): # Add Sigma self.p = Sigma.shape[0] self.Sigma = Sigma self.groups = groups self.opt_S = None # Output initialization self.opt_loss = np.inf # Save parameters for optimization self.lr = lr self.verbose = verbose self.max_epochs = max_epochs self.tol = tol self.line_search_iter = line_search_iter self.convergence_tol = convergence_tol # Sort by groups for ease of computation inds, inv_inds = utilities.permute_matrix_by_groups(groups) self.inds = inds self.inv_inds = inv_inds self.sorted_Sigma = self.Sigma[inds][:, inds] self.sorted_groups = self.groups[inds] # Loss calculator if losscalc is not None: self.losscalc = losscalc else: self.losscalc = MVRLoss( Sigma=self.sorted_Sigma, groups=self.sorted_groups, **kwargs ) # Initialize cache of optimal S with torch.no_grad(): init_loss = self.losscalc(smoothing=0) if init_loss < 0: init_loss = np.inf self.cache_S(init_loss) # Initialize attributes which save losses over time self.all_losses = [] self.projected_losses = [] self.improvements = [] ``` -------------------------------- ### MargCorrStatistic Initialization Source: https://github.com/amspector100/knockpy/blob/master/docs/_modules/knockpy/knockoff_stats.html Initializes the MargCorrStatistic class, inheriting from FeatureStatistic. No specific setup is required beyond instantiation. ```python super().__init__() ``` -------------------------------- ### Initialize GibbsGridSampler Source: https://github.com/amspector100/knockpy/blob/master/docs/ksamplers.html Initializes the GibbsGridSampler for generating knockoffs on a discrete Gibbs grid using a divide-and-conquer approach with metropolized knockoff sampling. Requires the design matrix, Gibbs graph, and covariance matrix. ```python sampler = knockpy.metro.GibbsGridSampler(X, gibbs_graph, Sigma, max_width=6) ``` -------------------------------- ### Check Pyglmnet Availability Source: https://github.com/amspector100/knockpy/blob/master/docs/_modules/knockpy/utilities.html Checks if pyglmnet is installed and raises a ValueError with instructions if it's not available. This is required for functionalities that depend on pyglmnet. ```python def check_pyglmnet_available(purpose): try: import pyglmnet as pyglmnet except ImportError as err: raise ValueError( f"pyglmnet is required for {purpose}, but importing pyglmnet raised {err}. See https://github.com/glm-tools/pyglmnet/." ) ``` -------------------------------- ### Initialize KnockoffFilter Source: https://github.com/amspector100/knockpy/blob/master/docs/_modules/knockpy/knockoff_filter.html Initializes the KnockoffFilter with data, feature statistics, and knockoff sampling parameters. Handles data centering and regularization parameter selection for specific statistics. ```python self.fstat.fit( X=self.X, Xk=self.Xk, y=self.y, groups=self.groups, **self.fstat_kwargs ) # Inherit some attributes self.fdr = fdr self.Z = self.fstat.Z self.W = self.fstat.W self.score = self.fstat.score self.score_type = self.fstat.score_type self.rejections = self.make_selections(self.W, self.fdr) # Return return self.rejections ``` -------------------------------- ### Check PyTorch Availability Source: https://github.com/amspector100/knockpy/blob/master/docs/_modules/knockpy/utilities.html Checks if PyTorch is installed and raises a ValueError with instructions if it's not available. This is required for functionalities that depend on PyTorch. ```python def check_kpytorch_available(purpose): try: import torch as torch except ImportError as err: raise ValueError( f"Pytorch is required for {purpose}, but importing torch raised {err}. See https://pytorch.org/get-started/." ) ``` -------------------------------- ### Clean Knockpy Virtual Environment and Caches Source: https://github.com/amspector100/knockpy/blob/master/README.md Removes the virtual environment and cached files associated with the knockpy project. This is useful for starting with a clean slate. ```bash make clean ``` -------------------------------- ### BlockTSampler Initialization Source: https://github.com/amspector100/knockpy/blob/master/docs/ksamplers.html Initializes the BlockTSampler for block-wise knockoff sampling. Requires design matrix X, covariance matrix Sigma, and degrees of freedom df_t. ```python class knockpy.metro.BlockTSampler(_X, _Sigma, _df_t, **kwargs) ``` -------------------------------- ### Get Variable Ordering for Sampler Source: https://github.com/amspector100/knockpy/blob/master/docs/source/apiref.md Takes a junction tree and returns a variable ordering for the metro knockoff sampler. The code is adapted from a provided arXiv paper. ```python knockpy.metro.get_ordering(T) ``` -------------------------------- ### Perform knockoff filtering and get rejections Source: https://github.com/amspector100/knockpy/blob/master/docs/kfilter.html Applies the configured KnockoffFilter to the generated data (X, y) to perform knockoff inference and returns the list of rejected feature indices. ```python rejections = kfilter.forward(X=dgprocess.X, y=dgprocess.y) ``` -------------------------------- ### Generate Fake Data for GGM Source: https://github.com/amspector100/knockpy/blob/master/docs/apiref.html Create synthetic data for testing Gaussian Graphical Model discovery. This example generates a random matrix suitable for GGM analysis. ```python import numpy as np X = np.random.randn(300, 30) ``` -------------------------------- ### Sample Knockoffs (Divide-and-Conquer) Source: https://github.com/amspector100/knockpy/blob/master/docs/_modules/knockpy/metro.html Samples knockoffs using the divide-and-conquer approach. Accepts keyword arguments for the underlying MetropolizedKnockoffSampler. ```python def sample_knockoffs(self, **kwargs): """ Samples knockoffs using divide-and-conquer approach. Parameters ---------- kwargs : dict Keyword args for `MetropolizedKnockoffSampler.sample_knockoffs`. Returns ------- ``` -------------------------------- ### Instantiate KnockoffFilter with Custom Sampler Source: https://github.com/amspector100/knockpy/blob/master/docs/_sources/usage.ipynb.txt Pass a custom knockoff sampler class, inheriting from KnockoffSampler, to the KnockoffFilter constructor. This example uses a customized metropolized knockoff sampler. ```python kfilter_metro = KnockoffFilter(ksampler=metrosampler, fstat="ridge") ``` -------------------------------- ### Initialize Projected Gradient Descent Solver for MRC Knockoffs Source: https://github.com/amspector100/knockpy/blob/master/docs/kpytorch.html Initializes a solver for MRC knockoffs using projected gradient descent. This method is suitable for non-convex loss objectives and requires a correlation matrix and group assignments. It includes parameters for learning rate, convergence tolerance, and maximum epochs. ```python knockpy.kpytorch.mrcgrad.PSGDSolver(Sigma, groups, losscalc=None, lr=0.01, verbose=False, max_epochs=100, tol=1e-05, line_search_iter=10, convergence_tol=0.1, **kwargs) ``` -------------------------------- ### Block Diagonal Approximation Setup Source: https://github.com/amspector100/knockpy/blob/master/docs/_modules/knockpy/smatrix.html Sets up the block-diagonal approximation for large matrices. It determines blocks based on feature grouping or random merging and prepares inputs for recursive subcalls. ```python if p > max_block and method not in NON_APPROX_METHODS: if np.all(groups == np.arange(1, p + 1, 1)): blocks = divide_computation(Sigma, max_block) else: blocks = merge_groups(groups, max_block) block_sizes = utilities.calc_group_sizes(blocks) nblocks = block_sizes.shape[0] print( f"Using blockdiag approx. with nblocks={nblocks} and max_size={block_sizes.max()}..." ) Sigma_blocks = utilities.blockdiag_to_blocks(Sigma, blocks) group_blocks = [] for j in range(int(blocks.min()), int(blocks.max()) + 1): group_blocks.append(utilities.preprocess_groups(groups[blocks == j])) # Recursive subcall for each block. Possibly use multiprocessing. constant_inputs = { "method": method, "solver": solver, "max_block": p, } for key in kwargs: constant_inputs[key] = kwargs[key] ``` -------------------------------- ### Get DP Dictionary Key Source: https://github.com/amspector100/knockpy/blob/master/docs/_modules/knockpy/metro.html Generates a byte key for dynamic programming dictionaries based on active frontier indices and flag values. This is used for efficient state lookup. ```python inds = list( self.active_frontier[j] ) arr_key = x_flags[0, inds] key = arr_key.tobytes() ```