Basic Graph Analysis

This example demonstrates the basic workflow of using NEExT to analyze graph data, including: loading data, computing node features, creating graph embeddings, and analyzing feature importance.

Loading Graph Data

First, we’ll load some graph data from CSV files. We’re using the NCI1 dataset, which is a collection of chemical compounds represented as graphs, where each graph is labeled as either active or inactive against non-small cell lung cancer.

from NEExT import NEExT
import numpy as np

# Initialize NEExT
nxt = NEExT()
nxt.set_log_level("INFO")

# Define paths to data files
edge_file = "https://raw.githubusercontent.com/AnomalyPoint/NEExT_datasets/refs/heads/main/real_world_networks/csv_format/NCI1/edges.csv"
node_graph_mapping_file = "https://raw.githubusercontent.com/AnomalyPoint/NEExT_datasets/refs/heads/main/real_world_networks/csv_format/NCI1/node_graph_mapping.csv"
graph_label_file = "https://raw.githubusercontent.com/AnomalyPoint/NEExT_datasets/refs/heads/main/real_world_networks/csv_format/NCI1/graph_labels.csv"

# Load data with node reindexing and largest component filtering
graph_collection = nxt.read_from_csv(
    edges_path=edge_file,
    node_graph_mapping_path=node_graph_mapping_file,
    graph_label_path=graph_label_file,
    reindex_nodes=True,
    filter_largest_component=True,
    graph_type="networkx"
)

Computing Node Features

Next, we’ll compute various node-level features for each graph. These features capture both local and global structural properties of the nodes.

# Compute node features
features = nxt.compute_node_features(
    graph_collection=graph_collection,
    feature_list=["all"],  # Compute all available features
    feature_vector_length=3,  # Number of hops for neighborhood aggregation
    show_progress=True
)

# Normalize features for better model performance
features.normalize(type="StandardScaler")

Creating Graph Embeddings

Now we’ll create graph-level embeddings using the computed node features. These embeddings will represent each graph as a fixed-size vector, making them suitable for machine learning.

# Compute graph embeddings
embeddings = nxt.compute_graph_embeddings(
    graph_collection=graph_collection,
    features=features,
    embedding_algorithm="approx_wasserstein",
    embedding_dimension=3,
    random_state=42
)

Training and Evaluating Models

With our graph embeddings, we can now train a machine learning model to classify the graphs.

# Train a classification model
model_results = nxt.train_ml_model(
    graph_collection=graph_collection,
    embeddings=embeddings,
    model_type="classifier",
    sample_size=50,  # Number of train/test splits
    balance_dataset=False
)

# Print model results
print(f"Average Accuracy: {np.mean(model_results['accuracy']):.4f}")
print(f"Average F1 Score: {np.mean(model_results['f1_score']):.4f}")

Analyzing Feature Importance

Finally, we’ll analyze which node features are most important for the classification task. We’ll use the fast supervised method which is more efficient than the greedy approach.

# Compute feature importance
importance_df = nxt.compute_feature_importance(
    graph_collection=graph_collection,
    features=features,
    feature_importance_algorithm="supervised_fast",
    embedding_algorithm="approx_wasserstein",
    n_iterations=5
)

# Print feature importance results
print("\nFeature Importance Results:")
print(importance_df)

The feature importance results show which node features contribute most to the model’s performance, ranked from most to least important. This can help in feature selection and understanding which structural properties are most relevant for the task.