Ceci est une page optimisée pour les mobiles. Cliquez sur ce texte pour afficher la vraie page.

Microsoft 365 Récupération équation courbe de tendance

Boostez vos compétences Excel avec notre communauté !

Rejoignez Excel Downloads, le rendez-vous des passionnés où l'entraide fait la force. Apprenez, échangez, progressez – et tout ça gratuitement ! 👉 Inscrivez-vous maintenant !

Karkasse

XLDnaute Nouveau
Bonjour,

Dans le but de récupérer les coefficients des courbes de tendances de différentes courbes, j'ai récemment travailler sur un code qui permet de traiter toutes les séries que j'ai en fonction de différents "évènements".
J'ai debug au fur et à mesure le code (il me semble ne plus avoir de coquilles), la structure est opérationnelle cependant le coeur même de ma méthode, à savoir se servir des courbes de tendance pour en extraire les équations, ne fonctionnent pas.

Après différent tests, j'ai vite vu que l'ajout de Trendline fonctionne et que c'est l'affichage de l'équation qui ne fonctionne pas et donc toute l'extraction ne renvoie rien.
Je me suis renseigné sur la manière dont on doit afficher les label des trendline et il me semble le faire correctement, donc je ne sais pas quoi corriger pour que cela fonctionne.
 
Solution
Voici le code complet si jamais le problème est antérieur à l'affichage du label :

VB:
Sub ExtraireCoefEquationGraphV2()
    Dim co As ChartObject, ch As Chart
    Dim b As Series, s As Series
    Dim eq As String
    Dim LastRow As Long
    Dim ws As Worksheet, vs As Worksheet
    Dim matches As Object
    Dim regex As Object
    Dim ligneCible As Long
    Dim valeurRecherchee As Variant
    Dim eventsArr() As Variant
    Dim evt As Variant
    Dim j As Long, i As Long, l As Long
    Dim lastEvt As Long
    Dim tl As Trendline, t0 As Single
    
    ' --- Feuilles ---
    Set ws = ThisWorkbook.Sheets("S80H9 F3")
    Set vs = ThisWorkbook.Sheets("Graph S80H9 F3 Vieillissement")
    
    ' --- Graphique ---
    Set co = ThisWorkbook.Sheets("Graph S80H9_update").ChartObjects("Graphique 1")
    co.Activate
    Set ch = co.Chart
    
    ' --- LastRow colonne des butées ---
    LastRow = 0
    For l = 3 To 34
        If Trim(vs.Cells(l, 15).Value) = "" Then
            LastRow = l - 1
            Exit For
        End If
    Next l
    If LastRow = 0 Then LastRow = 34
    Debug.Print "LastRow : [" & LastRow & "]"
    
    ' Récupère le nombre d'EVENT depuis la première ligne
    g = 45 ' 1ère ligne de la 1ère butée, à modifier si chgt
    Do While vs.Cells(g, 2) <> 2
        g = g + 1
        If vs.Cells(g, 3) <> 0 And vs.Cells(g, 3) <> "" Then
            lastEvt = lastEvt + 1
        End If
    Loop
    Debug.Print "lastEvt :[" & lastEvt & "]"
    Debug.Print "g : [" & g & "]"
    
    ' --- RegExp pour extraire les coefficients ---
    Set regex = CreateObject("VBScript.RegExp")
    regex.Global = True
    regex.IgnoreCase = True
    regex.Pattern = "[-+]?\d*\.?\d+(?:[Ee][-+]?\d+)?"
    
    ' --- Boucle sur chaque butée ---
    For Each Cell In vs.Range(vs.Cells(3, 15), vs.Cells(LastRow, 15))
        valeurRecherchee = Trim(Cell.Value)
        Debug.Print "valeurRecherchee : [" & valeurRecherchee & "]"
        
        ' --- Recherche ligne de départ (fusion possible) ---
        ligneCible = 0
        For l = 45 To 195
            If CStr(Trim(vs.Cells(l, 2).MergeArea.Cells(1, 1).Value)) = CStr(valeurRecherchee) Then
                ligneCible = l
                Exit For
            End If
        Next l
        If ligneCible = 0 Then
            Debug.Print "Butée " & valeurRecherchee & " non trouvée"
            GoTo NextButée
        End If
        
        ' --- Ajustement index relatif si besoin ---
        ligneCible = ligneCible + 44
        
        ' --- Recherche série dans le graphique ---
        Set b = Nothing
        For Each s In ch.SeriesCollection
            If CStr(s.Name) = CStr(valeurRecherchee) Then
                Set b = s
                Exit For
            End If
        Next s
        
        If b Is Nothing Then
            Debug.Print "Série de butée " & valeurRecherchee & " inexistante"
            GoTo NextButée
        End If
        
        ' --- Supprime les anciennes trendlines ---
        On Error Resume Next
        For Each tl In b.Trendlines
            tl.Delete
        Next tl
        On Error GoTo 0
        
        ' --- Boucle sur chaque EVENT ---
        For j = 1 To lastEvt
            Debug.Print " vs.Cells(45 + j - 1, 3) : [" & vs.Cells(45 + j - 1, 3).Value & "]"
            ws.Range("BG3").Value = vs.Cells(45 + j - 1, 3).Value
            Debug.Print " BG3 : [" & ws.Range("BG3").Value & "]"
            
            ' Force le recalcul des cellules et refresh graph
            Application.CalculateFull
            DoEvents
            ch.Refresh
            
            ' Check si série non vide
            Dim valArray As Variant
            valArray = b.Values
            If Application.WorksheetFunction.Count(valArray) = 0 Then
                Debug.Print "Série " & b.Name & "vide"
            Else
                Debug.Print "Série contient des valeurs"
            End If
            Debug.Print " 1ère valeur : " & valArray(LBound(valArray) + 200) & " 2ème valeur : " & valArray(UBound(valArray) - 200)
            
            
            ' --- Vérifie si la série contient des valeurs ---
            If Application.WorksheetFunction.Count(b.Values) = 0 Then
                Debug.Print "Série " & valeurRecherchee & " pour l'EVENT " & vs.Cells(lastEvt, 3).Value & " sans données, on passe."
                GoTo NextEVENT
            End If
            
            
            ' --- Ajout de la trendline ---
            On Error Resume Next
            Set tl = b.Trendlines.Add(Type:=xlPolynomial, Order:=3)
            On Error GoTo 0
            
            If tl Is Nothing Then
                Debug.Print "Impossible de créer la trendline pour la série " & b.Name
                GoTo NextEVENT
            End If
            
            ' --- Affiche l'équation ---
            On Error Resume Next
            With Charts(co).SeriesCollection(b).Trendlines(1)
                .DisplayEquation = True
            End With
            On Error GoTo 0
            
            ' --- Attente que l'équation soit calculée ---
            t0 = Timer
            Do
                DoEvents
                eq = ""
                On Error Resume Next
                eq = tl.DataLabel.Text
                On Error GoTo 0
                If eq <> "" Then Exit Do
                If Timer - t0 > 2 Then Exit Do ' timeout 2 secondes
            Loop
            Debug.Print "tl : [" & tl.DataLabel.Text & "]"
            
            
            If eq = "" Then
                Debug.Print "Pas d'équation trouvée pour série " & b.Name & " (Event=" & j & ")"
                GoTo NextEVENT
            End If
            
            ' --- Nettoyage de l'équation ---
            eq = Replace(eq, "y=", "")
            eq = Replace(eq, " ", "")
            eq = Replace(eq, ",", ".")
            Debug.Print " eq : [" & eq & "]"
            
            ' --- Extraction des coefficients ---
            Set matches = regex.Execute(eq)
            For i = 0 To matches.Count - 1
                If IsNumeric(matches(i).Value) Then
                    vs.Cells(ligneCible + (j - 1), 4 + i).Value = CDbl(matches(i).Value)
                    Debug.Print " matches(i) : [" & CDbl(matches(i).Value) & "]"
                End If
            Next i
            
            ' --- Suppression sûre de la trendline ---
            On Error Resume Next
            tl.Delete
            On Error GoTo 0
            DoEvents

            
            ' --- Nettoyage de l'équation ---
            eq = Replace(eq, "y=", "")
            eq = Replace(eq, " ", "")
            eq = Replace(eq, ",", ".")
            Debug.Print " eq : [" & eq & "]"
            
            ' --- Extraction des coefficients ---
            Set matches = regex.Execute(eq)
            For i = 0 To matches.Count - 1
                If IsNumeric(matches(i).Value) Then
                    vs.Cells(ligneCible + (j - 1), 4 + i).Value = CDbl(matches(i).Value)
                    Debug.Print " matches(i) : [" & CDbl(matches(i).Value) & "]"
                End If
            Next i
            
            ' --- Supprime trendline pour la prochaine boucle ---
            With Charts(co).SeriesCollection(b).Trendlines(1)
                .DisplayEquation = False
            End With

            
NextEVENT:
        Next j
        
NextButée:
    Next Cell
    
    Debug.Print "Extraction terminée."
End Sub
 
bonjour, c'est assez compliqué ce que vous faites là.
Avez-vous un fichier avec vos données et le résultat voulu ?
Avec linesch on a directement tous les paramètres.
LINEST aurait été une solution oui, mais étant donné la quantité de données d'entrées et de la grandeur de leur plage utilisée LINEST serait extrêmement fastidieux je pense ?

J'ai des plages de données que ce soit X et Y de 1800 lignes et ça pour chaque courbes de chaque série. Série qui sont déjà au nombre de 14, qui ont elles mêmes 10 courbes différentes (tracé par ces X et Y justement).

Dans le fichier que j'ai mis en pièce jointe, j'ai mis uniquement un seul élément (qui correspond à une série à savoir ici 1) avec donc les EVENT qui sont les différentes colonnes de la 1ere feuille (Pre 2025, Post T01 etc).

Le résultat voulu à la toute fin c'est les coefficients de la courbe de tendance (ici polynôme d'ordre 3), écris par la macro dans le tableau de la feuille "Graph S80H9 F3 Vieillissement".
 

Pièces jointes

Dernière édition:
J'ai essayé une version simplifié à 100% du principe de mon code complet et ca fonctionne très bien, j'ai la courbe de tendance, l'équation qui s'affiche ainsi que la copie de celle-ci dans une cellule de ma feuille.
Cependant j'ai observé un "lag" ou délais de excel lors de l'ajout de la courbe de tendance et du label (alors que j'ai fais tourner le code step by step avec F8).
Est-ce que le problème viendrait pas du temps que met Excel à réellement créer la courbe de tendance (visible par la macro je veux dire) ?

Le code utilsié pour tester ça est le suivant :
VB:
Sub Test()

    Dim co As ChartObject, ch As Chart
    Dim eq As String
    
    ' --- Feuilles ---
    Set ws = ThisWorkbook.Sheets("S80H9 F3")
    Set vs = ThisWorkbook.Sheets("Graph S80H9 F3 Vieillissement")
    
    ' --- Graphique ---
    ThisWorkbook.Sheets("Graph S80H9_update").ChartObjects("Graphique 2").Activate
    Set co = ThisWorkbook.Sheets("Graph S80H9_update").ChartObjects("Graphique 2")
    Set ch = co.Chart

    ch.SeriesCollection(1).Trendlines.Add Type:=xlPolynomial, Order:=3
    With ch.SeriesCollection(1).Trendlines(1)
        .DisplayEquation = True
    End With
    DoEvents
    eq = ch.SeriesCollection(1).Trendlines(1).DataLabel.Text
    vs.Cells(2, 2) = eq
    
End Sub

Je ne sais pas ce que vous en pensez ou si vous avez déjà été confronté à ce problème et avez des réponses à mes interrogations ?
 
un essai
Enrichi (BBcode):
Sub M_Linesch()
     Dim i, j, X, Y, Arr, s
     t = Timer
     With Sheets("S80H9 F3")
          With .Range("BG4:BG7129")
               Y = "'" & .Parent.Name & "'!" & .Address(0, 0)
               X = "'" & .Parent.Name & "'!" & .Offset(, 1).Address(0, 0)
          End With
          s = Replace(Replace("LINEST(#1,power(#2,{1,2,3}),1,1)", "#1", Y), "#2", X) 'polynom 3 degrees
          Arr = Evaluate(s)
          .Range("CA4").Resize(UBound(Arr), UBound(Arr, 2)).Value2 = Arr
     End With
   
     s = ""
     For i = 1 To UBound(Arr, 2)
          s = s & "  " & IIf(Arr(1, i) >= 0, "+", "") & Format(Arr(1, i), "0.000") & IIf(i < UBound(Arr, 2), " *X^" & UBound(Arr, 2) - i, "")
     Next
     s = s & "    r2=" & Format(Arr(3, 1), "0.00")
     MsgBox Format(Timer - t, "0.00\s") & vbLf & s
   
End Sub
 
Merci !
Oui effectivement ta méthode marche plutôt bien. Biensur je ne l'ai pas pris tel quel car j'ai des contraintes dû à mes données d'entrées et aussi à comment je dois traiter celles-ci (présence d'hystérésis dans mes courbes donc séparation des valeurs en 2 pour avoir un polynôme représentatif par exemple).

Je pensais honnêtement que LinEst aurait été un calvaire avec ce que j'avais en tête, mais ça s'avère être plus simple que prévu et surtout ULTRA RAPIDE ! (comparé à la méthode avec le graph qui prend du temps et qui plante pcq excel est tanké...)

Encore merci pour le coup de main avec ton exemple de code, ça m'a bien débloqué !



Je met mon code complet en-dessous si jamais quelqu'un se retrouve bloqué comme je l'ai été :
Enrichi (BBcode):
Sub ExtraireCoef_Separation()
    Dim ws As Worksheet, vs As Worksheet
    Dim LastRowBute As Long, ligneCible As Long
    Dim valeurRecherchee As Variant
    Dim j As Long, i As Long, l As Long, z As Long, y As Long
    Dim xVals() As Double, yVals() As Double
    Dim coeffs As Variant
    Dim evt As Variant
    Dim validCount As Long
    Dim startRow As Long, endRow As Long
    Dim lastEvt As Long
    
    ' --- Feuilles ---
    Set ws = ThisWorkbook.Sheets("S80H9 F3")
    Set vs = ThisWorkbook.Sheets("Graph S80H9 F3 Vieillissement")
    
    'Clear tableau
    vs.Range(vs.Cells(45, 4), vs.Cells(364, 8)).ClearContents

    ' --- Détermination du LastRow pour les butées ---
    LastRowBute = 0
    For j = 3 To 34
        If Trim(vs.Cells(j, 15).Value) = "" Then
            LastRowBute = j - 1
            Exit For
        End If
    Next j
    If LastRowBute = 0 Then LastRowBute = 34
    Debug.Print " LastRowBute :" & LastRowBute

    ' --- Boucle sur chaque butée ---
    For Each Cell In vs.Range(vs.Cells(3, 15), vs.Cells(LastRowBute, 15))
        valeurRecherchee = Trim(Cell.Value)
        Debug.Print "valeurRecherchee :" & valeurRecherchee

        ' --- Ligne de départ dans le tableau de coef ---
        ligneCible = 0
        For j = 45 To 364
            If CStr(Trim(vs.Cells(j, 2).MergeArea.Cells(1, 1).Value)) = CStr(valeurRecherchee) Then
                ligneCible = j
                Exit For
            End If
        Next j
        If ligneCible = 0 Then GoTo NextButée
        Debug.Print " ligneCible : " & ligneCible

        startRow = 0
        endRow = 0

        ' --- Trouver les lignes correspondant à la butée dans la colonne A fusionnée ---
        For l = 4 To 59931
            If CStr(Trim(ws.Cells(l, 1).MergeArea.Cells(1, 1).Value)) = CStr(valeurRecherchee) Then
                If startRow = 0 Then startRow = l
                endRow = l
            End If
        Next l
        Debug.Print "startRow :" & startRow
        Debug.Print " endRow :" & endRow

        If startRow = 0 Then
            Debug.Print "Aucune donnée pour la butée " & valeurRecherchee
            GoTo NextButée
        End If

        ' --- Compter les événements valides ---
        ' Ancien, avec les cellules fusionnées ne marchent plus correctement
'        lastEvt = 0
'        Dim cellEvt As Range
'        For j = 45 To 64
'            Set cellEvt = vs.Cells(j, 3)
'            ' On ne compte qu'une seule fois par bloc fusionné
'            If cellEvt.MergeArea.Cells(1, 1).Row = cellEvt.Row Then
'                If cellEvt.Value <> 0 And cellEvt.Value <> "" Then
'                    lastEvt = lastEvt + 1
'                End If
'            End If
'        Next j

        ' --- Identifier les lignes d’EVENT (fusionnées sur 2 lignes) ---
        Dim EventRows() As Long
        Dim cellEvt As Range
        lastEvt = 0
        
        For j = 45 To 64
            Set cellEvt = vs.Cells(j, 3)
            ' Si c’est la première ligne d’un bloc fusionné et qu’il contient un EVENT
            If cellEvt.MergeArea.Cells(1, 1).Row = cellEvt.Row Then
                If Trim(cellEvt.Value) <> "" And cellEvt.Value <> 0 Then
                    lastEvt = lastEvt + 1
                    ReDim Preserve EventRows(1 To lastEvt)
                    EventRows(lastEvt) = j ' On retient la 1ère ligne fusionnée
                End If
            End If
        Next j
        
        Debug.Print "NombreEvent détectés : " & lastEvt

        ' --- Boucle sur chaque EVENT ---
        For j = 1 To lastEvt
            evt = vs.Cells(EventRows(j), 3).Value
            Debug.Print "Event actuel :" & evt
            ws.Range("BG3").Value = evt

            ' --- Préparer les tableaux X et Y ---
            validCount = 0
            ReDim xVals(1 To endRow - startRow + 1)
            ReDim yVals(1 To endRow - startRow + 1)

            For z = startRow To endRow
                If IsNumeric(ws.Cells(z, 60).Value) And ws.Cells(z, 60).Value <> 0 And _
                   IsNumeric(ws.Cells(z, 59).Value) And ws.Cells(z, 59).Value <> 0 Then
                    validCount = validCount + 1
                    xVals(validCount) = ws.Cells(z, 60).Value  ' BH
                    yVals(validCount) = ws.Cells(z, 59).Value  ' BG
                End If
            Next z

            If validCount = 0 Then GoTo NextEVENT
            ReDim Preserve xVals(1 To validCount)
            ReDim Preserve yVals(1 To validCount)

            ' --- DÉTECTION DU MAX DE LA COURBE ---
            Dim idxMax As Long, yMax As Double
            yMax = -1E+99
            For i = 1 To validCount
                If yVals(i) > yMax Then
                    yMax = yVals(i)
                    idxMax = i
                End If
            Next i
            Debug.Print "  -> yMax=" & yMax & " à index " & idxMax

            ' === PARTIE 1 (avant le max) ===
            Call DoLinEst(ws, vs, xVals, yVals, 1, idxMax, EventRows(j) + ligneCible - 45, CStr(valeurRecherchee), "Partie 1")

            ' === PARTIE 2 (après le max) ===
            If idxMax < validCount Then
                Call DoLinEst(ws, vs, xVals, yVals, idxMax, validCount, EventRows(j) + 1 + ligneCible - 45, CStr(valeurRecherchee), "Partie 2")
            End If
            vs.Cells(EventRows(j) + ligneCible - 45, 8).Value = yMax

NextEVENT:
        Next j
NextButée:
    Next Cell

    MsgBox "Extraction coefficients terminée."
End Sub


' --- Sous-fonction : fait le LINEST sur une plage donnée et écrit les coefficients ---
Private Sub DoLinEst(ws As Worksheet, vs As Worksheet, xVals() As Double, yVals() As Double, _
                     startIdx As Long, endIdx As Long, ligneOut As Long, butee As String, label As String)

    Dim n As Long
    n = endIdx - startIdx + 1
    If n < 5 Then
        Debug.Print "   [" & label & "] Pas assez de points pour " & butee
        Exit Sub
    End If

    Dim tmpX() As Double, tmpY() As Double
    ReDim tmpX(1 To n)
    ReDim tmpY(1 To n)
    Dim i As Long
    For i = 1 To n
        tmpX(i) = xVals(startIdx + i - 1)
        tmpY(i) = yVals(startIdx + i - 1)
    Next i

    ws.Range("ZZ1:AAA10000").ClearContents
    ws.Range("ZZ1").Resize(n, 1).Value = Application.Transpose(tmpY)
    ws.Range("AAA1").Resize(n, 1).Value = Application.Transpose(tmpX)

    Dim yRangeStr As String, xRangeStr As String, formulaStr As String
    yRangeStr = "'" & ws.Name & "'!$ZZ$1:$ZZ$" & n
    xRangeStr = "'" & ws.Name & "'!$AAA$1:$AAA$" & n
    formulaStr = "LINEST(" & yRangeStr & ",POWER(" & xRangeStr & ",{1,2,3}),TRUE,FALSE)"


    Dim coeffs As Variant
'    coeffs = Evaluate(formulaStr)
'
'    If IsArray(coeffs) Then
'        For i = LBound(coeffs, 2) To UBound(coeffs, 2)
'            vs.Cells(ligneOut, 3 + i).Value = coeffs(1, i)
'        Next i
'        Debug.Print "   [" & label & "] OK (" & n & " points) -> ligne " & ligneOut
'    Else
'        Debug.Print "   [" & label & "] LINEST vide"
'    End If
    
    
    'Test
    coeffs = Evaluate(formulaStr)
    Dim compteur As Long
    compteur = 0

    If Not IsEmpty(coeffs) Then
        If IsArray(coeffs) Then
            Dim r As Long, c As Long
            On Error Resume Next
            ' Essai dimension 2
            For r = LBound(coeffs, 1) To UBound(coeffs, 1)
                For c = LBound(coeffs, 2) To UBound(coeffs, 2)
                    Debug.Print "coeffs(" & r & "," & c & ") = " & coeffs(r, c)
                Next c
            Next r
            If Err.Number <> 0 Then
                Err.Clear
                ' Cas tableau 1D
                For r = LBound(coeffs) To UBound(coeffs)
                    Debug.Print "coeffs(" & r & ") = " & coeffs(r)
                    vs.Cells(ligneOut, 4 + compteur).Value = coeffs(r)
                    debut.Print " ligneOut :" & ligneOut
                    compteur = compteur + 1
                Next r
            End If
            On Error GoTo 0
        Else
            Debug.Print "coeffs n'est pas un tableau : " & coeffs
        End If
    Else
        Debug.Print "coeffs est vide"
    End If
    
End Sub
 
- Navigue sans publicité
- Accède à Cléa, notre assistante IA experte Excel... et pas que...
- Profite de fonctionnalités exclusives
Ton soutien permet à Excel Downloads de rester 100% gratuit et de continuer à rassembler les passionnés d'Excel.
Je deviens Supporter XLD

Discussions similaires

Réponses
5
Affichages
948
Les cookies sont requis pour utiliser ce site. Vous devez les accepter pour continuer à utiliser le site. En savoir plus…