Автор оригинала: Adam McQuistan.
Использование машинного обучения для прогнозирования погоды: Часть 1
Часть 1: Сбор Данных О Погоде Под Землей
Это первая статья из многосерийной серии об использовании Python и машинного обучения для построения моделей прогнозирования температуры погоды на основе данных, собранных из Weather Underground. Серия будет состоять из трех различных статей, описывающих основные аспекты проекта машинного обучения. Темы, которые будут рассмотрены, следующие:
- Сбор и обработка данных (данная статья)
- Линейные регрессионные модели ( статья 2 )
- Нейросетевые модели ( статья 3 )
Данные, используемые в этой серии, будут собраны из бесплатного веб-сервиса API уровня Weather Underground. Я буду использовать библиотеку запросов для взаимодействия с API для получения данных о погоде с 2015 года для города Линкольн, штат Небраска. После сбора данные должны быть обработаны и объединены в формат, подходящий для анализа данных, а затем очищены.
Вторая статья будет посвящена анализу тенденций в данных с целью выбора подходящих признаков для построения модели Линейной регрессии с использованием библиотек statsmodels и scikit-learn Python. Я расскажу о важности понимания допущений, необходимых для использования модели линейной регрессии, и продемонстрирую, как оценить характеристики для построения надежной модели. Эта статья завершится обсуждением тестирования и валидации модели линейной регрессии.
Заключительная статья будет посвящена использованию нейронных сетей. Я буду сравнивать процесс построения нейросетевой модели, интерпретацию результатов и общую точность между Линейной регрессионной моделью, построенной в предыдущей статье, и нейросетевой моделью.
Знакомство с погодой под землей
Weather Underground – это компания, которая собирает и распространяет данные о различных погодных измерениях по всему миру. Компания предоставляет широкий спектр API, доступных как для коммерческого, так и для некоммерческого использования. В этой статье я опишу, как программно извлекать ежедневные данные о погоде из Weather Underground, используя их бесплатный уровень сервиса, доступный для некоммерческих целей.
Если вы хотите следовать вместе с учебником, вы захотите зарегистрироваться на их бесплатную учетную запись разработчика здесь. Эта учетная запись предоставляет API-ключ для доступа к веб-сервису со скоростью 10 запросов в минуту и в общей сложности до 500 запросов в день.
Weather Underground предоставляет множество различных API веб-сервисов для доступа к данным, но нас будет интересовать их API истории. API истории предоставляет сводку различных погодных измерений для города и штата в определенный день.
Формат запроса ресурса history API выглядит следующим образом:
http://api.wunderground.com/api/API_KEY/history_YYYYMMDD/q/STATE/CITY.json
API_KEY
: API_KEY, который Weather Underground предоставляет вместе с вашей учетной записьюYYYY MM DD
: Строка, представляющая целевую дату вашего запросаSTATE
: Двухбуквенная аббревиатура штата в Соединенных ШтатахCITY
: Название города, связанного с запрашиваемым вами штатом
Выполнение запросов к API
Чтобы сделать запросы к API Weather Underground history и обработать возвращенные данные, я буду использовать несколько стандартных библиотек, а также некоторые популярные сторонние библиотеки. Ниже приведена таблица библиотек, которые я буду использовать, и их описание. Инструкции по установке см. в приведенной документации.
Стандартная библиотека | дата и время | Используется для увеличения наших запросов на день |
Стандартная библиотека | время | Используется для задержки запросов на пребывание менее 10 в минуту |
Стандартная библиотека | коллекции | Используйте namedtuples для структурированного сбора данных |
Сторонняя Библиотека | панды | Используется для обработки, организации и очистки данных |
Сторонняя Библиотека | запросы | Используется для выполнения сетевых запросов к API |
Сторонняя Библиотека | matplotlib | Используется для графического анализа |
Давайте начнем с импорта этих библиотек:
from datetime import datetime, timedelta import time from collections import namedtuple import pandas as pd import requests import matplotlib.pyplot as plt
Теперь я определю пару констант, представляющих мой API_KEY
и BASE_URL
конечной точки API, которую я буду запрашивать. Примечание. Вам нужно будет зарегистрироваться в Weather Underground и получить свой собственный API_KEY
. К тому времени, как эта статья будет опубликована, я ее уже деактивирую.
BASE_URL
– это строка с двумя заполнителями, представленными фигурными скобками. Первый {}
будет заполнен ключом API_KEY
, а второй {}
будет заменен строковой датой. Оба значения будут интерполированы в строку BASE_URL
с помощью функции str.format (…) .
API_KEY = '7052ad35e3c73564' BASE_URL = "http://api.wunderground.com/api/{}/history_{}/q/NE/Lincoln.json"
Затем я инициализирую целевую дату до первого дня 2015 года. Затем я укажу функции, которые я хотел бы проанализировать из ответов, возвращенных из API. Функции-это просто ключи, присутствующие в части history - > dailysummary
ответа JSON. Эти функции используются для определения namedtuple
called Daily Summary
, который я буду использовать для организации данных отдельного запроса в списке DailySummary кортежей.
target_date = datetime(2016, 5, 16) features = ["date", "meantempm", "meandewptm", "meanpressurem", "maxhumidity", "minhumidity", "maxtempm", "mintempm", "maxdewptm", "mindewptm", "maxpressurem", "minpressurem", "precipm"] DailySummary = namedtuple("DailySummary", features)
В этом разделе я буду делать фактические запросы к API и собирать успешные ответы, используя функцию, определенную ниже. Эта функция принимает параметры url
, api_key
, target_date
и days
.
def extract_weather_data(url, api_key, target_date, days): records = [] for _ in range(days): request = BASE_URL.format(API_KEY, target_date.strftime('%Y%m%d')) response = requests.get(request) if response.status_code == 200: data = response.json()['history']['dailysummary'][0] records.append(DailySummary( date=target_date, meantempm=data['meantempm'], meandewptm=data['meandewptm'], meanpressurem=data['meanpressurem'], maxhumidity=data['maxhumidity'], minhumidity=data['minhumidity'], maxtempm=data['maxtempm'], mintempm=data['mintempm'], maxdewptm=data['maxdewptm'], mindewptm=data['mindewptm'], maxpressurem=data['maxpressurem'], minpressurem=data['minpressurem'], precipm=data['precipm'])) time.sleep(6) target_date += timedelta(days=1) return records
Я начинаю с определения списка под названием records, который будет содержать проанализированные данные как DailySummary
| namedtuple s. Цикл for определяется таким образом, что он повторяет цикл в течение количества дней, переданных функции.
Затем запрос форматируется с помощью функции str.format()
для интерполяции объекта API_KEY
и строки в формате target_date
. После форматирования переменная запроса передается методу get()
объекта requests
, а ответ присваивается переменной с именем response
.
С возвращенным ответом я хочу убедиться, что запрос был успешным, оценив, что код состояния HTTP равен 200. Если это успешно, то я разбираю тело ответа в JSON, используя метод json()
возвращаемого объекта ответа. Прикованный к одному и тому же вызову метода json()
я выбираю индексы структур history и daily summary, затем хватаю первый элемент в списке dailysummary
и назначаю его переменной с именем data
.
Теперь, когда у меня есть dict-подобная структура данных, на которую ссылается переменная data
, я могу выбрать нужные поля и создать новый экземпляр DailySummary
| namedtuple , который добавляется к списку
records .
Наконец, каждая итерация цикла завершается вызовом метода sleep
модуля time, чтобы приостановить выполнение цикла на шесть секунд, гарантируя, что в минуту будет сделано не более 10 запросов, удерживая нас в пределах Weather Underground.
Затем target_date
увеличивается на 1 день с помощью объекта timedelta
модуля datetime
, так что следующая итерация цикла извлекает ежедневную сводку за следующий день.
Первая партия запросов
Без дальнейших проволочек я начну первый набор запросов на максимально выделенный ежедневный запрос под бесплатным аккаунтом разработчика в размере 500. Затем я предлагаю вам снова выпить кофе (или другой любимый напиток) и заняться любимым телешоу, потому что эта функция займет не менее часа в зависимости от задержки сети. Таким образом, мы исчерпали наши запросы на день, и это только половина данных, с которыми мы будем работать.
Итак, приходите завтра, где мы закончим последнюю партию запросов, и тогда мы сможем начать работать над обработкой и форматированием данных способом, подходящим для нашего проекта машинного обучения.
records = extract_weather_data(BASE_URL, API_KEY, target_date, 500)
Завершение поиска данных
Итак, теперь, когда наступил новый день, у нас есть чистый лист и до 500 запросов, которые можно сделать к API Weather Underground history. Наша партия из 500 запросов, выданных вчера, началась 1 января 2015 года и закончилась 15 мая 2016 года (при условии, что у вас не было никаких неудачных запросов). Еще раз давайте запустим еще одну партию из 500 запросов, но на этот раз не оставляйте меня на целый день, потому что, как только этот последний кусок данных будет собран, мы начнем форматировать его в фрейм данных Pandas и извлекать потенциально полезные функции.
# if you closed our terminal or Jupyter Notebook, reinitialize your imports and # variables first and remember to set your target_date to datetime(2016, 5, 16) records += extract_weather_data(BASE_URL, API_KEY, target_date, 500)
Настройка фрейма данных наших панд
Теперь, когда у меня есть хороший и значительный список записей Daily Summary
именованных кортежей, я буду использовать его для создания Pandas DataFrame . Фрейм данных Pandas-это очень полезная структура данных для многих задач программирования, которые наиболее широко известны для очистки и обработки данных, используемых в проектах машинного обучения (или экспериментах).
Я буду использовать Панд.DataFrame(...)
конструктор класса для создания экземпляра объекта DataFrame. Параметры, передаваемые конструктору, представляют собой записи, представляющие данные для фрейма данных, список функций, который я также использовал для определения DailySummary
| namedtuple s, который будет указывать столбцы фрейма данных. Метод
set_index() привязан к экземпляру фрейма данных, чтобы указать дату в качестве индекса.
df = pd.DataFrame(records, columns=features).set_index('date')
Вывод функций
Проекты машинного обучения, также называемые экспериментами, часто имеют несколько характеристик, которые немного оксюморонны. Под этим я подразумеваю, что весьма полезно иметь предметные знания в исследуемой области, чтобы помочь в выборе значимых признаков для исследования в сочетании с вдумчивым предположением о вероятных закономерностях в данных.
Однако я также видел, как очень влиятельные объясняющие переменные и паттерны возникают из-за наличия почти наивных или, по крайней мере, очень открытых и минимальных предпосылок о данных. Обладание интуицией, основанной на знаниях, чтобы знать, где искать потенциально полезные функции и паттерны, а также способность непредвзято искать непредвиденные особенности-чрезвычайно важная часть успешного аналитического проекта.
В связи с этим мы выбрали довольно много функций при анализе возвращаемых ежедневных сводных данных для использования в нашем исследовании. Тем не менее, я полностью ожидаю, что многие из них окажутся либо неинформативными в предсказании погодных температур, либо неподходящими кандидатами в зависимости от типа используемой модели, но суть в том, что вы просто не знаете, пока тщательно не исследуете данные.
Сейчас я не могу сказать, что обладаю значительными знаниями в области метеорологии или моделей прогнозирования погоды, но я сделал минимальный поиск предыдущей работы по использованию машинного обучения для прогнозирования погодных температур. Как оказалось, существует довольно много исследовательских статей на эту тему, и в 2016 году Холмстром, Лю и Во описывают использование линейной регрессии именно для этого. В своей статье Машинное обучение Применительно к прогнозированию погоды они использовали данные о погоде за предыдущие два дня для следующих измерений.
- максимальная температура
- минимальная температура
- средняя влажность
- среднее атмосферное давление
Я буду расширять их список функций, используя те, которые перечислены ниже, и вместо того, чтобы использовать только предыдущие два дня, я вернусь на три дня назад.
- средняя температура
- средняя точка росы
- среднее давление
- максимальная влажность
- минимальная влажность
- максимальная точка росы
- минимальная точка росы
- максимальное давление
- минимальное давление
- осадок
Итак, следующий шаг-найти способ включить эти новые функции в качестве столбцов в наш фрейм данных. Для этого я сделаю меньшее подмножество текущего фрейма данных, чтобы облегчить работу с ним при разработке алгоритма создания этих функций. Я сделаю фрейм данных tmp
, состоящий всего из 10 записей и функций mean temp
и meandewptm
.
tmp = df[['meantempm', 'meandewptm']].head(10) tmp
2015-01-01 | -6 | -12 |
2015-01-02 | -6 | -9 |
2015-01-03 | -4 | -11 |
2015-01-04 | -14 | -19 |
2015-01-05 | -9 | -14 |
2015-01-06 | -10 | -15 |
2015-01-07 | -16 | -22 |
2015-01-08 | -7 | -12 |
2015-01-09 | -11 | -19 |
2015-01-10 | -6 | -12 |
Давайте разберем то, что мы надеемся достичь, а затем переведем это в код. Для каждого дня (строка) и для данного объекта (столбец) Я хотел бы найти значение для этой функции за N дней до этого. Для каждого значения N (1-3 в нашем случае) Я хочу сделать новый столбец для этой функции, представляющий измерение N-го предыдущего дня.
# 1 day prior N = 1 # target measurement of mean temperature feature = 'meantempm' # total number of rows rows = tmp.shape[0] # a list representing Nth prior measurements of feature # notice that the front of the list needs to be padded with N # None values to maintain the constistent rows length for each N nth_prior_measurements = [None]*N + [tmp[feature][i-N] for i in range(N, rows)] # make a new column name of feature_N and add to DataFrame col_name = "{}_{}".format(feature, N) tmp[col_name] = nth_prior_measurements tmp
2015-01-01 | -6 | -12 | Никто |
2015-01-02 | -6 | -9 | -6 |
2015-01-03 | -4 | -11 | -6 |
2015-01-04 | -14 | -19 | -4 |
2015-01-05 | -9 | -14 | -14 |
2015-01-06 | -10 | -15 | -9 |
2015-01-07 | -16 | -22 | -10 |
2015-01-08 | -7 | -12 | -16 |
2015-01-09 | -11 | -19 | -7 |
2015-01-10 | -6 | -12 | -11 |
Итак, похоже, что у нас есть основные шаги, необходимые для создания наших новых функций. Теперь я оберну эти шаги в многоразовую функцию и заставлю ее работать над созданием всех желаемых функций.
def derive_nth_day_feature(df, feature, N): rows = df.shape[0] nth_prior_measurements = [None]*N + [df[feature][i-N] for i in range(N, rows)] col_name = "{}_{}".format(feature, N) df[col_name] = nth_prior_measurements
Теперь я напишу цикл для перебора функций в списке функций, определенном ранее, и для каждой функции, которая не является “датой”, и для N дней с 1 по 3 мы вызовем нашу функцию, чтобы добавить производные функции, которые мы хотим оценить для прогнозирования температуры.
for feature in features: if feature != 'date': for N in range(1, 4): derive_nth_day_feature(df, feature, N)
И для хорошей меры я взгляну на колонки, чтобы убедиться, что они выглядят так, как ожидалось.
df.columns
Index(['meantempm', 'meandewptm', 'meanpressurem', 'maxhumidity', 'minhumidity', 'maxtempm', 'mintempm', 'maxdewptm', 'mindewptm', 'maxpressurem', 'minpressurem', 'precipm', 'meantempm_1', 'meantempm_2', 'meantempm_3', 'meandewptm_1', 'meandewptm_2', 'meandewptm_3', 'meanpressurem_1', 'meanpressurem_2', 'meanpressurem_3', 'maxhumidity_1', 'maxhumidity_2', 'maxhumidity_3', 'minhumidity_1', 'minhumidity_2', 'minhumidity_3', 'maxtempm_1', 'maxtempm_2', 'maxtempm_3', 'mintempm_1', 'mintempm_2', 'mintempm_3', 'maxdewptm_1', 'maxdewptm_2', 'maxdewptm_3', 'mindewptm_1', 'mindewptm_2', 'mindewptm_3', 'maxpressurem_1', 'maxpressurem_2', 'maxpressurem_3', 'minpressurem_1', 'minpressurem_2', 'minpressurem_3', 'precipm_1', 'precipm_2', 'precipm_3'], dtype='object')
Отлично! Похоже, у нас есть то, что нам нужно. Следующее, что я хочу сделать, – это оценить качество данных и очистить их там, где это необходимо.
Очистка Данных – Самая Важная Часть
Как говорится в названии раздела, самая важная часть аналитического проекта-убедиться, что вы используете качественные данные. Пресловутая поговорка “мусор входит, мусор выходит”, как всегда, уместна, когда речь заходит о машинном обучении. Однако очистка данных в аналитическом проекте-это не только одна из самых важных частей, но и самая трудоемкая и трудоемкая. Чтобы обеспечить качество данных для этого проекта, в этом разделе я постараюсь выявить ненужные данные, недостающие значения, согласованность типов данных и выбросы, а затем принять некоторые решения о том, как с ними обращаться, если они возникнут.
Первое, что я хочу сделать, это удалить все столбцы фрейма данных, которые меня не интересуют, чтобы уменьшить объем данных, с которыми я работаю. Цель проекта-предсказать будущую температуру на основе последних трех дней измерений погоды. Имея это в виду, мы хотим сохранить только минимальную, максимальную и среднюю температуру для каждого дня плюс все новые производные переменные, которые мы добавили в последних разделах.
# make list of original features without meantempm, mintempm, and maxtempm to_remove = [feature for feature in features if feature not in ['meantempm', 'mintempm', 'maxtempm']] # make a list of columns to keep to_keep = [col for col in df.columns if col not in to_remove] # select only the columns in to_keep and assign to df df = df[to_keep] df.columns
Index(['meantempm', 'maxtempm', 'mintempm', 'meantempm_1', 'meantempm_2', 'meantempm_3', 'meandewptm_1', 'meandewptm_2', 'meandewptm_3', 'meanpressurem_1', 'meanpressurem_2', 'meanpressurem_3', 'maxhumidity_1', 'maxhumidity_2', 'maxhumidity_3', 'minhumidity_1', 'minhumidity_2', 'minhumidity_3', 'maxtempm_1', 'maxtempm_2', 'maxtempm_3', 'mintempm_1', 'mintempm_2', 'mintempm_3', 'maxdewptm_1', 'maxdewptm_2', 'maxdewptm_3', 'mindewptm_1', 'mindewptm_2', 'mindewptm_3', 'maxpressurem_1', 'maxpressurem_2', 'maxpressurem_3', 'minpressurem_1', 'minpressurem_2', 'minpressurem_3', 'precipm_1', 'precipm_2', 'precipm_3'], dtype='object')
Следующее, что я хочу сделать, – это использовать некоторые встроенные функции Pandas, чтобы лучше понять данные и потенциально определить некоторые области, на которых можно сосредоточить свою энергию. Первая функция-это метод фрейма данных, называемый info()
который, большой сюрприз… предоставляет информацию о фрейме данных. Интерес представляет столбец “тип данных” выходных данных.
df.info()
DatetimeIndex: 1000 entries, 2015-01-01 to 2017-09-27 Data columns (total 39 columns): meantempm 1000 non-null object maxtempm 1000 non-null object mintempm 1000 non-null object meantempm_1 999 non-null object meantempm_2 998 non-null object meantempm_3 997 non-null object meandewptm_1 999 non-null object meandewptm_2 998 non-null object meandewptm_3 997 non-null object meanpressurem_1 999 non-null object meanpressurem_2 998 non-null object meanpressurem_3 997 non-null object maxhumidity_1 999 non-null object maxhumidity_2 998 non-null object maxhumidity_3 997 non-null object minhumidity_1 999 non-null object minhumidity_2 998 non-null object minhumidity_3 997 non-null object maxtempm_1 999 non-null object maxtempm_2 998 non-null object maxtempm_3 997 non-null object mintempm_1 999 non-null object mintempm_2 998 non-null object mintempm_3 997 non-null object maxdewptm_1 999 non-null object maxdewptm_2 998 non-null object maxdewptm_3 997 non-null object mindewptm_1 999 non-null object mindewptm_2 998 non-null object mindewptm_3 997 non-null object maxpressurem_1 999 non-null object maxpressurem_2 998 non-null object maxpressurem_3 997 non-null object minpressurem_1 999 non-null object minpressurem_2 998 non-null object minpressurem_3 997 non-null object precipm_1 999 non-null object precipm_2 998 non-null object precipm_3 997 non-null object dtypes: object(39) memory usage: 312.5+ KB
Обратите внимание, что тип данных каждого столбца имеет тип “объект”. Нам нужно преобразовать все эти столбцы признаков в поплавки для того типа численного анализа, который мы надеемся выполнить. Для этого я буду использовать метод apply()
DataFrame, чтобы применить метод Pandas to_numeric
ко всем значениям фрейма данных. Параметр error='coerce'
заполнит любые текстовые значения в NaNs . Обычно в данных из дикой природы можно найти текстовые значения, которые обычно исходят от сборщика данных, где данные отсутствуют или недействительны.
df = df.apply(pd.to_numeric, errors='coerce') df.info()
DatetimeIndex: 1000 entries, 2015-01-01 to 2017-09-27 Data columns (total 39 columns): meantempm 1000 non-null int64 maxtempm 1000 non-null int64 mintempm 1000 non-null int64 meantempm_1 999 non-null float64 meantempm_2 998 non-null float64 meantempm_3 997 non-null float64 meandewptm_1 999 non-null float64 meandewptm_2 998 non-null float64 meandewptm_3 997 non-null float64 meanpressurem_1 999 non-null float64 meanpressurem_2 998 non-null float64 meanpressurem_3 997 non-null float64 maxhumidity_1 999 non-null float64 maxhumidity_2 998 non-null float64 maxhumidity_3 997 non-null float64 minhumidity_1 999 non-null float64 minhumidity_2 998 non-null float64 minhumidity_3 997 non-null float64 maxtempm_1 999 non-null float64 maxtempm_2 998 non-null float64 maxtempm_3 997 non-null float64 mintempm_1 999 non-null float64 mintempm_2 998 non-null float64 mintempm_3 997 non-null float64 maxdewptm_1 999 non-null float64 maxdewptm_2 998 non-null float64 maxdewptm_3 997 non-null float64 mindewptm_1 999 non-null float64 mindewptm_2 998 non-null float64 mindewptm_3 997 non-null float64 maxpressurem_1 999 non-null float64 maxpressurem_2 998 non-null float64 maxpressurem_3 997 non-null float64 minpressurem_1 999 non-null float64 minpressurem_2 998 non-null float64 minpressurem_3 997 non-null float64 precipm_1 889 non-null float64 precipm_2 889 non-null float64 precipm_3 888 non-null float64 dtypes: float64(36), int64(3) memory usage: 312.5 KB
Теперь, когда все наши данные имеют нужный мне тип данных, я хотел бы взглянуть на некоторые сводные статистические данные функций и использовать эмпирическое статистическое правило для проверки наличия экстремальных выбросов. Метод фрейма данных describe()
создаст фрейм данных, содержащий количество, среднее значение, стандартное отклонение, мин, 25-й процентиль, 50-й процентиль (или медиану), 75-й процентиль и максимальное значение. Это может быть очень полезной информацией для оценки распределения данных признаков.
Я хотел бы добавить к этой информации еще один выходной столбец, указывающий на существование выбросов. Эмпирическое правило для определения экстремального выброса-это значение, которое меньше 3 межквартильных диапазонов ниже 25-го процентиля или 3 межквартильных диапазона выше 75-го процентиля. Межквартильный диапазон-это просто разница между 75-м процентилем и 25-м процентилем.
# Call describe on df and transpose it due to the large number of columns spread = df.describe().T # precalculate interquartile range for ease of use in next calculation IQR = spread['75%'] - spread['25%'] # create an outliers column which is either 3 IQRs below the first quartile or # 3 IQRs above the third quartile spread['outliers'] = (spread['min']<(spread['25%']-(3*IQR)))|(spread['max'] > (spread['75%']+3*IQR)) # just display the features containing extreme outliers spread.ix[spread.outliers,]
83.0 | 90.0 | 93.00 | 100.00 | 47.0 | 9.273053 | 88.107107 | 999.0 | True | max humidity_1 |
83.0 | 90.0 | 93.00 | 100.00 | 47.0 | 9.276407 | 88.102204 | 998.0 | True | max humidity_2 |
83.0 | 90.0 | 93.00 | 100.00 | 47.0 | 9.276775 | 88.093280 | 997.0 | True | max humidity_3 |
1015.0 | 1019.0 | 1024.00 | 1055.00 | 993.0 | 7.751874 | 1019.924925 | 999.0 | True | максимальное давление_1 |
1015.0 | 1019.0 | 1024.00 | 1055.00 | 993.0 | 7.755482 | 1019.922846 | 998.0 | True | максимальное давление_2 |
1015.0 | 1019.0 | 1024.00 | 1055.00 | 993.0 | 7.757805 | 1019.927783 | 997.0 | True | максимальное давление_3 |
1008.0 | 1012.0 | 1017.00 | 1035.00 | 956.0 | 7.882062 | 1012.329329 | 999.0 | True | минимальное давление_1 |
1008.0 | 1012.0 | 1017.00 | 1035.00 | 956.0 | 7.885560 | 1012.326653 | 998.0 | True | минимальное давление_2 |
1008.0 | 1012.0 | 1017.00 | 1035.00 | 956.0 | 7.889511 | 1012.326981 | 997.0 | True | минимальное давление_3 |
0.0 | 0.0 | 0.51 | 95.76 | 0.0 | 8.874345 | 2.908211 | 889.0 | True | precip_1 |
0.0 | 0.0 | 0.51 | 95.76 | 0.0 | 8.874345 | 2.908211 | 889.0 | True | precip_2 |
0.0 | 0.0 | 0.51 | 95.76 | 0.0 | 8.860608 | 2.888885 | 888.0 | True | осадок_3 |
Оценка потенциального воздействия выбросов – сложная часть любого аналитического проекта. С одной стороны, вы должны быть обеспокоены возможностью введения ложных артефактов данных, которые значительно повлияют на ваши модели. С другой стороны, выбросы могут быть чрезвычайно значимыми при прогнозировании результатов, возникающих в особых обстоятельствах. Мы обсудим каждый из этих выбросов, содержащих признаки, и посмотрим, сможем ли мы прийти к разумному выводу о том, как их лечить.
Первый набор функций, по-видимому, связан с максимальной влажностью. Глядя на данные, я могу сказать, что выброс для этой категории функций обусловлен, по-видимому, очень низким минимальным значением. Это действительно выглядит довольно низким значением, и я думаю, что хотел бы поближе взглянуть на него, предпочтительно в графическом виде. Для этого я буду использовать гистограмму.
%matplotlib inline plt.rcParams['figure.figsize'] = [14, 8] df.maxhumidity_1.hist() plt.title('Distribution of maxhumidity_1') plt.xlabel('maxhumidity_1') plt.show()
Глядя на гистограмму значений max humidity
, данные демонстрируют довольно большой отрицательный перекос. Я хочу иметь это в виду при выборе моделей прогнозирования и оценке силы воздействия максимальной влажности. Многие из лежащих в основе статистических методов предполагают, что данные распределены нормально. На данный момент я думаю, что оставлю их в покое, но будет хорошо иметь это в виду и иметь определенную долю скептицизма по этому поводу.
Далее я рассмотрю распределение характеристик минимального давления.
df.minpressurem_1.hist() plt.title('Distribution of minpressurem_1') plt.xlabel('minpressurem_1') plt.show()
Этот сюжет демонстрирует еще одну интересную особенность. Исходя из этого графика, данные являются мультимодальными , что заставляет меня полагать, что в этих данных наблюдаются два очень разных набора экологических обстоятельств. Я не решаюсь удалить эти значения, так как знаю, что колебания температуры в этой области страны могут быть довольно экстремальными, особенно между сезонами года. Я беспокоюсь, что удаление этих низких значений может иметь некоторую объяснительную полезность, но в то же время я снова буду скептически относиться к этому.
Последняя категория объектов, содержащих выбросы, – осадки, – довольно легко поддается пониманию. Поскольку сухие дни (то есть без осадков) встречаются гораздо чаще, разумно видеть здесь выбросы. Для меня это не повод удалять эти функции.
Последняя проблема качества данных, которую необходимо решить, – это проблема пропущенных значений. Из-за того, как я построил фрейм данных, пропущенные значения представлены NANS. Вы, вероятно, помните, что я намеренно ввел недостающие значения для первых трех дней данных, собранных путем получения признаков, представляющих предыдущие три дня измерений. Только на третий день мы можем начать выводить эти функции, поэтому ясно, что я хочу исключить эти первые три дня из набора данных.
Посмотрите еще раз на выходные данные с последнего раза, когда я выпустил метод info
. Существует столбец выходных данных, в котором перечислены ненулевые значения для каждого столбца объектов. Глядя на эту информацию, вы можете видеть, что по большей части функции содержат относительно немного отсутствующих (null/NaN) значений, в основном только те, которые я ввел. Однако в колонках осадков, по-видимому, отсутствует значительная часть их данных.
Отсутствие данных создает проблему, поскольку большинство методов машинного обучения требуют полных наборов данных, лишенных каких-либо отсутствующих данных. Помимо того, что многие методы машинного обучения требуют полных данных, если бы я удалил все строки только потому, что функция осадков содержит недостающие данные, то я бы выбросил много других полезных измерений функций.
Как я вижу у меня есть несколько вариантов решения этой проблемы недостающих данных:
- Я могу просто удалить строки, содержащие пропущенные значения, но, как я уже упоминал ранее, выбрасывание такого количества данных удаляет из них большую ценность
- Я могу заполнить недостающие значения интерполированным значением, которое является разумной оценкой истинных значений.
Поскольку я предпочел бы сохранить как можно больше данных, где существует минимальный риск введения ошибочных значений, я собираюсь заполнить недостающие значения осадков наиболее распространенным значением нуля. Я считаю, что это разумное решение, потому что подавляющее большинство значений в измерениях осадков равны нулю.
# iterate over the precip columns for precip_col in ['precipm_1', 'precipm_2', 'precipm_3']: # create a boolean array of values representing nans missing_vals = pd.isnull(df[precip_col]) df[precip_col][missing_vals] = 0
Теперь, когда я заполнил все недостающие значения, которые я могу, будучи осторожным, чтобы не негативно повлиять на качество, я был бы удобен просто удалить оставшиеся записи, содержащие недостающие значения из набора данных. Довольно легко удалить строки из фрейма данных, содержащего NAN. Все, что мне нужно сделать, это вызвать метод dropna ()
, и Панды сделают всю работу за меня.
df = df.dropna()
Ресурсы
Хотите изучить инструменты, машинное обучение и анализ данных, используемые в этом учебнике? Вот несколько замечательных ресурсов, которые помогут вам начать работу:
Вывод
В этой статье я описал процесс сбора, очистки и обработки достаточно большого набора данных, который будет использоваться для будущих статей по проекту машинного обучения, в котором мы предсказываем будущие погодные температуры.
Хотя это, вероятно, будет самая сухая из статей, задерживающих этот проект машинного обучения, я попытался подчеркнуть важность сбора качественных данных, подходящих для ценного эксперимента по машинному обучению.
Спасибо за чтение, и я надеюсь, что вы с нетерпением ждете предстоящих статей по этому проекту.
Ищете части 2 и 3 этой серии? Вот так:
- Использование машинного обучения для прогнозирования погоды: Часть 2
- Использование машинного обучения для прогнозирования погоды: Часть 3