Стендап Сьогодні
Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті
Підписатись на RSS
📢
Канал в Telegram @stendap_sogodni
🦣
@stendap_sogodni@shevtsov.me в Федиверсі
16.12.2023
Дев-адвент 16: інтеграція регресії
Отже, вчора обрав влучні параметри функції ціни, щоб покривати графік відрізками регресії. Залишилось повернутися назад в головну програму та зробити робочу версію.
В чому проблема: я писав, що придумав, як уникнути кубічної складності при обчисленні розвʼязка. Поясню. Сам алгоритм динамічного програмування має квадратичну складність: один цикл для збільшення діапазону розвʼязку, а всередині — ще один, для перебору з розвʼязків для менших діапазонів.
Регресію я знайшов, як робити паралельно з внутрішнім циклом. Але оцінка якості регресії — теж цикл; необхідно порівняти значення в кожній точці. Виходить, що щоб оцінити, який розвʼязок найкращий, доведеться зробити ще один цикл — оцінки регресії.
Квадратична складність на декількох тисячах точок — не проблема для сучасного процесору, а кубічна — вже займає помітний час, декілька секунд.
Все це значить банально те, що розвʼязок потрібно зберігати, а не обчислювати наново щоразу. Бо минула вага є константою, тож і розбиття на відрізки теж. Хіба що у випадку редагування або імпорту старих даних доведеться обчислити повторно.
А решта мені подобається — модель нарешті математично акуратна.
15.12.2023
Дев-адвент 15: цінність прототипування
📈 Нарешті, взяв експортовані дані та зробив з ними тестовий каркас. Щоправда, обрав не Excel, а JavaScript та D3.js, бо тут мені звичніше.Отримав за годину більше прогресу ніж за дні абстрактних міркувань та розглядання чисел.
Щоб зробити дещо просте в D3, достатньо одного файлу HTML. Скрипт — там же ж. Дані — там же ж. Ніякої збірки. Prettier легко впорається з автоформатуванням. (До речі, щоб не чіпати CSV, перетворив їх на JSON через один рядок Ruby.)
Відтворив алгоритм на JavaScript, та зробив так, що кожний крок робиться або за таймером, або натисканням кнопки. Мені було потрібно зрозуміти поведінку регресії та підібрати функцію ціни. Побудова регресії на графіку робить поведінку буквально очевидною.
З функцією ціни трохи складніше, бо треба уявляти поведінку математичних функцій. Наприклад, мені вже відомо, що щоб алгоритм віддавав перевагу довшим відрізкам (замість декількох коротких), можна взяти ступінь від довжини. Але яку ступінь? Це легко зрозуміти, якщо результати відтворювати на графіку.
На мою думку, досвідчений інженер повинен вміти робити не тільки великі, красиві та акуратні рішення, а й працездатні рішення в найкоротший час. Це не тільки експериментів стосується, а й скриптів для разових дій, прототипів, досліджень.
14.12.2023
Дев-адвент 14: Експорт в CSV
Отже, експорт даних в CSV. Зробив. Фіча ця збирається з послідовності компонентів, як з кубиків.
-
Дані в мене вже є. Окрім зважувань з Apple Health, я експортую обчислене “правильне” значення, а також нотатки.
-
Для генерації CSV пішов найпростішим шляхом, тобто набираю дані в рядок з комами посередині та й усе. Ще за досвідом Сінтри дійшов того, що до CSV можна ставитись просто. Добре, коли в мові є зручна вбудована бібліотека для генерації CSV (Ruby, Go навіть), але у Swift немає. Точніше, є така можливість у класу DataFrame зі стандартної бібліотеки для машинного навчання. Спочатку навіть спробував залучити його. Але швидко відмовився через безглуздість цього підходу.
-
У Swift найбільш дивний підхід до запису в файл з тих, що я бачив, виглядає це так:
csvString.write(
to: URL.temporaryDirectory.appending(path: "diet.csv"),
atomically: true,
encoding: .utf8
)
Так, запис у файл це метод на рядку. А ще у Swift шляхи до файлів є URL.
- Такий файл буде доступний тільки моїй програмі — доступ до диска в iOS чітко розділений. Є відомий спосіб віддати файл користувачу — діалог активностей — той самий, який зʼявляється по кнопці “Поділитися”. Знайшов десять рядків коду, які покажуть цей діалог. Віддаєш діалогу файл, та користувач вже сам вільний надіслати його туди, куди потрібно — зберегти в “Файли”, відкрити прямо в Numbers, або навіть — скопіювати та вставити на ноутбук за допомогою універсального буфера обміну.
Імпорт буде складніше, бо CSV доведеться інтерпретувати.
13.12.2023
Дев-адвент 13: лінійна регресія
Згадав ще один спосіб розбивати графік на відрізки. А саме, розглядати кожний відрізок як окрему лінійну регресію.
Регресія — то, попросту кажучи, пряма, що наближує ламану, описану точками. Зрозуміло, що весь графік одною регресією не наблизити, але, оскільки алгоритм розбиття на відрізки в мене вже є, можна приєднати до нього регресію як функцію ціни та подивитися, що вийде.
Що тут таке ціна? На перший погляд, все знову очевидно — ціна — це сума розбіжностей між наближеннями з регресії та реальними значеннями. Але цього недостатньо, бо, наприклад, для двох точок регресія дає ідеальне наближення, та й з більшою кількістю буде віддавати перевагу відрізкам, де виміри однакові. (Про збіги ваг взагалі можна окремий пост.) Тобто окрім розбіжностей, ціна має враховувати довжину відрізка; чим довше, тим більше ми можемо пробачити.
(Не обовʼязково зі збільшенням числа точок розбіжності збільшуються; навпаки, якщо процес є лінійним, то більше точок будуть давати краще наближення через позбавлення від шуму. Наша задача як раз виявити в графіку більш-менш лінійні процеси. Якщо я не помиляюсь, то вага зазвичай змінюється лінійно, бо вона є функцією образа життя, який все ж має сталість.)
Поки знайшов гарну інкрементальну формулу для обчислення регресії. Інкрементальна формула для мене дуже важлива, щоб з квадратичної складності не отримати кубічну.
А ще зрозумів, що експорт даних в CSV суттєво розблокує мені дослідження, бо тоді можна буде робити експерименти не на Swift, та результати бачити не в консолі, а в Екселі — який для таких експериментів встократ краще пристосований.
12.12.2023
Дев-адвент 12: фокусування
Все ж моєю метою є випустити продукт до різдвяних канікул в App Store. Тому роблю паузу, щоб зібрати з фіч цілісну картинку.
Пропозиція програми — допомогти з контролем ваги. Для цього вона витягує з вимірів ваги якомога більше розуміння. Це: побудова графіка за методом рухомого середнього (це не є придумав, книжка є);автоматичне визначення періодів схуднення, стабільної ваги та набору ваги; та побудова тренду.
Мета всього цього: завжди мати наочне відображення ваги як процесу. На рівні тваринних рефлексів, а не аналітичного мислення. На моєму досвіді, це дійсно покращує контроль, бо відбирає можливість втекти у “все складно, я краще зараз не буду думати, а завтра все зʼясую.”
Щодо власного досвіду: звісно, цільова аудиторія це такі люди, як я. А саме, інженери, які вже стежать за вагою, але не довіряють типовим програмам через їхню ускладненість та перенасиченість функціями, першою з яких, на мою думку, є щоденник харчування. Нам би бачити простий показник та тягнути його вниз.
Доводиться балансувати між MVP, який реально зробити, та функціями, які хочеться мати самому, бодай в примітивному стані.
Одним з найскладніших аспектів програми є пристосування до користувача без історії, бо тоді вона фактично не вміє нічого, але все ж потрібно привертати увагу принаймні тиждень. Так що нагадування — в першу чергу хотілка — все ж стануть до нагоди.
11.12.2023
Дев-адвент 11: сповіщення
Раз мій метод ведення графіку потребує щоденних зважень, про них потрібно нагадувати. Для того є в iOS проста система локальних сповіщень.
З боку користувача цього не видно, але сповіщення відбуваються не тільки через виклик з інтернету (що називається push-сповіщення). Будь-яка програма здатна запланувати локальні сповіщення, які відбудуться за інтервалом, календарем, або навіть геолокацією.
Локальні сповіщення не запускають додаток, поки на них не натиснеш, тобто як і з віджетами, ми мусимо заздалегідь задати всі параметри. Поки я зробив найпростіше зі сповіщень — щоденне.
Навіть навколо найпростішого сповіщення є список задач. Спочатку потрібно запросити дозвіл — причому нам дано це зробити лише раз; якщо користувач відмовить, в наступні рази можемо тільки попросити піти до додатка “Налаштування” та увімкнути вручну.
Далі, маємо зберігати власні налаштування того, чи включені сповіщення. Для того ідеально підходить база даних UserDefaults
. У Swift робота з нею виглядає приблизно так:
.onAppear {
isEnabled = UserDefaults.standard.bool(forKey: "enabled")
}.onChange(of: isEnabled) {
UserDefaults.standard.setValue(isEnabled, forKey: "enabled")
}
Та, нарешті, залишається утворити графік сповіщень — поки є дозвіл, ми можемо змінювати графік коли завгодно, тобто підтримувати його актуальність.
10.12.2023
Дев-адвент 10: Прибирання
Трохи ресурсів, якими я користуюсь для розробки на Swift.
-
Щодо редактора, якщо писати програми для платформ Apple, то все ж доведеться це робити в XCode. Я пробував розробку в VSCode, та там бракує повноцінних підказок. В теорії є й доповнення, і мовний сервер, але він чомусь не володіє контекстом щодо доступних модулів. (Окрім того, на Swfit можна писати програми для всіх платформ, але з цим в мене досвіду немає.)
-
SwiftLint - канонічний лінтер, в якого є понад 200 правил. Я б радив не підключати його для простих експериментів, бо лінтер доволі суворий. Наприклад, він примушує безпечно перевіряти всі
Optional
, які у Swift повертає половина функцій — це дуже цінно для робочої програми, але суттєво сповільнює експериментування. -
SwiftFormat - потужний автоформатувальник. В його використанні є єдиний недолік: при форматуванні через XCode втрачається історія змін. Тому форматувати на збереження файлу, як я це звик робити, тут не вийде. Я б радив ставити SwiftFormat на
git pre-commit
. -
SwiftGen - кодогенератор правильних, типізованих модулів для використання ресурсів — таких, як зображення або кольори. Це не тільки захистить від помилок, але й допоможе в написанні коду, бо не доведеться вручну вводити назви ресурсів.
PS: чи знаєте ви, що у Swift є офіційна співачка? Може то й занадто розкрутки від Apple, але ж музика чудова! 💃
09.12.2023
Дев-адвент 9: виявлення проміжків стабільності
Окей, зрозумів, що без стабільності — нікуди. А саме в аналізі графіка. Без стабільності аналіз утворює зигзаг “вгору-вниз”, бо так алгоритм побудований: або відрізок їде вгору, або вниз, та звісно, вони чергуються, бо інакше бути не може.
Але в реальності стан стабільної ваги навіть важливіший за зріст чи падіння, а ще важливіше помітити, що він закінчився. Отже, займався сьогодні стабілізацією алгоритму.
Звучить, воно, може, й просто — стабільність це коли вага незмінна. Але маленькі коливання нас теж влаштують. Але маленькі коливання — це скільки? Та навіть маленькі коливання мають бути збалансованими.
По-перше, відмовився від ідеї “ціни” відрізка як математичної функції. Довго намагався (в чудовій програмі Grapher) винайти формулу, яка виконує всі мої вимоги, але реально набагато простіше зробити парою if
. Відповідно, відрізок або стабільний (за рядом ознак), або направлений. Щоб перевірити стабільність, дивлюся на мале значення зміни ваги, а також щоб відношення між зміною в обидва напрямки було майже 1
(тобто була збалансованість).
Поки є проблемний випадок: за одною сумою змін стабільність можна виявити тільки на коротких проміжках. На проміжках більших десь за 3 тижні сума маленьких змін накопичується настільки, що вже незрозуміло, чи то результат непомітних коливань, чи суттєвого набору, а потім скиду ваги. Що вже стабільністю не назвеш. Тому зʼявляються відрізки стабільності, які йдуть один за одним, щоб не перебільшувати межу по сумі. А якщо нормалізувати суму на тривалість, тоді інша проблема — будь-який достатньо довгий проміжок часу виглядає як стабільність.
Треба придумати ще додаткову метрику, щоб це розрізняти. Можливо, підійде дельта між найбільшим та найменшим значенням за проміжок.
08.12.2023
Дев-адвент 8: збереження додаткової інформації
Apple Health - не універсальна база даних, та окрім ваги туди нічого корисного не запишеш (Ну, хіба що ще обхват талії. Чому саме талії, а не будь-якої іншої частини тіла? Бо, як я розумію, обхват талії використовується для виявлення діабету. Порівняно з цим, для відстеження харчування є метрика для кожного мікроелемента. Мабуть, записувати, скільки зʼїв хрому важливіше, ніж обхват груді чи стегна, чи біцепса. Але то таке.)
Але для мене головне, що в Apple Health не можна анотувати показники текстом. Бо без подробиць з історією ваги мало що зробиш (а точніше, їх доведеться згадувати в будь-якому разі.)
Детального щоденника я не робити хочу — це забагато шуму. Краще коли анотації будуть тільки на важливі події. Наприклад, “вечірка”, “перший раз пішов до спортзалу”, “застудився”. В майбутньому, думаю, програма буде сама запрошувати внести коментар, коли з вагою відбувається щось неочікуване.
Для збереження анотацій додав SwiftData, про який я писав місяць тому. Ще раз зрадів тому, наскільки мало для цього потрібно зробити — буквально пару рядків (плюс сама модель, запити — тобто значущий код.) Так само легко додати й синхронізацію бази через iCloud.
Є ще така фантазія: оскільки програма знає дати локальних мінімумів та максимумів ваги, вона може пропонувати обрати фотографію з тих дат, та утворити справжню галерею “до/після” в напівавтоматичному режимі.
07.12.2023
Дев-адвент 7: інтерпретація графіка та побудова історії
Ламана на графіку не дуже потрібна. Справжня цінність вчорашнього алгоритму — в тому, що програма тепер розуміє зміст графіку: відрізки зросту та спадання, точки переламу. З цього можна витягнути декілька корисних функцій.
Що я тут зрозумів: досліджувати історію змін за величезним графіком не дуже зручно, який масштаб не обирай. Бо ті самі відрізки доведеться шукати на графіку вручну, та ще й приблизно.
Отже, найпростіше, що можна зробити з ламаною — це показати її у вигляді не графіку, а списку відрізків. Так історія відразу стає зрозумілою: тут два місяці худнули, тут місяць набирали. Можна було б ще розбити список на роки, але поки те, що є на скриншоті, вже приємно.
З інших моментів: гарно було б також виявляти відрізки стабільності… для цього треба лише поміняти формулу ціни відрізка так, щоб відрізки без значних змін отримували перевагу. Взагалі вчорашній алгоритм завдяки цій формулі вийшов досить гнучкий.
Наприклад, хочу я, щоб проміжки без зважувань теж відокремлювалися… але якщо це проміжок не у 2-3 дні. Можна видумувати ще алгоритм, а можна просто доробити функцію ціни.