В DataFrameABC появился тип DateTime
В табличных данных часто встречаются столбцы с датами: дата регистрации, время создания заказа, момент события в логе и т.д.
Теперь в DataFrameABC для таких данных есть отдельный тип столбца:
Это значит, что дату и время можно хранить не как строку, а как полноценное значение
В первом случае тип столбца задаётся явно через схему:
После этого к значениям можно обращаться как к
Во втором случае используется автоопределение типов:
При выводе можно задать формат отображения:
Тип
В табличных данных часто встречаются столбцы с датами: дата регистрации, время создания заказа, момент события в логе и т.д.
Теперь в DataFrameABC для таких данных есть отдельный тип столбца:
ColumnType.ctDateTime
Это значит, что дату и время можно хранить не как строку, а как полноценное значение
DateTime.uses MLABC;
begin
var text := '''
id,created_at,name
1,2024-01-15,Alice
2,2024-01-16 12:30:00,Bob
''';
var schema := new Dictionary<string, ColumnType>;
schema['created_at'] := ColumnType.ctDateTime;
var df1 := CsvLoader.LoadFromLines(
text.ToLines,
schema := schema
);
Println('Явная схема:');
df1.Print;
Println(df1.DateTime('created_at')[0].Year);
var df2 := CsvLoader.LoadFromLines(
text.ToLines,
inferTypes := True
);
Println;
Println('Автоопределение:');
df2.Print(dateTimeFormat := 'dd.MM.yy');
Println(df2.GetColumnType('created_at'));
end.
В первом случае тип столбца задаётся явно через схему:
schema['created_at'] := ColumnType.ctDateTime;
После этого к значениям можно обращаться как к
DateTime:df1.DateTime('created_at')[0].YearВо втором случае используется автоопределение типов:
inferTypes := True
CsvLoader сам распознаёт столбец created_at как дату и время.При выводе можно задать формат отображения:
df2.Print(dateTimeFormat := 'dd.MM.yy');
Тип
DateTime делает работу с временными данными в DataFrameABC более естественной: даты больше не нужно хранить как обычные строки, а значит, их можно корректно обрабатывать, форматировать и использовать дальше при подготовке данных для анализа и машинного обучения.👍2❤1
📌 ЕГЭ по информатике: задача №3 в новой версии PascalABC.NET
В преддверии ЕГЭ по информатике покажем, как можно решать задачу №3 из демо варианта с помощью новых возможностей PascalABC.NET.
Задача №3 обычно связана с поиском данных в таблицах: нужно объединить несколько листов, отфильтровать строки по условиям и посчитать итоговую величину.
В новой версии PascalABC.NET это можно сделать в стиле современных инструментов анализа данных — через
Что здесь происходит:
1️⃣ Загружаем три таблицы из ODS-файла: движение товаров, список товаров и список магазинов.
2️⃣ Объединяем таблицы:
После этого в одной таблице оказываются и данные о продажах, и названия товаров, и районы магазинов.
3️⃣ Фильтруем только нужные строки:
товар —
район —
операция —
дата — с 5 по 14 октября 2024 года включительно.
4️⃣ Считаем итоговую сумму продаж:
и суммируем по всем найденным строкам.
Такой код хорошо показывает, что PascalABC.NET развивается не только как учебный язык программирования, но и как удобная среда для работы с таблицами, данными и задачами анализа информации.
Задача №3 ЕГЭ становится не набором ручных действий в электронных таблицах, а понятной программой: загрузили данные, объединили таблицы, отфильтровали, посчитали ответ.
🚀 PascalABC.NET — современное программирование для реальных задач анализа данных.
Скачайте последнюю версию!
В преддверии ЕГЭ по информатике покажем, как можно решать задачу №3 из демо варианта с помощью новых возможностей PascalABC.NET.
Задача №3 обычно связана с поиском данных в таблицах: нужно объединить несколько листов, отфильтровать строки по условиям и посчитать итоговую величину.
В новой версии PascalABC.NET это можно сделать в стиле современных инструментов анализа данных — через
DataFrame.uses MLABC;
begin
var movement := DataFrame.FromODS('DEMO_3.ods', 'Движение_товаров');
var goods := DataFrame.FromODS('DEMO_3.ods', 'Товар');
var shops := DataFrame.FromODS('DEMO_3.ods', 'Магазин');
var d1 := new DateTime(2024, 10, 5);
var d2 := new DateTime(2024, 10, 14);
var df := movement
.Join(goods, 'Артикул')
.Join(shops, 'ID магазина');
var answer :=
df.Filter(r ->
(r['Наименование товара'] = 'Варенец термостатный') and
(r['Район'] = 'Нагорный') and
(r['Тип операции'] = 'Продажа') and
(r['Дата'] >= d1) and
(r['Дата'] <= d2)
)
.Sum(r ->
r['Количество упаковок, шт'] *
r['Цена за упаковку']
);
Println(answer);
end.
Что здесь происходит:
1️⃣ Загружаем три таблицы из ODS-файла: движение товаров, список товаров и список магазинов.
2️⃣ Объединяем таблицы:
movement
.Join(goods, 'Артикул')
.Join(shops, 'ID магазина')
После этого в одной таблице оказываются и данные о продажах, и названия товаров, и районы магазинов.
3️⃣ Фильтруем только нужные строки:
товар —
Варенец термостатный;район —
Нагорный;операция —
Продажа;дата — с 5 по 14 октября 2024 года включительно.
4️⃣ Считаем итоговую сумму продаж:
Количество упаковок * Цена за упаковку
и суммируем по всем найденным строкам.
Такой код хорошо показывает, что PascalABC.NET развивается не только как учебный язык программирования, но и как удобная среда для работы с таблицами, данными и задачами анализа информации.
Задача №3 ЕГЭ становится не набором ручных действий в электронных таблицах, а понятной программой: загрузили данные, объединили таблицы, отфильтровали, посчитали ответ.
🚀 PascalABC.NET — современное программирование для реальных задач анализа данных.
Скачайте последнюю версию!
👍11🔥4❤🔥1❤1🥰1👏1🙈1🫡1
PascalABC.NET под net 9 и Codex
Как знают читатели нашего канала, мы давно пытаемся перевести консольный компилятор PascalABC.NET с .NET Framework под современный .NET.
С версии .NET 9.0 это стало возможно - появилась возможность генерировать сборку и сохранять ее на диск.
Примерно год назад нам удалось создать компилирующийся проект консольного компилятора - в нем проходило примерно 95% наших тестов.
Но некоторые тесты падали из-за различий в NET Framework и .NET 9.0.
Этот год мы собирались с духом чтобы начать их исправлять.
И вот - возникла идея загрузить проект в Codex и пообщаться с ним, как можно исправить некоторые ошибки.
В этом посте делюсь, как была исправлена одна.
Вот этот код
падал на строке
которая генерировала IL код вызова конструктора List.
Понятно, что дело именно в нашем пользовательском типе enum Days, потому что на других типах всё работает.
И вот - вишенка на торте - проект загружен в Codex и после пары запросов с просьбой исследовать проект и главный файл генерации IL-кода я загрузил код падающего теста и задал вопрос, можно ли исправить ошибку.
Полминуты. Да-да, CODEX думал ровно полминуты.
И затем выдал: Ошибка исправлена в NETGenerator.cs (line 2665)
Всё!
Ошеломленный, я пообщался еще с Codex о причине ошибки:
Но
пытается найти исходное metadata-определение
При этом внутри
Собственно, еще раз.
Год - страх человека исправить непростую ошибку из за отсутствия нормальной документации.
И полминуты на исправление ошибки ИИ-агентом.
Как знают читатели нашего канала, мы давно пытаемся перевести консольный компилятор PascalABC.NET с .NET Framework под современный .NET.
С версии .NET 9.0 это стало возможно - появилась возможность генерировать сборку и сохранять ее на диск.
Примерно год назад нам удалось создать компилирующийся проект консольного компилятора - в нем проходило примерно 95% наших тестов.
Но некоторые тесты падали из-за различий в NET Framework и .NET 9.0.
Этот год мы собирались с духом чтобы начать их исправлять.
И вот - возникла идея загрузить проект в Codex и пообщаться с ним, как можно исправить некоторые ошибки.
В этом посте делюсь, как была исправлена одна.
Вот этот код
type
Days = (Mon,Tue,Wed,Thi,Fri,Sat);
begin
var ld := new List<Days>;
end.
падал на строке
il.Emit(OpCodes.Newobj, cnstr);
которая генерировала IL код вызова конструктора List.
Понятно, что дело именно в нашем пользовательском типе enum Days, потому что на других типах всё работает.
И вот - вишенка на торте - проект загружен в Codex и после пары запросов с просьбой исследовать проект и главный файл генерации IL-кода я загрузил код падающего теста и задал вопрос, можно ли исправить ошибку.
Полминуты. Да-да, CODEX думал ровно полминуты.
И затем выдал: Ошибка исправлена в NETGenerator.cs (line 2665)
Всё!
Ошеломленный, я пообщался еще с Codex о причине ошибки:
Но
PersistedAssemblyBuilder в .NET 9 при выполнении:il.Emit(OpCodes.Newobj, cnstr);
пытается найти исходное metadata-определение
.ctor. Для generic-типа с аргументом EnumBuilder он этого сделать не может и выдаёт:A MemberInfo that matches '.ctor' could not be found
При этом внутри
EnumBuilder уже существует настоящий TypeBuilder, описывающий создаваемое перечисление. Исправление состоит в том, чтобы при формировании generic-типа передавать именно его.Собственно, еще раз.
Год - страх человека исправить непростую ошибку из за отсутствия нормальной документации.
И полминуты на исправление ошибки ИИ-агентом.
👍12❤4🔥4👏1
Понятно из-за чего - двумерные Generic-массивы.
Попросил Codex разобраться.
И тут - произошла потрясающая вещь!
Codex нашел ошибку не в PascalABC.NET, а в Runtime .Net 9.0!
Причем, Codex сам проверил, что на компьютере установлен Runtime .NET 10.0, откомпилировал пример под ней и убедился, что он работает!
Программист пытался бы исправить ошибку, предполагая что она внутри PascalABC.NET, потратив на это безрезультатно неопределенное время...
Даже не знаю, что еще говорить... Одни эмоции )
Нет, знаю :)
Следующим запросом я попросил перевести весь проект под .NET 10.0. Codex любезно согласился, попросил сделать коммит на Github от греха, пошуршал, ответил вот это:
===============================
Проект переведён на .NET 10.
Изменено:
целевая платформа, каталог
автоматическое копирование
генерируемые
исправлено падение
TestRunner переключён на
обновлён README.
===================
То есть сделал больше чем от него ожидалось - не только перевел проект под .NET 10, но и автоматически создал для него папку, нашел на компьютере и скопировал дополнительные файлы, необходимые для запуска, проверил, что запускаются старые тесты, исправил за компанию еще одну ошибку с
Уф...
Ощущения - Codex-агент - это такой джинн из бутылки, который является опытным программистом и за токены делает крайне нетривиальные действия и дает советы.
Что ж - пишите комментарии!
Попросил Codex разобраться.
И тут - произошла потрясающая вещь!
Codex нашел ошибку не в PascalABC.NET, а в Runtime .Net 9.0!
Причем, Codex сам проверил, что на компьютере установлен Runtime .NET 10.0, откомпилировал пример под ней и убедился, что он работает!
Программист пытался бы исправить ошибку, предполагая что она внутри PascalABC.NET, потратив на это безрезультатно неопределенное время...
Даже не знаю, что еще говорить... Одни эмоции )
Нет, знаю :)
Следующим запросом я попросил перевести весь проект под .NET 10.0. Codex любезно согласился, попросил сделать коммит на Github от греха, пошуршал, ответил вот это:
===============================
Проект переведён на .NET 10.
Изменено:
целевая платформа, каталог
bin\net10 и символ NET10_0 в [Directory.Build.props (line 5)](D:/PABC_Console_Git_NET9/Directory.Build.props:5);автоматическое копирование
Lib, Lng, Ico и Highlighting из bin\shared;генерируемые
runtimeconfig.json теперь используют .NET 10 в [NETGenerator.cs (line 205)](D:/PABC_Console_Git_NET9/src/NETGenerator/NETGenerator.cs:205);исправлено падение
MarkSequencePoint без отладочного документа в [NETGenerator.cs (line 319)](D:/PABC_Console_Git_NET9/src/NETGenerator/NETGenerator.cs:319);TestRunner переключён на
bin\net10 в [runtests.bat (line 1)](D:/PABC_Console_Git_NET9/TestSuite/runtests.bat:1);обновлён README.
===================
То есть сделал больше чем от него ожидалось - не только перевел проект под .NET 10, но и автоматически создал для него папку, нашел на компьютере и скопировал дополнительные файлы, необходимые для запуска, проверил, что запускаются старые тесты, исправил за компанию еще одну ошибку с
MarkSequencePoint, обновил ReadMe где упоминалось что используется .NET 9, и порекомендовал установить VS 2026.Уф...
Ощущения - Codex-агент - это такой джинн из бутылки, который является опытным программистом и за токены делает крайне нетривиальные действия и дает советы.
Что ж - пишите комментарии!
🔥11❤3
Вычисление выражений с помощью Eval
В модуле Utils появилась стандартная функция Eval позволяющая вычислять арифметические выражения, заданные в виде строки
Выражение например можно ввести с клавиатуры
В модуле Utils появилась стандартная функция Eval позволяющая вычислять арифметические выражения, заданные в виде строки
Выражение например можно ввести с клавиатуры
uses Utils;
begin
Println(Eval('2 + 3 * 4'));
Println(Eval('(2.4 + 3 % 2) * 6 - 1'));
Println(Eval('(5 > 3) and (2 < 4)'));
Println(Eval('IIF(17 % 2 = 0, ''even'', ''odd'')'));
Println(Eval('Len(''PascalABC.NET'')'));
var i := EvalInt('2 + 3');
var x := EvalReal('10 / 4.0');
var b := EvalBool('(10 >= 5) and (3 <> 4)');
var s := EvalStr('''PascalABC'' + ''.NET''');
Println(i, x, b, s)
end.
🔥6👍1
У официального канала PascalABC.NET появилось зеркало в MAX:
https://max.ru/channel_pabc_official
Оба канала заработают полным ходом с сентября.
Появилась также группа для обсуждения в MAX вот с такой красивой пригласительной ссылкой
https://max.ru/join/D28ks-E4Olzc7BN7mVlAqhNr-NXbgijzaRnGNQ9FXZQ
Она также полноценно заработает с сентября. Кому удобнее обсуждать в MAX - присоединяйтесь!
Между тем, нашим основным каналом остается Телеграм.
https://max.ru/channel_pabc_official
Оба канала заработают полным ходом с сентября.
Появилась также группа для обсуждения в MAX вот с такой красивой пригласительной ссылкой
https://max.ru/join/D28ks-E4Olzc7BN7mVlAqhNr-NXbgijzaRnGNQ9FXZQ
Она также полноценно заработает с сентября. Кому удобнее обсуждать в MAX - присоединяйтесь!
Между тем, нашим основным каналом остается Телеграм.
MAX
PascalABC.NET Официальный канал
Официальный канал системы программирования PascalABC.NET
Канал в Телеграм: https://t.me/pascalabc_official
Канал в Телеграм: https://t.me/pascalabc_official
💩18❤3🔥1
🎓 С Днём знаний!
Поздравляем всех подписчиков канала PascalABC․NET с началом нового учебного года! К 1 сентября у PascalABC․NET сразу несколько важных обновлений.
Новый сайт pascalabc.net.
Современный дизайн, новая структура и удобный доступ к материалам о системе, документации и обучении.
Заходите, смотрите и делитесь впечатлениями в комментариях!
PascalABC․NET 4.0.
● В новой версии появилась собственная библиотека ML PascalABC․NET для машинного обучения — без внешних зависимостей. Теперь задачи анализа данных и классического машинного обучения можно решать средствами самого PascalABC․NET.
● Ещё одно важное обновление — компилятор для .NET 10.0.
С 1 сентября! Успешного и интересного учебного года!
Поздравляем всех подписчиков канала PascalABC․NET с началом нового учебного года! К 1 сентября у PascalABC․NET сразу несколько важных обновлений.
Новый сайт pascalabc.net.
Современный дизайн, новая структура и удобный доступ к материалам о системе, документации и обучении.
Заходите, смотрите и делитесь впечатлениями в комментариях!
PascalABC․NET 4.0.
● В новой версии появилась собственная библиотека ML PascalABC․NET для машинного обучения — без внешних зависимостей. Теперь задачи анализа данных и классического машинного обучения можно решать средствами самого PascalABC․NET.
● Ещё одно важное обновление — компилятор для .NET 10.0.
С 1 сентября! Успешного и интересного учебного года!
❤21🔥4
🎓 ML PascalABC․NET — машинное обучение без внешних зависимостей
Одно из главных нововведений PascalABC.NET 4.0 — новая библиотека ML PascalABC․NET.
Это уже не набор отдельных учебных алгоритмов, а целостная библиотека для анализа данных и классического машинного обучения.
В ней есть всё необходимое для полного ML-процесса:
● работа с табличными данными (
● подготовка признаков: пропуски, кодирование категорий, масштабирование
● классификация и регрессия: Logistic Regression, kNN, Decision Tree, Random Forest, Gradient Boosting
● кластеризация: KMeans и DBSCAN
● метрики, разбиение выборок и кросс-валидация
● ML-конвейеры для объединения подготовки данных и моделей
● встроенные датасеты для обучения и экспериментов
API библиотеки построен вокруг привычной современной схемы
И принципиальный момент — никаких внешних зависимостей.
Мы подготовили отдельный сайт с документацией, обучающими материалами и примерами:
ML PascalABC.NET — документация и обучение
Если вы хотите познакомиться с машинным обучением на PascalABC․NET, теперь для этого есть полноценная среда — от загрузки и подготовки данных до обучения модели и оценки её качества.
Одно из главных нововведений PascalABC.NET 4.0 — новая библиотека ML PascalABC․NET.
Это уже не набор отдельных учебных алгоритмов, а целостная библиотека для анализа данных и классического машинного обучения.
В ней есть всё необходимое для полного ML-процесса:
● работа с табличными данными (
DataFrame), статистика и визуализация● подготовка признаков: пропуски, кодирование категорий, масштабирование
● классификация и регрессия: Logistic Regression, kNN, Decision Tree, Random Forest, Gradient Boosting
● кластеризация: KMeans и DBSCAN
● метрики, разбиение выборок и кросс-валидация
● ML-конвейеры для объединения подготовки данных и моделей
● встроенные датасеты для обучения и экспериментов
API библиотеки построен вокруг привычной современной схемы
Fit / Transform / Predict. По организации работы и удобству программирования ML PascalABC.NET следует подходам scikit-learn, оставаясь при этом нативной библиотекой PascalABC․NET.И принципиальный момент — никаких внешних зависимостей.
DataFrame, линейная алгебра, модели, препроцессинг, метрики и валидация реализованы непосредственно в экосистеме PascalABC․NET.Мы подготовили отдельный сайт с документацией, обучающими материалами и примерами:
ML PascalABC.NET — документация и обучение
Если вы хотите познакомиться с машинным обучением на PascalABC․NET, теперь для этого есть полноценная среда — от загрузки и подготовки данных до обучения модели и оценки её качества.
🔥15❤🔥1
🚀 Предварительная версия PascalABC․NET 4.0.1 уже доступна
На странице скачивания PascalABC․NET появился новый раздел — «Предварительная версия». Сейчас в нём доступна PascalABC․NET 4.0.1 Beta. Именно эта версия будет постепенно накапливать новые возможности и последние исправления.
По мере готовности она станет основной версией PascalABC․NET, а пока её можно использовать для знакомства с нововведениями и тестирования.
Сейчас доступны:
— StandardPack 4.0.1 Beta
— консольный компилятор 4.0.1 Beta для .NET 10
— список изменений в 4.0.1
Скачать предварительную версию:
https://pascalabc.net/ssyilki-dlya-skachivaniya/
На странице скачивания PascalABC․NET появился новый раздел — «Предварительная версия». Сейчас в нём доступна PascalABC․NET 4.0.1 Beta. Именно эта версия будет постепенно накапливать новые возможности и последние исправления.
По мере готовности она станет основной версией PascalABC․NET, а пока её можно использовать для знакомства с нововведениями и тестирования.
Сейчас доступны:
— StandardPack 4.0.1 Beta
— консольный компилятор 4.0.1 Beta для .NET 10
— список изменений в 4.0.1
Скачать предварительную версию:
https://pascalabc.net/ssyilki-dlya-skachivaniya/
❤8
PascalABC․NET + VS Code + .NET 10
В PascalABC․NET 4.0 появился официальный плагин для Visual Studio Code.
https://pascalabc.net/vscode/
Он поддерживает:
◉ компиляцию и запуск программ;
◉ IntelliSense;
◉ диагностику ошибок;
◉ подсказки типов и параметров;
◉ .NET Framework 4.8 и .NET 10.
С .NET 10 PascalABC․NET можно использовать в VS Code на Windows, Linux и macOS.
На скриншоте — PascalABC․NET в VS Code с работающим IntelliSense и запуском программы под .NET 10.
В PascalABC․NET 4.0 появился официальный плагин для Visual Studio Code.
https://pascalabc.net/vscode/
Он поддерживает:
◉ компиляцию и запуск программ;
◉ IntelliSense;
◉ диагностику ошибок;
◉ подсказки типов и параметров;
◉ .NET Framework 4.8 и .NET 10.
С .NET 10 PascalABC․NET можно использовать в VS Code на Windows, Linux и macOS.
На скриншоте — PascalABC․NET в VS Code с работающим IntelliSense и запуском программы под .NET 10.
👍11🔥7❤🔥3❤3
Расширение PascalABC.NET для VS Code на Mac — работает! 🍎
Доступна версия 0.5.1 расширения PascalABC.NET для Visual Studio Code.
На macOS уже работают:
✅ запуск программ
✅ подсветка синтаксиса
✅ IntelliSense — автодополнение и информация о символах
Скачать расширение можно здесь:
https://pascalabc.net/vscode/
Доступна версия 0.5.1 расширения PascalABC.NET для Visual Studio Code.
На macOS уже работают:
✅ запуск программ
✅ подсветка синтаксиса
✅ IntelliSense — автодополнение и информация о символах
Скачать расширение можно здесь:
https://pascalabc.net/vscode/
❤8👏3
RayTracer, поправленный ИИ
Давний пример, публиковался здесь.
Попросил ИИ его ускорить. Ускорил в 10 раз
https://gist.github.com/miks1965/592a9ee7a812cafa89dde0440b8ee9a3
Сейчас эта сцена рендерится за 0.3 сек.
Чтож, спасибо, Claude!
Давний пример, публиковался здесь.
Попросил ИИ его ускорить. Ускорил в 10 раз
https://gist.github.com/miks1965/592a9ee7a812cafa89dde0440b8ee9a3
Сейчас эта сцена рендерится за 0.3 сек.
Чтож, спасибо, Claude!
🔥11❤1
Спаниели или бульдоги? 🐶
Поговорим о машинном обучении в PascalABC.NET и о том, почему это круто.
Возьмём простую учебную задачу. У нас есть вес и высота собак двух пород. Обучим логистическую регрессию и посмотрим, как она разделит бульдогов и спаниелей.
Несколько строк — и перед нами настоящий алгоритм машинного обучения: загрузка данных, подготовка признаков, обучение модели и визуализация найденной границы между классами.
И всё это — на PascalABC.NET.
Для обучения это особенно интересно: можно сначала разобраться, что такое признаки, классы, обучение модели и разделяющая граница, а уже потом переходить к более сложным моделям и инструментам.
Поговорим о машинном обучении в PascalABC.NET и о том, почему это круто.
Возьмём простую учебную задачу. У нас есть вес и высота собак двух пород. Обучим логистическую регрессию и посмотрим, как она разделит бульдогов и спаниелей.
uses MLABC, PlotML;
begin
var df := DataFrame.FromCsv('dogs.csv');
var X := df.ToMatrix(['Вес', 'ВысотаВХолке']);
var target := df.EncodeTarget('Порода');
var model := new LogisticRegression;
model.Fit(X, target.Labels);
Plot.Points(
df['Вес'].FloatValues,
df['ВысотаВХолке'].FloatValues,
target.Labels,
classNames := target.ClassNames
);
var (A, B, C) := model.DecisionBoundary.Coefficients;
Plot.Line(A, B, C);
Plot.SetLabels(
'Разделяющая прямая для двух пород',
'Вес, кг',
'Высота в холке, см'
);
end.
Несколько строк — и перед нами настоящий алгоритм машинного обучения: загрузка данных, подготовка признаков, обучение модели и визуализация найденной границы между классами.
И всё это — на PascalABC.NET.
Для обучения это особенно интересно: можно сначала разобраться, что такое признаки, классы, обучение модели и разделяющая граница, а уже потом переходить к более сложным моделям и инструментам.
❤2
