Страницы: 1 2 След.
RSS
Аппроксимация полиномом в MS Excel., Сравнение разных способов аппроксимации полиномом. Низкое "качество" аппроксимации встроенной функцией ЛИНЕЙН.
 
Уважаемые сопланетники!

Столкнулся с проблемой, прошу помочь.

Для аппроксимации передаточной характеристики измерительных приборов часто используют полином. С точки зрения электроники - это очень удобный способ, требующий от МК относительно мало ресурсов и предоставляющий очень хорошую точность.

Для получения коэффициентов однофакторного полинома я традиционно использовал встроенную в Excel функцию ЛИНЕЙН. Однако в имеющейся в данный момент задаче выяснилось, что погрешность аппроксимации этим способом очень высока, - значительно выше, чем при использовании математических пакетов или линии тренда графика в Excel.

Предположив, что проблема в некорректном использовании функции ЛИНЕЙН, перешерстил интернет, пробовал сторонние файлы. Оказалось, проблема известная, и связана она с алгоритмом работы этой функции (подробности я не понял, в математике не очень).

Я взял одну и ту же выборку и сравнил четыре способа получения коэффициентов:
1. с использованием ЛИНЕЙН
2. с помощью утилиты Agraph
3. построением линии тренда на графике Excel
4. с помощью функций polyfit/polyval в Octave


Выяснилось, что на большой выборке (13 точек по 600 измерений в каждой) первый способ (ЛИНЕЙН) даёт примерно в 1,5...2 раза бОльшую погрешность, чем другие.


При меньшей выборке (13 точек по 5 в каждой, итого 65 точек) ещё интереснее: и ЛИНЕЙН, и Agraph дают погрешность в 4 раза больше, чем два последних.
Agraph имеет ещё два недостатка - максимальное количество обрабатываемых точек - 5000, а количество значащих цифр в коэффициентах - не более 9.



Два последних способа дают отличный результат, но есть одна проблема - сложность извлечения коэффициентов.
Требуется постоянно менять данные, и, соответственно, производить множество вспомогательных действий для загрузки/конвертирования данных и "выдёргивания" коэффициентов.

Хотелось бы все действия максимально автоматизировать и привязать к Excel. Возможно, можно как-то исхитриться и заставить ЛИНЕЙН работать по другому?
Или есть способ (может, с помощью VBA) автоматически выдернуть коэффициенты из линии тренда?
Попадались в сети фрагменты VBA для вычисления коэффициентов, но тоже приемлемого ничего не подобрал...

p.s. Движок запрещает грузить файлы свыше 100к, поэтому нарезал скринов.
 
Коэффициенты можно найти формулами:
Код
=(СЧЁТ(A1:A3)*СУММПРОИЗВ(A1:A3;B1:B3)-СУММ(A1:A3)*СУММ(B1:B3))/(СЧЁТ(A1:A3)*СУММПРОИЗВ(A1:A3;A1:A3)-СУММ(A1:A3)*СУММ(A1:A3))
=(СУММ(B1:B3)-(СЧЁТ(A1:A3)*СУММПРОИЗВ(A1:A3;B1:B3)-СУММ(A1:A3)*СУММ(B1:B3))/(СЧЁТ(A1:A3)*СУММПРОИЗВ(A1:A3;A1:A3)-СУММ(A1:A3)*СУММ(A1:A3))*СУММ(A1:A3))/СЧЁТ(A1:A3)
И через VBA.
Код
Function МЛИНЕЙН(известные_значения_y As Range, известные_значения_x As Range) As Variant
    Dim xrr As Variant
    Dim yrr As Variant
    xrr = известные_значения_x
    yrr = известные_значения_y
    Dim nn As Long
    nn = известные_значения_y.Cells.Count
    Dim x As Double
    Dim y As Double
    Dim xx As Double
    Dim xy As Double
    
    Dim i As Long
    For i = 1 To nn
        x = x + xrr(i, 1)
        y = y + yrr(i, 1)
        xy = xy + xrr(i, 1) * yrr(i, 1)
        xx = xx + xrr(i, 1) * xrr(i, 1)
    Next
    Dim aa As Double
    Dim bb As Double
    aa = (nn * xy - x * y) / (nn * xx - x * x)
    bb = (y - aa * x) / nn
    
    Dim arr As Variant
    ReDim arr(1 To 2)
    arr(1) = aa
    arr(2) = bb
    
    МЛИНЕЙН = arr
End Function
Изменено: МатросНаЗебре - 03.12.2021 15:02:39
 
Цитата
Алексей Назаров написал:
Или есть способ (может, с помощью VBA) автоматически выдернуть коэффициенты из линии тренда?
Да, есть.
тут выкладывал решение
Код
' Проведение интерполяции с использованием функционала Excel
' На выходе - коэффициенты полинома. Число точек должно быть минимум на одну больше, чем степень полинома.
' Данные берутся из программы
Public Sub Linia_trenda(ByRef Y() As Double, ByRef X() As Double, ByVal PolyStep As Integer, ByRef c() As Double, Optional ByRef r2 As Double)
    Dim stepen As Long
    ' Ввожу проверку не превышения степени массива
    If (UBound(Y) - LBound(Y) - 1) < PolyStep Then
        stepen = UBound(Y) - LBound(Y)
    Else
        stepen = PolyStep
    End If
    ' Объявляю переменные, создаю матрицы под размер данных и степень полинома.
    Dim X1() As Double, y1() As Double
    ReDim X1(LBound(Y) To UBound(Y), 1 To stepen) As Double
    ReDim y1(LBound(Y) To UBound(Y), 1 To 1) As Double
    ReDim c(1 To stepen + 1) As Double
    ' Заполню массив Х в соответствии со степенью уравнения.
    For i = LBound(X) To UBound(X)
        y1(i, 1) = Y(i)
        X1(i, 1) = X(i)
        For N = 2 To stepen
            X1(i, N) = X1(i, 1) ^ N
        Next N
    Next i
    ' Нахожу уравнение.
    Dim Coefs As Variant
        Coefs = WorksheetFunction.LinEst(y1, X1, True, True)
    ' Вытаскиваю коэффициенты полинома.
    For i = 1 To stepen + 1
        c(i) = Coefs(1, i)
    Next i
    ' Вытаскиваю величину достоверности аппроксимации.
    r2 = Coefs(3, 1)
End Sub

Однако обратите внимание, что далеко не всегда коэфф-ты вытаскиваются верно. Я в практике с таким не сталкивался, но такое имеет место быть
Тут выкладывали проблему.
Поэтому в своих расчётах применяю кусочную интерполяцию...
Изменено: tutochkin - 03.12.2021 15:02:10 (очепятка)
 
Благодарю за подсказки, пробую использовать.

На VBA не писал очень давно, попал в ступор.
Не могу передать массив в Linia_trenda.

Пытаюсь например, так, получаю ошибку:
Код
Public Sub Linia_START()

Dim myRangeX() As Double
Dim myRangeY() As Double
Dim myRangeC() As Double

myRangeX = Range("B4:B68") '.Value
myRangeY = Range("C4:C68") '.Value
myRangeC = Range("w5:w11") '.Value

Call Linia_trenda(myRangeY, myRangeX, 1, myRangeC)

End Sub

Чувствую, не учёл какую-то  мелочь, а сообразить не могу..
 
Алексей Назаров,
А какая цель вообще? Получить уравнение линии тренда?

Код
' Программа формирования текста уравнения по всем точкам

Public Function Case_uravnenie(xVal As Range, yVal As Range, PolyStep As Long, _
                            Optional NameX As String = "X") As Variant
Dim j As Long
Dim N As Long
Dim k As Long
Dim stepen As Long
Dim xn() As Double    ' заявляем массив X
Dim yn() As Double    ' заявляем массив Y
Dim c() As Double    ' заявляем массив c - коэффециенты уравнения полинома


Case_uravnenie = "" & Chr(10)

Dim Nna4 As Long 'Номер начала диапазона.
Dim Nkon As Long 'Номер конца диапазона.

Nna4 = 1
Nkon = xVal.Count
' Проверяем на соответствие число элементов участка степени полинома
    If (Nkon - Nna4) < PolyStep Then
        stepen = (Nkon - Nna4)
    Else
        stepen = PolyStep
    End If
' Заполняем матрицы участка
    ReDim xn(1 To (Nkon - Nna4 + 1), 1 To stepen)
    ReDim yn(1 To (Nkon - Nna4 + 1), 1 To 1)
    ReDim c(1 To stepen + 1) As Double
    For j = 1 To (Nkon - Nna4 + 1)
        xn(j, 1) = xVal.Rows(j + Nna4 - 1)
        For N = 2 To stepen
            xn(j, N) = xn(j, 1) ^ N
        Next N
        yn(j, 1) = yVal.Rows(j + Nna4 - 1)
    Next j
' Делаем расчёт и вывод.
    For k = 1 To stepen + 1 Step 1
        c(k) = Format(Application.Index(WorksheetFunction.LinEst(yn, xn, True, True), 1, k), "0.####E+")
        
        If c(k) >= 0 And k > 1 Then
            Case_uravnenie = Case_uravnenie & " + " & c(k)
        Else
            Case_uravnenie = Case_uravnenie & c(k)
        End If
        
        If (stepen + 1 - k) > 0 Then
            Case_uravnenie = Case_uravnenie & " * " & NameX & " ^ " & (stepen + 1 - k) & " "
        End If
    Next k
   
Case_uravnenie = Case_uravnenie & Chr(10)
End Function
Изменено: tutochkin - 07.12.2021 13:07:24
 
Цель - получить коэффициенты аппроксимирующего полинома 5-6 порядка по исходной выборке, находящейся в книге Excel.
Без лишних танцев с бубном, свойственным известным мне надёжным методам, типа линии тренда или Октавы (перекодировки, замены символов, копипасты и т.п.).

Почему именно Excel - в ней я собираю данные с измерительных приборов (с помощью VBA), управляю внешними устройствами  (с помощью VBA же), обрабатываю полученные данные и т.п., и любой прыжок в сторону превращается в неудобный костыль...

И при экспериментах часто вариантов данных очень много, каждый нужно обработать, проверить и т.п.
Функция ЛИНЕЙН по удобству устраивала полностью, но, в определённый момент, перестала устраивать по точности.
Изменено: Алексей Назаров - 08.12.2021 10:17:38 (Орф. ошибки)
 
Алексей Назаров, ну вытащить коэфф-ты из того что я выше привёл совсем не сложно - они там уже есть (матрица с). Однако они не будет отличаться от =ИНДЕКС(ЛИНЕЙН(F4:F13;E4:E13^{1;2;3;4;5;6});1;1) , проверено
Цитата
Алексей Назаров написал:
Функция ЛИНЕЙН по удобству устраивала полностью, но, в определённый момент, перестала устраивать по точности.
Понятие точности относительно. Вот на рисунке несколько видов интерполяции через одни и те-же опорные точки.... А какая разница в промежутках :) Выбирай на вкус.

Вот так выглядят вариации аппроксимации полиномами разных степеней одних и тех-же точек. А вы именно аппроксимацию хотите делать, как я понял...

Для себя пришёл к выводу, что кусочная интерполяция рулит. Но и тут есть подводные камни.
Как пример разница при интерполяции полиномом первой и второй степени... по одним и тем же опорным

А если просто посчитать, то (с) БМВ
Код
Public Function polinomEx(xVal As Range, yVal As Range, X As Single, stepen As Integer)
Dim I As Integer
Dim Seria
Seria = Array(1, 2, 3, 4, 5, 6, 7)
If stepen > 7 Then stepen = 7
If xVal.Count < stepen + 1 Then stepen = xVal.Count - 1
polinomEx = 0#
ReDim Preserve Seria(stepen - 1)
For I = 1 To stepen + 1
    polinomEx = polinomEx + _
    (X ^ (stepen + 1 - I)) * _
        Application.Index(WorksheetFunction.LinEst(yVal, _
        IIf(stepen = 1, xVal, Application.Power(xVal, Seria)), _
        True, True), 1, I)
Next I
End Function
Изменено: tutochkin - 07.12.2021 14:16:00
 
Ага, уже что-то получается!
Большое спасибо!

На команду
Код
Range("w5:w11") = Case_uravnenie(Range("B4:B68"), Range("C4:C68"), 6)
Получаю результат:
во всех ячейках диапазона полная формула

    4,7737E-38 * X ^ 6 -5,455E-30 * X ^ 5    + 2,1638E-22 * X ^ 4 -3,0827E-15 * X ^ 3  + 0 * X ^ 2    + 0 * X ^ 1  + 5512600
Осталось "раздербанить" строку на составляющие, но есть одно большое НО: в результате всего 5 значащих цифр.....
Нужно не менее 9, лучше все 14..

Кроме того, значения коэффициентов отличаются... Теоретически, как здесь:

Завтра попробую копать LinEst
 
Цитата
Алексей Назаров написал:
во всех ячейках диапазона полная формула
Так этот макрос и выводит полную формулу... Там же написано - "' Программа формирования текста уравнения по всем точкам"
Цитата
Алексей Назаров написал:
в результате всего 5 значащих цифр....
Так это в макросе и прописано... Format(Application.Index(WorksheetFunction.LinEst(yn, xn, True, True), 1, k), "0.####E+") увеличивайте, или вообще удалите.
Если нужны просто коэфф-ты (зачем?) то вот
Код
Public Function Koef(xVal As Range, yVal As Range, stepen As Integer)
Dim i As Integer
Dim Seria
Dim polinomEx() As Single
Seria = Array(1, 2, 3, 4, 5, 6, 7)
If stepen > 7 Then stepen = 7
If xVal.Count < stepen + 1 Then stepen = xVal.Count - 1
ReDim Preserve Seria(stepen - 1)
ReDim polinomEx(stepen)
For i = 1 To stepen + 1
    polinomEx(i - 1) = Application.Index(WorksheetFunction.LinEst(yVal, _
        IIf(stepen = 1, xVal, Application.Power(xVal, Seria)), _
        True, True), 1, i)
Next i

Koef = polinomEx
End Function

=ИНДЕКС(Koef($D$5:$D$12;$E$5:$E$12;6);1;G5) - вывод элементов
или
=ИНДЕКС(Koef2($D$5:$D$12;$E$5:$E$12;6);1;G5)
Но они не будут отличаться от того что выдаст
=ИНДЕКС(ЛИНЕЙН($E$5:$E$12;$D$5:$D$12^{1;2;3;4;5;6});1;G5)



Код
Public Function Koef2(Xish As Variant, Yish As Variant, ByRef stepen As Variant)
Dim c() As Double
Dim X() As Double
Dim Y() As Double
ReDim c(1 To stepen + 1)
Подготовка_данных Xish, Yish, X, Y
Linia_trenda Y, X, stepen, c
Koef2 = c
End Function


Код
' Подпрограмма переопределения значений исходных данных
Function Подготовка_данных(Xvh As Variant, Yvh As Variant, _
                           Xvih() As Double, Yvih() As Double) As Variant
    
    Dim NumYRows As Long, i As Long
    If TypeName(Xvh) = "Range" Then
    ' Если данные взяты с листа эксель
        Xvh = Xvh.Value2
        Yvh = Yvh.Value2
        NumXRows = UBound(Xvh)
        NumYRows = UBound(Yvh)
    
        ReDim Xvih(0 To NumXRows - 1)
        ReDim Yvih(0 To NumXRows - 1)
    
        For i = 1 To NumXRows
            Xvih(i - 1) = Xvh(i, 1)
            Yvih(i - 1) = Yvh(i, 1)
        Next i
    
    Else
    ' Если данные переданы из функции
        NumXRows = UBound(Xvh) + 1
        NumYRows = UBound(Yvh) + 1
        
        ReDim Xvih(0 To NumXRows - 1)
        ReDim Yvih(0 To NumXRows - 1)

        For i = 0 To NumXRows - 1
            Xvih(i) = Xvh(i)
            Yvih(i) = Yvh(i)
        Next i
    End If
End Function


Цитата
Алексей Назаров написал:
Кроме того, значения коэффициентов отличаются...
Да, это известная проблема при больших Х.
Изменено: tutochkin - 07.12.2021 15:17:32
 
Цитата
Так этот макрос и выводит полную формулу... Там же написано - "' Программа формирования текста уравнения по всем точкам"
Какой инженер читает инструкции? )))
Я не обратил внимания на эти строки, поэтому удивился.
Но это не особая проблема, распотрошить текстовую строку по составляющим - дело техники.

Цитата
Так это в макросе и прописано... Format(Application.Index(WorksheetFunction.LinEst(yn, xn, True, True), 1, k), "0.####E+") увеличивайте, или вообще удалите.
Да, благодарю, разрядность получил.

Цитата
Если нужны просто коэфф-ты (зачем?) то вот
Сейчас попробую

Цитата
Но они не будут отличаться от того что выдаст  =ИНДЕКС(ЛИНЕЙН($E$5:$E$12;$D$5:$D$12^{1;2;3;4;5;6});1;G5)
К сожалению....  :cry:

В самом первом сообщении в этой ветке я постарался подробно расписать, почему именно меня не удовлетворяют коэффициенты, полученные функцией ЛИНЕЙН.
Коэффициенты из Октавы оказываются значительно лучше! (в том смысле, что отличие любой точки аппроксимированной кривой от экспериментальных данных получается существенно меньше).
И меня очень поразило, что коэффициенты линии тренда в Excel почти совпадают с коэффициентами из Октавы (а при больших выборках и с коэффициентами AGraphe), и линии погрешности этих способов практически совпадают, обеспечивая очень хорошие результаты!! Значительно лучше, чем ЛИНЕЙН.

И цель этой ветки была следующей:
Если в Excel уже есть механизм расчета "хороших" коэффициентов (линия тренда), то нужно попытаться из этого инструмента коэффициенты выдернуть!!
Либо рассчитать коэффициенты средствами VBA, но чтобы они оказались "по качеству не хуже", чем коэффициенты линии тренда/Октавы.
А коэффициенты, аналогичные ЛИНЕЙН, увы, не имеют смысла... (((

Спасибо за помощь, буду пробовать дальше.
Уважаемый tutochkin,  два последних фрагмента кода - это что? Дают ли они коэффициенты, отличные от ЛИНЕЙН, или то же самое?
В любом случае спасибо, сейчас буду смотреть.
 
Цитата
написал:
Да, это известная проблема при больших Х.
Боюсь, большие Х здесь не главная причина.
Даже с малыми Х результаты ЛИНЕЙН не соответствуют линии тренда, погрешность для данной выборки около 0,02%.
Сейчас специально уменьшил Х на шесть порядков.
При этом и коэффициенты от Линейн, и от линии тренда сохранили неизменными первые 7..9 знаков, и общая форма линий осталась той же.

Цитата
написал:
Понятие точности относительно. Вот на рисунке несколько видов интерполяции через одни и те-же опорные точки.... А какая разница в промежутках  Выбирай на вкус.
Вы привели пример интерполяции, у меня - аппроксимация, причём количество точек не просто превышает порядок полинома, оно составляет от нескольких десятков до нескольких тысяч.

В измерительных устройствах является стандартом де-факто использование именно полиномиальной аппроксимации.
Во-первых, погрешность преобразования первичных датчиков/преобразователей имеет вид, очень хорошо аппроксимируемый полиномами.
Во-вторых, коэффициенты полинома занимают мало памяти в МК, и процесс вычислений проходит всегда по одному и тому же алгоритму, независимо от значения входного сигнала, никаких условий, сравнений, смен формул и т.п., что обеспечивает низкую интегральную погрешность.

Фактически, этот способ полностью и меня удовлетворяет, остался один нюанс - нужно выработать инструмент для удобного расчёта адекватных коэффициентов. ))

P.S.
Почитал про WorksheetFunction.LinEst.
Фактически, это и есть ЛИНЕЙН, так что всё, ранее написанное, не имеет смысла...  :cry:
Я изначально не вникал в код и не понял, что коэффициенты вовсе не "выдёргиваются" из инструмента работы с графиками, а вычисляются другой функцией, не связанной с ними.
, спасибо, что попытались помочь.

Сейчас попробую разобраться в примерах МатросНаЗебре, может, здесь что получится.
 
Цитата
Алексей Назаров Дают ли они коэффициенты, отличные от ЛИНЕЙН, или то же самое?
так я про это и писал:
Цитата
=ИНДЕКС(Koef($D$5:$D$12;$E$5:$E$12;6);1;G5) - вывод элементов
или
=ИНДЕКС(Koef2($D$5:$D$12;$E$5:$E$12;6);1;G5)
Но они не будут отличаться от того что выдаст
=ИНДЕКС(ЛИНЕЙН($E$5:$E$12;$D$5:$D$12^{1;2;3;4;5;6});1;G5)

Цитата
Алексей Назаров написал:
В измерительных устройствах является стандартом де-факто
а куда 4...20мА дели ?

Цитата
Алексей Назаров написал:
Я изначально не вникал в код
Вот это зря.
 
Цитата
написал:
а куда 4...20мА дели ?
4..20 кануло в лету.
Т.е. оно ещё применяется, но уже редко.
Но, даже у датчиков, в которых используется, сигнал предварительно обрабатывается МК с использованием тех самых полиномов.
И уже затем передаётся по каким либо протоколам (Eth, HART, либо через АЦП - 4...20)

Цитата
написал:
Вот это зря.
Виноват, исправлюсь! )))


Сейчас разбираюсь со свойством .DataLabel.Text, пытаюсь корректно выдернуть из него данные.
 
Цитата
Алексей Назаров написал:
Т.е. оно ещё применяется, но уже редко.
Да вот не правда. 99% КИПиА на новых станциях на нём. Все метраны ДД ДИ (да и Метран-280, Метран-2700) на 4-20 с харт протоколом. Сименс аналогично. А вот потом уже идёт преобразование хоть по среднемедианному хоть по среднеарефметическому. Но это не суть. Для моих работ выковыривание из лэйблов никуда не годится, посему не рассматривалось.
 
Цитата
на 4-20 с харт протокол
Вот именно, что с HART протоколом, т.е. от 4-20  осталось, фактически, только питание и характеристики физического уровня передачи данных.
Но и эти используются только как общепромышленные.
Я общепромышленным оборудованием не занимаюсь, с монстрами конкурировать сложно.

Цитата
А вот потом уже идёт преобразование хоть по среднемедианному хоть по среднеарефметическому
Это не преобразование, это усреднение/фильтрация, её делают, в зависимости от конкретного применения.
А внутри самого датчика сигнал всё равно обрабатывается в 95% случаев на МК по нелинейным алгоритмам.
А уж дальше выходной сигнал можно и в HART, и в токовый 4..20 преобразовать..

Цитата
Для моих работ выковыривание из лэйблов никуда не годится
Для моих годится всё, что НОРМАЛЬНО РАБОТАЕТ.
А нафига мне нужна аппроксимация, которая даёт погрешность в 4 раза больше, чем "выковырянная" откуда-то? ))

Сейчас разбираюсь с .DataLabel.Text, данные получить удаётся, но либо при пошаговом выполнении кода, либо, если в тексте есть ошибка, вызывающая остановку компилятора. После пропуска ошибки данные и появляются. (((
Код
Sub AddTrend()
    ActiveSheet.Shapes.AddChart2(240, xlXYScatterLines).Select
    ActiveChart.SetSourceData Source:=Range( _
        "'Pdm2_2021_11_17_102_(усреднен.)'!$B$4:$C$68")
    ActiveChart.FullSeriesCollection(1).Trendlines.Add
    ActiveChart.FullSeriesCollection(1).Trendlines(1).Select
        
    With ActiveChart.FullSeriesCollection(1).Trendlines(1)
        .Type = xlPolynomial
        .Order = 6
        .DisplayEquation = True
        .DisplayRSquared = False
        .DataLabel.NumberFormat = "0,00000000000000E+00"
        
        'Здесь (например) нужно код тормознуть, иначе дальнейшие действия не имеют смысла....

        Range("Y17").Value = .DataLabel.Text
        MsgBox (.DataLabel.Text)
    End With

        Range("Y17").Value = ActiveChart.FullSeriesCollection(1).Trendlines(1).DataLabel.Text
End Sub
 
Цитата
А нафига мне нужна аппроксимация, которая даёт погрешность в 4 раза больше, чем "выковырянная" откуда-то? ))
Ну во первых вы так и не показали как считаете погрешность. Во вторых - интерполируйте кусочным способом и получите в узлах 0-ю погрешность.
 
Цитата
написал:
Ну во первых вы так и не показали как считаете погрешность. Во вторых - интерполируйте кусочным способом и получите в узлах 0-ю погрешность.
Под погрешностью аппроксимации я имею в виду максимальную разницу между любой точкой экспериментальных данных и соответствующей точкой, вычисленной по полиному, построенному по этим данным.

В том числе и между узлами. Например, для проверки качества полинома можем сделать 1000 измерений, по 100 точкам (каждой 10-й) построить полином, а погрешность проверить по всем 1000 точкам.
И между узлами погрешность не менее важна, чем в узлах!

Кусочно-линейную пробовали, но при разумном количестве точек (не более нескольких десятков) погрешность аппроксимации наших данных превышает 0,03%, а нужно не более 0,01%.
Также при КЛ погрешность определения значения опорной точки выливается в погрешность измерения в данной точке, а при полиномиальной мы можем делать аппроксимацию по любому количеству точек, при этом ошибочные измерения видны, как на ладони, и сразу отфильтровываются (либо, если они единичные, то мало влияют на результат аппроксимации).
 
Цитата
Алексей Назаров написал:
Кусочно-линейную пробовали, но при разумном количестве точек (не более нескольких десятков) погрешность аппроксимации наших данных превышает 0,03%, а нужно не более 0,01%.
При кусочной интерполяции в узловых точках погрешность нулевая. Точно так же как и при интерполяцией сплайном. Между узлов зависит от типа функции интерполяции и граничных условий. Рисунки приводил выше.
Цитата
Алексей Назаров написал:
ошибочные измерения видны, как на ладони, и сразу отфильтровываются
Т.е. вы с помощью аппроксимации фильтруете точки и говорите о погрешности аппроксимации? Серьёзно? Не пробовали в начале хотя бы по медианному фильтру откинуть шумы?
Ладно, это к экселю не относится. И не имея исходных данных говорить не о чем (то что на скринах - трэш и угар для расчёта полинома). Да и имея нет желания.
Удачи в построениях.
 
Цитата
написал:
При кусочной интерполяции в узловых точках погрешность нулевая
Мне не важна погрешность в узловых точках, важна В ЛЮБОЙ точке в заданном интервале. И этого КЛ не обеспечивает.
Если, например, Ваши весы в точках 1 и 1,5 кг дадут погрешность, близкую к нулю, то с какой погрешностью Вы измерите 1,2кг?
Извините, но формулировка "погрешность в узловых точках" применительно к измерительным приборам не имеет никакого смысла.

Цитата
написал:
Т.е. вы с помощью аппроксимации фильтруете точки и говорите о погрешности аппроксимации? Серьёзно?
Не фильтрую, Вы не так поняли (или я неясно объяснил), но при исследовании иногда это использую.
Ибо при исследованиях при выборке конечного размера не всегда есть большая избыточность, и при обработке исходного и обработанного фильтрами массива бывает разница.
А при полиномиальной обработке любые, выходящие из ряда, точки сразу бросаются в глаза. Причём фильтрация (хоть СА, хоть СКв, хоть медианная) далеко не всегда дают приемлемый результат (например, несколько точек подряд с высоким СКО), и можно принять решение, снимать часть данных заново, либо достаточно исключить из выборки несколько точек.
И не нужно делать большие глаза и говорить о нестандартизованных методиках и т.п. В экспериментах все методы хороши, если дают хороший результат.

Цитата
написал:
И не имея исходных данных говорить не о чем
Тут согласен, полноценные (даже сильно урезанные) файлы в лимит не влазят, а кидать на файлообменники не с руки было.
Поэтому я скринил только результат.
Сейчас данных под рукой нет, завтра только могу выложить.
Здесь моя вина - лень было.
Ведь имея актуальную выборку (хоть и урезанную), можно проанализировать и сравнить разные методы.
 
Цитата
Алексей Назаров написал:
Если, например, Ваши весы в точках 1 и 1,5 кг дадут погрешность, близкую к нулю, то с какой погрешностью Вы измерите 1,2кг?
А Вы? ;) Как вы таки определили погрешность то в промежуточных точках? Погрешность это (по определению) отклонение измеренного/расчётного значения от истинного. Вне базовых точек вы истинные значения не знаете. А  базовых точках результат не всегда достоверный (судя по скринам там дребезг процентов под 50 и больше). О какой погрешности идёт речь? На глаз определяете 0,01%?
Цитата
Алексей Назаров написал:
В экспериментах все методы хороши, если дают хороший результат.
Чушь. Говорю как человек почти 20 лет занимающийся экспериментами на энергетическом оборудовании. Включая проведение гарантийных испытаний турбин Siemens и GE. Хорошим является только верный метод. А остальное называется подгон под условия.
 
Цитата
написал:
А Вы?  Как вы таки определили погрешность то в промежуточных точках? Погрешность это (по определению) отклонение измеренного/расчётного значения от истинного. Вне базовых точек вы истинные значения не знаете.
Вы читаете через строчку.
Во-первых, я везде говорил только о погрешности аппроксимации (а не о полной погрешности измерений), и везде старался это подчёркивать.
И как проверяю её в промежуточных точках, я писал, при наличии избыточности выборки это легко. Для погрешности аппроксимации за истинные принимаются экспериментальные значения.

Во-вторых, даже если говорить о полной погрешности, то параметры определяются на готовом изделии по эталонному прибору. На любой промежуточной точке, включая рекомендованные ГОСТами для данных СИ.

Цитата
написал:
судя по скринам там дребезг процентов под 50 и больше
На первом графике числа по оси Y обрезаны, извините, недоглядел.
Но на втором они есть. На первом значения те-же, но шум неотфильтрован.

Шум +-2...3 емзр от полного значения 30000...150000 это примерно 0,01...0,0015% от измеренного значения. т.е. не более 0,002%FS, это немного ниже, чем 50%?  ;)

Кстати, и ошибка аппроксимации (по коэффициентам Октавы и линии тренда) в данном случае максимальная около 1,9ЕМЗР, т.е. находится на уровне шумов эталонного прибора. Думаю, при использовании более высокоточного СИ и погрешность аппроксимации будет ниже.

Цитата
написал:
Чушь. Говорю как человек почти 20 лет занимающийся экспериментами на энергетическом оборудовании. Включая проведение гарантийных испытаний
Вот именно, Вы занимаетесь метрологией, а я нормативными актами и МИ не особо ограничен, я больше исследователь-разработчик.
Кое-что производим, и на многие наши СИ методики пишутся персонально, т.к. они аналогов не имеют.


В любом случае спасибо Вам за помощь, что помогли разобраться. Хоть этот метод нам и не подошёл, но отрицательный результат - тоже результат!

P.S. Добавил файлы с выборками.
Изменено: Алексей Назаров - 09.12.2021 08:57:15
 
О, вроде получилось загрузить файл на 260 кБ, а ранее выше 100 не получалось.
 
Вариант, возвращающий коэффициенты полинома.
Код
Option Explicit

Sub Polynomial()
    Dim rX As Range
    Dim rY As Range
    Dim rOut As Range
    Set rX = ActiveSheet.Range("B4:B68")
    Set rY = ActiveSheet.Range("C4:C68")
    Set rOut = ActiveSheet.Range("E4")
    
    Dim dataLabelText As String
    dataLabelText = GetDataLabelText(rX, rY)
    Dim coefficients As Variant
    coefficients = GetCoefficients(dataLabelText)
    
    rOut.Resize(UBound(coefficients, 1), UBound(coefficients, 2)) = coefficients
End Sub

Function GetCoefficients(dataLabelText As String) As Variant
    Dim txt As String
    txt = dataLabelText
    txt = Replace(txt, "y = ", "")
    Dim arr As Variant
    arr = Split(txt, " ")
    Dim brr As Variant
    Dim dic As Object
    Set dic = CreateObject("Scripting.Dictionary")
    Dim ii As Integer
    For ii = LBound(arr) To UBound(arr)
        Select Case arr(ii)
        Case "+", "-"
        Case Else
            brr = Split(arr(ii), "x")
            If UBound(brr) = 0 Then
                ReDim brr(0 To 1)
                brr(0) = arr(ii)
                brr(1) = "0"
            Else
                If ii > 0 Then
                    brr(0) = arr(ii - 1) & brr(0)
                End If
                If brr(1) = "" Then brr(1) = "1"
            End If
            dic.Item(brr(1)) = brr(0)
        End Select
    Next
    
    Dim orr As Variant
    If dic.Count = 0 Then
        ReDim orr(1 To 1, 1 To 2)
    Else
        arr = dic.Keys()
        brr = dic.Items()
        Set dic = Nothing
        ReDim orr(1 To UBound(arr) - LBound(arr) + 1, 1 To 2)
        Dim jj As Integer
        For ii = UBound(arr) To LBound(arr) Step -1
            jj = jj + 1
            orr(jj, 1) = arr(ii)
            orr(jj, 2) = CDbl(brr(ii))
        Next
    End If
    GetCoefficients = orr
End Function

Function GetDataLabelText(rX As Range, rY As Range) As String
    If rX.Rows.Count = 1 Then Exit Function
    Dim arX As Variant
    Dim arY As Variant
    arX = rX.Columns(1)
    arY = rY.Cells(1, 1).Resize(UBound(arX, 1), 1)
    
'    Application.ScreenUpdating = False
    
    Dim wb As Workbook
    Set wb = Workbooks.Add(1)
    With ActiveSheet
        .Cells(1, 1).Resize(UBound(arX, 1), 1) = arX
        .Cells(1, 2).Resize(UBound(arX, 1), 1) = arY
        
        .Shapes.AddChart2(240, xlXYScatter).Select
        With ActiveChart
            .SetSourceData Source:=Cells(1, 1).Resize(UBound(arX, 1), 2) 'Range("Лист1!$A$1:$B$3")
            .FullSeriesCollection(1).Trendlines.Add
            With .FullSeriesCollection(1).Trendlines(1)
                .Type = xlPolynomial
                .Order = 2
                .Type = xlPolynomial
                .Order = 6
                .DisplayEquation = True
                .DataLabel.NumberFormat = "# ##0,00000000000000000000000000000000000000000000000000000000000000000000000000"
                DoEvents
'                Application.Wait Now + TimeSerial(0, 0, 1)
                GetDataLabelText = .DataLabel.Text
            End With
        End With
    End With
    wb.Close False
    
    Application.ScreenUpdating = True
End Function
 
Цитата
Алексей Назаров написал:
Во-первых, я везде говорил только о погрешности аппроксимации (а не о полной погрешности измерений), и везде старался это подчёркивать.
Я так и не увидел как Вы её считаете. Судя по приложенному файлу - контроль отклонений в заданных точках.
Ну и где отклонения при кусочной интерполяции?

Цитата
Алексей Назаров написал:
Вы читаете через строчку.
Это Вы про меня? Серьёзно?
Цитата
Алексей Назаров написал:
Вот именно, Вы занимаетесь метрологией
Я не занимаюсь метрологией. Но моя работа требует знаний по средствам измерений, как раз из-за того что некоторые делают на глаз, а потом баланс расходов не сходится.
 
МатросНаЗебре, большое спасибо за (почти) правильно работающий код!

У меня вылезает ошибка 9 (Subscript out of range) на строке
Код
                If brr(1) = "" Then brr(1) = "1"
После замены формата числа на экспоненциальный
Код
.DataLabel.NumberFormat = "0,00000000000000E+00"
ошибка пропала, но и признаки работы ограничились перерисовкой экрана...

Попробовал тормознуть код перед  
Код
GetDataLabelText = .DataLabel.Text
И... вуаля!
Всё (почти) отлично работает!!!!  :D


Осталась мелочь - обойтись без остановки кода.

Такая проблема обсуждалась, например, здесь, здесь, попадалось и на других форумах, но панацеи никто не знает.
Многое зависит от системы, версии Офиса. ((
Изменено: alenco - 09.12.2021 11:00:48
 
Цитата
alenco написал:
Всё (почти) отлично работает!!!!   Осталась мелочь - обойтись без остановки кода.
И Вас ничего не смущает?
 
Цитата
tutochkin написал:
Я так и не увидел как Вы её считаете. Судя по приложенному файлу - контроль отклонений в заданных точках.
Судя по скрину, всё увидели и даже для своего варианта посчитали?

Цитата
tutochkin написал:
Ну и где отклонения при кусочной интерполяции?
Не знаю, с функцией kus_interp я не знаком.
А всё как раз и определяется алгоритмом её работы, количеством точек и параметрами исходной выборки.

Я когда-то пробовал КЛ на своих выборках, при небольшом количестве опорных точек (по моему, 10 или 20, уже не помню) она дала приличную погрешность. Дальше не заморачивался, остановился полностью на полиномах, по образцу предприятий, с кем мы работаем.
Изменено: alenco - 09.12.2021 11:24:26
 
Цитата
tutochkin написал:
И Вас ничего не смущает?
Кроме малого количества значащих цифр на вашем скрине - ничего.
Я использую 14.

Если Вы про необходимость остановки кода - я верю, что эта проблема будет решена.
Изменено: alenco - 09.12.2021 11:31:18
 
Цитата
alenco написал:
Не знаю, с функцией kus_interp я не знаком.
Читайте не через строчку - давал вариант. Урезанный правда, только с линейным вариантом, но давал.
Цитата
alenco написал:
Кроме малого количества значащих цифр на вашем скрине - ничего.Я использую 14.
У как всё запущено... А ещё на глаз погрешности определяете.


А зачем сменили Имя-фамилию на ник? ;)
 
Вариант, вычисляющий коэффициенты через решение системы линейных уравнений. Точность хуже, чем у метода, через уравнение из линии тренда. Вероятно, точность теряется при работе с длинными числами.
Код
Option Explicit

Const kk = 6 'Степень полинома.

Sub Polynomial2()
    Dim rX As Range
    Dim rY As Range
    Dim rOut As Range
    Set rX = ActiveSheet.Range("B4:B68")
    Set rY = ActiveSheet.Range("C4:C68")
    Set rOut = ActiveSheet.Range("E4")
    
    Dim vecA As Variant, vecB As Variant
    Dim coefficients As Variant
    coefficients = GetArrCoefficients(rX, rY, vecA, vecB)
    
    If Not IsEmpty(coefficients) Then
        rOut.Cells(1, 1).Resize(UBound(coefficients, 1), UBound(coefficients, 2)) = coefficients
    End If
End Sub

Function GetArrCoefficients(rX As Range, rY As Range, vecA As Variant, vecB As Variant) As Variant
    If rX.Rows.Count = 1 Then Exit Function
    Dim arX As Variant
    Dim arY As Variant
    arX = rX.Columns(1)
    arY = rY.Cells(1, 1).Resize(UBound(arX, 1), 1)
    
    ReDim vecA(1 To kk + 1, 1 To kk + 1)
    ReDim vecB(1 To kk + 1, 1 To 1)
    
    Dim i As Long
    Dim N As Long
    N = UBound(arX, 1)
    Dim y As Integer
    Dim x As Integer
    For y = 0 To kk
        For x = 0 To kk
            For i = 1 To N
                vecA(y + 1, x + 1) = vecA(y + 1, x + 1) + arX(i, 1) ^ (y + x)
            Next
        Next
        For i = 1 To N
            vecB(y + 1, 1) = vecB(y + 1, 1) + (arX(i, 1) ^ y) * arY(i, 1)
        Next
    Next
    Dim obrA As Variant
    Dim AmB As Variant
    
    
'    Dim wb As Workbook
'    Set wb = Workbooks.Add(1)
'    With wb.Sheets(1)
'        .Cells(1, 1).Resize(UBound(vecA, 1), UBound(vecA, 2)) = vecA
'        .Cells(1, UBound(vecA, 2) + 2).Resize(UBound(vecB, 1), 1) = vecB
'
'        .Cells(UBound(vecA, 1) + 2, 1).Resize(UBound(vecA, 1), UBound(vecA, 2)).FormulaArray = "=MINVERSE(R1C1:R" & UBound(vecA, 1) & "C" & UBound(vecA, 1) & ")"
'        .Cells(UBound(vecA, 1) + 2, UBound(vecA, 1) + 2).Resize(UBound(vecA, 1), 1).FormulaArray = "=MMULT(R" & UBound(vecA, 1) + 2 & "C1:R" & 2 * UBound(vecA, 1) + 1 & "C" & UBound(vecA, 1) & ",R1C" & UBound(vecA, 1) + 2 & ":R" & UBound(vecA, 1) & "C" & UBound(vecA, 1) + 2 & ")"
'    End With
'    wb.Close False
    
    obrA = WorksheetFunction.MInverse(vecA)
    AmB = WorksheetFunction.MMult(obrA, vecB)
    
    Dim orr As Variant
    ReDim orr(1 To UBound(AmB, 1), 1 To 2)
    For y = 1 To UBound(AmB, 1)
        orr(y, 1) = y - 1
        orr(y, 2) = AmB(y, 1)
    Next
    GetArrCoefficients = orr
End Function
Изменено: МатросНаЗебре - 09.12.2021 14:35:22
Страницы: 1 2 След.
Наверх