Introduction

Au-delà des formules simples, Python dans Grist vous permet d'effectuer des calculs sophistiqués : modélisation financière, analyses statistiques, algorithmes complexes. Ce guide explore les techniques avancées pour exploiter tout le potentiel de Python dans vos spreadsheets.

Prérequis

  • Maîtrise des bases de Python dans Grist
  • Compréhension des fonctions et classes Python
  • Connaissance des bibliothèques scientifiques de base

Calculs Financiers Avancés

1. Calcul de la Valeur Actuelle Nette (VAN)

def calculate_npv(cash_flows, discount_rate):
    """
    Calcule la Valeur Actuelle Nette
    cash_flows: liste des flux de trésorerie
    discount_rate: taux d'actualisation (ex: 0.1 pour 10%)
    """
    npv = 0
    for t, cash_flow in enumerate(cash_flows):
        npv += cash_flow / ((1 + discount_rate) ** t)
    return round(npv, 2)

# Exemple d'utilisation dans Grist
# =calculate_npv($Cash_Flows, $Discount_Rate)

2. TRI (Taux de Rendement Interne)

from scipy.optimize import newton

def calculate_irr(cash_flows, initial_guess=0.1):
    """
    Calcule le Taux de Rendement Interne
    """
    def npv(rate):
        return sum([cf / (1 + rate) ** i for i, cf in enumerate(cash_flows)])
    
    try:
        irr = newton(npv, initial_guess)
        return round(irr * 100, 2)  # Retourne en pourcentage
    except:
        return None

# Utilisation
# =calculate_irr($Investment_Flows)

3. Amortissement Dégressif

def declining_balance_depreciation(cost, salvage, life, factor=2):
    """
    Calcul de l'amortissement dégressif
    factor: 2 pour dégressif double, 1.5 pour 150%
    """
    depreciation_schedule = []
    book_value = cost
    
    for year in range(1, life + 1):
        depreciation = min(book_value * (factor / life), book_value - salvage)
        book_value -= depreciation
        depreciation_schedule.append({
            'year': year,
            'depreciation': round(depreciation, 2),
            'book_value': round(book_value, 2)
        })
    
    return depreciation_schedule

Analyses Statistiques Avancées

1. Régression Linéaire Multiple

import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score

def multiple_regression_analysis(y, X):
    """
    Effectue une régression linéaire multiple
    y: variable dépendante (liste)
    X: variables indépendantes (liste de listes)
    """
    X_array = np.array(X)
    y_array = np.array(y)
    
    model = LinearRegression()
    model.fit(X_array, y_array)
    
    predictions = model.predict(X_array)
    r2 = r2_score(y_array, predictions)
    
    return {
        'coefficients': model.coef_.tolist(),
        'intercept': model.intercept_,
        'r_squared': r2,
        'predictions': predictions.tolist()
    }

# Dans Grist
# result = multiple_regression_analysis($Sales, [$Marketing_Spend, $Price, $Competitor_Price])

2. Analyse de Corrélation

import numpy as np

def correlation_matrix(data_dict):
    """
    Calcule la matrice de corrélation entre plusieurs variables
    """
    import pandas as pd
    
    df = pd.DataFrame(data_dict)
    corr_matrix = df.corr()
    
    # Formater pour affichage dans Grist
    result = {}
    for col in corr_matrix.columns:
        result[col] = corr_matrix[col].to_dict()
    
    return result

# Identifier les corrélations significatives
def significant_correlations(corr_matrix, threshold=0.7):
    significant = []
    variables = list(corr_matrix.keys())
    
    for i, var1 in enumerate(variables):
        for var2 in variables[i+1:]:
            corr_value = corr_matrix[var1][var2]
            if abs(corr_value) >= threshold:
                significant.append({
                    'variable_1': var1,
                    'variable_2': var2,
                    'correlation': round(corr_value, 3)
                })
    
    return significant

3. Test d'Hypothèses (T-Test)

from scipy import stats

def perform_t_test(sample1, sample2, alpha=0.05):
    """
    Effectue un test t de Student pour comparer deux échantillons
    """
    t_statistic, p_value = stats.ttest_ind(sample1, sample2)
    
    return {
        't_statistic': round(t_statistic, 4),
        'p_value': round(p_value, 4),
        'significant': p_value < alpha,
        'conclusion': 'Différence significative' if p_value < alpha else 'Pas de différence significative'
    }

# Test A/B
# result = perform_t_test($Group_A_Conversions, $Group_B_Conversions)

Algorithmes et Optimisation

1. Problème du Sac à Dos (Knapsack)

def knapsack_optimization(items, max_weight):
    """
    Résout le problème du sac à dos 0/1
    items: liste de dicts avec 'value', 'weight', 'name'
    max_weight: capacité maximale
    """
    n = len(items)
    
    # Tableau de programmation dynamique
    dp = [[0 for _ in range(max_weight + 1)] for _ in range(n + 1)]
    
    for i in range(1, n + 1):
        for w in range(max_weight + 1):
            if items[i-1]['weight'] <= w:
                dp[i][w] = max(
                    items[i-1]['value'] + dp[i-1][w-items[i-1]['weight']],
                    dp[i-1][w]
                )
            else:
                dp[i][w] = dp[i-1][w]
    
    # Reconstruction de la solution
    selected_items = []
    w = max_weight
    for i in range(n, 0, -1):
        if dp[i][w] != dp[i-1][w]:
            selected_items.append(items[i-1])
            w -= items[i-1]['weight']
    
    return {
        'total_value': dp[n][max_weight],
        'total_weight': sum(item['weight'] for item in selected_items),
        'selected_items': selected_items
    }

2. Optimisation de Portefeuille (Markowitz)

import numpy as np
from scipy.optimize import minimize

def optimize_portfolio(returns, cov_matrix, risk_free_rate=0.02):
    """
    Optimisation de portefeuille selon le modèle de Markowitz
    """
    n_assets = len(returns)
    
    def portfolio_performance(weights):
        portfolio_return = np.sum(returns * weights)
        portfolio_risk = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
        sharpe_ratio = (portfolio_return - risk_free_rate) / portfolio_risk
        return portfolio_return, portfolio_risk, sharpe_ratio
    
    def negative_sharpe(weights):
        return -portfolio_performance(weights)[2]
    
    # Contraintes et bornes
    constraints = {'type': 'eq', 'fun': lambda x: np.sum(x) - 1}
    bounds = tuple((0, 1) for _ in range(n_assets))
    
    # Optimisation
    initial_weights = np.array([1/n_assets] * n_assets)
    result = minimize(negative_sharpe, initial_weights, 
                     method='SLSQP', bounds=bounds, constraints=constraints)
    
    optimal_weights = result.x
    opt_return, opt_risk, opt_sharpe = portfolio_performance(optimal_weights)
    
    return {
        'optimal_weights': optimal_weights.round(4).tolist(),
        'expected_return': round(opt_return, 4),
        'portfolio_risk': round(opt_risk, 4),
        'sharpe_ratio': round(opt_sharpe, 4)
    }

Calculs Temporels Complexes

1. Calcul des Jours Ouvrés

from datetime import datetime, timedelta
import numpy as np

def calculate_business_days(start_date, end_date, holidays=None):
    """
    Calcule les jours ouvrés entre deux dates
    """
    if holidays is None:
        holidays = []
    
    business_days = 0
    current_date = start_date
    
    while current_date <= end_date:
        # Vérifier si c'est un jour de semaine (0=Lundi, 6=Dimanche)
        if current_date.weekday() < 5 and current_date not in holidays:
            business_days += 1
        current_date += timedelta(days=1)
    
    return business_days

def add_business_days(start_date, days_to_add, holidays=None):
    """
    Ajoute un nombre de jours ouvrés à une date
    """
    if holidays is None:
        holidays = []
    
    current_date = start_date
    days_added = 0
    
    while days_added < days_to_add:
        current_date += timedelta(days=1)
        if current_date.weekday() < 5 and current_date not in holidays:
            days_added += 1
    
    return current_date

2. Calcul d'Âge Précis

from datetime import datetime

def calculate_exact_age(birth_date, reference_date=None):
    """
    Calcule l'âge exact en années, mois et jours
    """
    if reference_date is None:
        reference_date = datetime.now()
    
    years = reference_date.year - birth_date.year
    months = reference_date.month - birth_date.month
    days = reference_date.day - birth_date.day
    
    # Ajustements
    if days < 0:
        months -= 1
        # Jours dans le mois précédent
        if reference_date.month == 1:
            prev_month = 12
            prev_year = reference_date.year - 1
        else:
            prev_month = reference_date.month - 1
            prev_year = reference_date.year
        
        import calendar
        days_in_prev_month = calendar.monthrange(prev_year, prev_month)[1]
        days += days_in_prev_month
    
    if months < 0:
        years -= 1
        months += 12
    
    return {
        'years': years,
        'months': months,
        'days': days,
        'total_days': (reference_date - birth_date).days
    }

Traitement de Données Complexes

1. Détection d'Anomalies (Z-Score)

import numpy as np

def detect_outliers_zscore(data, threshold=3):
    """
    Détecte les valeurs aberrantes avec la méthode du Z-Score
    """
    mean = np.mean(data)
    std = np.std(data)
    
    outliers = []
    for i, value in enumerate(data):
        z_score = abs((value - mean) / std)
        if z_score > threshold:
            outliers.append({
                'index': i,
                'value': value,
                'z_score': round(z_score, 2)
            })
    
    return outliers

# Méthode IQR (Interquartile Range)
def detect_outliers_iqr(data):
    """
    Détecte les outliers avec la méthode IQR
    """
    q1 = np.percentile(data, 25)
    q3 = np.percentile(data, 75)
    iqr = q3 - q1
    
    lower_bound = q1 - 1.5 * iqr
    upper_bound = q3 + 1.5 * iqr
    
    outliers = []
    for i, value in enumerate(data):
        if value < lower_bound or value > upper_bound:
            outliers.append({
                'index': i,
                'value': value,
                'bound': 'lower' if value < lower_bound else 'upper'
            })
    
    return {
        'outliers': outliers,
        'lower_bound': lower_bound,
        'upper_bound': upper_bound,
        'q1': q1,
        'q3': q3
    }

2. Interpolation et Extrapolation

from scipy import interpolate
import numpy as np

def interpolate_missing_values(x_known, y_known, x_all, method='linear'):
    """
    Interpole les valeurs manquantes
    """
    if method == 'linear':
        f = interpolate.interp1d(x_known, y_known, fill_value='extrapolate')
    elif method == 'cubic':
        f = interpolate.CubicSpline(x_known, y_known)
    elif method == 'polynomial':
        coeffs = np.polyfit(x_known, y_known, min(3, len(x_known)-1))
        f = np.poly1d(coeffs)
    
    y_interpolated = f(x_all)
    return y_interpolated.tolist()

# Lissage par moyenne mobile
def moving_average_smoothing(data, window_size=3):
    """
    Applique un lissage par moyenne mobile
    """
    smoothed = []
    for i in range(len(data)):
        start = max(0, i - window_size // 2)
        end = min(len(data), i + window_size // 2 + 1)
        window_avg = sum(data[start:end]) / (end - start)
        smoothed.append(round(window_avg, 2))
    return smoothed

Formules Personnalisées Avancées

1. Fonction de Recherche Floue

from difflib import SequenceMatcher

def fuzzy_lookup(search_term, candidates, threshold=0.6):
    """
    Recherche floue pour correspondances approximatives
    """
    matches = []
    for candidate in candidates:
        similarity = SequenceMatcher(None, search_term.lower(), 
                                   candidate.lower()).ratio()
        if similarity >= threshold:
            matches.append({
                'value': candidate,
                'similarity': round(similarity, 3)
            })
    
    # Trier par similarité décroissante
    matches.sort(key=lambda x: x['similarity'], reverse=True)
    return matches[:5]  # Retourner les 5 meilleures correspondances

# Utilisation
# =fuzzy_lookup($Company_Name, $Reference_Companies)

2. Calcul de Distance Géographique

import math

def haversine_distance(lat1, lon1, lat2, lon2):
    """
    Calcule la distance entre deux points GPS (formule de Haversine)
    Retourne la distance en kilomètres
    """
    R = 6371  # Rayon de la Terre en km
    
    lat1_rad = math.radians(lat1)
    lat2_rad = math.radians(lat2)
    delta_lat = math.radians(lat2 - lat1)
    delta_lon = math.radians(lon2 - lon1)
    
    a = (math.sin(delta_lat/2) ** 2 + 
         math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(delta_lon/2) ** 2)
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
    
    distance = R * c
    return round(distance, 2)

# Trouver les points les plus proches
def find_nearest_points(reference_point, points_list, n=5):
    """
    Trouve les n points les plus proches d'un point de référence
    """
    distances = []
    for point in points_list:
        dist = haversine_distance(
            reference_point['lat'], reference_point['lon'],
            point['lat'], point['lon']
        )
        distances.append({**point, 'distance_km': dist})
    
    distances.sort(key=lambda x: x['distance_km'])
    return distances[:n]

Meilleures Pratiques

  • Validation des entrées : Toujours vérifier les types et valeurs avant calcul
  • Gestion des erreurs : Utilisez try/except pour les calculs risqués
  • Documentation : Commentez vos fonctions complexes
  • Tests unitaires : Validez vos algorithmes avec des cas connus
  • Optimisation : Utilisez numpy pour les calculs sur grandes datasets
  • Précision numérique : Attention aux erreurs d'arrondi dans les calculs financiers

Conclusion

Ces formules avancées ouvrent des possibilités infinies pour l'analyse de données dans Grist. De la finance à la statistique, en passant par l'optimisation, Python transforme votre spreadsheet en puissant outil d'analyse.

Pour commencer avec les bases, consultez notre Guide Python pour Débutants dans Grist.