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

Open in Colab

Copyright Allen B. Downey

License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International

telegram

Этот пример демонстрирует важные шаги практически в любом проекте по анализу данных:

  1. Определение данных, которые помогут ответить на вопрос.

  2. Получение данных и их загрузка в Python.

  3. Проверка данных и устранение ошибок.

  4. Выбор соответствующих подмножеств из данных.

  5. Использование гистограмм для визуализации распределения значений.

  6. Использование сводной статистики для описания данных таким образом, чтобы наилучшим образом ответить на вопрос.

  7. Рассмотрение возможных источников ошибок и ограничений в наших выводах.

Начнем с получения данных.

Чтение данных

Мы будем использовать данные Национального исследования роста семьи (NSFG).

Это исследование, проведенное отделом Статистики здравоохранения Центра по контролю и профилактике заболеваний, чтобы понять тенденции, связанные с фертильностью, структурой семьи и демографией в Соединенных Штатах.

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

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

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

Респонденты NSFG предоставляют общую информацию о себе, которая хранится в файле респондентов, и информацию о каждой беременности, которая хранится в файле о беременности.

Мы будем работать с файлом беременности, который содержит по одной строке для каждой беременности и 248 переменных. Каждая переменная представляет собой ответы на вопрос анкеты NSFG.

Данные хранятся в формате фиксированной ширины (fixed-width format), это означает, что каждая строка имеет одинаковую длину и каждая переменная охватывает фиксированный диапазон столбцов.

В дополнение к файлу данных (2015_2017_FemPregData.dat) нам также понадобится словарь данных (2015_2017_FemPregSetup.dct), который включает имена переменных и указывает диапазон столбцов, в которых появляется каждая переменная.

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

Pandas может читать данные в наиболее распространенных форматах, включая CSV, Excel и формате фиксированной ширины, но не может читать словарь данных, который находится в формате Stata.

Для этого мы будем использовать библиотеку Python под названием parse_stata_dict.

Следующая ячейка при необходимости устанавливает parse_stata_dict.

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

В результате получается объект, содержащий атрибуты

Каждый кортеж в colspecs определяет первый и последний столбцы, в которых появляется переменная.

Эти значения - именно те аргументы, которые нам нужны для использования read_fwf, функции Pandas, считывающей файл в формате фиксированной ширины.

Результатом вызова read_hdf() стал DataFrame, который является основным типом Pandas для хранения данных.

В DataFrame есть метод head(), который показывает первые 5 строк:

Первый столбец - это CASEID, который представляет собой уникальный идентификатор для каждого респондента.

Первые три строки содержат один и тот же CASEID, поэтому респондентка сообщила информацию о трех беременностях.

Второй столбец - это PREGORDR, который указывает порядок беременностей для каждой респондентки, начиная с 1.

Мы узнаем больше о других переменных по мере исследования.

В дополнение к таким методам, как head, nsfg имеет несколько атрибутов, которые представляют собой переменные, связанные с определенным типом.

Например, у nsfg есть атрибут под названием shape, который представляет собой количество строк и столбцов:

В этом наборе данных 9553 строки, по одной для каждой беременности, и 248 столбцов, по одной для каждой переменной.

nsfg также имеет атрибут под названием columns, который содержит имена столбцов:

Имена столбцов хранятся в Index, который является типом Pandas, похожим на список.

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

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

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

Если вы выполните поиск в этом документе по запросу "weigh at birth", вы должны найти эти переменные, связанные с массой тела при рождении.

Подобные переменные существуют для 2-го или 3-го ребенка, в случае двойни или тройни. Сейчас мы сосредоточимся на первом ребенке от каждой беременности и вернемся к вопросу о многоплодных родах.

Series

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

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

head показывает первые пять значений в серии, имя серии и тип данных:

Одно из значений - NaN, что означает "Not a Number".

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

Упражнение: Переменная BIRTHWGT_OZ1 содержит часть веса при рождении в унциях.

Выберите столбец 'BIRTHWGT_OZ1' из фрейма данных nsfg и присвойте его новой переменной с именем ounces. Затем отобразите первые пять элементов ounces.

Упражнение: Вы можете найти документацию по типам данных Pandas по адресам:

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

Проверка

На этом этапе мы определили столбцы, которые нам нужны для ответа на вопрос, и присвоили их переменным с именами pounds и ounces.

Прежде чем что-либо делать с этими данными, мы должны их проверить (validate). Одна часть проверки - это подтверждение того, что мы правильно интерпретируем данные.

Мы можем использовать метод value_counts, чтобы увидеть, какие значения появляются в pounds и сколько раз появляется каждое значение.

По умолчанию результаты сортируются сначала по наиболее частому значению, но вместо этого мы можем использовать sort_index, чтобы отсортировать их:

Как и следовало ожидать, наиболее частыми значениями являются 6-8 фунтов, но есть несколько очень легких детей, несколько очень тяжелых детей и два специальных значения, 98 и 99. Согласно кодовой книге, эти значения указывают на то, что респондент отказался отвечать на вопрос (98) или не знал (99).

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

Значение Метка Итого
. НЕПРИМЕНИМО (INAPPLICABLE) 2863
0-5 ДО 6 ФУНТОВ 901
6 6 ФУНТОВ 1644
7 7 ФУНТОВ 2268
8 8 ФУНТОВ 1287
9-95 9 ФУНТОВ ИЛИ БОЛЬШЕ 499
98 Отказано (Refused) 2
99 Не знаю 89
Итого 9553

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

Упражнение: В фрейме данных nsfg столбец 'OUTCOME' кодирует исход каждой беременности, как показано ниже:

Значение Смысл
1 Рождение (Live birth)
2 Искусственный аборт (Induced abortion)
3 Мертворождение (Stillbirth)
4 Выкидыш (Miscarriage)
5 Внематочная беременность (Ectopic pregnancy)
6 Текущая беременность (Current pregnancy)

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

Сводные статистические данные

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

Вот результаты для pounds.

count - это количество значений, не считая NaN. Для этой переменной есть 6690 значений, отличных от NaN.

mean и std - это среднее значение и стандартное отклонение.

min и max - это минимальное и максимальное значения, а между ними - 25, 50 и 75 процентили. 50-й процентиль - это медиана.

Среднее значение составляет около 8.05, но это мало что значит, потому что оно включает специальные значения 98 и 99. Прежде чем мы действительно сможем вычислить среднее значение, мы должны заменить эти значения на NaN, чтобы идентифицировать их как отсутствующие данные.

Метод replace() делает то, что мы хотим:

replace принимает список значений, которые мы хотим заменить, и значение, которым мы хотим их заменить.

np.nan означает, что мы получаем специальное значение NaN из библиотеки NumPy, которая импортируется как np.

Результатом replace() является новая серия, которую я присваиваю переменной pounds_clean.

Если мы снова запустим describe, мы увидим, что count включает только допустимые значения.

Средний вес новой серии составляет около 6,7 фунтов. Помните, что среднее значение оригинальной серии было более 8 фунтов. Это имеет большое значение, когда вы убираете несколько 99-фунтовых младенцев!

Упражнение: Используйте describe, чтобы суммировать ounces.

Затем используйте replace, чтобы заменить специальные значения 98 и 99 на NaN, и присвойте результат переменной ounces_clean.

Снова запустите describe. Насколько эта очистка влияет на результат?

Арифметика с сериями

Теперь мы хотим объединить pounds и ounces в одну серию, содержащую общий вес при рождении. Арифметические операторы работают с объектами Series; так, например, чтобы преобразовать pounds в унции, мы могли бы написать

pounds * 16

Затем мы могли бы добавить ounces вот так

pounds * 16 + ounces

Упражнение: Используйте pounds_clean и ounces_clean, чтобы вычислить общий вес при рождении, выраженный в килограммах (это примерно 2,2 фунта на килограмм). Какой средний вес при рождении в килограммах?

Упражнение: Для каждой беременности в наборе данных NSFG переменная 'AGECON' кодирует возраст респондента на момент зачатия, а 'AGEPREG' - возраст респондента в конце беременности.

Обе переменные записываются как целые числа с двумя неявными десятичными знаками, поэтому значение 2575 означает, что возраст респондента был 25.75.

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

Гистограммы

Вернемся к первоначальному вопросу: каков средний вес новорожденных в США? В качестве ответа мы могли бы взять результаты из предыдущего раздела и вычислить среднее значение:

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

Распределение - это набор возможных значений и их частот. Одним из способов визуализации распределения является гистограмма, которая показывает значения по оси x и их частоты по оси y.

Series предоставляет метод hist, который строит гистограммы. И мы можем использовать Matplotlib для маркировки осей.

Ключевой аргумент bins, указывает hist разделить диапазон весов на 30 интервалов, называемых bins, и подсчитать, сколько значений попадает в каждую ячейку.

По оси x отложена масса тела при рождении в фунтах; ось y - это количество рождений в каждой ячейке (bin).

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

В этом есть смысл, потому что в распределение включены некоторые недоношенные дети.

Упражнение: hist принимает ключевые аргументы, которые определяют тип и внешний вид гистограммы.

Найдите документацию по hist и посмотрите, сможете ли вы выяснить, как построить гистограмму в виде незаполненной линии (unfilled line).

Упражнение: Как мы видели в предыдущем упражнении, набор данных NSFG включает столбец под названием AGECON, в котором записывается возраст на момент зачатия для каждой беременности.

Логическая серия (boolean series)

Мы видели, что распределение веса при рождении смещено влево; то есть легких младенцев больше, чем тяжелых, и они дальше от средних. Это потому, что недоношенные дети, как правило, легче.

Наиболее частая продолжительность беременности составляет 39 недель, что является "доношенной"; "недоношенность" обычно определяется как срок менее 37 недель.

Чтобы узнать, какие дети недоношены, мы можем использовать PRGLNGTH, который содержит продолжительность беременности в неделях и вычисляет ее как 37.

Когда вы сравниваете Series со значением, результатом является логическая серия; то есть каждый элемент является логическим значением True или False. В этом случае для каждого недоношенного ребенка - это True, в противном случае - False. Мы можем использовать head, чтобы увидеть первые 5 элементов.

Если вы вычисляете сумму логической серии, она обрабатывает True как 1 и False как 0, поэтому сумма представляет собой количество значений True, то есть количество недоношенных детей, около 3700.

Если вы вычисляете среднее значение логической серии, вы получаете долю (fraction) от значений True. В данном случае это около 0,38; то есть около 38% беременностей длится менее 37 недель.

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

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

Упражнение: Какая часть всех рождений является недоношенными?

Другие распространенные логические операторы:

Логические операторы обрабатывают NaN так же, как False. Таким образом, вы должны быть осторожны при использовании оператора НЕ с серией, содержащей значения NaN.

Например, ~preterm будут включать не только доношенные беременности, но и беременности с неизвестной продолжительностью.

Упражнение: Какая доля всех беременностей является доношенной, то есть 37 недель или более? Какая доля всех рожденных является доношенными?

Фильтрация

Мы можем использовать логическую серию в качестве фильтра; то есть мы можем выбрать только те строки, которые удовлетворяют условию или удовлетворяют некоторому критерию.

Например, мы можем использовать preterm и оператор скобки для выбора значений из birth_weight, так что preterm_weight получает вес при рождении для недоношенных детей.

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

Для выбора веса при рождении доношенных детей используйте:

Как и ожидалось, доношенные дети в среднем тяжелее недоношенных. Чтобы быть более точным, мы также можем ограничить результаты рождением, например:

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

Упражнение: Давайте посмотрим, есть ли разница в весе между одноплодными и многоплодными родами (двойняшки, тройни и т. д.).

Переменная NBRNALIV представляет количество детей, рожденных живыми от одной беременности.

Используйте nbrnaliv и live, чтобы создать логический ряд под названием multiple, который является верным для множественных рождений.

Какая доля всех рождений приходится на многоплодие?

Упражнение: Создайте логический ряд под названием single, который подходит для одноплодных рождений.

Какая часть всех одноплодных родов является преждевременными?

Какая часть всех родов являются преждевременными?

Упражнение: Каков средний вес при рождении живыми (live), одноплодными (single) и доношенными (full-term births)?

Средневзвешенное значение

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

NSFG не совсем репрезентативен для населения США. По замыслу, некоторые группы чаще появляются в выборке, чем другие; то есть они передискретизированы (oversampled). Передискретизация помогает гарантировать, что у вас будет достаточно людей в каждой подгруппе для получения надежной статистики, но это немного усложняет анализ данных.

Каждая беременность в наборе данных имеет вес выборки (sampling weight), который указывает, сколько беременностей она представляет. В nsfg вес выборки хранится в столбце с именем wgt2015_2017. Вот как это выглядит.

Среднее значение (50-й процентиль) в этом столбце составляет около 7292, что означает, что беременность с таким весом представляет собой 7292 беременностей в популяции.

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

Чтобы учесть эти веса, мы можем вычислить среднее арифметическое взвешенное (weighted mean).

Вот шаги:

  1. Умножьте вес при рождении для каждой беременности на веса выборки и сложите произведения.

  2. Сложите выборочные веса.

  3. Разделите первую сумму на вторую.

Чтобы сделать это правильно, мы должны быть осторожны с пропущенными (missing) данными. Чтобы помочь с этим, мы будем использовать два метода Series: isna и notna.

isna возвращает логическое значение Series, равное True, где соответствующее значение - NaN.

В birth_weight 3013 пропущенных значений (в основном для беременностей, которые не закончились рождением).

notna возвращает логическое значение Series, которое имеет значение True, где соответствующее значение не NaN.

Мы можем комбинировать valid с другими вычисленными нами логическими Series, чтобы идентифицировать одноплодные (single), доношенные рождения с допустимым весом при рождении.

Упражнение: Используйте selected, birth_weight и sampling_weight, чтобы вычислить средневзвешенное значение веса при рождении для живых (live), одноплодных (single) и доношенных детей (full term).

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

Резюме

В этом Блокноте задается, казалось бы, простой вопрос: каков средний вес новорожденных в Соединенных Штатах?

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

Чтобы исследовать данные, мы использовали value_counts, hist, describe и другие методы Pandas. А для выбора релевантных данных мы использовали логическое значение Series.

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

И нам нужно было подумать о процессе семплирования (sampling process). По замыслу респонденты NSFG не являются репрезентативными для населения США, но мы можем использовать веса выборки, чтобы скорректировать этот эффект.

Даже простой вопрос может стать сложным проектом в области науки о данных.

telegram