Domain: Biology — Ecology

Problem:
Dietary macronutrient composition strongly modulates the gut microbial metabolome, leading to altered production of short-chain fatty acids, bile acids, and tryptophan derivatives. These microbial metabolites can influence host neurotransmitter synthesis (serotonin, dopamine, GABA) via the gut–brain axis.
Develop a computational model following the modelling framework provided to quantify how microbial metabolites predict host neurotransmitter concentrations, and identify the metabolite that most strongly contributes to each neurotransmitter’s variance (i.e., the predictor with the largest absolute coefficient or standardized coefficient).
A synthetic dataset of N = \(300\) subjects is provided, containing: Dietary inputs: fiber_intake, fat_intake, protein_intake \((g/day)\), Microbial metabolites: acetate, butyrate, propionate, indole3_acetate, succinate, taurine, tryptamine, bile_acid_A, bile_acid_B, lactate; Host neurotransmitters (targets): serotonin, dopamine, GABA.

Modelling Framework is as follows: 
polynomial regression model \(f_j(X)\) for each neurotransmitter using the microbial metabolites as predictors. 

Record:

\(R_j^2\) ;  \({RMSE}_j\) ; Regression coefficients (to quantify contribution of each metabolite)

Show intermediate calculations and numerics. Give the result for each neurotransmitter with precision upto \(3\) decimal places in a array format [Metabolite: \(R^2\) Value].

Answer: [BA1:\(0.242\)],[Try:\(0.492\)],[Try:\(0.782\)]

Solution code:
import pandas as pd
import numpy as np

# Load the dataset
data = pd.read_csv('gutneurodiet.csv')

# Define microbial metabolite columns (predictors)
metabolites = ['Ac','Bu','Prop','IA','Suc','Tau','Try','BA1','BA2','Lac']

# Define neurotransmitter targets
targets = ['Ser','Dopa','GABA']

# Optionally, create polynomial features (degree 2) including interactions
def polynomial_features(X):
    """
    Generate polynomial features up to degree 2 including interactions.
    X: np.array (n_samples, n_features)
    Returns: np.array (n_samples, n_poly_features)
    """
    n_samples, n_features = X.shape
    # Start with bias term
    X_poly = [np.ones(n_samples)]
    
    # Linear terms
    X_poly.extend([X[:, i] for i in range(n_features)])
    
    # Quadratic terms (squares)
    X_poly.extend([X[:, i]**2 for i in range(n_features)])
    
    # Interaction terms
    for i in range(n_features):
        for j in range(i+1, n_features):
            X_poly.append(X[:, i] * X[:, j])
    
    return np.column_stack(X_poly)

# Prepare predictor matrix
X_raw = data[metabolites].values
X_poly = polynomial_features(X_raw)

# Store results
results = {}

# Solve regression for each neurotransmitter
for target in targets:
    y = data[target].values
    
    # Fit polynomial regression using least squares
    beta, residuals, rank, s = np.linalg.lstsq(X_poly, y, rcond=None)
    
    # Predictions
    y_pred = X_poly @ beta
    
    # Metrics
    ss_res = np.sum((y - y_pred)**2)
    ss_tot = np.sum((y - np.mean(y))**2)
    R2 = 1 - ss_res/ss_tot
    RMSE = np.sqrt(ss_res / len(y))
    
    # Compute contribution of each metabolite (linear term coefficient)
    # Linear term coefficients start from index 1 (after bias)
    linear_coefs = beta[1:1+len(metabolites)]
    
    # Identify metabolite with max absolute coefficient
    max_idx = np.argmax(np.abs(linear_coefs))
    top_metabolite = metabolites[max_idx]
    
    # Store results with 3 decimal places
    results[target] = {
        'R2': round(R2, 3),
        'RMSE': round(RMSE, 3),
        'coefficients': {met: round(coef, 3) for met, coef in zip(metabolites, linear_coefs)},
        'top_metabolite': top_metabolite
    }

# Print results
for target, res in results.items():
    print(f"\n{target.capitalize()}:")
    print(f"  R2: {res['R2']}")
    print(f"  RMSE: {res['RMSE']}")
    print(f"  Coefficients: {res['coefficients']}")
    print(f"  Top contributing metabolite: {res['top_metabolite']}")
    print(f"  Result array: [{res['top_metabolite']}: {res['R2']}]")


# Create final summary array for all neurotransmitters
final_array = []

for target, res in results.items():
    final_array.append([f"{res['top_metabolite']}: {res['R2']}"])

print("\nFinal Result Array:")
print(final_array)

print( "Each element of the array corresponds to one neurotransmitter in the order they were processed i.e serotonin, dopamine, GABA")