Сообщество - MS, Libreoffice & Google docs

MS, Libreoffice & Google docs

762 поста 14 934 подписчика

Популярные теги в сообществе:

44

Excel. Долгая дорога оцифровки. Часть 6. Кусочная интерполяция

Ну теперь пора перейти именно к интерполяции исходных данных. Итак, я напомню - у нас был лист с распечатанным графиком, мы его отсканировали, получили набор точек ХY и... имеем вот такую (в лучшем случае) картину (см.первый скрин). Т.е. по данному набору точек невозможно сделать корректную апроксимацию полиномом.

Выходом из данной ситуации является разбиение данных на несколько частей, в данном случае 2 с общей точкой Do=428, создание кусучнозаданной функции (ЕСЛИ меньше 428 одна функция, если больше - вторая функция). Но так в данном случае, а если надо сделать два, три...и больше разбиений? Кропотливая работа. Но зачем, если можно заставить Excel в автоматическом режиме выбирать малое количество точек и проводить через них интерполяционную функцию.

Отчасти кусочную интерполяцию показывал в прошлом посте серии ( Excel. Долгая дорога оцифровки. Часть 5. Создание пользовательской функции для двух аргументов. Ручной вариант ) при поиске решения между заданных критериев.


Как простые варианты рассмотрим кусочную интерполяцию по двум и по четырём точкам для заданных ниже данных.

Допустим нужно определить значение Y при X = 2.5.

При кусочной интерполяции по двум точкам используются две ближайшие заданные точки к X = 2.5. , т.е. 2 и 3, через данные точки провидится линия,и по ней находится Y при X = 2.5..

При кусочной интерполяции по четырём точкам используются две ближайшие заданные точки к X = 2.5. справа и две две ближайшие заданные точки к X = 2.5. слева, т.е. 1 и 2 и 3 и 4, через данные точки провидится кривая (полином 3-й степени),и по ней находится Y при X = 2.5.

Это справедливо для данных за 2-й и перед предпоследней известной точкой. Для данных отрезков и для экстраполяции использую линейную интерполяцию (по 2-м точкам).

Как видно использование кусочной интерполяции по 4-м точкам (голубая линия)немного сглаживает итоговую функцию, что позволяет снимать при оцифровке чуть меньше точек :) .

Вообще правило такое, в зависимости от вида графика:

Ну и собственно с помощью чего сие выполняется:


Макрос кусочной интерполяции при использовании данных с листа

======

' Интерполяция по 2-м, 3-м или 4-м ближайшим (до и после) к заданной (Xisk) точке

' В подпрограмму передаются все заданные точки

' Интерполяция происходит косочно, по количеству заданных точек с учётом расположения заданного Х

' Данные из листа Excel.

Public Function kus_interp_Ex(Xt As Range, Yt As Range, Xisk As Single, Optional ByVal toch As Integer = 2) As Variant

Dim i As Long

Dim xd() As Double

Dim yd() As Double

Dim cd() As Double

' toch - указание поиска решения с использованием количества точек (2, 3, 4).

Select Case toch

Case 2 ' Уравнение а·х+b

kus_interp_Ex = linterp(Xt.Rows(Xt.Count - 1), Xt.Rows(Xt.Count), Yt.Rows(Xt.Count - 1), Yt.Rows(Xt.Count), Xisk)

For i = 1 To Xt.Count - 1

If Xisk < Xt.Rows(i + 1) Then

kus_interp_Ex = linterp(Xt.Rows(i), Xt.Rows(i + 1), Yt.Rows(i), Yt.Rows(i + 1), Xisk)

Exit For

End If

Next i

Case 3 ' Уравнение а·х^2+b·x+c Интерполяция по принципу х1 Х х2 х3

kus_interp_Ex = kubterp(Xt.Rows(Xt.Count - 2), Xt.Rows(Xt.Count - 1), Xt.Rows(Xt.Count), _

Yt.Rows(Xt.Count - 2), Yt.Rows(Xt.Count - 1), Yt.Rows(Xt.Count), Xisk)

For i = 1 To Xt.Count - 2

If Xisk < Xt.Rows(i + 1) Then

kus_interp_Ex = kubterp(Xt.Rows(i), Xt.Rows(i + 1), Xt.Rows(i + 2), _

Yt.Rows(i), Yt.Rows(i + 1), Yt.Rows(i + 2), Xisk)

Exit For

End If

Next i

Case 4 ' Уравнение а·х^3+b·x^2+c·x+d Интерполяция по принципу х1 х2 X х3 x4

ReDim xd(1 To 4) As Double

ReDim yd(1 To 4) As Double

If Xisk < Xt.Rows(2) Then ' Экстраполяция ДО и интерполяция ДО второй известной точки - линейна

kus_interp_Ex = linterp(Xt.Rows(1), Xt.Rows(2), Yt.Rows(1), Yt.Rows(2), Xisk)

Else

If Xisk >= Xt.Rows(Xt.Count - 1) Then ' Экстраполяция ЗА и интерполяция ПОСЛЕ второй известной точки - линейна

kus_interp_Ex = linterp(Xt.Rows(Xt.Count - 1), Xt.Rows(Xt.Count), Yt.Rows(Xt.Count - 1), Yt.Rows(Xt.Count), Xisk)

Else ' Между ними считаю по интерполяции полиномом с расположением заданного икса между двух пар точек

For i = 3 To Xt.Count - 1

If Xisk < Xt.Rows(i) Then

xd(1) = Xt.Rows(i - 2): xd(2) = Xt.Rows(i - 1): xd(3) = Xt.Rows(i): xd(4) = Xt.Rows(i + 1)

yd(1) = Yt.Rows(i - 2): yd(2) = Yt.Rows(i - 1): yd(3) = Yt.Rows(i): yd(4) = Yt.Rows(i + 1)

Linia_trenda yd, xd, 3, cd

kus_interp_Ex = cd(1) * Xisk ^ 3 + cd(2) * Xisk ^ 2 + cd(3) * Xisk ^ 1 + cd(4)

Exit For

End If

Next i

End If

End If

Case Else

End Select

End Function

======

Входные данные:

Xt - Столбец исходных Х

Yt  - Столбец исходных Y

Xisk  - X при котором требуется определить Y

toch - количество используемых точек при интерполяции.


Наблюдательный заметит, что в макросе присутствует и интерполяция с использованием 3-х точек. (0_о)


Дополнительная функция, требуемая для макроса кусочной интерполяции - определение коэффициентов полинома линии тренда:


======

' Проведение интерполяции с использованием функционала 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

======

Макрос linterp был представлен в прошлый раз.


Одним из замечательных применений кусочной интерполяции является возможность автоматического создания макросов функций без проблем с невозможностью достоверной апроксимации исходных данных вот в таком виде:


' Поправки Сербия Панчево Страница 39 из 77

Public Function ТЭХ_ПТ80_Рис3(ByRef Go As Single) As Variant

Dim Xt As Variant

Dim Yt As Variant

Xt = Array(13.0042194092827, 13.4767932489451, 14.0675105485232)

Yt = Array(-6.38888888888889E-02, -6.38888888888889E-02, -0.06875)

ТЭХ_ПТ80_Рис3 = kus_interp(Xt, Yt, Go, 4, 2)

End Function


Учтите что kus_interp при использовании данных с листа и из макроса отличаются...

Но об этом в следующий раз.


===========================

Планы на будущее

1. Часть 7. Автоматическое создание макроса функции с использованием кусочной интерполяции.

2. Построим поиск решения.

3. Строим график функции.

Показать полностью 5
168

Базы данных в Excel. Или ВПР по неограниченному количеству условий

Ответ на пост из соседней темы о ВПР по 2-м условиям.

Довольно часто я встречаю решения выполнения поиска в таблице по двум/трём условиям с применением ВПР/ГПР. Подчас решения очень интересные. Однако что ВПР, что ГПР выдают только одно значение, и решения не масштабируемы. А что делать если необходимо из массива выбрать всю строку, которая будет подчиняться набору критериев?

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

Итак, есть некая таблица и необходимо из неё выбрать все строки подчиняющиеся неким условиям.

Ход поиска однотипен:

1. Столбцы исходной таблицы имеют уникальные заголовки.

2. Для выборки из данной таблицы создаём таблицу условий. При этом заголовок таблицы условий должен совпадать с названием столбца из исходной таблицы, по которому будет проходить выборка. Если выборка по одному столбцу, но по нескольким условиям, то можно написать всё в один столбец (см.скрин ниже). Если условия по нескольким столбцам - по одному условию на столбец, наименования столбцов могут повторяться.

3. Затем переходим в вкладку "Данные" - "Сортировка и фильтр" - "Дополнительно". В открывшемся окне

3.1. отмечаем "скопировать результат в новое место". В этом случае исходная таблица не изменится.

3.2. Исходный диапазон - таблица исходная, вместе с шапкой

3.3. Диапазон условий - таблица условий, вместе с шапкой

3.4. Адрес ячейки с которой будет заполняться итоговая таблица согласно выборки.

Итоговая таблица при этом никак не связана с исходной. И с ней можно проводить любые операции.

Вынос итоговой таблицы в отдельный лист с помощью меню выполнить нельзя, однако можно через макрос, если полностью указать путь с учётом имени страницы назначения :

=====

Sub Выборка()

Range("B3:B37").AdvancedFilter Action:=xlFilterCopy, _

CriteriaRange:=Range("I3:I6"), _

CopyToRange:=Range("K3"), _

Unique:=False

End Sub

=====

где

Range("B3:B37") - исходная таблица

Range("I3:I6") - таблица критериев

Range("K3") - начало вывода результирующей таблицы


Соответственно можно повесить выполнением макроса на кнопку, менять условия и получать новые выборки.


Вызов функции выборки:

AdvancedFilter (Действие, CriteriaRange, CopyToRange, Уникальный)

Причём работа с таблицами как с базами на этом не ограничивается, в частности она позволяет выполнять быстрые расчёты с учётом критериев. Например находить минимальные, максимальные и средние значения только для строк удовлетворяющих критериям. Заметьте - проблемы с числом критериев нет.

Например, если я хочу просуммировать все значения из столбца "Продажи" с учётом того, что они проводились в марте, и контактов было от 35...36, то я создаю таблицу критериев (см.скрин ниже) и в любой ячейке листа формулу  =БДСУММ(B6:G21;F6;B2:В3)

где

B6:G21 - Диапазон исходной таблицы, включая заголовок;

F6 - по какому столбцу буду суммировать;

B2:D3 - при каких условиях суммировать.

Да, сейчас это можно заменить на СУММЕСЛИ, но если будет больше условий? А если суммируется при сложных условиях содержания ячеек?

Насколько способ с использованием баз нагляднее, не так ли?

Варианты задания условий при этом поражают разнообразием. Не всегда можно сходу подобрать условия для СУММЕСЛИ, а тут это просто содержание одной ячейки.

Ну и естественно умные таблицы используются без проблем

==================


Рассмотрим ещё один метод  поиска значения по 2-м, 3-м и более критериям без применение ВПР.

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

Допустим есть таблица исходных данных (Таблица1), в корой представлены список  сотрудников (столбец Name) с датой их приёма на работу (Start) увольнения с должности (Stop) и названием должности (Role). При этом если человек принят, но ещё не уволился, то его ячейка Stop пустая. Нужно найти должность человека (F3) на какую то дату (F4). Попробуйте это через ВПР прописать ради интереса. А вот так делается без ВПР:

В ячейке F6 собственно формула поиска.

=ЕСЛИОШИБКА(ПРОСМОТР(2;1/(Таблица1[Name]=F3)/(Таблица1[Start]<=F4)/((Таблица1[Stop]="")+(Таблица1[Stop]>=F4));Таблица1[Role]);"нет данных")


Разберём структуру поиска и задания условий:

Таблица1[Name]=F3 - совпадение имени

Таблица1[Start]<=F4 - дата зачисления на работу меньше даты поиска

(Таблица1[Stop]="")+(Таблица1[Stop]>=F4) - дата поиска или меньше даты увольнения или дата увольнения пустая.

Таблица1[Role] - вывод ячейки для которой все три условия истина.


Как видно формула легко масштабируется под любое количество условий.


Ну вот как то так.

Этим постом я хотел показать, что применение ВПР(ГПР) не всегда оправдано (хотя несомненно знание этих функций обязательно), и есть более простые и лёгкие способы.

Показать полностью 7
79

Поиск наименования по 2 критериям (ВПР)

Задача такая: По наименованию бренда и типу упаковки найти цену товара и затем рассчитать общую сумму от цены и объема:

Основную сложность здесь представляет поиск цены, так как надо искать и по бренду, и по упаковке. Есть 2 способа решения: 1) простой, развернутый, с доп.столбцами, и 2) компактный, но требующий определенных навыков работы.


Способ 1

Были бы исходные данные только в одном столбце (и бренд, и упаковка), искать можно было бы обычным ВПР. Но данные в 2-х столбцах. Поэтому для начала объединим бренд и упаковку в одно наименование:

1. Создадим новый столбец (С) с формулой: =А3&В3.

2. Аналогичный столбец создадим и в маленькой таблице:

3. Теперь, когда искомые значения готовы, с помощью ВПР можно выполнить поиск. Создаем еще один новый столбец (D) с формулой: =ВПР(C3;$N$3:$O$8;2;0), где

С3 — искомое значение, $N$3:$O$8 — диапазон с таблицей поиска,

2 — столбец для вывода информации, 0 — точный поиск:

4. Теперь, когда цена найдена, можно рассчитать конечные суммы:

В общем, все! Все найдено, все подсчитано. В несколько шагов, правда. Продуктивно. Но не изящно.

Поэтому предлагаю


Способ 2.

Сразу и поиск, и подсчет реализуем в окончательных столбцах. При этом принцип будет аналогичный: исходные данные требуют объединения (&).


Итак, действия:

1. Объединить 2 ячейки в момент поиска с указанием также 2-х объединенных столбцов для поиска может функция ПОИСКПОЗ. Для этого связку А3&B3 помещаем как искомое значение, а массивом поиска станет такая же связка J3:J8&K3:K8. Вот так:

=ПОИСКПОЗ(A3&B3; J3:J8&K3:K8; 0)


2. При этом ПОИСКПОЗ сам по себе найдет только позицию результата, поэтому помещаем его в ИНДЕКС. Вот так будет выглядеть окончательная формула:

=ИНДЕКС(J3:L8;ПОИСКПОЗ(A3&B3;J3:J8&K3:K8;0);3)

где

J3:L8 — таблица с ценами,

3 — номер столбца с ценой:

3. Для завершения формулы осталось только дописать *С3 (умножить на объем в январе) и проставить закрепления ($) во все диапазоны (обратите внимание, закрепление не везде абсолютное):

4. ОЧЕНЬ ВАЖНЫЙ ПУНКТ!

Если у вас Excel не 365, а другой -  ничего, что сделано выше, работать не будет, если не выполнить теперь этот шаг!

Обычные формулы ПОИСКПОЗ и ИНДЕКС не могут поддерживать связки с такими объединениями (&), поэтому завершать ввод формулы будем не [Enter], а [Ctrl]+[Shift]+[Enter]. Это формула массива, ее можно вводить только так! Иначе будет ошибка. И при любых изменениях формулы заканчиваем редактирование тоже сочетанием [Ctrl]+[Shift]+[Enter].

Если у вас Excel 365, нажимайте просто [Enter], там массивы уже более современные.


Вот так выглядит формула массива после [Ctrl]+[Shift]+[Enter]:

А вот копировать формулу (растягивать по столбцам) можно уже как обычно. Полностью законченная таблица выглядит так:

Все расчеты при этом получилось сделать в нужных столбцах без добавления дополнительных столбцов.

Показать полностью 7
9

Скрипт обработки событий календаря

Такое дело.. Продолжая баловство с GS наткнулся на невозможность получения событий календаря путем их перечисления. Например код


function opros() {

var today = new Date();

var calendar = CalendarApp.getCalendarById('tutpochta@gmail.com');

var task=calendar.getEventsForDay(today) ;

for (var i=0;i<task.length;i++) {

//перечисляем события дня.

var details=task[i].getColor();

if (details==11){

clearRR.clear(); //внешняя функция для вызова

}

}

}

..а нет событий красного (11) цвета. А они точно есть, специально создавал. Почему по цвету? Пробовал  перечислять календари и события в них- всё пусто, даже свойств календарей не получить. Явно что-то не то, но вот что? Помогите кто знает пожалуйста.

Показать полностью
152

Преобразование отчета из какой-то левой БД в плоскую таблицу в Excel

Кому лениво читать статью, видео со всеми действиями в конце статьи

Приложить файл примера не знаю как. Если кто подскажет, добавлю файл.

***


Ой, как я «люблю» отчеты из 1С анализировать! (Нет, нет и еще раз нет!) Там такие неудобные шапки, сводки, да еще объединенные ячейки везде. Выполнить анализ в Excel примерно такого отчета без предварительных «танцев с бубном» раньше было сложно:

Сейчас же у нас есть волшебный Power Query, позволяющий в несколько шагов очистить данные от лишних заголовков и объединений, и «развернуть» сводку данных в плоскую таблицу.


Итак, шаги по порядку.

1 шаг. Сформировать запрос к таблице

Лучше делать это из чистой книги.

1. На вкладке Данные выбираем Получить данные — Из файла — Из книги, находим файл, нажимаем Импорт:

2. Выбираем лист с таблицей, нажимаем Преобразовать (данные):

3. Пугаемся того, что открылось. Знакомимся с окном Power Query и открытой импортированной таблицей:

Для тех, что ранее не работал с окном Power Query:

В правой части окна видно имя запроса (его можно изменить, если необходимо) и примененные изменения (шаги). В списке шагов будет видна последовательность всех примененных действий. Если нужно, шаги можно удалять кнопкой «Х» рядом с соответствующим шагом.

Сверху в окне Power Query находятся командные вкладки для преобразования данных.


2 шаг. Преобразование данных

1. Удаление лишних строк

Так как данные листа импортированы полностью, а таблица начинается лишь с 4-й строки на листе, нужно удалить первые 3 строки: вкладка Главная — Удалить строки — Удаление верхних строк. В появившемся окне ввести «3», нажать ОК:

2. Повышение заголовков

Еще нужно удалить лишние столбцы, но пока столбцы без названий, определять их неудобно. Чтобы первую строку таблицы установить заголовками, на Главной вкладке нажимаем Использовать первую строку в качестве заголовков:

3. Переименуем столбец Column3 в «Наименование товара», т.к. здесь не было понятного заголовка: двойной клик по наименованию Column3:

4. Удаляем лишние столбцы

Все столбцы, что идут с названием ColumnХХ - пустые. Они получились в результате разъединения объединенных ячеек. Чтобы их удалить, на Главной вкладке нажимаем Выбор столбцов — Выбор столбцов, а затем снимаем флажки со всех столбцов с заголовками ColumnХХ, а также со столбца Итого:

Результат, который получается на данный момент:

5. Заполняем пустые строки в столбце Категория

Выделив этот столбец, на вкладке Преобразование выбираем Заполнить Заполнить вниз. По всему столбцу будут заполнены категории товаров:

6. Удалим пустые строки

Оставшиеся пустые строки (null) содержат либо ненужные заголовки, либо суммы по категориям товаров. Эти сведения для плоской таблицы не нужны. Убрать их можно фильтрацией по столбцу Наименование товара: нажав кнопку фильтра столбца, убрать флаг NULL:

Результат после 6-го действия:

7. «Развернем» данные

Нужно выделить первые 2 столбца (Категория и Наименование товара), затем на вкладке Преобразование в команде Отменить свертывание столбцов выбрать Отменить свертывание других столбцов:

8. Данные из столбцов будет расположены в стоки, появятся столбцы Атрибут с датами (месяцами продаж) и Значение с суммами. Имеет смысл их сразу переименовать в Период и Сумма соответственно:

9. Для корректного анализа данных Периоду нужно присвоить формат данных Дата, а Сумме — формат Валюта: кнопка «АВС123» в заголовках столбцов:

Результат после 9 действия (4 столбца и 126 строк):

10. Последнее действие — выгрузить данные на лист Excel: команда Закрыть и Загрузить на вкладке Главная:

На листе Excel появится таблица и сведения о запросе:

Область с именем запроса не нужна для отображения, ее можно закрыть.

А дальше уже работаем с получившейся таблицей, как необходимо.

Например, можно сделать такой отчет:

***

Видео со всеми шагами:

Показать полностью 19 1
25

Excel. Долгая дорога оцифровки. Часть 5. Создание пользовательской функции для двух аргументов. Ручной вариант

Итак, если понятно как получить уравнение по имеющемуся графику одного аргумента, то перейдём к следующему этапу: Созданию макроса-функции по диаграмме двух аргументов или Y=f(X1,X2). Внешний вид таких диаграмм на скрине ниже.

Excel. Долгая дорога оцифровки. Часть 5. Создание пользовательской функции для двух аргументов. Ручной вариант

Так повелось, что для удобства я называю второй аргумент критерием. Просто кроме зависимости от одного или двух аргументов существуют варианты зависимости от трёх и четырёх аргументов. У меня на практике доходило до пяти... Но для всех таких диаграмм была, как правило, общая ось Х.

В данном случае имеется зависимость от двух аргументов (Go, Qт). При этом для второго аргумента есть 10 критериев (Qт = 0, 20…180).

Решение происходит в два этапа:

Этап 1: для каждого критерия прописывается значение критерия и уравнение линии (не обязательно прямой как в данном примере), соответствующей критерию.

Этап 2: производится проверка перебором соотношения заданного критерия и имеющихся. Как только соотношение krit_kriv(i) Krit < krit_kriv(i + 1) выполняется, происходит поиск значения функции с использованием линейной (в данном случае) интерполяции (или просто через пропорцию) по точкам Y(Xзад, krit_kriv(i)) и Y(Xзад, krit_kriv(i+1.))


Т.е. например нужно определить значение при Gо=400 и Qт = 30. Соответственно я понимаю что искомое находится между критериями Qт = 20 и Qт = 40. Нахожу при данных критериях и при Go = 400 значения Gцнд. И через пропорцию определяю каким будет значение при Qт = 30.


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

=====

' Функция линейной интерполяции по двум точкам методом пропорции

' Необходимое условие X1 < X2

Public Function linterp(ByVal X1 As Single, ByVal X2 As Single, ByVal Y1 As Single, ByVal Y2 As Single, ByVal X As Single) As Single

If X2 = X1 Then X2 = X1 + X1 / 10000# ' Убираем совпадение иксов

linterp = Y2 - ((Y2 - Y1) / (X2 - X1)) * (X2 - X)

End Function

=====

Ну и собственно макрос


' Программа является унифицированной для минимизации изменений.

Public Function ris_71(x As Single, Krit As Single) As Single

Dim kriv() As Single ' объявляем динамический массив

Dim krit_kriv() As Single ' объявляем динамический массив

Dim N_kriv As Integer, i As Integer ' объявляем тип числа уравнений

N_kriv = 10 ' ВВОДИМ число кривых

ReDim kriv(1 To N_kriv) ' Изменяем размер массива в соответствии с числом кривых.

ReDim krit_kriv(1 To N_kriv) ' Изменяем размер массива в соответствии с числом кривых

' требование - рост критериев должен быть по нарастающей. Критерий - это второй аргумент функции.

' ВВОДИМ критерии с первой по последнюю кривую в порядке возрастания

krit_kriv(1) = 0#

krit_kriv(2) = 20#

krit_kriv(3) = 40#

krit_kriv(4) = 60#

krit_kriv(5) = 80#

krit_kriv(6) = 100#

krit_kriv(7) = 120#

krit_kriv(8) = 140#

krit_kriv(9) = 160#

krit_kriv(10) = 180#

' ВВОДИМ уравнения кривых в соответствии с критериями

kriv(1) = 0.7324 * x - 1.576 ' соответствует krit_kriv(1) = 0# и т.д.

kriv(2) = 0.7343 * x - 30.41

kriv(3) = 0.7574 * x - 68.76

kriv(4) = 0.7536 * x - 102.2

kriv(5) = 0.756 * x - 142.9

kriv(6) = 0.7311 * x - 173.1

kriv(7) = 0.7582 * x - 221.6

kriv(8) = 0.7461 * x - 260.2

kriv(9) = 0.7894 * x - 323#

kriv(10) = 0.7798 * x - 357.2

If Krit > krit_kriv(N_kriv) Then

' предварительный расчёт результата если критерий больше максимального имеющегося

ris_71 = linterp(krit_kriv(N_kriv - 1), krit_kriv(N_kriv), _

kriv(N_kriv - 1), kriv(N_kriv), Krit)

Else

' проверка положения критерия относительно имеющихся кривых, и проведение линейной аппроксимации.

For i = 1 To N_kriv - 1

If Krit <= krit_kriv(i + 1) Then

ris_71 = linterp(krit_kriv(i), krit_kriv(i + 1), _

kriv(i), kriv(i + 1), Krit)

Exit For

End If

Next i

End If

End Function


Макрос описывающий наш пример приведён выше. Ввиду того, что приходилось делать большое количество таких апроксимаций, данный макрос оптимизирован для минимизации действий по его созданию.


Т.е. для нового графика потребуется
1. Заменить название. Внимание - менять по всему макросу.

2. Указать количество критериев.

3. Указать значения критериев

4. Указать уравнения описывающие критерии.

Показать полностью 1
70

Excel. Долгая дорога оцифровки. Часть 4.  Макрос по созданию макросов апроксимации простых графиков полиномом

"Позабыты хлопоты, остановлен бег, Вкалывают роботы, счастлив человек!"(с)ПЭ

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

Всё базируется на трёх китах:

1. Унификация.

2. Результатом действия макроса может являться текст;

3. В текстовых переменных можно использовать спец символы:

3.1. Знак возврата каретки. vbCr она же символ Chr(13);

3.2. Знак перевода строки. vbLf она же символ Chr(10);

3.3. Символ объёдинения &.

Ну а теперь пройдём все шаги вместе.


В прошлом посте я говорил про макрос расчёта на основании построения тренда.


====

' Апроксимация полиномом для всего массива исходных данных

' В подпрограмму передаются все заданные точки и апроксимация ведётся по всем точкам!

' Данные из листа Excel

Public Function polinomEx_all(xVal As Range, yVal As Range, x As Single, Optional stepen As Long = 2) As Variant

Dim i As Integer

' Проверка требования "число элементов массива на 1 больше чем степень полинома"

If xVal.Count < stepen + 1 Then

stepen = xVal.Count - 1

End If

polinomEx_all = 0#


Select Case stepen


Case 1 ' Уравнение а·х+b

For i = 1 To stepen + 1

polinomEx_all = polinomEx_all + (x ^ (stepen + 1 - i)) * Application.Index(WorksheetFunction.LinEst(yVal, xVal, True, True), 1, i)

Next i


Case 2 ' Уравнение а·х^2+b·x+c

For i = 1 To stepen + 1

polinomEx_all = polinomEx_all + _

(x ^ (stepen + 1 - i)) * Application.Index(WorksheetFunction.LinEst(yVal, Application.Power(xVal, Array(1, 2)), True, True), 1, i)

Next i


Case 3 ' Уравнение а·х^3+b·x^2+c·x+d

For i = 1 To stepen + 1

polinomEx_all = polinomEx_all + _

(x ^ (stepen + 1 - i)) * Application.Index(WorksheetFunction.LinEst(yVal, Application.Power(xVal, Array(1, 2, 3)), True, True), 1, i)

Next i


Case 4 ' Уравнение а·х^4+b·x^3+c·x^2+d·x+e

For i = 1 To stepen + 1

polinomEx_all = polinomEx_all + _

(x ^ (stepen + 1 - i)) * Application.Index(WorksheetFunction.LinEst(yVal, Application.Power(xVal, Array(1, 2, 3, 4)), True, True), 1, i)

Next i


Case 5 ' Уравнение а·х^5+b·x^4+c·x^3+d·x^2+e·x+f

For i = 1 To stepen + 1

polinomEx_all = polinomEx_all + _

(x ^ (stepen + 1 - i)) * Application.Index(WorksheetFunction.LinEst(yVal, Application.Power(xVal, Array(1, 2, 3, 4, 5)), True, True), 1, i)

Next i


Case 6 ' Уравнение а·х^6+b·x^5+c·x^4+d·x^3+e·x^2+f·x+g

For i = 1 To stepen + 1

polinomEx_all = polinomEx_all + _

(x ^ (stepen + 1 - i)) * Application.Index(WorksheetFunction.LinEst(yVal, Application.Power(xVal, Array(1, 2, 3, 4, 5, 6)), True, True), 1, i)

Next i


Case 7 ' Уравнение а·х^7+b·x^6+c·x^5+d·x^4+e·x^3+f·x^2+g·x+h

For i = 1 To stepen + 1

polinomEx_all = polinomEx_all + _

(x ^ (stepen + 1 - i)) * Application.Index(WorksheetFunction.LinEst(yVal, Application.Power(xVal, Array(1, 2, 3, 4, 5, 6, 7)), True, True), 1, i)

Next i

Case Else

End Select

End Function

====

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

Т.е.

WorksheetFunction.LinEst(yVal, Application.Power(xVal, Array(1, 2, 3, 4)), True, True)

полностью совпадает с

ЛИНЕЙН(Y; X{1;2;3;4}; True; True)


Ну а теперь просто заменим расчёт на составление текстовой переменной

=====

' Апроксимация полиномом для всего массива исходных данных

' В подпрограмму передаются все заданные точки и апроксимация ведётся по всем точкам!

' Данные из листа Excel

' Результат работы программы - текст (уравнение полинома)

Public Function polinomExStr(ByVal xVal As Range, ByVal yVal As Range, Optional stepen As Long = 2) As Variant

' Проверка требования "число элементов массива на 1 больше чем степень полинома"

Dim i As Integer

If xVal.Count < stepen + 1 Then

stepen = xVal.Count - 1

End If

polinomExStr = ""


Select Case stepen


Case 1 ' Уравнение а·x+c

For i = 1 To 2

polinomExStr = polinomExStr & " + X ^ " & (2 - i) & " * " _

& Format(Application.Index(WorksheetFunction.LinEst(yVal, Application.Power(xVal, Array(1)), True, True), 1, i), "0.###E+")

Next i


Case 2 ' Уравнение а·х^2+b·x+c

For i = 1 To 3

polinomExStr = polinomExStr & " + X ^ " & (3 - i) & " * " _

& Format(Application.Index(WorksheetFunction.LinEst(yVal, Application.Power(xVal, Array(1, 2)), True, True), 1, i), "0.###E+")

Next i


Case 3 ' Уравнение а·х^3+b·x^2+c·x+d

For i = 1 To 4

polinomExStr = polinomExStr & " + X ^ " & (4 - i) & " * " _

& Format(Application.Index(WorksheetFunction.LinEst(yVal, Application.Power(xVal, Array(1, 2, 3)), True, True), 1, i), "0.###E+")

Next i


Case 4 ' Уравнение а·х^4+b·x^3+c·x^2+d·x+e

For i = 1 To 5

polinomExStr = polinomExStr & " + X ^ " & (5 - i) & " * " _

& Format(Application.Index(WorksheetFunction.LinEst(yVal, Application.Power(xVal, Array(1, 2, 3, 4)), True, True), 1, i), "0.###E+")

Next i


Case 5 ' Уравнение а·х^5+b·x^4+c·x^3+d·x^2+e·x+f

For i = 1 To 6

polinomExStr = polinomExStr & " + X ^ " & (6 - i) & " * " _

& Format(Application.Index(WorksheetFunction.LinEst(yVal, Application.Power(xVal, Array(1, 2, 3, 4, 5)), True, True), 1, i), "0.###E+")

Next i


Case 6 ' Уравнение а·х^6+b·x^5+c·x^4+d·x^3+e·x^2+f·x+g

For i = 1 To 7

polinomExStr = polinomExStr & " + X ^ " & (7 - i) & " * " _

& Format(Application.Index(WorksheetFunction.LinEst(yVal, Application.Power(xVal, Array(1, 2, 3, 4, 5, 6)), True, True), 1, i), "0.###E+")

Next i


Case 7 ' Уравнение а·х^7+b·x^6+c·x^5+d·x^4+e·x^3+f·x^2+g·x+h

For i = 1 To 8

polinomExStr = polinomExStr & " + X ^ " & (8 - i) & " * " _

& Format(Application.Index(WorksheetFunction.LinEst(yVal, Application.Power(xVal, Array(1, 2, 3, 4, 5, 6, 7)), True, True), 1, i), "0.###E+")

Next i

Case Else

End Select

End Function

=====


Ну или немного в другом виде с учётом ряда особенностей и модификаций

=====

' Программа формирования текста макроса для функции одного уравнения

Public Function fun_macros_Y(xVal As Range, yVal As Range, PolyStep As Long, _

Optional Name_f As String = "Nomogramma", _

Optional Opisanie As String = " Уравнение ", _

Optional NameX As String = "Xisk") 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 - коэффециенты уравнения полинома

fun_macros_Y = "" & Chr(10) & "' " & Opisanie & Chr(10)

fun_macros_Y = fun_macros_Y & "Public Function " & Name_f & "(ByRef " & NameX & " As Single) As Variant" & 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

' Делаем расчёт и вывод.

fun_macros_Y = fun_macros_Y & Name_f & " = "

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

fun_macros_Y = fun_macros_Y & " + " & c(k)

Else

fun_macros_Y = fun_macros_Y & c(k)

End If


If (stepen + 1 - k) > 0 Then

fun_macros_Y = fun_macros_Y & " * " & NameX & " ^ " & (stepen + 1 - k) & " "

End If

Next k


fun_macros_Y = fun_macros_Y & Chr(10) & "End Function" & Chr(10)

End Function

=====


Макрос ждёт в качестве вводных данных:

xVal - столбец известных Х

yVal - столбец известных Y

PolyStep - желаемую степень уравнения. Если точек будет меньше чем требуется для степени - на уменьшится

Name_f - название получаемого макроса. Опционально. Если не задать будет Nomogramma

Opisanie - описание получаемого макроса. Опционально. Если не задать будет Уравнение

NameX - название/имя аргумента. Опционально. Если не задать будет Xisk


Вызов макроса:

=ПОДСТАВИТЬ(fun_macros_Y(X; Y; 3; "fun_пример"; "Пример создания макроса"; "Go");",";".")


=ПОДСТАВИТЬ( ;",";".") требуется для замены запятых на точки. Иначе будет казус - VBA в качестве разделителя целой и дробной части использует точку, а в текстом виде (по крайней мере в рус.экселе) разделитель запятая.

Обратите внимание, что

"fun_пример"; "Пример создания макроса"; "Go" - текстовые, т.е. заключаются в кавычки

"fun_пример";  "Go" - должны соответствовать требованиям к переменным. Т.е. не должны содержать пробелов, не должны совпадать с имеющимися переменными или названиями ячеек/диапазонов.


Результатом выполнения макроса будет (поставил 3-ю степень чтобы результат влез в окно поста):

"

' Пример создания простого макроса

Public Function fun_Wтф(ByRef Go As Single) As Variant

fun_Wтф = 0.00000056401 * Go ^ 3 -0.001952 * Go ^ 2 + 1.3842 * Go ^ 1 + 25.341

End Function

"

Останется скопировать данный текст в модуль VBA и удалить двойные кавычки в начале и конце текстовки.


Если есть желание повысить количество знаков коэффициентов - правим формат "0.####E+"


Для ускорения работы у меня собраны листы/шаблоны позволяющие не лезть в заполнение вызова макросов.

Вызов макроса для данного случая у меня выглядит так (в Е9):

=ПОДСТАВИТЬ(fun_macros_Y(B3:ДВССЫЛ("B"&E4);C3:ДВССЫЛ("C"&E4);M7;E7;G5;G7);",";".")

Как заполнены дополнительные столбцы А, D и ячейки Е4 и т.д. видно на скрине.

Столбец А - контроль верности снятия данных (по возрастанию Х).

Столбец D - подсчёт снятых точек.

В итоге выполнение/изготовление макроса для меня сводится в вставке исходных данных начиная с ячейки В3, и затем жёлтых полей ввода описания, ввода названия новой функции и аргумента, выбора степени. Т.е. занимает не более минуты.


Для наблюдательных - присутствующие в макросе Dim Nna4 As Long 'Номер начала диапазона.

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

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


Для продвинутых - да, можно обойтись без доп.столбцов А и D, да и E4 лишнее. И то и другое можно реализовать в макросе, но...но данный лист был так сформирован на основании удобства для меня - могу оперативно проверить правильность и полноту вставки исходных данных, отсутствие сбоев "снятия" точек с картинки при массовой оцифровке. И вообще - "работает? Стабильно? Без сбоев? Не трожь!" (с)Анекдот. Вам ничто не мешает сделать иначе.


=========

dixi


Краткий план:

Теория вкратце [ Часть 1. ]

Забираем данные с листа. [ Часть 2. ]

Апроксимация простых графиков полиномом средствами Excel [ Часть 3.]

Макрос по созданию макросов апроксимации простых графиков полиномом [ Часть 4.] Этот пост

Апроксимация графиков двух аргументов полиномом [ Часть 5.]

Кусочная интерполяция простых графиков [ Часть 6.]

Показать полностью 2
46

Excel. Долгая дорога оцифровки. Часть 3. Апроксимация простых графиков полиномом средствами Excel

Итак, мы имеем набор точек XY и нам требуется определить значение между заданными (опорными) точками. Начнём с самого простого варианта - набор точек позволяет найти уравнение полиномиального вида, которое с достаточной нам точностью описывает поведение функции с учётом имеющихся точек. Это будет в 90% апроксимация т.к. помним про погрешности связанные со снятием точек. Т.е. значения полученные по данной функции будут отличаться от заданных изначально. Кроме вариантов при степени полинома на 1 меньше количества точек (например полином 5-й степени, а известных точек 6-ть).

Стоит учитывать, что описанный ниже способ подходит только в ограниченном количестве случаев:

- требует заказчик;

- зависимость явно полиномиальная.

Итак, в общем случае всё сводится к пяти шагам:

1. По имеющимся данным построить точечный график.

2. По построенным точкам выполнить построение линии тренда.

3. Подобрать степень полиномиальной зависимости таким образом, чтобы внешний вид (прохождение около/через заданные точки) соответствовал изначальному графику (тот что был на картинке). Проверить, возможно полином не лучший вариант.

4. Отобразить уравнение линии тренда на диаграмме и, если зависимость полиномиальная, становить формат чисел "Экспоненциальный" Число знаков - не менее 3х знаков.

5. Скопировать полученное уравнение и использовать в дальнейшем.

Есть ли способы без построения? Естественно есть (о них чуть подальше), но не видя как расположена линия тренда можно нарваться на неприятности.

Ещё одно заблуждение - "чем больше степень полинома, тем точнее". К сожалению, если по оси Х значения в десятках тысяч, а по оси Y в единицах фактически не реально найти полином выше 5-й степени. Точнее определить с достаточной достоверностью его коэффициенты (просто не хватает 15-ти знаков).

Определение коэффициентов полинома.

Как видно на скриншоте выше извлечение коэффициентов полинома происходит совсем не сложно.

=ИНДЕКС(ЛИНЕЙН(F4:F13;E4:E13^{1;2;3;4;5;6});1;7)

где {1;2;3;4;5;6} - степень полинома, 7 - порядковый номер коэффициента.

И об этом написано в многих местах. Но вот то что не написано - извлечённые таким способом иногда не совсем соответствуют коэф-ам уравнения на диаграмме, а иногда совсем не соответствуют.

Это внутренняя математика Excel и  может быть вызвано целым рядом причин. Основные:

- Значительный разрыв исходных данных. Например есть несколько сот снятых точек от 0 до 10, затем отсутствие снятых точек от 10 до 20, затем несколько сот точек с 20 до 50.

- Значительные степени чисел (как на скрине выше).

Выходом из данной ситуации является следующий макрос-костыль который забирает данные из указанного диапазона ( в примере - "B2:B6"  Данные Х  и "C2:C6" -  Данные Y), строит график, на графике строит линию тренда с заявленной степенью (в примере вторая - Order = 2), копирует строку уравнения, распарсивает её и выкидывает в столбец (  в примере - начиная с ячейки E2 и вниз) коэффициенты полин.уравнения. Построенный график удаляется...


Sub Polynomial()

Dim rX As Range

Dim rY As Range

Dim rOut As Range

Dim dataLabelText As String

Dim coefficients As Variant

Set rX = ActiveSheet.Range("B2:B6") ' Данные Х

Set rY = ActiveSheet.Range("C2:C6") ' Данные Y

Set rOut = ActiveSheet.Range("E2") ' Место выгрузки коэф-в

dataLabelText = Извлечение_Полинома(rX, rY)

coefficients = Извлечение_коэффициентов(dataLabelText)

With rOut.Resize(UBound(coefficients, 1) + 1, UBound(coefficients, 2))

'назначаем формат для избежания ошибок при вставке получившихся формул

.NumberFormat = "#.####E+00"

.Value = coefficients

End WithEnd Sub


Private Function Извлечение_коэффициентов(dataLabelText As String) As Variant

Dim i As Integer

Dim rez() As Variant, txt As Variant

txt = Split(dataLabelText, "x")

ReDim rez(LBound(txt) To UBound(txt), 1 To 2)

For i = LBound(txt) To UBound(txt)

txt(i) = Right(txt(i), IIf(i = LBound(txt), (Len(txt(i)) - 2), (Len(txt(i)) - 1)))

rez(i, 1) = i: rez(i, 2) = txt(i)

Next i

Извлечение_коэффициентов = rez

End Function


Function Извлечение_Полинома(rX As Range, rY As Range) As String

Dim MyChart As Chart

Dim text As String

Dim dt As Date

Set MyChart = ActiveSheet.Shapes.AddChart2(, , , , 450, 300).Chart

With MyChart

.SeriesCollection.NewSeries

.SeriesCollection(1).XValues = rX

.SeriesCollection(1).Values = rY

.ChartType = xlXYScatter

.FullSeriesCollection(1).Trendlines.Add

With .FullSeriesCollection(1).Trendlines(1)

.Type = xlPolynomial

.Order = 2 ' Указываем степень полинома

.DisplayEquation = True

.DataLabel.NumberFormat = "#.####E+00"

dt = Now

DoEvents ' Задержка. См. ниже

DoEvents ' Задержка. См. ниже

Do

If .DataLabel.text <> "" Then Exit Do

If dt < Now - TimeSerial(0, 1, 0) Then Exit Do

For i = 1 To 100: DoEvents: Next

Loop

text = .DataLabel.text

End With

End With

Извлечение_Полинома = text

MyChart.Parent.Delete

End Function


Т.е. делает то, что можно сделать и руками с наглядным выбором вида апроксимации.


Быстрое определение искомого Y по заданному X.

Если Вы уверены в своём глазомере, не боитесь подводных камней и хотите быстро получить значение, то можно воспользоваться вот таким макросом.

Сокращённый вид макроса за авторством БМВ расположен ниже. Расширенный частично на скрине выше. Он понадобится нам в следующем посте, когда будем делать макрос по созданию макросов ), и там будет представлен полностю.


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

Т.е. в функцию передаются столбцы исходных данных, значение Х, при котором требуется найти Y и степень полинома линии тренда.


И да, все заметили что посредством макроса есть возможность построить полином 7-й степени, тогда как линия тренда позволяет выполнять это только до 6-й?


Дальнейшее использование уравнения апроксимации.

Существует всего два подхода:

- Хранить исходные данные на листе. Или в виде таблицы, или в виде уравнения в ячейке.

- Хранить уравнение в виде макроса.


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


О том как делать макросы для простых графиков, в том числе и в автоматизированном режиме, расскажу в следующий раз.

===========

Краткий план:

Теория вкратце [ Часть 1. ]

Забираем данные с листа. [ Часть 2. ]

Апроксимация простых графиков полиномом средствами Excel [ Часть 3.] Этот пост

Макрос по созданию макросов апроксимации простых графиков полиномом [ Часть 4.]

Апроксимация графиков двух аргументов полиномом [ Часть 5.]

Кусочная интерполяция простых графиков [ Часть 6.]

Показать полностью 5
Отличная работа, все прочитано!