Библиотека pandas
предоставляет быстрые, гибкие и выразительные структуры данных, которые делают работу с "реляционными" или "помеченными" данными простой и интуитивно понятной. Pandas
призван стать фундаментальным строительным блоком высокого уровня для практического анализа реальных данных в Python.
Pandas
хорошо подходит для разных типов данных:
pandas
.Две основные структуры данных, pandas.Series
(1-мерная) и pandas.DataFrame
(2-мерная), обрабатывают подавляющее большинство типичных случаев использования в финансах, статистике, социальных науках и многих областях техники. Библиотека pandas
построена на базе NumPy
и предназначена для хорошей интеграции в среду научных вычислений со многими другими сторонними библиотеками.
Для специалистов по обработке данных работа обычно делится на несколько этапов: обработка и очистка данных, их анализ/моделирование, последующая организация результатов анализа в форму, подходящую для построения графиков или отображения в виде таблиц. Библиотека pandas
- идеальный инструмент для всех этих задач.
pandas
в виртуальное окружение:Pandas
можно установить через pip
из PyPI.
# создаем виртуальное окружение, если нет $ python3 -m venv .venv --prompt VirtualEnv # активируем виртуальное окружение $ source .venv/bin/activate # ставим модуль `pandas` (VirtualEnv) Idea@Centre:~$ python3 -m pip install -U pandas
В Windows, рекомендуемый способ установки pandas
с помощью Miniconda. Miniconda позволяет создать минимальную автономную установку Python и использовать менеджер пакетов Conda
для установки дополнительных пакетов и создания виртуальной среды. Среда conda
похожа на виртуальную среду, которая позволяет указать конкретную версию Python и набор библиотек.
# установка `pandas` > conda create -c conda-forge -n name_env python pandas # активация среды `conda` > activate name_env
Для пользователей, которые не знакомы с Python, самый простой способ установить Python, pandas
и пакеты, составляющие стек PyData
(SciPy
, NumPy
, Matplotlib
и другие), - это использовать кроссплатформенный дистрибутив Anaconda
:
pandas
;pandas
;pandas
;pandas
;pandas
;pandas
;If/then
над данными в pandas
;pandas
;pandas
;pandas
;pandas
;pandas
;pandas
;pandas
Так как многие "будущие" пользователи pandas
в некоторой степени знакомы с программами для работы с электронными таблицами, такими как Excel (LibreOffice Calc), то по возможности здесь будут примеры того, как различные операции с электронными таблицами можно было бы выполнять с помощью pandas
.
Перевод общей терминологии:
pandas.DataFrame
- можно сравнить с рабочим листом электронных таблиц. pandas.Series
- можно сравнить со столбцом на рабочем листе электронных таблиц.pandas.Index
- можно сравнить с заголовками строк (числа строк в электронных таблицах).NaN
- можно сравнить с пустой ячейкой в электронных таблицах.pandas.DataFrame
Объект pandas.DataFrame
представляет собой двумерную помеченную структуру данных со столбцами потенциально разных типов. Об этом объекте можно думать как об электронной таблице, таблице SQL или наборе объектов pandas.Series
. Как правило, это наиболее часто используемый объект библиотеки pandas
.
pandas.Series
Объект pandas.Series
- это одномерный помеченный массив, способный хранить данные любого типа (целые числа, строки, числа с плавающей запятой, объекты Python и т. д.). Метки осей в совокупности называются индексом. Другими словами, Series
представляет один столбец DataFrame
. Работа с серией аналогична ссылке на столбец электронной таблицы.
>>> import pandas as pd >>> import numpy as np >>> d = {"A": ['one', 'two', 'three'], "B": [1, 2, 3]} >>> df = pd.DataFrame(d) >>> df # A B # 0 one 1 # 1 two 2 # 2 three 3 # извлечем столбец `A` и посмотрим его тип >>> type(df['A']) # <class 'pandas.core.series.Series'>
pandas.Index
Каждый pandas.DataFrame
и pandas.Series
имеют индекс pandas.Index
, который представляет собой метки строк данных. В pandas
, если индекс не указан, то по умолчанию используется RangeIndex
(первая строка = 0, вторая строка = 1 и т.д.), аналогично числам строк в электронных таблицах.
В pandas
индексам можно задать одно (или несколько) уникальных значений, что похоже на использование столбца в качестве идентификатора строки на листе. В отличие от большинства электронных таблиц, эти значения индекса фактически можно использовать для ссылки на строки. Например, в электронных таблицах, ссылаться на первую строку можно как A1:Z1
, а в pandas
можно использовать DataFrame.loc[number_row]
.
Например, построим фрейм данных из значений. В электронной таблице значения можно вводить непосредственно в ячейки.
>>> import pandas as pd >>> import numpy as np >>> df = pd.DataFrame({"col1": [1, 3, 5], "col2": [2, 4, 6]}) >>> df # col1 col2 # 0 1 2 # 1 3 4 # 2 5 6
Смотрим как можно взять набор данных из строки и из колонки:
# набор данных из второй строки >>> df.loc[1] # col1 3 # col2 4 # Name: 1, dtype: int64 # набор данных из второго столбца >>> df['col2'] # 0 2 # 1 4 # 2 6 # Name: col2, dtype: int64
Значения индекса являются постоянными, следовательно, если изменить порядок строк в
DataFrame
, то метка конкретной строки не изменится.
Например, отсортируем значения первой колонки по убыванию и убедимся, что индексы в строках не поменялись:
# возможна сортировка данных по нескольким колонкам >>> df.sort_values(by=['col1'], ascending=False) # col1 col2 # 2 5 6 # 1 3 4 # 0 1 2
Фрейм данных pandas
может быть создан множеством различных способов, но для небольшого числа значений удобно указывать его как словарь Python, где ключами являются имена столбцов, а значениями - данные.
>>> import pandas as pd >>> import numpy as np >>> d = { "A": ['one', 'two', 'three', 'four', 'five'], "B": [1, 2, 3, 4, 5], "C": [0.1, 0.2, 0.3, 0.4, 0.5] } >>> df = pd.DataFrame(d) >>> df # A B C # 0 one 1 0.1 # 1 two 2 0.2 # 2 three 3 0.3 # 3 four 4 0.4 # 4 five 5 0.5
Свойства DataFrame.index
или DataFrame.columns
отображает все индексы и столбцы соответственно:
>>> df.index # RangeIndex(start=0, stop=5, step=1) >>> df.columns # Index(['A', 'B', 'C'], dtype='object')
pandas
И Excel, и pandas
могут импортировать данные из разных источников в разных форматах. Для работы с примерами загрузим и отобразим набор данных советов из тестов pandas
, который представляет собой файл CSV. В Excel сначала необходимо скачать файл, а затем открыть как CSV. В pandas
можно просто передать URL-адрес или локальный путь к CSV-файлу в метод pandas.read_csv()
:
url = ( "https://raw.githubusercontent.com/pandas-dev" "/pandas/main/pandas/tests/io/data/csv/tips.csv" ) >>> tips = pd.read_csv(url) >>> tips # total_bill tip sex smoker day time size # 0 16.99 1.01 Female No Sun Dinner 2 # 1 10.34 1.66 Male No Sun Dinner 3 # 2 21.01 3.50 Male No Sun Dinner 3 # 3 23.68 3.31 Male No Sun Dinner 2 # 4 24.59 3.61 Female No Sun Dinner 4 # .. ... ... ... ... ... ... ... # 239 29.03 5.92 Male No Sat Dinner 3 # 240 27.18 2.00 Female Yes Sat Dinner 2 # 241 22.67 2.00 Male Yes Sat Dinner 2 # 242 17.82 1.75 Male No Sat Dinner 2 # 243 18.78 3.00 Female No Thur Dinner 2 # # [244 rows x 7 columns]
Как и мастер импорта данных Excel, метод pandas.read_csv()
может принимать ряд аргументов, определяющих способ анализа данных. Например, если бы данные были разделены табуляцией и не имели имен столбцов, то команда pandas
была бы такой:
# например данные разделены табуляцией tips = pd.read_csv("tips.csv", sep="\t", header=None) # метод `pandas.read_table` - это псевдоним для # `pandas.read_csv()` с разделителем табуляцией tips = pd.read_table("tips.csv", header=None)
Excel открывает различные форматы файлов электронных таблиц, дважды щелкнув по нем или используя меню "Открыть". В pandas
можно использовать специальные методы для чтения и записи в файлы Excel.
Например сохраним считанные данные в формате Excel
>>> tips.to_excel("./tips.xlsx", sheet_name="Лист1")
Файлы с расширением .xlsx
будут записаны с использованием xlsxwriter
(если доступно) или openpyxl
. Если необходимо получить доступ к данным в файле Tips.xlsx
, то можно считать их:
>>> pd.read_excel("./tips.xlsx", sheet_name="Лист1", index_col=0)
pandas
Программы для работы с электронными таблицами будут отображать данные только на экране и нет необходимости ограничивать вывод. В pandas
дела обстоят немного по другому. По умолчанию pandas
обрезает вывод больших фреймов данных и показывает несколько первых и последних строк. Такое поведение можно переопределить, используя метод DataFrame.head(n)
для вывода на экран n
первых строк или DataFrame.tail(n)
последних строк. Для отрицательных значений n
эти методы покажут строки, кроме первых/последних n
строк в зависимости от метода.
# Отобразим первые 7 строк данных >>> tips.head(7) # total_bill tip sex smoker day time size # 0 16.99 1.01 Female No Sun Dinner 2 # 1 10.34 1.66 Male No Sun Dinner 3 # 2 21.01 3.50 Male No Sun Dinner 3 # 3 23.68 3.31 Male No Sun Dinner 2 # 4 24.59 3.61 Female No Sun Dinner 4 # 5 25.29 4.71 Male No Sun Dinner 4 # 6 8.77 2.00 Male No Sun Dinner 2
Метод DataFrame.info()
даст представление о том, что содержит набор данных (название столбцов, количество строк, тип данных в строках и т.д.):
>>> tips.info() # <class 'pandas.core.frame.DataFrame'> # RangeIndex: 244 entries, 0 to 243 # Data columns (total 8 columns): # # Column Non-Null Count Dtype # --- ------ -------------- ----- # 0 Unnamed: 0 244 non-null int64 # 1 total_bill 244 non-null float64 # 2 tip 244 non-null float64 # 3 sex 244 non-null object # 4 smoker 244 non-null object # 5 day 244 non-null object # 6 time 244 non-null object # 7 size 244 non-null int64 # dtypes: float64(2), int64(2), object(4) # memory usage: 15.4+ KB
Метод DataFrame.describe()
показывает краткую статистическую сводку данных:
>>> tips.describe() # total_bill tip size # count 244.000000 244.000000 244.000000 # mean 19.785943 2.998279 2.569672 # std 8.902412 1.383638 0.951100 # min 3.070000 1.000000 1.000000 # 25% 13.347500 2.000000 2.000000 # 50% 17.795000 2.900000 2.000000 # 75% 24.127500 3.562500 3.000000 # max 50.810000 10.000000 6.000000
Метод DataFrame.mean()
рассчитает среднее значение для столбца с числовым значением:
>>> tips[['total_bill', 'tip']].mean() # total_bill 19.785943 # tip 2.998279 # dtype: float64
Методы
DataFrame.min()
иDataFrame.max()
покажут соответственно минимальное и максимальное значение для столбца с числовым значением, а методDataFrame.sum()
посчитает сумму каждого столбца, содержащего числовые данные.
>>> tips[['total_bill', 'tip']].sum() # total_bill 4339.77 # tip 731.58 # dtype: float64
pandas
Информация об индексных метках осей в объектах pandas
служит многим целям:
DataFrame
Используемые понятия:
DataFrame
при помощи аргумента index
и могут быть, например, датами или строками. Если при создании DataFrame
аргумент index
не задан, то pandas
автоматически создает метки строк, которые будут совпадать с их позицией.Будем извлекать данные 2 строки столбца 'total_bill':
# медленный способ, но наиболее интуитивно понятный # использует индексные метки столбца и стоки соответственно >>> tips['total_bill'][1] # 10.34 # медленный способ использует индексную метку столбца # и метод `dict.values`, который принимает позицию строки # (обратите внимание на скобки `.values[]`) >>> tips['total_bill'].values[1] # 10.34 # быстрый способ - использует индексные метки # строки и столбца соответственно >>> tips.loc[1, 'total_bill'] # 10.34 # быстрый способ - использует позиции # строки и столбца соответственно >>> tips.iloc[1, 0] # 10.34 # самый быстрый способ использует индексные # метки строки и столбца соответственно >>> tips.at[1, 'total_bill'] # 10.34 # самый быстрый способ - использует позиции # строки и столбца соответственно >>> tips.iat[1, 0] # 10.34
Описание методов:
DataFrame
с помощью метода dict.values()
- это встроенный метод словаря Python, который возвращает объект словаря view
. Объект view
содержит значения словаря в виде списка.DataFrame.loc[]
обеспечивает доступ к группе строк и столбцов по индексным меткам строки и столбца в данном DataFrame
.Dataframe.iloc[]
обеспечивает доступ к группе строк и столбцов по позиции строки и столбца в DataFrame
. Например, когда индексная метка фрейма данных отличается от числовой серии 0, 1, 2, 3….n или в случае, если пользователь не знает индексную метку.DataFrame.at[]
обеспечивает доступ к конкретному значению ячейки по индексным меткам строки и столбца в данном DataFrame
. Похож на Dataframe.loc[]
, только DataFrame.at[]
возвращает одно значение и поэтому выполняется быстрее. Является предпочтительным при изменении данных в конкретной ячейке.DataFrame.iat[]
обеспечивает доступ к конкретному значению ячейки по позиции строки и столбца в DataFrame
. Похож на Dataframe.iloc[]
, только DataFrame.iat[]
возвращает одно значение и поэтому выполняется быстрее. Является предпочтительным при изменении данных в конкретной ячейке.DataFrame
Изменение данных конкретной ячейки DataFrame
происходит простым присвоением:
# изменяем данные методом `DataFrame.at[]` >>> tips.at[5, 'total_bill'] # 25.29 >>> tips.at[5, 'total_bill'] = tips.at[5, 'total_bill'] + 10 >>> tips.at[5, 'total_bill'] # 35.29 # изменяем данные методом `DataFrame.iat[]` >>> tips.iat[6, 1] # 2.0 >>> tips.iat[6, 0] # 8.77 >>> tips.iat[6, 1] = tips.iat[6, 0] * tips.iat[6, 1] >>> tips.iat[6, 1] # 17.54
DataFrame
.Основы индексации данных в pandas
заключаются в следующем:
Операция | Синтаксис | Результат |
Выбор столбцов | df[`col`] | Series |
Выбор строк по индексным меткам | df.loc[label] | Series |
Выбор строки по позиции | df.iloc[loc] | Series |
Срез строк по позиции | df[5:10] | DataFrame |
Выбор строк по логическому вектору | df[bool_vec] | DataFrame |
Получение доступа к серии данных столбца по его индексной метке в DataFrame
.
# набор данных 'total_bill' >>> tips['total_bill'].head() # 0 16.99 # 1 10.34 # 2 21.01 # 3 23.68 # 4 24.59 # Name: total_bill, dtype: float64
Получение доступа к данным нескольких столбцов по их индексным меткам в DataFrame
.
# данные столбцов 'total_bill' и 'size' >>> tips[['total_bill','size']].head() total_bill size 0 16.99 2 1 10.34 3 2 21.01 3 3 23.68 2 4 24.59 4
Получить доступ к данным нескольких строк в DataFrame
можно указав срез строк внутри []
:
>>> tips[5:11] # total_bill tip sex smoker day time size # 5 25.29 4.71 Male No Sun Dinner 4 # 6 8.77 2.00 Male No Sun Dinner 2 # 7 26.88 3.12 Male No Sun Dinner 4 # 8 15.04 1.96 Male No Sun Dinner 2 # 9 14.78 3.23 Male No Sun Dinner 2 # 10 10.27 1.71 Male No Sun Dinner 2
Здесь используется все преимущества срезов:
# выбираем каждую 5 строку >>> tips[::5].head() # total_bill tip sex smoker day time size # 0 16.99 1.01 Female No Sun Dinner 2 # 5 25.29 4.71 Male No Sun Dinner 4 # 10 10.27 1.71 Male No Sun Dinner 2 # 15 21.58 3.92 Male No Sun Dinner 2 # 20 17.92 4.08 Male No Sat Dinner 2 # разворачиваем строки >>> tips[::-1].head() # total_bill tip sex smoker day time size # 243 18.78 3.00 Female No Thur Dinner 2 # 242 17.82 1.75 Male No Sat Dinner 2 # 241 22.67 2.00 Male Yes Sat Dinner 2 # 240 27.18 2.00 Female Yes Sat Dinner 2 # 239 29.03 5.92 Male No Sat Dinner 3
Срез данных в pandas
можно получить методами DataFrame.loc[]
или DataFrame.iloc[]
.
Использование метода DataFrame.loc[]
:
# получаем срез индексных меток строк с 5 по 10, # которые расположены в столбцах 'total_bill' и 'tip' >>> tips.loc[5:10, ['total_bill', 'tip']] # total_bill tip # 5 35.29 4.71 # 6 8.77 17.54 # 7 26.88 3.12 # 8 15.04 1.96 # 9 14.78 3.23 # 10 10.27 1.71 # получаем срез индексных меток строк с 5 по 10, которые # расположены в срезе индексных меток столбцов с 'tip' по 'smoker' >>> tips.loc[5:10, 'tip':'smoker'] # tip sex smoker # 5 4.71 Male No # 6 17.54 Male No # 7 3.12 Male No # 8 1.96 Male No # 9 3.23 Male No # 10 1.71 Male No
Получаем тоже самое, только используя метод DataFrame.iloc[]
:
# получаем срез данных по позиции строк и столбцов # в данном случае индексные метки строк совпадают с их позицией >>> tips.iloc[5:11, 0:2] # total_bill tip # 5 35.29 4.71 # 6 8.77 17.54 # 7 26.88 3.12 # 8 15.04 1.96 # 9 14.78 3.23 # 10 10.27 1.71 # получаем срез индексных меток строк с 5 по 10, которые # расположены в срезе индексных меток столбцов с 'tip' по 'smoker' >>> tips.iloc[5:11, 1:4] # tip sex smoker # 5 4.71 Male No # 6 17.54 Male No # 7 3.12 Male No # 8 1.96 Male No # 9 3.23 Male No # 10 1.71 Male No
В электронных таблицах формулы часто создаются в отдельных ячейках, а затем перетаскиваются в другие ячейки, для вычисления других столбцов. В pandas
можно выполнять операции напрямую с целыми столбцами.
Библиотека pandas
предоставляет векторизованные операции, указывая отдельные Series
в DataFrame
. Новые столбцы можно назначить таким же образом.
# изменяем данные столбца "total_bill" >>> tips["total_bill"] = tips["total_bill"] - 2 # создаем новый столбец "new_bill" и вставляем # в него данные расчета `tips["total_bill"] / 2` >>> tips["new_bill"] = tips["total_bill"] / 2 # смотрим изменения >>> tips[['total_bill', 'new_bill']].head() # total_bill new_bill # 0 14.99 7.495 # 1 8.34 4.170 # 2 19.01 9.505 # 3 21.68 10.840 # 4 22.59 11.295
Обратите внимание, что НЕ НУЖНО указывать
pandas
выполнять вычитание по ячейкам, библиотека сделает это сама.
pandas
по условиюМетод
.apply()
предоставляется как объектомpandas.Series
так иpandas.DataFrame
.
Метод DataFrame.apply()
применяет пользовательскую функцию вдоль оси axis
(передается как аргумент) к каждой строке/столбцу DataFrame
в зависимости от того что передается в axis
(может принимать 0 или 'index'
, 1 или 'columns'
, по умолчанию 0).
Такой же метод есть у объекта Series.apply()
, который не имеет аргумента axis
в виду размерности 1d. Зато этот метод имеет общие (для обоих объектов) аргументы, которые в свою очередь принимают дополнительные аргументы для пользовательской функции: arg=()
- позиционные в виде кортежа и **kwargs
- ключевые аргументы в виде словаря.
Сама пользовательская функция по умолчанию принимает один аргумент значения столбца/строки, в зависимости от того что передается в axis
. У объекта Series
, функцией также может быть функция NumPy
(например, numpy.sum()
), которая применяется ко всей серии или функция Python, которая работает только с отдельными значениями.
Немного запутанно... Лучше посмотреть на примере. Изменять будем серию tips['total_bill']
, значение умножаем на 10 если оно < 8:
# создадим серию, на основе которой потом проверим изменения >>> s = tips[tips['total_bill'] < 8]['total_bill'] >>> s # 67 3.07 # 92 5.75 # 111 7.25 # 149 7.51 # 172 7.25 # 195 7.56 # 218 7.74 # Name: total_bill, dtype: float64 # определим пользовательскую функцию, которая # будет применяться к каждому элементу серии def my_func(row): if row < 8: return row * 10 return row # изменяем `Series` простым присваиванием >>> tips['total_bill'] = tips['total_bill'].apply(my_func) # функцию можно записать как `lambda` - функцию # tips['total_bill'] = tips['total_bill'].apply(lambda x: x*10 if x<8 else x) # проверяем >>> tips.loc[s.index, s.name] # 67 30.7 # 92 57.5 # 111 72.5 # 149 75.1 # 172 72.5 # 195 75.6 # 218 77.4 # Name: total_bill, dtype: float64
DataFrame
Получаем срез необходимых данных (новый DataFrame
), например, одним из вышеуказанных способов и сохраняем его в переменную:
>>> slice_tips = tips.iloc[100:106, 0:3] >>> slice_tips # total_bill tip sex # 100 11.35 2.50 Female # 101 15.38 3.00 Female # 102 44.30 2.50 Female # 103 22.42 3.48 Female # 104 20.92 4.08 Female # 105 15.36 1.64 Male
Библиотека pandas
индексирует как строки, так и столбцы DataFrame
, а также и имеет атрибуты DataFrame.index
и DataFrame.columns
, которые содержат списки индексных меток строк и столбцов соответственно:
# смотрим индексные метки полученного `DataFrame` >>> slice_tips.columns # Index(['total_bill', 'tip', 'sex'], dtype='object') >>> slice_tips.index # RangeIndex(start=100, stop=106, step=1)
Располагая данной информацией можно легко пройтись по каждой ячейке полученного DataFrame
:
# пройдемся построчно for row in slice_tips.index: # извлекая данные из каждой колонки for col in slice_tips.columns: # получаем элемент среза данных и что-то # с ним делаем, например, можно изменить данные print(f'Строка: {row}, столбец {col}:', slice_tips.at[row, col]) # Строка: 100, столбец total_bill: 11.35 # Строка: 100, столбец tip: 2.5 # Строка: 100, столбец sex: Female # Строка: 101, столбец total_bill: 15.38 # Строка: 101, столбец tip: 3.0 # Строка: 101, столбец sex: Female # Строка: 102, столбец total_bill: 44.3 # Строка: 102, столбец tip: 2.5 # Строка: 102, столбец sex: Female # Строка: 103, столбец total_bill: 22.42 # Строка: 103, столбец tip: 3.48 # Строка: 103, столбец sex: Female # Строка: 104, столбец total_bill: 20.92 # Строка: 104, столбец tip: 4.08 # Строка: 104, столбец sex: Female # Строка: 105, столбец total_bill: 15.36 # Строка: 105, столбец tip: 1.64 # Строка: 105, столбец sex: Male
Обратите внимание, что
pandas.Series
также имеет атрибутыSeries.index
, но не имеет атрибута.columns
, т.к. серия содержит один столбецDataFrame
.
>>> type(tips["total_bill"]) >>> tips["total_bill"].index # RangeIndex(start=0, stop=244, step=1)
pandas
В электронных таблицах можно выбирать нужные столбцы следующим образом:
Так как названия столбцов электронной таблицы располагаются в первой строке, то переименование столбца - это просто изменение текста в первой ячейке столбца.
Те же операции представлены ниже для библиотеки pandes
.
pandas
Отображаем, а при необходимости можно сохранить определенные столбцы в новый DataFrame
, а затем записать в результирующий файл.
# например, отобразим определенные столбцы >>> tips[["sex", "total_bill", "tip"]].head() # sex total_bill tip # 0 Female 14.99 1.01 # 1 Male 8.34 1.66 # 2 Male 19.01 3.50 # 3 Male 21.68 3.31 # 4 Female 22.59 3.61 # Отобразим столбцы срезом позиции >>> tips[tips.columns[0:5]].head() # total_bill tip sex smoker day # 0 16.99 1.01 Female No Sun # 1 10.34 1.66 Male No Sun # 2 21.01 3.50 Male No Sun # 3 23.68 3.31 Male No Sun # 4 24.59 3.61 Female No Sun
pandas
# удаление одного столбца по индексной метке >>> tips = tips.drop(columns="months_between") # удаление нескольких столбцов по индексным меткам >>> tips = tips.drop(columns=["date1_next", "date2_month", "date1_year", "bucket"]) # удаление нескольких столбцов по срезу позиции # получим названия колонок в заданных позициях >>> tips.columns[5:7] # Index(['time', 'size'], dtype='object') # подставляем данные в метод `DataFrame.drop` # tips.drop(columns=tips.columns[5:7])
Обратите внимание, что для сохранения результата необходимо пересохранить
DataFrame
в переменнуюtips
или создать новую.
pandas
# переименование одного столбца >>> tips = tips.rename(columns={"new_bill": "total_bill_2"}) # переименование нескольких столбцов >>> tips = tips.rename(columns={"date1": "dt1", "date2": "dt2"}) >>> tips.head() # total_bill tip sex smoker day time size total_bill_2 dt1 dt2 # 0 14.99 1.01 Female No Sun Dinner 2 7.495 2023-01-15 2024-02-15 # 1 8.34 1.66 Male No Sun Dinner 3 4.170 2023-01-15 2024-02-15 # 2 19.01 3.50 Male No Sun Dinner 3 9.505 2023-01-15 2024-02-15 # 3 21.68 3.31 Male No Sun Dinner 2 10.840 2023-01-15 2024-02-15 # 4 22.59 3.61 Female No Sun Dinner 4 11.295 2023-01-15 2024-02-15
Обратите внимание, что для сохранения результата необходимо пересохранить
DataFrame
в переменнуюtips
или создать новую.
pandas
Удаление строк по индексным меткам:
# удаление конкретной строки по индексной метке >>> tips = tips.drop(index=15) # можно видеть, что 15 строки нет >>> tips.loc[14:16, 'tip'] # 14 3.02 # 16 1.67 # Name: tip, dtype: float64 # удаление нескольких строк по меткам >>> tips = tips.drop(index=[16, 20, 21]) >>> tips.loc[14:22, 'tip'] # 14 3.02 # 17 3.71 # 18 3.50 # 19 3.35 # 22 2.23 # Name: tip, dtype: float64
Удаление строк срезом позиций (не индексных меток):
>>> tips = tips.drop(tips.index[15:20]) >>> tips.iloc[14:17, 0:2] # total_bill tip # 14 14.83 3.02 # 24 19.82 3.18 # 25 17.81 2.34
Обратите внимание, что для сохранения результата необходимо пересохранить
DataFrame
в переменнуюtips
или создать новую.
pandas.DataFrame
Предполагая, что в качестве индекса используется RangeIndex
(с номерами 0, 1 и т. д.), можно использовать функцию pandas.concat()
для добавления строки в нижнюю часть DataFrame
.
# создаем строку с данными >>> new_row = pd.DataFrame([["E", 51, True]], ... columns=["class", "student_count", "all_pass"]) # добавляем >>> pd.concat([df, new_row]) # class student_count all_pass # 0 A 42 Yes # 1 A 35 Yes # 2 A 42 Yes # 3 B 50 No # 4 C 47 No # 5 D 45 Yes # 0 E 51 True
Обратите внимание, что для сохранения результата необходимо пересохранить
DataFrame
в переменнуюtips
или создать новую.
pandas
В Excel фильтрация осуществляется через графическое меню. В DataFrame
можно фильтровать несколькими способами. Наиболее интуитивным из них является использование логической индексации.
>>> tips[tips["total_bill"] > 10].head() # total_bill tip sex smoker day time size new_bill # 0 14.99 1.01 Female No Sun Dinner 2 7.495 # 2 19.01 3.50 Male No Sun Dinner 3 9.505 # 3 21.68 3.31 Male No Sun Dinner 2 10.840 # 4 22.59 3.61 Female No Sun Dinner 4 11.295 # 5 23.29 4.71 Male No Sun Dinner 4 11.645
Приведенный выше оператор просто передает DataFrame
серию объектов True
/False
, отбирая и возвращая все строки с True
.
Для комбинации нескольких условий pandas
использует &
(И), |
(ИЛИ) и ~
(НЕ):
>>> tips[(tips["total_bill"] > 10) & (tips["sex"] == "Male")].head() # total_bill tip sex smoker day time size # 1 10.34 1.66 Male No Sun Dinner 3 # 2 21.01 3.50 Male No Sun Dinner 3 # 3 23.68 3.31 Male No Sun Dinner 2 # 5 25.29 4.71 Male No Sun Dinner 4 # 7 26.88 3.12 Male No Sun Dinner 4 # для получения среза добавим в конец выражения необходимый метод >>> tips[(tips["total_bill"] > 10) & (tips["sex"] == "Male")].loc[100:106, ['total_bill', 'tip']] # total_bill tip # 105 15.36 1.64 # 106 20.49 4.06
If/then
над данными в pandas
Допустим, необходимо создать создать столбец сегмента со значениями low
и high
в зависимости от того, составляет ли total_bill
меньше или больше 10.
В электронных таблицах логическое сравнение можно выполнять с помощью условных формул. То есть в Excel используют формулу =IF(A2 < 10, "low", "high")
, которую необходимо перетащить на все ячейки в новом столбце bucket
.
Ту же операцию в pandas
можно выполнить, используя метод .where()
из numpy
.
>>> tips["bucket"] = np.where(tips["total_bill"] < 10, "low", "high") # отображаем нужные столбцы >>> tips[['total_bill', 'bucket']].head() # total_bill bucket # 0 14.99 high # 1 8.34 low # 2 19.01 high # 3 21.68 high # 4 22.59 high
pandas
В этом разделе будут использоваться даты, но временные метки timestamps
обрабатываются аналогично.
Функциональность даты можно разделить на две части: синтаксический анализ и вывод. В электронных таблицах значения дат обычно анализируются автоматически, хотя, если необходимо, есть функция DATEVALUE
. В pandas
необходимо явно преобразовать обычный текст в объекты datetime
либо при чтении из CSV, либо один раз в DataFrame
.
После анализа, электронные таблицы отображают даты в формате по умолчанию, хотя формат можно изменить. В pandas
, обычно нужно сохранять даты как объекты datetime
, пока выполняются вычисления с ними. Вывод частей дат (например, года) в электронных таблицах осуществляется с помощью функций даты, а в pandas свойствами datetime
.
# добавляем в `DataFrame` новые столбцы с датами >>> tips["date1"] = pd.Timestamp("2023-01-15") >>> tips["date2"] = pd.Timestamp("2024-02-15") # добавляем столбец со значением года >>> tips["date1_year"] = tips["date1"].dt.year # добавляем столбец со значением месяца >>> tips["date2_month"] = tips["date2"].dt.month # добавляем столбец с датой начала следующего месяца # (дата начала предыдущего месяца вычисляется знаком `-`) >>> tips["date1_next"] = tips["date1"] + pd.offsets.MonthBegin() # добавляем столбец с периодом >>> tips["months_between"] = (tips["date2"].dt.to_period("M") - ... tips["date1"].dt.to_period("M")) # отображаем нужные столбцы >>> tips[["date1", "date2", "date1_year", ... "date2_month", "date1_next", "months_between"]].head() # date1 date2 date1_year date2_month date1_next months_between # 0 2023-01-15 2024-02-15 2023 2 2023-02-01 <13 * MonthEnds> # 1 2023-01-15 2024-02-15 2023 2 2023-02-01 <13 * MonthEnds> # 2 2023-01-15 2024-02-15 2023 2 2023-02-01 <13 * MonthEnds> # 3 2023-01-15 2024-02-15 2023 2 2023-02-01 <13 * MonthEnds> # 4 2023-01-15 2024-02-15 2023 2 2023-02-01 <13 * MonthEnds>
Дополнительные сведения смотрите в разделе "Доступ к методам datetime объектов Series/index в pandas".
pandas
Сортировка в электронных таблицах осуществляется через диалог сортировки. В pandas
есть метод DataFrame.sort_values()
, который принимает список столбцов для сортировки.
>>> tips = tips.sort_values(["sex", "total_bill"]) >>> tips[['total_bill', 'tip', 'sex']].head() # total_bill tip sex # 67 1.07 1.00 Female # 92 3.75 1.00 Female # 111 5.25 1.00 Female # 145 6.35 1.50 Female # 135 6.51 1.25 Female
Обратите внимание, что отсортировать по убыванию можно передав ключевой аргумент
ascending=False
. Для сортировки данных по индексу (номерам строк) используйте методDataFrame.sort_index()
.
pandas
pandas
В электронных таблицах количество символов в тексте можно найти с помощью функции LEN
. Ее можно использовать с функцией TRIM
для удаления лишних пробелов.
=LEN(TRIM(A2))
В pandas
можно узнать длину строки символов с помощью Series.str.len()
. В Python 3 все строки являются строками Unicode. Функция len()
включает конечные пробелы. Чтобы исключить из подсчета пробелы, нужно дополнительно применить к строке функцию str.strip()
. В итоге вызов получиться следующий Series.str.strip().str.len()
.
>>> tips["time"].str.strip().str.len().head() # 67 6 # 92 6 # 111 6 # 145 5 # 135 5 # Name: time, dtype: int64
pandas
Функция электронной таблицы FIND
возвращает позицию подстроки, первый символ которой равен 1.
В pandas
можно найти позицию символа в столбце строк с помощью метода Series.str.find()
. Метод str.find()
ищет первую позицию подстроки. Если подстрока найдена, то метод возвращает ее позицию. Если не найден, возвращается -1. Имейте в виду, что индексы Python начинаются с нуля.
>>> tips["sex"].str.find("ale") >>> tips["sex"].str.find("ale") # 67 3 # 92 3 # 111 3 # 145 3 # 135 3 # .. # 182 1 # 156 1 # 59 1 # 212 1 # 170 1 # Name: sex, Length: 244, dtype: int64
pandas
В электронных таблицах есть формула MID
для извлечения подстроки из заданной позиции. Чтобы получить первый символ:
=MID(A2,1,1)
С помощью pandas
можно использовать нотацию среза []
для извлечения подстроки из строки по местоположениям. Имейте в виду, что индексы Python начинаются с нуля.
>>> tips["sex"].str[0:1] # 67 F # 92 F # 111 F # 145 F # 135 F # .. # 182 M # 156 M # 59 M # 212 M # 170 M # Name: sex, Length: 244, dtype: object
pandas
В Excel для этого можно использовать мастер преобразования текста в столбцы для разделения текста и получения определенного столбца.
Самый простой способ извлечь слова в pandas
- разделить строки пробелами, а затем ссылаться на слово по индексу. Обратите внимание, что есть более мощные подходы.
# создадим `DataFrame` >>> firstlast = pd.DataFrame({"String": ["John Smith", "Jane Cook"]}) >>> firstlast # String # 0 John Smith # 1 Jane Cook # добавляем столбец "First_Name" >>> firstlast["First_Name"] = firstlast["String"].str.split(" ", expand=True)[0] # добавляем столбец "Last_Name" >>> firstlast["Last_Name"] = firstlast["String"].str.rsplit(" ", expand=True)[1] >>> firstlast # String First_Name Last_Name # 0 John Smith John Smith # 1 Jane Cook Jane Cook
pandas
В электронных таблицах предусмотрены функции UPPER
, LOWER
и PROPER
для преобразования текста в верхний, нижний регистр и регистр заголовка соответственно.
Эквивалентными методами pandas
являются Series.str.upper()
, Series.str.lower()
и Series.str.title()
.
>>> firstlast = pd.DataFrame({"string": ["John Smith", "Jane Cook"]}) >>> firstlast # string # 0 John Smith # 1 Jane Cook # добавляем соответствующие столбцы с измененными данными >>> firstlast["upper"] = firstlast["string"].str.upper() >>> firstlast["lower"] = firstlast["string"].str.lower() >>> firstlast["title"] = firstlast["string"].str.title() >>> firstlast # string upper lower title # 0 John Smith JOHN SMITH john smith John Smith # 1 Jane Cook JANE COOK jane cook Jane Cook
pandas
В примерах слияния будут использоваться следующие таблицы:
>>> df1 = pd.DataFrame({"key": ["A", "B", "C", "D"], "value": np.random.randn(4)}) >>> df1 # key value # 0 A 0.872520 # 1 B 0.659742 # 2 C 1.423087 # 3 D -1.676140 >>> df2 = pd.DataFrame({"key": ["B", "D", "D", "E"], "value": np.random.randn(4)}) >>> df2 # key value # 0 B 0.255974 # 1 D -0.377506 # 2 D -0.164284 # 3 E 0.125792
В Excel объединение таблиц можно выполнить с помощью VLOOKUP
.
Фреймы данных pandas
имеют метод DataFrame.merge()
, который обеспечивает аналогичную функциональность. Данные не нужно сортировать заранее, а различные типы соединений выполняются с помощью ключевого аргумента how
.
>>> inner_join = df1.merge(df2, on=["key"], how="inner") >>> inner_join # key value_x value_y # 0 B 0.659742 0.255974 # 1 D -1.676140 -0.377506 # 2 D -1.676140 -0.164284 >>> left_join = df1.merge(df2, on=["key"], how="left") >>> left_join # key value_x value_y # 0 A 0.872520 NaN # 1 B 0.659742 0.255974 # 2 C 1.423087 NaN # 3 D -1.676140 -0.377506 # 4 D -1.676140 -0.164284 >>> right_join = df1.merge(df2, on=["key"], how="right") >>> right_join key value_x value_y # 0 B 0.659742 0.255974 # 1 D -1.676140 -0.377506 # 2 D -1.676140 -0.164284 # 3 E NaN 0.125792 >>> outer_join = df1.merge(df2, on=["key"], how="outer") >>> outer_join # key value_x value_y # 0 A 0.872520 NaN # 1 B 0.659742 0.255974 # 2 C 1.423087 NaN # 3 D -1.676140 -0.377506 # 4 D -1.676140 -0.164284 # 5 E NaN 0.125792
Слияние в pandas
имеет ряд преимуществ перед VLOOKUP
:
DataFrame
будет включать все столбцы из таблицы поиска, а не только один указанный столбецDataFrame
поддерживает более сложные операции объединенияpandas
Сводные таблицы из электронных таблиц могут быть воспроизведены в pandas
с помощью изменения формы и метода DataFrame.pivot_table()
. Возьмем за основу ранее используемый фрейм данных tips
и найдем среднее значение столбца tip
в зависимости от размера size
и пола sex
.
>>> tips[['tip', 'sex', 'size']] # tip sex size # 67 1.00 Female 1 # 92 1.00 Female 2 # 111 1.00 Female 1 # 145 1.50 Female 2 # 135 1.25 Female 2 # .. ... ... ... # 182 3.50 Male 3 # 156 5.00 Male 6 # 59 6.73 Male 4 # 212 9.00 Male 4 # 170 10.00 Male 3 # создаем сводную таблицу >>> table = pd.pivot_table( ... tips, values="tip", index=["size"], columns=["sex"], aggfunc=np.average ... ) >>> table # sex Female Male # size # 1 1.276667 1.920000 # 2 2.528448 2.614184 # 3 3.250000 3.476667 # 4 4.021111 4.172143 # 5 5.140000 3.750000 # 6 4.600000 5.850000
Для сохранения сводной таблицы, присвойте полученный вывод переменной Python.
pandas
Excel имеет встроенную функцию удаления повторяющихся значений. В pandas
это поддерживается через DataFrame.drop_duplicated()
.
>>> df = pd.DataFrame( ... { ... "class": ["A", "A", "A", "B", "C", "D"], ... "student_count": [42, 35, 42, 50, 47, 45], ... "all_pass": ["Yes", "Yes", "Yes", "No", "No", "Yes"], ... } ... ) # удаление дубликатов строк >>> df.drop_duplicates() # class student_count all_pass # 0 A 42 Yes # 1 A 35 Yes # 3 B 50 No # 4 C 47 No # 5 D 45 Yes # удаление строк с дублирующими записями в определенных столбцах >>> df.drop_duplicates(["class", "student_count"]) # class student_count all_pass # 0 A 42 Yes # 1 A 35 Yes # 3 B 50 No # 4 C 47 No # 5 D 45 Yes
pandas
Диалоговое окно поиска в Excel приведет вас к соответствующим ячейкам, одну за другой. В pandas
эта операция обычно выполняется сразу для всего столбца или DataFrame
с помощью условных выражений.
>>> tips.head() # total_bill tip sex smoker day time size total_bill_2 dt1 dt2 # 67 1.07 1.00 Female Yes Sat Dinner 1 0.535 2023-01-15 2024-02-15 # 92 3.75 1.00 Female Yes Fri Dinner 2 1.875 2023-01-15 2024-02-15 # 111 5.25 1.00 Female No Sat Dinner 1 2.625 2023-01-15 2024-02-15 # 145 6.35 1.50 Female No Thur Lunch 2 3.175 2023-01-15 2024-02-15 # 135 6.51 1.25 Female No Thur Lunch 2 3.255 2023-01-15 2024-02-15 # поиск во всем `DataFrame` >>> (tips == "Sat").head() # total_bill tip sex smoker day time size total_bill_2 dt1 dt2 # 67 False False False False True False False False False False # 92 False False False False False False False False False False # 111 False False False False True False False False False False # 145 False False False False False False False False False False # 135 False False False False False False False False False False # поиск в определенном столбце >>> tips["day"].str.contains("S").head() # 67 True # 92 False # 111 True # 145 False # 135 False
Замена данных в определенном столбце
>>> tips["smoker"] = tips["smoker"].str.replace("Yes", "---") >>> tips.head() # total_bill tip sex smoker day time size total_bill_2 dt1 dt2 # 67 1.07 1.00 Female --- Sat Dinner 1 0.535 2023-01-15 2024-02-15 # 92 3.75 1.00 Female --- Fri Dinner 2 1.875 2023-01-15 2024-02-15 # 111 5.25 1.00 Female No Sat Dinner 1 2.625 2023-01-15 2024-02-15 # 145 6.35 1.50 Female No Thur Lunch 2 3.175 2023-01-15 2024-02-15 # 135 6.51 1.25 Female No Thur Lunch 2 3.255 2023-01-15 2024-02-15
Метод DataFrame.replace()
в pandas
сравним с функцией Excel "Заменить все".
>>> tips.replace("Thur", "---").head() # total_bill tip sex smoker day time size total_bill_2 dt1 dt2 # 67 1.07 1.00 Female Yes Sat Dinner 1 0.535 2023-01-15 2024-02-15 # 92 3.75 1.00 Female Yes Fri Dinner 2 1.875 2023-01-15 2024-02-15 # 111 5.25 1.00 Female No Sat Dinner 1 2.625 2023-01-15 2024-02-15 # 145 6.35 1.50 Female No --- Lunch 2 3.175 2023-01-15 2024-02-15 # 135 6.51 1.25 Female No --- Lunch 2 3.255 2023-01-15 2024-02-15
pandas
В электронной таблице это можно сделать, нажав клавишу SHIFT+перетаскивание после ввода первого числа или введя первые два или три значения, а затем перетащив их.
В pandas
такого результата можно добиться, создав неиндексированную серию (например обычный список) и присвоив ее нужному срезу ячеек в конкретном столбце.
# создадим `DataFrame` >>> df = pd.DataFrame({"AAA": [1] * 8, "BBB": list(range(0, 8))}) >>> df # AAA BBB # 0 1 0 # 1 1 1 # 2 1 2 # 3 1 3 # 4 1 4 # 5 1 5 # 6 1 6 # 7 1 7 # создадим список >>> lst = list(range(1, 5)) >>> lst # [1, 2, 3, 4] # присвоим список срезу столбца "AAA" >>> df.loc[2:5, "AAA"] = lst >>> df # AAA BBB # 0 1 0 # 1 1 1 # 2 1 2 # 3 2 3 # 4 3 4 # 5 4 5 # 6 1 6 # 7 1 7
pandas
Если создать индексированную серию pandas.Series
, то при ее вставке pandas
будет сверять индексы строк столбца DataFrame
с индексами созданной серии, при этом данные с совпавшими индексами будут заменены на данные из серии, а данные с не совпавшими индексами будут потеряны. Другими словами произойдет выравнивание индексов. Смотрим на примере:
>>> df = pd.DataFrame({"AAA": [1] * 8, "BBB": list(range(0, 8))}) >>> df # AAA BBB # 0 1 0 # 1 1 1 # 2 1 2 # 3 1 3 # 4 1 4 # 5 1 5 # 6 1 6 # 7 1 7 # создадим индексированную Series # (обратите внимание на индексы серии) >>> s = pd.Series(range(1, 5)) >>> s # 0 1 # 1 2 # 2 3 # 3 4 # dtype: int64 # вставлять будем в срез столбца `DataFrame` # (обратите внимание на индексы серии столбца) >>> df.loc[2:5, "AAA"] # 2 1 # 3 2 # 4 3 # 5 4 # Name: AAA, dtype: int64 # присвоим серию срезу столбца "AAA" >>> df.loc[2:5, "AAA"] = s >>> df # AAA BBB # 0 1.0 0.0 # 1 1.0 1.0 # 2 3.0 2.0 # 3 4.0 3.0 # 4 NaN 4.0 # 5 NaN 5.0 # 6 1.0 6.0 # 7 1.0 7.0
Что произошло? При изменении данных pandas
проверил индексы серии столбца df.loc[2:5, "AAA"]
и индексы созданной серии s
, данные с совпавшими индексами (2 и 3) в DataFrame
изменились, данные в индексах (4 и 5) считаются потерянными (таких индексов во вставляемой серии нет).
Что бы достичь первого результата (смотри выше) необходимо создать Series
с одинаковыми индексами или вставить просто массив значений серии Series.values
без индексов:
>>> df = pd.DataFrame({"AAA": [1] * 8, "BBB": list(range(0, 8))}) # создадим индексированную Series # (обратите внимание на индексы серии) >>> s = pd.Series(range(1, 5), index=list(range(2, 6))) >>> s # 2 1 # 3 2 # 4 3 # 5 4 # dtype: int64 # присвоим серию срезу столбца "AAA" >>> df.loc[2:5, "AAA"] = s >>> df # AAA BBB # 0 1 0 # 1 1 1 # 2 1 2 # 3 2 3 # 4 3 4 # 5 4 5 # 6 1 6 # 7 1 7 # массив значений серии без индексов >>> s.values # array([1, 2, 3, 4])
Теперь индексы в обоих сериях совпали и потерь данных нет.