ПОЛНОЕ РУКОВОДСТВО ПО ПАНДАМ - ЧАСТЬ I
Как освоить основную библиотеку анализа данных Python за 20 минут
Краткое руководство по основным функциям Pandas.
В этой статье рассматриваются типичные проблемы и задачи, с которыми регулярно сталкиваются начинающие специалисты по обработке данных и аналитики данных. Мы решим эти проблемы и решим их, используя самую мощную библиотеку Python для обработки и анализа данных, Pandas.
В этой статье мы рассмотрим следующие темы:
- "Настраивать"
- "Загрузка данных"
- Просмотр / Сортировка / Фильтрация данных
- Аналитические функции
Вы можете найти полную версию 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']
, ordata.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
позволяет выбирать строки (и столбцы) с помощью:
- ярлык / указатель или
- с логическим / условным поиском
Чтобы лучше объяснить первый пункт, а также лучше отличить его от 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, но, учитывая длину статьи, я переместил эти темы во вторую часть этой серии:
Тогда увидимся и продолжай исследовать!