Calculs Complexes et Formules Avancées Python dans Grist
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_scheduleAnalyses 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 significant3. 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_date2. 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 smoothedFormules 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.