Що може піти не так із Data Science? Збір даних

Що може піти не так із Data Science? Збір даних
Сьогодні існує 100500 курсів з Data Science і давно відомо, що найбільше грошей у Data Science можна заробити саме курсами з Data Science (навіщо копати, коли можна продавати лопати?). Основний мінус цих курсів у тому, що вони не мають нічого спільного з реальною роботою: ніхто не дасть вам чистих, оброблених даних у потрібному форматі. І коли ви виходите з курсів і починаєте вирішувати справжнє завдання, спливає багато нюансів.

Тому ми починаємо серію нотаток "Що може піти не так з Data Science", заснованих на реальних подіях, що трапилися зі мною, моїми товаришами та колегами. Розбиратимемо на реальних прикладах типові завдання з Data Science: як це насправді відбувається. Почнемо сьогодні із завдання збору даних.

І перше про що спотикаються люди, почавши працювати з реальними даними - це власне збір цих найрелевантніших нам даних. Ключове посилання цієї статті:

Ми систематично недооцінюємо час, ресурси та зусилля на збір, очищення та підготовку даних.

А головне, обговоримо, що робити, щоби цього не допустити.

За різними оцінками, очищення, трансформація, data processing, feature engineering тощо займають 80-90% часу, а аналіз 10-20%, тоді як майже весь навчальний матеріал фокусується лише з аналізі.

Давайте розберемо як типовий приклад просте аналітичне завдання у трьох варіантах і побачимо, якими бувають «обтяжуючі обставини».

І для прикладу знову ж таки, ми розглянемо подібні варіації завдання збору даних та порівняння спільнот для:

  1. Двох сабреддитів Reddit
  2. Двох розділів Хабра
  3. Двох груп Однокласників

Умовний підхід у теорії

Відкрити сайт і почитати приклади, якщо зрозуміло, закласти кілька годин на читання, кілька годин на код за прикладами та налагодження. Додати кілька годин на збирання. Накинути кілька годин про запас (помножити на дві і додати N годин).

Ключовий момент: тимчасова оцінка полягає в припущеннях і здогадах у тому, скільки це триватиме часу.

Розпочати аналіз часу необхідно з оцінки наступних параметрів для умовного завдання, описаного вище:

  • Який розмір даних та скільки його потрібно фізично збирати (*див нижче*).
  • Який час збирання одного запису і скільки потрібно чекати, перш ніж можна зібрати другий.
  • Закласти написання коду зберігає стан і рестарт, що починає, коли (а не якщо) все впаде.
  • Розібратися, чи потрібна нам авторизація та закласти час отримання доступу до API.
  • Закласти кількість помилок, як функцію складності даних - оцінити за конкретним завданням: структура, скільки перетворень, що і як екстрактим.
  • Закласти помилки мережі та проблеми з нестандартною поведінкою проекту.
  • Оцінити, якщо потрібна функція в документації і якщо ні, то як і скільки потрібно для робочоїправи.

Найважливіше, що для оцінки часу – вам фактично необхідно витратити час та зусилля для «розвідки боєм» – лише тоді ваше планування буде адекватним. Тому як би вас не пушили сказати «а скільки часу потрібно для збору даних» — вибийте собі часу на попередній аналіз і аргументуйте тим, наскільки час варіюватиметься залежно від реальних параметрів завдання.

І зараз ми продемонструємо конкретні приклади, де такі параметри змінюватимуться.

Ключовий момент: оцінка заснована на аналізі ключових факторів, що впливають на обсяг та складність роботи.

Оцінка, заснована на здогадах - це хороший підхід, коли функціональні елементи досить невеликі і не так багато факторів, які можуть суттєво вплинути на структуру завдання. Але у разі низки завдань Data Science таких факторів стає дуже багато і подібний підхід стає неадекватним.

Порівняння спільнот Reddit

Почнемо з найпростішого випадку (як потім виявиться). Взагалі, якщо чесно, перед нами практично ідеальний випадок, перевіримо наш чекіст складності:

  • Є акуратний, зрозумілий та документований API.
  • Вкрай просто і головне автоматично виходить токен.
  • є обгортка python - З купою прикладів.
  • Спільнота, яка займається аналізом і збором даних на реддіті (аж до youtube роликів, що пояснюють, як використовувати python wrapper) ось наприклад.
  • Потрібні нам методи, швидше за все, існують в API. Більш того, код виглядає компактно і чисто, нижче приклад функції, що збирає коментарі до посту.

def get_comments(submission_id):
    reddit = Reddit(check_for_updates=False, user_agent=AGENT)
    submission = reddit.submission(id=submission_id)
    more_comments = submission.comments.replace_more()
    if more_comments:
        skipped_comments = sum(x.count for x in more_comments)
        logger.debug('Skipped %d MoreComments (%d comments)',
                     len(more_comments), skipped_comments)
    return submission.comments.list()

Взято з цій добірки зручних утиліт для обгортки.

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

  • Ліміти API - ми змушені брати дані батч (спати між запитами і тд).
  • Час збору — для повного аналізу та порівняння доведеться закласти суттєвий час просто для павука пройтися сабреддитом.
  • Бот повинен крутитися на сервері - ви не можете просто запустити його на ноуті, скласти в рюкзак і поїхати у справах. Тому я запустив усе на VPS. За промокодом habrahabr10 можна заощадити ще 10% вартості.
  • Фізична недоступність деяких даних (вони видно адмінам або надто складно збираються) — це треба врахувати, не всі дані, в принципі, можна зібрати за адекватний час.
  • Помилки роботи мережі: робота з мережею – це біль.
  • Це живі справжні дані, вони чистими не бувають.

Звичайно, необхідно закласти у розробку зазначені нюанси. Конкретні години/дні залежать від досвіду розробки або досвіду роботи над подібними завданнями, проте ми бачимо, що тут завдання виключно інженерне і не вимагає додаткових рухів тіла для вирішення - можна все дуже добре оцінити, розписати і зробити.

Порівняння розділів Хабра

Переходимо до більш цікавого та нетривіального випадку порівняння потоків та/або розділів Хабра.

Перевіримо наш чекіст складності — тут, щоб зрозуміти кожен пункт, вже доведеться трохи потикатися в саме завдання і поекспериментувати.

  • Спочатку ви вважаєте, що є API, але його немає. Так-так, у Хабра є API, але він недоступний для користувачів (а може і зовсім не працює).
  • Потім просто починаєте парсити html - "import requests", що може піти не так?
  • А як взагалі парсити? Найпростіший і найчастіше використовуваний підхід — ітерувати за ID, відзначимо, що не найефективніший і доведеться обробляти різні випадки — ось для прикладу щільність реальних ID серед усіх існуючих.

    Що може піти не так із Data Science? Збір даних
    Взято з цій статті.

  • Сирі дані, загорнуті в HTML поверх мережі – це біль. Наприклад, ви хочете зібрати та зберегти рейтинг статті: видерли score з html і вирішили зберегти його як число для подальшої обробки: 

    1) int(score) кидає помилку: тому що на Хабрі мінус, як, наприклад у рядку "-5" - це коротке тире, а не знак мінуса (несподівано, так?), тому в якийсь момент довелося піднімати парсер до життя ось із таким жахливим фіксом.

    try:
          score_txt = post.find(class_="score").text.replace(u"–","-").replace(u"+","+")
          score = int(score_txt)
          if check_date(date):
            post_score += score
    

    Дати, плюсів та мінусів може взагалі не бути (як ми бачимо вище за функцією check_date і таке було).

    2) Неекрановані спецсимволи - вони прийдуть, потрібно бути готовим.

    3) Структура змінюється залежно від типу посту.

    4) Старі пости можуть мати **дивну структуру**.

  • По суті обробку помилок і що може чи не може статися, доведеться обробляти і не можна передбачити напевно, що піде не так і як ще може бути структура і що де відвалиться — доведеться просто пробувати і враховувати помилки, які кидає парсер.
  • Потім ви розумієте, що потрібно парсити в кілька потоків або парс в один потім займе 30+ годин (це чисто час виконання вже робочого однопоточного парсера, який спить і не потрапляє ні під які бани). У цій статті, це призвело в якийсь момент до подібної схеми:

Що може піти не так із Data Science? Збір даних

Разом чекліст за складністю:

  • Робота з мережею та парсом html з ітерацією та перебором за ID.
  • Документи неоднорідної структури.
  • Багато місць, де код може легко впасти.
  • Потрібно писати || код.
  • Відсутня потрібна документація, приклади коду та/або спільнота.

Умовна оцінка часу для цієї задачі буде в 3-5 разів вищою, ніж для збору даних з Реддіта.

Порівняння груп Однокласників

Перейдемо до найтехнічнішого випадку з описаних. Для мене він був цікавий саме тим, що на перший погляд він виглядає досить тривіальним, але зовсім таким не виявляється — як тільки ви тицьнете в нього паличкою.

Почне з нашого чекліста складності і зазначимо, що багато хто з них виявиться набагато складнішим, ніж виглядає спочатку:

  • API є, але майже повністю відсутні потрібні функції.
  • До певних функцій потрібно просити доступ поштою, тобто видача доступу не миттєва.
  • Він страшенно документований (почнемо з того, що скрізь заважають російські та англійські терміни, причому абсолютно непослідовно — іноді вам потрібно просто вгадати, що від вас десь хочуть) і, більше того, не підходить по дизайну для отримання даних, наприклад, потрібної нам функції.
  • Потребує сесії в документації, а насправді її не використовує — і немає жодного способу розібратися у всіх тонкощах режимів API, окрім як тикатись і сподіватися, що щось працюватиме.
  • Відсутні приклади та спільнота, єдина точка опори у збиранні інформації — невелика обгортка на пітоні (без великої кількості прикладів використання).
  • Найбільш робочим варіантом виглядає Selenium, оскільки багато потрібних даних під замком.
    1) Тобто йде авторизація через фіктивного користувача (та реєстрація ручками).

    2) Однак c Selenium ніяких гарантій щодо коректної та повторюваної роботи (принаймні у випадку з ok.ru точно).

    3) Сайт Ок.ру містить помилки JavaScript і іноді дивно і непослідовно поводиться.

    4) Потрібно займатися пагінацією, підвантаженням елементів і т.д.

    5) Помилки API, які віддає wrapper, доведеться костильно обробляти, наприклад, ось так (шматочок експериментального коду):

    def get_comments(args, context, discussions):
        pause = 1
        if args.extract_comments:
            all_comments = set()
    #makes sense to keep track of already processed discussions
            for discussion in tqdm(discussions): 
                try:
                    comments = get_comments_from_discussion_via_api(context, discussion)
                except odnoklassniki.api.OdnoklassnikiError as e:
                    if "NOT_FOUND" in str(e):
                        comments = set()
                    else:
                        print(e)
                        bp()
                        pass
                all_comments |= comments
                time.sleep(pause)
            return all_comments
    

    Моя улюблена помилка була:

    OdnoklassnikiError("Error(code: 'None', description: 'HTTP error', method: 'discussions.getComments', params: …)”)

    6) Зрештою варіант Selenium + API виглядає найбільш раціональним варіантом.

  • Необхідно збереження стану та рестарту системи, обробка безлічі помилок, у тому числі непослідовної поведінки сайту — причому ці помилки, які досить складно собі уявити (якщо ви не професійно пишіть парсери, зрозуміло).

Умовна оцінка часу для цієї задачі буде в 3-5 разів вищою, ніж для збору даних з Хабра. Незважаючи на те, що у випадку з Хабром ми використовуємо лобовий підхід з парсом HTML, а у випадку з ОК ми можемо в критичних місцях працювати з API.

Висновки

Як би з вас не вимагали оцінку термінів «на місці» (у нас сьогодні планування!) об'ємного модуля пайплана обробки даних, час виконання практично ніколи неможливо оцінити навіть якісно без аналізу параметрів завдання.

Якщо говорити трохи філософськи, то стратегії оцінки в agile непогано підходять для інженерних завдань, але із завданнями більш експериментальними і, в деякому сенсі, «творчими» і дослідницькими, тобто, менш передбачуваними, виникають труднощі, як у прикладах подібних тем що ми розібрали тут.

Звичайно, збір даних є просто яскравим ілюстративним прикладом — зазвичай це завдання здається неймовірно простим і технічно нескладним, і саме в деталях тут найчастіше таїться диявол. І саме на цьому завданні виходить показати весь спектр можливих варіантів того, що може піти не так і наскільки може затягтися робота.

Якщо пробігтися краєм ока за характеристиками завдання без додаткових експериментів, то Reddit та ОК виглядають схоже: є API, python wrapper, але по суті різниця величезна. Якщо судити з цих параметрів, то парс Хабра виглядає складніше, ніж ОК — а на практиці це навпаки і саме це можна з'ясувати, провівши прості експерименти з аналізу параметрів завдання.

На мій досвід найбільш ефективним підходом є приблизна оцінка часу, яка вам знадобиться на сам попередній аналіз і прості перші експерименти, читання документації — вони й дозволять вам дати точну оцінку для всієї роботи. У термінах популярної методології agile - я прошу завести мені тикет під "оцінку параметрів завдання", на основі якого я можу дати оцінку того, що можливо виконати в рамках "спринту" і дати більш точну оцінку за кожним завданням.

Тому найбільш ефективним, здається аргумент, який би показав «нетехнічному» фахівцеві, як сильно варіюватиметься час і ресурси в залежності від параметрів, які ще належить оцінити.

Що може піти не так із Data Science? Збір даних

Джерело: habr.com

Додати коментар або відгук