ПОЛНОЕ РУКОВОДСТВО ПО ПАНДАМ - ЧАСТЬ I

Как освоить основную библиотеку анализа данных Python за 20 минут

Краткое руководство по основным функциям Pandas.

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

В этой статье мы рассмотрим следующие темы:

  1. "Настраивать"
  2. "Загрузка данных"
  3. Просмотр / Сортировка / Фильтрация данных
  4. Аналитические функции

Вы можете найти полную версию Jupyter Notebook здесь. Но я призываю вас пройти эти шаги самостоятельно. В конце концов, практика ведет к совершенству.

Пререквесты:

Рабочая среда Python (я предлагаю Jupyter Notebook). Если вы этого не сделали, не волнуйтесь. В статье на прошлой неделе мы говорили о том, как настроить Anaconda, и объяснили, как открыть ваш первый блокнот Jupyter. Если вы еще этого не сделали, взгляните на связанную статью. Настройка и подготовка к работе занимает менее 10 минут.



1. Настройка

Прежде чем выполнять какие-либо манипуляции с данными, давайте получим некоторые данные. Мы будем использовать данные из World Happiness Report 2019. Я собираюсь предоставить слегка скорректированную версию необработанных данных, которая также включает континенты.

Этот репозиторий GitHub содержит данные и код. Если вы не знакомы с GitHub, вы также можете загрузить упакованный zip-файл по этой ссылке! Распакуйте файл и переместите содержимое (особенно thehappiness_with_continent.csv) в ту же папку, что и ваш блокнот Jupyter (если у вас его еще нет, создайте).

В новой записной книжке запустите import pandas as pd (т.е. импортируйте библиотеку Pandas в книгу, чтобы получить доступ к функциям.

Мне нравится настраивать параметры ноутбука примерно так:

from IPython.core.display import display, HTML
display(HTML("<style>.container {width:90% !important;}</style>"))

Эти команды делают ноутбук шире и, таким образом, занимают больше места на экране (обычно ноутбук имеет фиксированную ширину, что плохо с широкими экранами).

2. Загрузка данных

Pandas хранит данные либо как серии (один столбец), либо как DataFrames (один или несколько столбцов), где последние представляют собой просто одну или несколько объединенных серий.

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

pd.read_csv

Для меня основной способ загрузки данных - это панды read_csv. Он просто лучше всего согласуется с моим представлением о данных, которое в основном является табличным.

Вы можете загрузить данные из локального файла следующим образом:

data = pd.read_csv('happiness_with_continent.csv')

Или вы можете считывать данные из Интернета прямо в фрейм данных следующим образом:

data = pd.read_csv('https://raw.githubusercontent.com/FBosler/you-datascientist/master/happiness_with_continent.csv')

Из Excel или Google Таблиц

Чтение данных из Excel довольно просто. Google Таблицы немного сложнее, поскольку они требуют, чтобы вы сначала прошли процедуру аутентификации. Вы можете прочитать все об извлечении данных из Excel и Google Sheets здесь:



pd.read_clipboard

Я использую его редко, но он определенно работает для небольших столов. Просто отметьте и скопируйте (ctrl + c) таблицу, например, из листов Google и запустите pd.read_clipboard().

Пример: перейдите сюда (первый публичный лист, который я нашел) и отметьте область, как показано на скриншоте.

Некоторые примечательные параметры для функций на основе read_csv (и read_clipboard):

  • sep: разделитель столбцов (по умолчанию ,, но также может быть табуляция)
  • header: по умолчанию 'infer' (т.е. Pandas догадывается, что такое ваш заголовок), альтернативы - целое число или список целых чисел (для многоуровневых имен). Например, вы можете сделать header=3, и фрейм данных начнется со строки 4 (поскольку Python имеет индекс 0) в качестве заголовка. Если у ваших данных нет заголовка, используйте header=None
  • names: названия столбцов. Если вы хотите использовать этот параметр, чтобы переопределить любые имена столбцов, выведенные Pandas, вы должны указать header=0 (или любую другую строку, в которой находятся ваши имена столбцов), если вы этого не сделаете, у вас будут свои имена в качестве имен столбцов, а затем исходные имена столбцов в первой строке. Параметр names ожидает список, например ['your col 1', 'your col 2', ... 'your last col name']
  • index_col: устанавливает индекс при загрузке (т. Е. Мы устанавливаем индекс равным name. Подробнее об индексах мы узнаем позже)
  • skiprows: пропускает первые x строк, это полезно, когда файл содержит некоторые метаданные в начале, такие как автор и другая информация
  • skipfooter: пропускает последние x строк, полезно, когда в конце файла есть метаданные (например, сноски)
  • parse_date: этот параметр сообщает Pandas, какие столбцы он должен интерпретировать как дату (например, pd.read_csv(happiness_with_continent.csv,parse_dates=['Year']). Парсер по умолчанию работает достаточно хорошо из коробки. В случаях, когда вы сталкиваетесь с нестандартным форматированием данных, Pandas может работать с настраиваемыми парсерами даты (для которых вы пришлось бы указать логику синтаксического анализа).

Есть куча дополнительных (менее используемых) параметров. Вы можете прочитать о них, запустив pd.read_csv? в ячейке (добавление вопросительного знака после команды приведет к печати текста справки).

Независимо от того, как мы читаем данные, мы хотим сохранить их в переменной. Мы делаем это, присваивая результат чтения такой переменной, как эта data = pd.read_clipboard() или data = pd.read_csv('NAME_OF_YOUR_FILE.csv')

Дополнительные методы чтения:

Следующие методы чтения редко возникают у меня, но все они также реализованы в Pandas:

  • read_feather
  • read_fwf
  • read_gbq
  • read_hdf
  • read_html
  • read_json
  • read_msgpack
  • read_parquet
  • read_pickle
  • read_sas
  • read_sql
  • read_sql_query
  • read_sql_table
  • read_stata
  • read_table

3. Проверка / сортировка / фильтрация данных.

① Проверка - первые, последние, случайные строки

Существует три стандартных способа отображения данных в блокноте: head, tail и sample. head показывает первую, tail последнюю и sample случайный выбор строк.

Обратите внимание, что перед столбцом gini of household income reported in Gallop, by wp5-year есть точки. Точки означают, что есть столбцы, которые не отображаются. Чтобы изменить настройки записных книжек для отображения большего количества столбцов / строк, выполните следующие команды:

pd.set_option('display.max_columns', <number of columns you want>)
pd.set_option('display.max_rows', <number of rows you want>)
# I typically use
pd.set_option('display.max_columns', 50)
pd.set_option('display.max_rows', 8)

Однако имейте в виду, что часто файлы, которые вы загружаете, настолько велики (1 ГБ +), что невозможно отобразить все данные по соображениям производительности. По этой причине вам следует попытаться ознакомиться с данными на более высоком уровне и не зависеть от визуального просмотра строк.

② Проверка - форма, столбцы, индекс, информация, описание

data.shape возвращает размеры DataFrame. В нашем примере 1704 строки, 27 столбцов.

IN:
data.shape
OUT:
(1704, 27)

data.columns возвращает список всех имен столбцов в DataFrame.

IN:
data.columns
OUT:
Index(['Country name', 'Year', 'Life Ladder', 'Log GDP per capita',
       'Social support', 'Healthy life expectancy at birth',
       'Freedom to make life choices', 'Generosity',
       'Perceptions of corruption', 'Positive affect', 'Negative affect',
       'Confidence in national government', 'Democratic Quality',
       'Delivery Quality', 'Standard deviation of ladder by country-year',
       'Standard deviation/Mean of ladder by country-year',
       'GINI index (World Bank estimate)',
       'GINI index (World Bank estimate), average 2000-16',
       'gini of household income reported in Gallup, by wp5-year',
       'Most people can be trusted, Gallup',
       'Most people can be trusted, WVS round 1981-1984',
       'Most people can be trusted, WVS round 1989-1993',
       'Most people can be trusted, WVS round 1994-1998',
       'Most people can be trusted, WVS round 1999-2004',
       'Most people can be trusted, WVS round 2005-2009',
       'Most people can be trusted, WVS round 2010-2014',
       'Continent'],
      dtype='object')

data.index возвращает информацию об индексе. Подробнее об индексах мы поговорим в разделе сортировки и фильтрации. Думайте об индексе как об именах / номерах строк.

IN:
data.index
OUT:
RangeIndex(start=0, stop=1704, step=1)

data.info() возвращает информацию о типах и количестве ненулевых наблюдений в DataFrame.

IN:
data.info()
OUT:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1704 entries, 0 to 1703
Data columns (total 27 columns):
Country name                                                1704 non-null object
Year                                                        1704 non-null datetime64[ns]
Life Ladder                                                 1704 non-null float64
Log GDP per capita                                          1676 non-null float64
Social support                                              1691 non-null float64
Healthy life expectancy at birth                            1676 non-null float64
Freedom to make life choices                                1675 non-null float64
Generosity                                                  1622 non-null float64
Perceptions of corruption                                   1608 non-null float64
Positive affect                                             1685 non-null float64
Negative affect                                             1691 non-null float64
Confidence in national government                           1530 non-null float64
Democratic Quality                                          1558 non-null float64
Delivery Quality                                            1559 non-null float64
Standard deviation of ladder by country-year                1704 non-null float64
Standard deviation/Mean of ladder by country-year           1704 non-null float64
GINI index (World Bank estimate)                            643 non-null float64
GINI index (World Bank estimate), average 2000-16           1502 non-null float64
gini of household income reported in Gallup, by wp5-year    1335 non-null float64
Most people can be trusted, Gallup                          180 non-null float64
Most people can be trusted, WVS round 1981-1984             125 non-null float64
Most people can be trusted, WVS round 1989-1993             220 non-null float64
Most people can be trusted, WVS round 1994-1998             618 non-null float64
Most people can be trusted, WVS round 1999-2004             491 non-null float64
Most people can be trusted, WVS round 2005-2009             630 non-null float64
Most people can be trusted, WVS round 2010-2014             671 non-null float64
Continent                                                   1704 non-null object
dtypes: datetime64[ns](1), float64(24), object(3)
memory usage: 372.8+ KB

data.describe() возвращает некоторую описательную статистическую информацию (количество, среднее, стандартное, минимальное, 25%, 50%, 75%, максимальное) относительно числовых столбцов DataFrame:

① Сортировка - data.sort_values ​​()

Вызов sort_values для данных без параметра не принесет нам никакой пользы. Фактически, это вызовет ошибку, сообщив нам, что отсутствует аргумент с именем by. В этой ошибке есть смысл. Мы должны указать Pandas, по каким столбцам мы хотим отсортировать.

Мы могли бы, например, отсортировать наши данные по году или по году и названию страны следующим образом:

data.sort_values(by='Year')
data.sort_values(by=['Year','Country name'])
data.sort_values(by=['Country name','Year'])

Примечание. При передаче нескольких значений выполняется сортировка по значениям в порядке значений.

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

data.sort_values(by='Year', ascending=True)
data.sort_values(
  by=['Country name','Year'], 
  ascending=[False,True]
)

Примечание. По умолчанию для возрастания установлено значение True, т. е. сначала наименьшие значения. Если вы хотите, чтобы сначала были наибольшие значения, необходимо указать ascending = False.

② Сортировка - data.sort_index ()

Помимо сортировки по столбцам, существует также сортировка по индексу. Для сортировки по вызову индекса: data.sort_index() или data.sort_index(ascending=False). Первый - по возрастанию, второй - по убыванию.

① Фильтрация - столбцы

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

Выбор одного столбца:
Есть два способа выбрать один конкретный столбец. Допустим, мы хотим выбрать столбец Year. Мы могли сделать либо:

  • data['Year'] , or
  • data.Year (не используйте этот подход)

Оба делают одно и то же.

Примечание. Почему есть два способа сделать одно и то же, спросите вы? Причина в удобстве. Второй подход немного быстрее, так как вам нужны только две точки и имя столбца. Тогда как в первом способе вам понадобится имя столбца, две верхние галочки и две скобки.

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

Выбор нескольких столбцов:
Допустим, вы хотите выбрать Country name и Life Ladder, тогда вы сделаете это следующим образом (осторожно: двойные скобки):

Примечание. Обратите особое внимание на двойные скобки перед первым и после последнего столбца, который вы хотите выбрать! Всякий раз, когда вы используете двойные скобки, результатом будет DataFrame (даже если вы выберете только один столбец с двойными скобками).
Я не могу это подчеркнуть, поскольку даже я все еще иногда сталкиваюсь с этими ошибками! Если вы хотите выбрать несколько столбцов, но открыть только один набор скобок, содержимое между скобками будет считаться ОДНИМ столбцом. Излишне говорить, что ваши данные не содержат этого случайно объединенного столбца.

② Фильтрация - строки

Возможность выбрать определенные столбцы - это только половина дела. Однако выбирать строки так же просто.

Строки в пандах выбираются по индексу. Вы можете думать об индексах как об именах строк. Всякий раз, когда вы выбираете строки из DataFrame, это происходит путем наложения DataFrame на Series с тем же индексом, содержащим только значения True и False (True означает, что строка должна быть выбрана, False, что строка не должна выбираться). Однако в большинстве случаев этот явный выбор индекса не зависит от пользователя. Я все еще думаю, что чрезвычайно важно понимать, как работает процесс выбора строк под капотом.

Вы можете выбрать одну или несколько строк по индексу. Для этого есть два метода:

iloc:
data.iloc позволяет выбирать строки (и, возможно, столбцы) по позиции (т. е. по номеру строки).

iloc - Выбор одной строки:
Синтаксис выглядит так data.iloc[row_number (,col_number)], где часть в круглых скобках является необязательной.

Примечание. Форматирование выглядит немного нетрадиционным, потому что при выборе одной строки и только одной строки будет возвращена Серия.

iloc - Выбор нескольких строк:
синтаксис выглядит так data.iloc[start_row:end_row (,start_col:end_col)], где часть в круглых скобках является необязательной.

Кроме того, вы можете дополнительно указать, какие столбцы следует выбирать.

loc:
data.loc в отличие от iloc позволяет выбирать строки (и столбцы) с помощью:

  1. ярлык / указатель или
  2. с логическим / условным поиском

Чтобы лучше объяснить первый пункт, а также лучше отличить его от iloc, мы превратим название страны в индекс DataFrame. Для этого выполните следующую команду:

data.set_index('Country name',inplace=True)

Команда set_index устанавливает новый индекс для DataFrame. Также указав inplace=True, мы гарантируем, что DataFrame будет изменен. Если бы мы не указали inplace = True, мы бы увидели только то, как DataFrame будет выглядеть после применения операции, но никаких изменений в базовых данных не произойдет.

DataFrame теперь должен выглядеть так:

Мы видим, что DataFrame потерял индекс номеров строк (предыдущий) и получил новый индекс:

loc - Выбор строки (строк) по одной метке индекса:
Синтаксис выглядит следующим образом data.loc[index_label (,col_label)], где часть в круглых скобках является необязательной.

loc - выбор строк и столбца по метке индекса и метке столбца:

loc - Выбор строки (строк) по нескольким индексным меткам:

Примечания:

  • Как и раньше, при выборе нескольких столбцов мы должны обязательно заключить их в двойные скобки. Если мы забудем это сделать, столбцы будут считаться одним длинным (несуществующим) именем.
  • Мы используем образец (5), чтобы показать, что в этом миксе присутствует некоторая Германия. Предполагая, что вместо этого мы использовали head (5), мы начали бы видеть Германию только после 12 рядов Соединенных Штатов.
  • Loc возвращает строки в указанном порядке, независимо от их фактического порядка. Например, если бы мы указали сначала Германию, а затем Соединенные Штаты, у нас было бы 13 строк Германии, а затем 12 строк Соединенных Штатов.

loc - выбор строк и столбцов по нескольким индексным меткам:
вы также можете указать имена столбцов для выбранных строк, которые вы хотите вернуть.

Примечание. Мы распределяем выделенные строки ['Germany','United States] и столбцы ['Year','Life Ladder'] на две строки. Я считаю, что разделение оператора улучшает читаемость.

loc - Выбор строк по диапазону меток индекса:
Этот способ выбора строк может показаться немного странным, поскольку диапазон меток ('Denmark':'Germany') не такой интуитивно понятный, как используя диапазон чисел (903:907) для iloc.

Указание диапазона меток основано на текущем порядке индекса, и это не удастся для несортированного индекса.

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

loc - логический / условный поиск
Логический или условный поиск - это действительно то, что нужно. Как упоминалось ранее, всякий раз, когда выбираются строки, это происходит путем наложения на DataFrame маски значений True и False.

В следующем примере мы создаем небольшой DataFrame с индексом ['A','B','A','D'] и некоторыми случайными значениями от 0 до 10 в качестве значений.

Затем мы создаем overlay с тем же индексом со значениями [True,False,True,False].

Затем мы используем df.loc[overlay], чтобы выбрать только строки со значением True для их индекса.

IN:
from numpy.random import randint
index = ['A','B','A','D']
## create dummy DataFrame ##
df = pd.DataFrame(
    index = index,
    data = {
    'values':randint(10,size=len(index))
})
print('DataFrame:')
print(df)
OUT:
DataFrame:
   values
A       8
B       2
A       3
D       2
IN:
## create dummy overlay ##
overlay = pd.Series(
    index=index,
    data=[True,False,True,False]
)
print('\nOverlay:')
print(overlay)
OUT:
Overlay:
A     True
B    False
A     True
D    False
dtype: bool
IN:
## select only True rows ##
print('\nMasked DataFrame:')
print(df.loc[overlay])
OUT:
Masked DataFrame:
   values
A       8
A       3

Ту же логику можно использовать для выбора строк на основе (или нескольких) условий.

Сначала мы создаем такую ​​булеву маску:

А затем используйте эту маску, чтобы выбрать только те строки, которые соответствуют указанному условию, например:

Вариант 1, как и альтернатива, дает точно такой же результат. Однако альтернатива немного более разборчива. Улучшенная разборчивость становится еще более очевидной при применении нескольких условий:

Примечание. Мы использовали & (побитовое и) для фильтрации строк, в которых одновременно применяется несколько условий. Мы можем использовать | (побитовое или) для фильтрации столбцов, к которым применяется одно из условий.

loc - расширенный условный поиск с пользовательскими формулами

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

В следующем примере мы выбираем только годы, которые четко делятся на три, и континенты, содержащие слово Америка. Случай надуманный, но в нем есть смысл.

Вместо лямбда (анонимных) функций вы также можете определять и использовать гораздо более сложные функции. Вы даже можете (не то чтобы я рекомендовал это) выполнять вызовы API в настраиваемой функции и использовать результаты вызовов для фильтрации вашего фрейма данных.

4. Аналитические функции

Теперь, когда мы освоились с фильтрацией и сортировкой данных от начала до конца и наоборот, давайте перейдем к более продвинутым аналитическим функциям.

Стандартные функции:

Как и функции чтения, в Pandas реализовано множество аналитических функций.

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

  • ① макс / мин
  • ② сумма
  • ③ среднее / медиана / квантиль
  • ④ idxmin / idxmax

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

Всякий раз, когда мы вызываем вышеупомянутые функции, передается параметр по умолчанию axis=0 (для приложений по столбцам). Однако мы можем переопределить этот параметр и передать axis=1 (для построчного приложения).

① max / min
Вызов max() для данных вернет (где это возможно) максимум для каждого столбца. min() делает прямо противоположное.

IN:
data.max() # COLUMNWISE MAXIMUM
OUT: 
Year                                                        2018
Life Ladder                                              8.01893
Log GDP per capita                                       11.7703
Social support                                          0.987343
                                                       ...      
Most people can be trusted, WVS round 1999-2004         0.637185
Most people can be trusted, WVS round 2005-2009         0.737305
Most people can be trusted, WVS round 2010-2014         0.661757
Continent                                          South America
Length: 26, dtype: object
IN:
data.max(axis=1) # ROW-WISE MAXIMUM
OUT: 
Country name
Afghanistan    2008.0
Afghanistan    2009.0
Afghanistan    2010.0
Afghanistan    2011.0
                ...  
Zimbabwe       2015.0
Zimbabwe       2016.0
Zimbabwe       2017.0
Zimbabwe       2018.0
Length: 1704, dtype: float64

② sum
Вызов sum() для данных вернет (где это возможно) сумму для каждого столбца.

IN:
data.sum()
OUT:
Year                                                                                         3429014
Life Ladder                                                                                  9264.91
Log GDP per capita                                                                           15456.8
Social support                                                                               1370.67
                                                                         ...                        
Most people can be trusted, WVS round 1999-2004                                              131.623
Most people can be trusted, WVS round 2005-2009                                              166.532
Most people can be trusted, WVS round 2010-2014                                              159.358
Continent                                          AsiaAsiaAsiaAsiaAsiaAsiaAsiaAsiaAsiaAsiaAsiaEu...
Length: 26, dtype: object

Примечание. Sum объединит строки в одну длинную строку, в результате чего будет получено значение AsiaAsiaAsiaAsiaAsiaAsiaAsiaAsiaAsiaAsiaAsiaEu… для столбца Continent.

③ среднее / медиана / квантиль
Вызов mean, median или quantile для данных вернет среднее или медианное значение соответственно.

IN:
data.mean()
OUT:
Year                                               2012.332160
Life Ladder                                           5.437155
Log GDP per capita                                    9.222456
Social support                                        0.810570
                                                      ...     
Most people can be trusted, WVS round 1994-1998       0.249574
Most people can be trusted, WVS round 1999-2004       0.268070
Most people can be trusted, WVS round 2005-2009       0.264336
Most people can be trusted, WVS round 2010-2014       0.237493
Length: 25, dtype: float64
IN:
data.median()
OUT:
Year                                               2012.000000
Life Ladder                                           5.339557
Log GDP per capita                                    9.406206
Social support                                        0.833098
                                                      ...     
Most people can be trusted, WVS round 1994-1998       0.229924
Most people can be trusted, WVS round 1999-2004       0.232000
Most people can be trusted, WVS round 2005-2009       0.198380
Most people can be trusted, WVS round 2010-2014       0.193531
Length: 25, dtype: float64
IN:
data.quantile(q=.8)
OUT:
Year                                               2016.000000
Life Ladder                                           6.497157
Log GDP per capita                                   10.375623
Social support                                        0.913667
                                                      ...     
Most people can be trusted, WVS round 1994-1998       0.304498
Most people can be trusted, WVS round 1999-2004       0.388611
Most people can be trusted, WVS round 2005-2009       0.415082
Most people can be trusted, WVS round 2010-2014       0.373906
Name: 0.8, Length: 25, dtype: float64

④ idxmin / idxmax
Вызов idxmax или idxmin для данных вернет индекс строки, в которой найден первый минимум / максимум. Однако это можно вызвать только для столбцов с некоторым порядком их расположения.

IN:
data.iloc[:,:-1].idxmax() # We exclude the Continent Column
OUT:
Year                                               Afghanistan
Life Ladder                                            Denmark
Log GDP per capita                                       Qatar
Social support                                     New Zealand
                                                      ...     
Most people can be trusted, WVS round 1994-1998         Norway
Most people can be trusted, WVS round 1999-2004         Sweden
Most people can be trusted, WVS round 2005-2009         Norway
Most people can be trusted, WVS round 2010-2014    Netherlands
Length: 25, dtype: object

Это означает, например, что у Дании самый высокий Life Ladder, у Катара самый высокий Log GDP per capita и New Zealand самый высокий показатель социальной поддержки.

idxmin работает так же, как idxmax.

Резюме. Не забывайте, что вы можете применять все эти функции по столбцам (ось = 0) или по строкам (ось = 1).

Применить / Пользовательские функции:

Вы также можете написать собственные функции и использовать их в своих строках или столбцах. Есть два вида пользовательских функций:

  • Именованные функции
  • Лямбда-функции

Именованные функции - это функции, определяемые пользователем. Они определяются с помощью зарезервированного ключевого слова def следующим образом:

Именованные функции:

FUNCTION:
def above_1000_below_10(x):
    try:
        pd.to_numeric(x)
    except:
        return 'no number column'
    
    if x > 1000:
        return 'above_1000'
    elif x < 10:
        return 'below_10'
    else:
        return 'mid'
IN:
data['Year'].apply(above_1000_below_10)
OUT:
Country name
Afghanistan    above_1000
Afghanistan    above_1000
Afghanistan    above_1000
Afghanistan    above_1000
                  ...    
Zimbabwe       above_1000
Zimbabwe       above_1000
Zimbabwe       above_1000
Zimbabwe       above_1000
Name: Year, Length: 1704, dtype: object

Здесь мы определили функцию с именем above_1000_below_10 и применили ее к нашим данным.

Функция сначала проверяет, можно ли преобразовать значение в число, и, если нет, вернет «столбец без номера». В противном случае функция возвращает значение выше_1000, если значение выше 1000, и ниже_10, если значение ниже 10, в противном случае возвращает значение mid.

Лямбда-функции:
Для меня лямбда-функции встречаются гораздо чаще, чем именованные. По сути, это короткие одноразовые функции, предназначенные только для одноразового использования. Название звучит неуклюже, но как только вы освоите его, они станут довольно удобными. Например, мы могли бы сначала разделить столбец континента на пространство, а затем получить последнее слово результатов.

IN:
data['Continent'].apply(lambda x: x.split(' ')[-1])
OUT:
Country name
Afghanistan      Asia
Afghanistan      Asia
Afghanistan      Asia
Afghanistan      Asia
                ...  
Zimbabwe       Africa
Zimbabwe       Africa
Zimbabwe       Africa
Zimbabwe       Africa
Name: Continent, Length: 1704, dtype: object

Примечание. И именованные, и лямбда-функции мы применили к отдельным столбцам, а не ко всему фрейму данных. При применении функции к определенному столбцу функция идет строка за строкой. При применении функции ко всему DataFrame, функция идет столбец за столбцом, затем применяется ко всему столбцу и должна быть написана немного по-другому, например:

IN:
def country_before_2015(df):
    if df['Year'] < 2015:
        return df.name
    else:
        return df['Continent']
# Note the axis=1
data.apply(country_before_2015, axis=1)
OUT:
Country name
Afghanistan    Afghanistan
Afghanistan    Afghanistan
Afghanistan    Afghanistan
Afghanistan    Afghanistan
                  ...     
Zimbabwe            Africa
Zimbabwe            Africa
Zimbabwe            Africa
Zimbabwe            Africa
Length: 1704, dtype: object

В этом примере мы также идем строка за строкой (как указано в axis=1). Мы возвращаем имя строки (которое является индексом), когда Год этой строки меньше 2015 или же континент этой строки. Подобные задачи возникают, когда вам нужно выполнить условную очистку данных.

Объединение столбцов:

Иногда вы хотите сложить, вычесть или иным образом объединить два или несколько столбцов, что вряд ли может быть проще.

Допустим, мы хотим добавить Year и Life Ladder (я знаю, надуманный, но давайте сделаем это для аргументации).

IN:
data['Year'] + data['Life Ladder']
OUT:
Country name
Afghanistan    2011.723590
Afghanistan    2013.401778
Afghanistan    2014.758381
Afghanistan    2014.831719
                  ...     
Zimbabwe       2018.703191
Zimbabwe       2019.735400
Zimbabwe       2020.638300
Zimbabwe       2021.616480
Length: 1704, dtype: float64

То же самое с -, *, / и многими другими, вы также можете выполнять строковые операции, например:

IN:
data['Continent'] + '_' + data['Year'].astype(str)
OUT:
Country name
Afghanistan      Asia_2008
Afghanistan      Asia_2009
Afghanistan      Asia_2010
Afghanistan      Asia_2011
                  ...     
Zimbabwe       Africa_2015
Zimbabwe       Africa_2016
Zimbabwe       Africa_2017
Zimbabwe       Africa_2018
Length: 1704, dtype: object

Примечание. В приведенном выше примере мы хотим объединить два столбца в виде строк. Для этого мы должны интерпретировать data['Year'] как строку. Мы делаем это, используя .astype(str) в столбце. Для краткости мы не будем углубляться в типы и преобразование типов в этой статье, а обсудим эти темы в другой статье.

группа по

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

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

IN:
data.groupby(['Country name'])['Life Ladder'].max()
OUT:
Country name
Afghanistan    4.758381
Albania        5.867422
Algeria        6.354898
Angola         5.589001
                 ...   
Vietnam        5.767344
Yemen          4.809259
Zambia         5.260361
Zimbabwe       4.955101
Name: Life Ladder, Length: 165, dtype: float64

Допустим, мы хотим, чтобы в год страна с самым высоким Life Ladder.

IN:
data.groupby(['Year'])['Life Ladder'].idxmax()
OUT:
Year
2005    Denmark
2006    Finland
2007    Denmark
2008    Denmark
         ...   
2015     Norway
2016    Finland
2017    Finland
2018    Finland
Name: Life Ladder, Length: 14, dtype: object

Или многоуровневые группы, скажем, мы хотим, чтобы для каждого континента / года была комбинация страны с самым высоким Life Ladder.

IN:
data.groupby(['Year','Continent'])['Life Ladder'].idxmax()
OUT:
Year  Continent    
2005  Africa                  Egypt
      Asia             Saudi Arabia
      Europe                Denmark
      North America          Canada
                           ...     
2018  Europe                Finland
      North America          Canada
      Oceania           New Zealand
      South America           Chile
Name: Life Ladder, Length: 83, dtype: object

Как и раньше, мы можем использовать множество стандартных функций или пользовательских функций (названных или неназванных), например, чтобы вернуть случайную страну для каждой группы:

IN:
def get_random_country(group):
    return np.random.choice(group.index.values)
# Named function
data.groupby(['Year','Continent']).apply(get_random_country)
# Unnamed function
data.groupby(['Year','Continent']).apply(
  lambda group: np.random.choice(group.index.values)
)
OUT:
Year  Continent    
2005  Africa                  Egypt
      Asia                   Jordan
      Europe                 France
      North America          Mexico
                           ...     
2018  Europe           North Cyprus
      North America       Nicaragua
      Oceania             Australia
      South America           Chile
Length: 83, dtype: object

Примечание. groupby всегда возвращает ОДНО значение для каждой группы. Поэтому, если вы не группируете по столбцу, который содержит только уникальные значения, результатом будет меньший (агрегированный) набор данных.

преобразовать

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

IN:
data.groupby(['Country name'])['Life Ladder'].transform(sum)
OUT:
Country name
Afghanistan    40.760446
Afghanistan    40.760446
Afghanistan    40.760446
Afghanistan    40.760446
                 ...    
Zimbabwe       52.387015
Zimbabwe       52.387015
Zimbabwe       52.387015
Zimbabwe       52.387015
Name: Life Ladder, Length: 1704, dtype: float64

Где мы получаем сумму всех Life Ladder баллов для страны. Мы также могли:

IN:
data.groupby(['Country name'])['Life Ladder'].transform(np.median)
OUT:
Country name
Afghanistan    3.782938
Afghanistan    3.782938
Afghanistan    3.782938
Afghanistan    3.782938
                 ...   
Zimbabwe       3.826268
Zimbabwe       3.826268
Zimbabwe       3.826268
Zimbabwe       3.826268
Name: Life Ladder, Length: 1704, dtype: float64

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

IN:
data.groupby(['Country name'])['Life Ladder'].transform(np.median) \
- data['Life Ladder']
OUT:
Country name
Afghanistan    0.059348
Afghanistan   -0.618841
Afghanistan   -0.975443
Afghanistan   -0.048782
                 ...   
Zimbabwe       0.123077
Zimbabwe       0.090868
Zimbabwe       0.187968
Zimbabwe       0.209789
Name: Life Ladder, Length: 1704, dtype: float64

Эта статья должна дать вам пищу для размышлений. Изначально я также хотел включить аксессоры, манипуляции с типами и конкатенацию, слияние и объединение DataFrames, но, учитывая длину статьи, я переместил эти темы во вторую часть этой серии:



Тогда увидимся и продолжай исследовать!