Автор оригинала: Jose A Dianes.
Введение и получение обновления данных
Вот мы снова здесь, с новым эпизодом в нашей серии о том, как заниматься наукой о данных с помощью двух самых популярных платформ с открытым исходным кодом, которые вы можете использовать для работы в настоящее время. В этом случае мы рассмотрим важнейший этап процесса анализа данных, а именно Исследовательский анализ данных .
Исследовательский анализ данных проводится после сбора и очистки данных, а также перед любым моделированием и визуализацией/представлением результатов. Однако это часть итеративного процесса. После выполнения некоторых EDA мы можем попытаться построить некоторые модели или представить некоторые визуализации. В то же время, основываясь на результатах позже, мы можем выполнить еще несколько тестов и так далее. Все дело в быстром поиске подсказок, а не в деталях или эстетике. Среди основных целей этого типа анализа, конечно, знакомство с нашими данными, их тенденциями и качеством, а также проверка или даже начало формулирования нашей гипотезы.
И с учетом этой идеи мы объясним, как использовать описательную статистику и базовое построение графиков вместе с фреймами данных, чтобы ответить на некоторые вопросы и направить наш дальнейший анализ данных.
Весь исходный код для различных частей этой серии учебных пособий и приложений можно проверить на GitHub . Не стесняйтесь участвовать и делиться с нами своими успехами!
Получение данных
Мы продолжим использовать те же наборы данных, которые мы уже загрузили в партию, вводящую фреймы данных. Таким образом, вы можете либо продолжить с того места, где вы остановились в этом учебнике, либо повторно запустить раздел, который получает и подготавливает данные .
Вопросы, на которые мы хотим ответить
В любом процессе анализа данных есть один или несколько вопросов, на которые мы хотим ответить. Это самый основной и важный шаг во всем процессе, чтобы определить эти вопросы. Поскольку мы собираемся провести некоторый исследовательский анализ данных в нашем наборе данных TB, на эти вопросы мы хотим ответить:
- Какие страны имеют самую высокую и инфекционную заболеваемость туберкулезом?
- Какова общая мировая тенденция в период с 1990 по 2007 год?
- Какие страны не следуют этой тенденции?
- Какие еще факты о болезни мы знаем, которые мы можем проверить с помощью наших данных?
Описательная статистика
Питон
Метод описательной статистики основных данных для панды.DataFrame
is description()
. Это эквивалентно R data.frame
function summary()
.
df_summary = existing_df.describe() df_summary
… | 18.000000 | 18.000000 | 18.000000 | 18.000000 | 18.000000 | 18.000000 | 18.000000 | 18.000000 | 18.000000 | считать | 18.000000 | 18.000000 | 18.000000 | 18.000000 | 18.000000 | 18.000000 | 18.000000 | 18.000000 | 18.000000 | 18.000000 | 18.000000 |
… | 194.333333 | 413.444444 | 535.277778 | 36.944444 | 47.388889 | 25.277778 | 74.944444 | 28.055556 | 186.000000 | означать | 35.611111 | 282.666667 | 512.833333 | 61.222222 | 40.888889 | 128.888889 | 353.333333 | 12.277778 | 126.222222 | 43.388889 | 10.833333 |
… | 52.158131 | 97.751318 | 91.975576 | 6.915220 | 4.487091 | 7.274497 | 16.129885 | 3.717561 | 62.027508 | станд | 1.243283 | 57.322616 | 113.411925 | 20.232634 | 2.422660 | 15.911109 | 64.708396 | 9.886447 | 86.784083 | 8.332353 | 2.812786 |
… | 130.000000 | 281.000000 | 387.000000 | 22.000000 | 42.000000 | 17.000000 | 49.000000 | 23.000000 | 102.000000 | минута | 34.000000 | 220.000000 | 392.000000 | 35.000000 | 38.000000 | 102.000000 | 238.000000 | 0.000000 | 13.000000 | 31.000000 | 7.000000 |
… | 146.750000 | 321.250000 | 459.000000 | 32.000000 | 44.000000 | 19.250000 | 62.000000 | 25.000000 | 128.750000 | 25% | 35.000000 | 234.250000 | 420.750000 | 41.250000 | 39.000000 | 116.500000 | 305.000000 | 6.000000 | 63.250000 | 36.250000 | 9.000000 |
… | 184.500000 | 399.000000 | 521.500000 | 40.500000 | 45.500000 | 22.500000 | 77.000000 | 27.500000 | 185.000000 | 50% | 35.000000 | 257.000000 | 466.000000 | 60.500000 | 41.000000 | 131.500000 | 373.500000 | 9.000000 | 106.000000 | 43.000000 | 10.000000 |
… | 248.500000 | 512.000000 | 620.000000 | 42.000000 | 50.750000 | 31.500000 | 85.750000 | 30.750000 | 240.000000 | 75% | 36.000000 | 349.000000 | 616.750000 | 77.000000 | 42.000000 | 143.000000 | 404.500000 | 16.250000 | 165.750000 | 51.500000 | 12.750000 |
… | 265.000000 | 530.000000 | 680.000000 | 44.000000 | 56.000000 | 39.000000 | 99.000000 | 35.000000 | 278.000000 | максимум | 38.000000 | 365.000000 | 714.000000 | 96.000000 | 46.000000 | 152.000000 | 436.000000 | 42.000000 | 352.000000 | 55.000000 | 16.000000 |
8 строк × 207 столбцов
Там очень много информации. Мы можем получить доступ к отдельным резюме следующим образом.
df_summary[['Spain','United Kingdom']]
18.000000 | считать | 18.000000 |
30.666667 | означать | 9.611111 |
6.677442 | станд | 0.916444 |
23.000000 | минута | 9.000000 |
25.250000 | 25% | 9.000000 |
29.000000 | 50% | 9.000000 |
34.750000 | 75% | 10.000000 |
44.000000 | максимум | 12.000000 |
В Pandas существует множество методов описательной статистики (проверьте документацию ). Некоторые из них уже включены в наш сводный объект, но их гораздо больше. В следующих уроках мы будем хорошо использовать их, чтобы лучше понять наши данные.
Например, мы можем получить процентное изменение за эти годы числа случаев заболевания туберкулезом в Испании.
tb_pct_change_spain = existing_df.Spain.pct_change() tb_pct_change_spain
year 1990 NaN 1991 -0.045455 1992 -0.047619 1993 -0.075000 1994 -0.054054 1995 -0.028571 1996 -0.029412 1997 -0.090909 1998 0.000000 1999 -0.066667 2000 -0.035714 2001 -0.037037 2002 0.000000 2003 -0.038462 2004 -0.040000 2005 0.000000 2006 0.000000 2007 -0.041667 Name: Spain, dtype: float64
И оттуда получите максимальное значение.
tb_pct_change_spain.max()
0.0
И сделайте то же самое для Соединенного Королевства.
existing_df['United Kingdom'].pct_change().max()
0.11111111111111116
Если мы хотим знать значение индекса (год), мы используем arg max
(callex idmax
в более поздних версиях Pandas) следующим образом.
existing_df['Spain'].pct_change().argmax()
'1998'
existing_df['United Kingdom'].pct_change().argmax()
'1992'
То есть 1998 и 1992 годы были худшими годами в Испании и Великобритании соответственно в отношении увеличения числа случаев инфекционного туберкулеза.
R
Основным методом описательной статистики в R является, как мы уже говорили, функция summary()
.
existing_summary <- summary(existing_df) str(existing_summary)
## 'table' chr [1:6, 1:207] "Min. :238.0 " "1st Qu.:305.0 " ... ## - attr(*, "dimnames")=List of 2 ## ..$ : chr [1:6] "" "" "" "" ... ## ..$ : chr [1:207] " Afghanistan" " Albania" " Algeria" "American Samoa" ...
Он возвращает объект таблицы, в котором у нас есть сводная статистика для каждого из столбцов в фрейме данных. Объект таблицы хорош для визуализации данных, но не так хорош для доступа и индексирования его как фрейма данных. В основном мы используем позиционную индексацию для доступа к ней в виде матрицы. Таким образом, если нам нужна первая колонка, соответствующая Афганистану, мы сделаем это:
existing_summary[,1]
## ## "Min. :238.0 " "1st Qu.:305.0 " "Median :373.5 " "Mean :353.3 " ## ## "3rd Qu.:404.5 " "Max. :436.0 "
Трюк, который мы можем использовать для доступа по имени столбца, заключается в использовании имен столбцов в исходном фрейме данных вместе с which()
. Мы также можем построить новый фрейм данных с результатами.
data.frame( Spain=existing_summary[,which(colnames(existing_df)=='Spain')], UK=existing_summary[,which(colnames(existing_df)=='United Kingdom')])
## Spain UK ## 1 Min. :23.00 Min. : 9.000 ## 2 1st Qu.:25.25 1st Qu.: 9.000 ## 3 Median :29.00 Median : 9.000 ## 4 Mean :30.67 Mean : 9.611 ## 5 3rd Qu.:34.75 3rd Qu.:10.000 ## 6 Max. :44.00 Max. :12.000
Будучи R функциональным языком, мы можем применять такие функции, как sum
, mean
, sd
и т. Д. к векторам. Помните, что фрейм данных-это список векторов (т. Е. Каждый столбец-это вектор значений), поэтому мы можем легко использовать эти функции со столбцами. Наконец, мы можем объединить эти функции с lapply
или sapply
и применить их к нескольким столбцам в фрейме данных.
Однако в R существует семейство функций, которые могут быть применены к столбцам или строкам для непосредственного получения средних и сумм. Они более эффективны, чем использование функций apply, а также позволяют применять их не только по столбцам, но и по строкам. Например, если вы введете ” столбцы?”, на странице справки будут описаны все из них.
Допустим, мы хотим получить среднее количество существующих случаев в год. Нам нужен один вызов функции.
rowMeans(existing_df)
## X1990 X1991 X1992 X1993 X1994 X1995 X1996 X1997 ## 196.9662 196.4686 192.8116 191.1739 188.7246 187.9420 178.8986 180.9758 ## X1998 X1999 X2000 X2001 X2002 X2003 X2004 X2005 ## 178.1208 180.4734 177.5217 177.7971 179.5169 176.4058 173.9227 171.1836 ## X2006 X2007 ## 169.0193 167.2560
Построение
В этом разделе мы рассмотрим основные функции построения графиков в Python/Pandas и R. Однако существуют более мощные альтернативы, такие как ggplot2 , которые, хотя и изначально созданы для R, имеют свою собственную реализацию для Python от Yhat парней.
Питон
Фреймы данных Pandas реализуют до трех методов построения графиков из коробки (проверьте documentation ). Первый-это базовый линейный график для каждой серии, которую мы включаем в индексацию. Первая строка может потребоваться при построении графика при использовании IPython notebook.
%matplotlib inline existing_df[['United Kingdom', 'Spain', 'Colombia']].plot()
Или мы можем использовать прямоугольники для получения обобщенного представления данного ряда следующим образом.
existing_df[['United Kingdom', 'Spain', 'Colombia']].boxplot()
Существует также метод histogram ()
, но мы не можем использовать его с этим типом данных прямо сейчас.
R
Базовое построение в R не очень сложное по сравнению с ggplot2 , но все же мощное и удобное, поскольку многие типы данных реализовали пользовательские методы plot ()
, которые позволяют нам строить их с помощью одного вызова метода. Однако это не всегда так, и чаще всего нам нужно будет передать правильный набор элементов нашим основным функциям построения графиков.
Давайте начнем с базовой линейной диаграммы, как мы делали с Python/Pandas.
uk_series <- existing_df[,c("United Kingdom")] spain_series <- existing_df[,c("Spain")] colombia_series <- existing_df[,c("Colombia")]
xrange <- 1990:2007 plot(xrange, uk_series, type='l', xlab="Year", ylab="Existing cases per 100K", col = "blue", ylim=c(0,100)) lines(xrange, spain_series, col = "darkgreen") lines(xrange, colombia_series, col = "red") legend(x=2003, y=100, lty=1, col=c("blue","darkgreen","red"), legend=c("UK","Spain","Colombia"))
Вы можете сравнить, как легко было построить три серии в Pandas, и как выполнение того же самого с базовым построением в R становится более подробным. По крайней мере, нам нужно три вызова функций, для сюжета и линии, а затем у нас есть легенда и т. Д. Базовый график в R действительно предназначен для создания быстрых и грязных диаграмм.
Давайте теперь воспользуемся прямоугольными графиками.
boxplot(uk_series, spain_series, colombia_series, names=c("UK","Spain","Colombia"), xlab="Year", ylab="Existing cases per 100K")
Этот был намного короче, и нам даже не нужны цвета или легенда.
Ответы на Вопросы
Давайте теперь начнем с настоящего веселья. Как только мы узнаем наши инструменты (из предыдущего руководства о фреймах данных и этого), давайте использовать их, чтобы ответить на некоторые вопросы о заболеваемости и распространенности инфекционного туберкулеза в мире.
Вопрос : Мы хотим знать, в какой стране ежегодно регистрируется наибольшее число существующих и новых случаев заболевания туберкулезом.
Питон
Если нам нужны только верхние, мы можем использовать apply
и argmax
. Помните, что по умолчанию apply
работает со столбцами (в нашем случае со странами), и мы хотим применять его к каждому году. Поэтому нам нужно транспонировать фрейм данных перед его использованием, или мы можем передать аргумент axis=1
.
existing_df.apply(pd.Series.argmax, axis=1)
year 1990 Djibouti 1991 Djibouti 1992 Djibouti 1993 Djibouti 1994 Djibouti 1995 Djibouti 1996 Kiribati 1997 Kiribati 1998 Cambodia 1999 Korea, Dem. Rep. 2000 Djibouti 2001 Swaziland 2002 Djibouti 2003 Djibouti 2004 Djibouti 2005 Djibouti 2006 Djibouti 2007 Djibouti dtype: object
Но это слишком упрощенно. Вместо этого мы хотим получить те страны, которые находятся в четвертом квартиле. Но сначала нам нужно выяснить общую мировую тенденцию.
Мировые тенденции в случаях туберкулеза
Чтобы изучить общую тенденцию в мире, нам нужно суммировать значения каждой страны для трех наборов данных в год.
deaths_total_per_year_df = deaths_df.sum(axis=1) existing_total_per_year_df = existing_df.sum(axis=1) new_total_per_year_df = new_df.sum(axis=1)
Теперь мы создадим новый фрейм данных
с каждой суммой в серии, которую мы построим с помощью метода фрейма данных plot ()
.
world_trends_df = pd.DataFrame({ 'Total deaths per 100K' : deaths_total_per_year_df, 'Total existing cases per 100K' : existing_total_per_year_df, 'Total new cases per 100K' : new_total_per_year_df}, index=deaths_total_per_year_df.index)
world_trends_df.plot(figsize=(12,6)).legend( loc='center left', bbox_to_anchor=(1, 0.5))
Похоже, что общая тенденция заключается в уменьшении общего числа существующих случаев на 100 тыс. Однако число новых случаев растет, хотя, по-видимому, оно возвращается с 2005 года. Так как же возможно, что общее число существующих случаев уменьшается, если общее число новых случаев растет? Одной из причин может быть наблюдаемое увеличение числа смертей на 100 тыс., но главная причина, которую мы должны учитывать, заключается в том, что люди выздоравливают от туберкулеза благодаря лечению. Сумма коэффициента выздоровления плюс коэффициент смертности больше, чем коэффициент новых случаев. В любом случае, кажется, что новых случаев больше, но и мы лучше их лечим. Нам необходимо улучшить профилактику и борьбу с эпидемиями.
Страны вне тенденции
Так что предыдущая была общей тенденцией мира в целом. Так какие же страны с другой тенденцией (к худшему)? Чтобы выяснить это, сначала нам нужно знать распределение смертей по странам в среднем за год.
deaths_by_country_mean = deaths_df.mean() deaths_by_country_mean_summary = deaths_by_country_mean.describe() existing_by_country_mean = existing_df.mean() existing_by_country_mean_summary = existing_by_country_mean.describe() new_by_country_mean = new_df.mean() new_by_country_mean_summary = new_by_country_mean.describe()
Мы можем построить график этих распределений, чтобы иметь представление о том, как распределяются страны в среднем за год.
deaths_by_country_mean.order().plot(kind='bar', figsize=(24,6))
Мы хотим, чтобы эти страны выходили за пределы 1,5-кратного межквартильного диапазона (50%). У нас есть эти ценности в:
deaths_outlier = deaths_by_country_mean_summary['50%']*1.5 existing_outlier = existing_by_country_mean_summary['50%']*1.5 new_outlier = new_by_country_mean_summary['50%']*1.5
Теперь мы можем использовать эти значения, чтобы получить те страны, которые в период 1990-2007 годов превысили эти уровни.
# Now compare with the outlier threshold outlier_countries_by_deaths_index = deaths_by_country_mean > deaths_outlier outlier_countries_by_existing_index = existing_by_country_mean > existing_outlier outlier_countries_by_new_index = new_by_country_mean > new_outlier
Какая доля стран у нас не в тренде? Для смертей:
num_countries = len(deaths_df.T) sum(outlier_countries_by_deaths_index)/num_countries
0.39613526570048307
Для существующих случаев (распространенность):
sum(outlier_countries_by_existing_index)/num_countries
0.39613526570048307
Для новых случаев (заболеваемость):
sum(outlier_countries_by_new_index)/num_countries
0.38647342995169082
Теперь мы можем использовать эти индексы для фильтрации наших исходных фреймов данных.
outlier_deaths_df = deaths_df.T[ outlier_countries_by_deaths_index ].T outlier_existing_df = existing_df.T[ outlier_countries_by_existing_index ].T outlier_new_df = new_df.T[ outlier_countries_by_new_index ].T
Это серьезные вещи. У нас более трети стран мира являются выбросами по распределению существующих случаев заболевания, новых случаев заболевания и смертей от инфекционного туберкулеза. Но что, если мы посчитаем, что выброс в 5 раз превышает IQR? Давайте повторим предыдущий процесс.
deaths_super_outlier = deaths_by_country_mean_summary['50%']*5 existing_super_outlier = existing_by_country_mean_summary['50%']*5 new_super_outlier = new_by_country_mean_summary['50%']*5 super_outlier_countries_by_deaths_index = deaths_by_country_mean > deaths_super_outlier super_outlier_countries_by_existing_index = existing_by_country_mean > existing_super_outlier super_outlier_countries_by_new_index = new_by_country_mean > new_super_outlier
Какая доля у нас сейчас?
sum(super_outlier_countries_by_deaths_index)/num_countries
0.21739130434782608
Давайте возьмем фреймы данных.
super_outlier_deaths_df = deaths_df.T[ super_outlier_countries_by_deaths_index ].T super_outlier_existing_df = existing_df.T[ super_outlier_countries_by_existing_index ].T super_outlier_new_df = new_df.T[ super_outlier_countries_by_new_index ].T
Давайте сосредоточимся на борьбе с эпидемиями и посмотрим на структуру данных по новым случаям.
super_outlier_new_df
год | |||||||||||||||||||||
… | 308 | 540 | 258 | 167 | 163 | 297 | 184 | 1990 | 307 | 585 | 582 | 513 | 329 | 267 | 169 | 393 | 322 | 207 | 301 | 177 | 344 |
… | 314 | 516 | 286 | 185 | 250 | 349 | 201 | 1991 | 341 | 579 | 594 | 503 | 364 | 266 | 188 | 386 | 322 | 220 | 301 | 196 | 344 |
… | 320 | 492 | 314 | 197 | 272 | 411 | 218 | 1992 | 364 | 574 | 606 | 493 | 389 | 260 | 200 | 380 | 322 | 233 | 302 | 209 | 344 |
… | 326 | 470 | 343 | 212 | 296 | 460 | 244 | 1993 | 390 | 568 | 618 | 483 | 417 | 267 | 215 | 373 | 322 | 248 | 305 | 224 | 344 |
… | 333 | 449 | 373 | 225 | 306 | 501 | 280 | 1994 | 415 | 563 | 630 | 474 | 444 | 293 | 229 | 366 | 322 | 263 | 309 | 239 | 344 |
… | 339 | 428 | 390 | 241 | 319 | 536 | 323 | 1995 | 444 | 557 | 642 | 464 | 474 | 337 | 245 | 360 | 322 | 279 | 317 | 255 | 344 |
… | 346 | 409 | 389 | 254 | 314 | 554 | 362 | 1996 | 468 | 552 | 655 | 455 | 501 | 398 | 258 | 353 | 322 | 297 | 332 | 269 | 344 |
… | 353 | 391 | 401 | 273 | 320 | 576 | 409 | 1997 | 503 | 546 | 668 | 446 | 538 | 474 | 277 | 347 | 322 | 315 | 360 | 289 | 344 |
… | 360 | 373 | 412 | 294 | 326 | 583 | 461 | 1998 | 542 | 541 | 681 | 437 | 580 | 558 | 299 | 341 | 322 | 334 | 406 | 312 | 344 |
… | 367 | 356 | 417 | 319 | 324 | 603 | 519 | 1999 | 588 | 536 | 695 | 428 | 628 | 691 | 324 | 335 | 322 | 355 | 479 | 338 | 344 |
… | 374 | 340 | 425 | 348 | 340 | 602 | 553 | 2000 | 640 | 530 | 708 | 420 | 685 | 801 | 353 | 329 | 322 | 377 | 576 | 368 | 344 |
… | 382 | 325 | 414 | 376 | 360 | 627 | 576 | 2001 | 692 | 525 | 722 | 412 | 740 | 916 | 382 | 323 | 322 | 400 | 683 | 398 | 344 |
… | 389 | 310 | 416 | 402 | 386 | 632 | 613 | 2002 | 740 | 520 | 737 | 403 | 791 | 994 | 408 | 317 | 322 | 425 | 780 | 425 | 344 |
… | 397 | 296 | 410 | 419 | 396 | 652 | 635 | 2003 | 772 | 515 | 751 | 396 | 825 | 1075 | 425 | 312 | 322 | 451 | 852 | 444 | 344 |
… | 405 | 283 | 405 | 423 | 385 | 623 | 643 | 2004 | 780 | 510 | 766 | 388 | 834 | 1127 | 430 | 306 | 322 | 479 | 898 | 448 | 344 |
… | 413 | 270 | 391 | 418 | 370 | 588 | 639 | 2005 | 770 | 505 | 781 | 380 | 824 | 1141 | 425 | 301 | 322 | 509 | 925 | 443 | 344 |
… | 421 | 258 | 368 | 408 | 350 | 547 | 638 | 2006 | 751 | 500 | 797 | 372 | 803 | 1169 | 414 | 295 | 322 | 540 | 940 | 432 | 344 |
… | 429 | 246 | 346 | 397 | 330 | 506 | 637 | 2007 | 731 | 495 | 813 | 365 | 782 | 1198 | 403 | 290 | 322 | 574 | 948 | 420 | 344 |
18 строк × 22 столбца
Давайте сделаем несколько сюжетов, чтобы получить лучшее впечатление.
super_outlier_new_df.plot(figsize=(12,4)).legend(loc='center left', bbox_to_anchor=(1, 0.5))
У нас есть 22 страны, где число новых случаев заболевания в среднем за год более чем в 5 раз превышает медианное значение распределения. Давайте создадим страну, которая представляет собой среднее значение из этих 22 стран.
average_super_outlier_country = super_outlier_new_df.mean(axis=1) average_super_outlier_country
year 1990 314.363636 1991 330.136364 1992 340.681818 1993 352.909091 1994 365.363636 1995 379.227273 1996 390.863636 1997 408.000000 1998 427.000000 1999 451.409091 2000 476.545455 2001 502.409091 2002 525.727273 2003 543.318182 2004 548.909091 2005 546.409091 2006 540.863636 2007 535.181818 dtype: float64
Теперь давайте создадим страну, которая представляет весь остальной мир.
avearge_better_world_country = new_df.T[ - super_outlier_countries_by_new_index ].T.mean(axis=1) avearge_better_world_country
year 1990 80.751351 1991 81.216216 1992 80.681081 1993 81.470270 1994 81.832432 1995 82.681081 1996 82.589189 1997 84.497297 1998 85.189189 1999 86.232432 2000 86.378378 2001 86.551351 2002 89.848649 2003 87.778378 2004 87.978378 2005 87.086022 2006 86.559140 2007 85.605405 dtype: float64
Теперь давайте сравним эту страну со средней страной мира.
two_world_df = pd.DataFrame({ 'Average Better World Country': avearge_better_world_country, 'Average Outlier Country' : average_super_outlier_country}, index = new_df.index) two_world_df.plot(title="Estimated new TB cases per 100K",figsize=(12,8))
Тенденция к увеличению числа новых случаев действительно сильнее в средней стране с супер-выбросом, настолько сильнее, что трудно заметить ту же тенденцию в лучшем мире стране. Десятилетие 90-х годов привело к ужасному увеличению числа случаев заболевания туберкулезом в этих странах. Но давайте посмотрим на точные цифры.
two_world_df.pct_change().plot(title="Percentage change in estimated new TB cases", figsize=(12,8))
Исходя из этого графика, замедление и реверсия этой тенденции, по-видимому, происходят одновременно как в средних лучших, так и в более отдаленных странах, и что-то произошло примерно в 2002 году. Мы постараемся выяснить, что происходит в следующем разделе.
R
Мы уже знаем, что мы можем использовать max
со столбцом фрейма данных в R и получить максимальное значение. Кроме того, мы можем использовать which.max
для того, чтобы получить его позицию (аналогично использованию og argmax
в Pandas). Если мы используем транспонированный фрейм данных, мы можем использовать lapply
или sapply
для выполнения этой операции в каждом столбце year, получая затем либо список, либо вектор индексов (мы будем использовать sapply
, который возвращает вектор). Нам просто нужно немного подправить и использовать вектор стран, который мы будем индексировать, чтобы получить название страны вместо индекса в результате.
country_names <- rownames(existing_df_t) sapply(existing_df_t, function(x) {country_names[which.max(x)]})
## X1990 X1991 X1992 ## "Djibouti" "Djibouti" "Djibouti" ## X1993 X1994 X1995 ## "Djibouti" "Djibouti" "Djibouti" ## X1996 X1997 X1998 ## "Kiribati" "Kiribati" "Cambodia" ## X1999 X2000 X2001 ## "Korea, Dem. Rep." "Djibouti" "Swaziland" ## X2002 X2003 X2004 ## "Djibouti" "Djibouti" "Djibouti" ## X2005 X2006 X2007 ## "Djibouti" "Djibouti" "Djibouti"
Мировые тенденции в случаях туберкулеза
Опять же, чтобы изучить общую тенденцию в мире, нам нужно суммировать значения каждой страны для трех наборов данных в год.
Но сначала нам нужно загрузить два других набора данных по количеству смертей и количеству новых случаев.
# Download files deaths_file <- getURL("https://docs.google.com/spreadsheets/d/12uWVH_IlmzJX_75bJ3IH5E-Gqx6-zfbDKNvZqYjUuso/pub?gid=0&output=CSV") new_cases_file <- getURL("https://docs.google.com/spreadsheets/d/1Pl51PcEGlO9Hp4Uh0x2_QM0xVb53p2UDBMPwcnSjFTk/pub?gid=0&output=csv") # Read into data frames deaths_df <- read.csv( text = deaths_file, row.names=1, stringsAsFactor=F) new_df <- read.csv( text = new_cases_file, row.names=1, stringsAsFactor=F) # Cast data to int (deaths doesn't need it) new_df[1:18] <- lapply( new_df[1:18], function(x) { as.integer(gsub(',', '', x) )}) # Transpose deaths_df_t <- deaths_df deaths_df <- as.data.frame(t(deaths_df)) new_df_t <- new_df new_df <- as.data.frame(t(new_df))
А теперь суммы по строкам. Нам нужно преобразовать это в фрейм данных, так как функция возвращает числовой вектор.
deaths_total_per_year_df <- data.frame(total=rowSums(deaths_df)) existing_total_per_year_df <- data.frame(total=rowSums(existing_df)) # We pass na.rm = TRUE in order to ignore missing values in the new # cases data frame when summing (no missing values in other dataframes though) new_total_per_year_df <- data.frame(total=rowSums(new_df, na.rm = TRUE))
Теперь мы можем построить каждую линию, используя то, что мы узнали до сих пор. Чтобы получить вектор с подсчетами для передачи каждой функции построения графика, мы используем индексацию фрейма данных R по имени столбца.
xrange <- 1990:2007 plot(xrange, deaths_total_per_year_df$total, type='l', xlab="Year", ylab="Count per 100K", col = "blue", ylim=c(0,50000)) lines(xrange, existing_total_per_year_df$total, col = "darkgreen") lines(xrange, new_total_per_year_df$total, col = "red") legend(x=1992, y=52000, lty=1, cex = .7, ncol = 3, col=c("blue","darkgreen","red"), legend=c("Deaths","Existing cases","New cases"))
Выводы, очевидно, те же, что и при использовании Python.
Страны вне тенденции
Итак, какие страны являются выбросами этой тенденции (к худшему)? Опять же, чтобы выяснить это, сначала нам нужно знать распределение стран в среднем за год. Для этой цели мы используем coleman
.
deaths_by_country_mean <- data.frame(mean=colMeans(deaths_df)) existing_by_country_mean <- data.frame(mean=colMeans(existing_df)) new_by_country_mean <- data.frame(mean=colMeans(new_df, na.rm=TRUE))
Мы можем построить график этих распределений, чтобы иметь представление о том, как распределяются страны в среднем за год. Нас интересуют не столько отдельные страны, сколько само распределение.
barplot(sort(deaths_by_country_mean$mean))
Опять же, мы видим, что на графике есть три тенденции, с медленно уменьшающейся частью в начале, вторым более ступенчатым участком и последним пиком, который явно отличается от остальных.
Давайте на этот раз пропустим часть с выбросами 1,5 и перейдем непосредственно к выбросам 5,0. В R мы будем использовать другой подход. Мы будем использовать функцию quantile ()
, чтобы получить межквартильный диапазон и определить порог выброса.
Поскольку мы уже знаем результаты из нашего раздела Python, давайте сделаем это только для новых случаев, поэтому мы создадим также графики, которые мы делали раньше.
new_super_outlier <- quantile(new_by_country_mean$mean, probs = c(.5)) * 5.0 super_outlier_countries_by_new_index <- new_by_country_mean > new_super_outlier
И пропорция такова.
sum(super_outlier_countries_by_new_index)/208
## [1] 0.1057692
Давайте получим из этого фрейм данных только с теми странами, которые мы считаем выбросами.
super_outlier_new_df <- new_df[, super_outlier_countries_by_new_index ]
Теперь мы готовы их построить.
xrange <- 1990:2007 plot(xrange, super_outlier_new_df[,1], type='l', xlab="Year", ylab="New cases per 100K", col = 1, ylim=c(0,1800)) for (i in seq(2:ncol(super_outlier_new_df))) { lines(xrange, super_outlier_new_df[,i], col = i) } legend(x=1990, y=1800, lty=1, cex = 0.5, ncol = 7, col=1:22, legend=colnames(super_outlier_new_df))
Определенно, мы видим здесь преимущество использования базового построения Pandas по сравнению с базовым построением R!
Пока наши результаты совпадают. У нас есть 22 страны, где число новых случаев заболевания в среднем за год более чем в 5 раз превышает медианное значение распределения. Давайте создадим страну, которая в среднем представляет эти 22 страны. Мы будем использовать rowMeans()
здесь.
average_countries_df <- data.frame( averageOutlierMean=rowMeans(super_outlier_new_df, na.rm=T) ) average_countries_df
## averageOutlierMean ## X1990 314.3636 ## X1991 330.1364 ## X1992 340.6818 ## X1993 352.9091 ## X1994 365.3636 ## X1995 379.2273 ## X1996 390.8636 ## X1997 408.0000 ## X1998 427.0000 ## X1999 451.4091 ## X2000 476.5455 ## X2001 502.4091 ## X2002 525.7273 ## X2003 543.3182 ## X2004 548.9091 ## X2005 546.4091 ## X2006 540.8636 ## X2007 535.1818
Теперь давайте создадим страну, которая представляет весь остальной мир.
average_countries_df$averageBetterWorldMean <- rowMeans(new_df[ ,- super_outlier_countries_by_new_index ], na.rm=T) average_countries_df
## averageOutlierMean averageBetterWorldMean ## X1990 314.3636 105.2767 ## X1991 330.1364 107.3786 ## X1992 340.6818 108.0243 ## X1993 352.9091 110.0388 ## X1994 365.3636 111.6942 ## X1995 379.2273 113.9369 ## X1996 390.8636 115.0971 ## X1997 408.0000 118.6408 ## X1998 427.0000 121.2913 ## X1999 451.4091 124.8350 ## X2000 476.5455 127.6505 ## X2001 502.4091 130.5680 ## X2002 525.7273 136.0194 ## X2003 543.3182 136.0388 ## X2004 548.9091 136.8155 ## X2005 546.4091 135.5121 ## X2006 540.8636 134.4493 ## X2007 535.1818 133.2184
Теперь давайте построим график страны-аутсайдера со средней страной мира.
xrange <- 1990:2007 plot(xrange, average_countries_df$averageOutlierMean, type='l', xlab="Year", ylab="New cases per 100K", col = "darkgreen", ylim=c(0,600)) lines(xrange, average_countries_df$averageBetterWorldMean, col = "blue") legend(x=1990, y=600, lty=1, cex = 0.7, ncol = 2, col=c("darkgreen","blue"), legend=c("Average outlier country", "Average World Country"))
Поиск в Google о событиях и датах в Туберкулезе
В этом разделе мы будем использовать только Python. О гугле, на самом деле мы просто перешли прямо к Запись в Википедии об этой болезни . В разделах эпидемии мы обнаружили следующее:
Общее число случаев заболевания туберкулезом сокращается с 2005 года, в то время как новые случаи сократились с 2002 года.
Это подтверждается нашим предыдущим анализом.
Китай добился особенно значительного прогресса, снизив уровень смертности от туберкулеза примерно на 80% в период с 1990 по 2010 год. Давайте проверим это:
existing_df.China.plot(title="Estimated existing TB cases in China")
- В 2007 году страной с самым высоким, по оценкам, уровнем заболеваемости туберкулезом был Свазиленд-1200 случаев на 100 000 человек.
new_df.apply(pd.Series.argmax, axis=1)['2007']
'Swaziland'
Есть еще много выводов Википедии, которые мы можем подтвердить с помощью тех или иных наборов данных из Gapminder world. Например, туберкулез и ВИЧ часто ассоциируются с уровнем бедности. Было бы интересно объединить наборы данных и изучить тенденции в каждом из них. Мы призываем читателя попробовать и поделиться с нами своими выводами.
Другие веб – страницы для изучения
Некоторые интересные ресурсы о туберкулезе помимо веб-сайта Gapminder:
- Фундамент ворот:
- Фундамент ворот:
- Фундамент ворот:
Выводы
Исследовательский анализ данных является ключевым шагом в анализе данных. Именно на этом этапе мы начинаем формировать любую последующую работу. Он предшествует любой визуализации данных или работе с машинным обучением, показывая нам, хороши или плохи наши данные и наша гипотеза.
Традиционно R был предпочтительным оружием для большинства работ EDA, хотя использование более выразительной библиотеки построения графиков, такой как gglot2, довольно удобно. Фактически, базовая функциональность построения графиков, включенная в Pandas, делает процесс более чистым и быстрым при использовании Python. Однако вопросы, на которые мы ответили здесь, были очень простыми и не включали в себя несколько переменных и кодировок. В таких случаях будет сиять продвинутая библиотека, такая как ggplot2. Помимо предоставления более приятных диаграмм, он сэкономит нам довольно много времени благодаря своей выразительности и возможности повторного использования.
Но каким бы простым ни был наш анализ и диаграммы, мы смогли сделать вывод о том, насколько серьезен гуманитарный кризис в отношении такой болезни, как туберкулез, особенно если учесть, что эта болезнь относительно хорошо контролируется в более развитых странах. Мы видели, как некоторые навыки кодирования и хорошее любопытство позволяют нам создавать осведомленность в этих и других мировых проблемах.
Помните, что весь исходный код для различных частей этой серии учебных пособий и приложений можно проверить на GitHub . Не стесняйтесь участвовать и делиться с нами своими успехами!