Почему?
Почему я пишу миллиардную статью о Munging Datas Pandas? Разве другие статьи не достаточно? Может быть! Если вы уже прочитали несколько из них и чувствуете, что много знаете о пандах, возможно, ваше время будет лучше потратить на некоторые из этих материалов:
- Python для анализа данных Pandas Bdfl Wes McKinney
- Повышение производительности команды Pandas Dev
- Введение в DASK для параллельных вычислений в Python
- Справочник по науке о данных Python от Джейка Вандерпласа
Начиная
Быстрое примечание: на протяжении всей этой статьи я собираюсь ссылаться на функции и наблюдения. Когда вы начнете работать с многомерными проблемами, это важное различие, но в этой статье функция такая же, как колонка электронной таблицы, а наблюдение такое же, как строка электронной таблицы.
import numpy as np import pandas as pd
Мы будем работать с некоторыми случайными данными, которые вы можете построить напрямую. Панды DataFrames
построены из Pandas Серия
Объекты, которые, в свою очередь, используют Numpy
Объекты и операции для многих из их поведения.
# Create a DataFrame with some randomized columns df = pd.DataFrame({ "str_feature": [np.random.choice(["dog", "cat", "snake"]) for _ in range(10000)], "int_feature": np.arange(10000) })
.head
Метод может захватить верхнюю часть n
ряды, пять по умолчанию.
df.head()
0 | 0 | змея |
1 | 1 | собака |
2 | 2 | кошка |
3 | 3 | кошка |
4 | 4 | собака |
df.head(10)
0 | 0 | змея |
1 | 1 | собака |
2 | 2 | кошка |
3 | 3 | кошка |
4 | 4 | собака |
5 | 5 | змея |
6 | 6 | змея |
7 | 7 | собака |
8 | 8 | кошка |
9 | 9 | собака |
Конечно, в некоторых обстоятельствах мы не хотим принимать первые 5 или 10. Мы можем взять псевдолупольный образец данных, используя .sample
Анкет Обратите внимание, что .sample
по умолчанию 1 наблюдение.
df.sample(5)
2476 | 2476 | кошка |
2793 | 2793 | собака |
3120 | 3120 | змея |
9338 | 9338 | змея |
3593 | 3593 | собака |
Мы можем получить основные типы, получив доступ .dtypes
атрибут.
df.dtypes
str_feature object int_feature int32 dtype: object
Следует подчеркнуть, что объект
Особенности менее эффективны для памяти, чем int
Особенности. Обсуждение основного dtypes
включен в Официальное руководство по началу работы Анкет
Описательная статистика может быть сообщена с помощью .describe
– Обратите внимание, что будут обобщены только численные особенности.
df.describe()
считать | 10000.00000 |
иметь в виду | 4999.50000 |
std | 2886.89568 |
мин | 0.00000 |
25% | 2499.75000 |
50% | 4999.50000 |
75% | 7499.25000 |
Максимум | 9999.00000 |
Описательная статистика в этом отчете имеет свои собственные методы, например, вот std
:
df.std()
int_feature 2886.89568 dtype: float64
Pandas-Profiling
это новый пакет, который создает гораздо более подробные отчеты из DataFrame
Анкет Вы можете проверить это Здесь Анкет
.size
Возвращает количество наблюдений, умноженных на количество функций.
df.size
20000
.info
Возвращает метаданные о DataFrame
df.info()
RangeIndex: 10000 entries, 0 to 9999 Data columns (total 2 columns): str_feature 10000 non-null object int_feature 10000 non-null int32 dtypes: int32(1), object(1) memory usage: 78.2+ KB
Выбор и задание
Мы склонны получить доступ к данным из DataFrame
Используя следующие шаблоны:
DF [состояние]
df.iloc
иdf.loc
df.at
&df.iat
# First pattern df[df['str_feature'] == 'snake'].head()
0 | 0 | змея |
5 | 5 | змея |
6 | 6 | змея |
14 | 14 | змея |
18 | 18 | змея |
# Second pattern df.loc[:, "int_feature"]
0 0 1 1 2 2 3 3 4 4 ... 9995 9995 9996 9996 9997 9997 9998 9998 9999 9999 Name: int_feature, Length: 10000, dtype: int32
Обратите внимание, что здесь одинокий :
означает получить доступ ко всему на этой оси, одинаково my_list [:]
Рассказывает простой питон список
Чтобы получить доступ ко всему.
.iloc
ведет себя одинаково, но работает только на топорах.
df.iloc[0:5]
0 | 0 | змея |
1 | 1 | собака |
2 | 2 | кошка |
3 | 3 | кошка |
4 | 4 | собака |
df.iloc[[0,5]]
0 | 0 | змея |
5 | 5 | змея |
.в
и .iat
используются аналогично .loc
и .iloc
Но они не могут получить более одного наблюдения.
df.at [0: 5, 'str_feature']
даст ValueError
из -за 0: 5
.
df.at [0]
даст TypeError
Потому что нет col
указано.
df.at[0, 'str_feature']
'snake'
Есть Обсуждение Так что о том, как вы можете получить некоторый рост производительности, приняв ограничения .в
.
Есть еще один метод, который я хочу покрыть для доступа к данным из нашего DataFrame
:
df.int_feature.head()
0 0 1 1 2 2 3 3 4 4 Name: int_feature, dtype: int32
Это работает нормально, пока:
- Ваша функция названа
моя супер -функция
и вы пытаетесь получить доступDF.MY Super Featural
- Вы забываете, что обратная операция незаконна.
Это верно, вы не можете сделать это:
df.new_feature = df[df['int_feature'] == 1]
c:\users\cdl\documents\personalproject\common_pandas_blog\.venv\lib\site-packages\ipykernel_launcher.py:1: UserWarning: Pandas doesn't allow columns to be created via a new attribute name - see https://pandas.pydata.org/pandas-docs/stable/indexing.html#attribute-access """Entry point for launching an IPython kernel.
Попытка вызвать предупреждение, по крайней мере, в более новых версиях Панды
. Мы можем подтвердить, что эта функция не была добавлена в DataFrame
:
df.head()
0 | 0 | змея |
1 | 1 | собака |
2 | 2 | кошка |
3 | 3 | кошка |
4 | 4 | собака |
Вместо этого давайте добавим новую функцию как SO:
df['new_feature'] = df.loc[:, 'str_feature'] == 'snake'
Теперь мы видим, что новая функция была добавлена правильно:
df.head()
0 | 0 | True | змея |
1 | 1 | False | собака |
2 | 2 | False | кошка |
3 | 3 | False | кошка |
4 | 4 | False | собака |
Манипуляция
Давайте выполним некоторые общие задачи, чтобы манипулировать нашими данными. А именно:
- Значения фильтра
- Обрабатывать отсутствующие или неверные значения
- SQL-подобный присоединение к двум
DataFrame
s - SQL-подобная группа
1. Фильтрационные значения
Чтобы отфильтровать значения, мы можем просто перезаписать наши df
переменная как SO:
# We're cat people now. df = df[df.loc[:, 'str_feature'] == 'cat']
df.head()
2 | 2 | False | кошка |
3 | 3 | False | кошка |
8 | 8 | False | кошка |
10 | 10 | False | кошка |
11 | 11 | False | кошка |
Так как мы только что отфильтровали наш DataFrame
Использовать только кошачьи ряды, наши змеи только new_feature
будет полон Ложный
ценности а также str_feature
будет все кошка
с Давайте отфильтруем наши столбцы.
df.drop(["new_feature", "str_feature"], axis=1, inplace=True) df.head()
2 | 2 |
3 | 3 |
8 | 8 |
10 | 10 |
11 | 11 |
Как обсуждалось Здесь , Ось
Аргумент говорит Панды
опуститься на основе Особенности
Ось и в месте
Аргумент делает это эквивалентом df.drop (...)
.
В качестве последнего примечания не делайте ошибку, предполагая, что любой из этих методов автоматически освободит память. Вместо этого используйте такие инструменты, как Python del
и Панды
Особенности ввода/вывода, такие как low_memory
или memory_map
Анкет
2. Обрабатывать отсутствующие или неверные значения
Другая общая задача, связанная с тем, чтобы справиться с случаями, когда данные в некотором роде являются недействительными. Давайте сделаем новый, грязный DataFrame
:
# Create a DataFrame with some randomized columns df = pd.DataFrame({ "str_feature": [np.random.choice(["dog", "cat", "snake"]) for _ in range(10000)], "int_feature": np.arange(10000), "messy_feature": [np.random.choice([1, None, np.nan, False, True, -1]) for _ in range(10000)] })
Мы могли бы сделать эти простейшие вещи, это очистить наши данные, фильтруя все наблюдения с np.nan
, используя .isna
:
# Note the rarely used ~ negation operator below clean_df = df[~df['messy_feature'].isna()] clean_df.sample(10)
4134 | 4134 | кошка | -1 |
2127 | 2127 | змея | -1 |
7014 | 7014 | собака | ЛОЖЬ |
922 | 922 | собака | ЛОЖЬ |
5451 | 5451 | собака | Истинный |
5059 | 5059 | кошка | -1 |
1289 | 1289 | змея | ЛОЖЬ |
5016 | 5016 | собака | -1 |
9765 | 9765 | змея | Истинный |
7094 | 7094 | кошка | 1 |
Как видите, этот метод очистит только НАН
ценности. Это здорово, но что, если мы хотим только значения, которые правдивы в Python?
df [df ['messy_feature']]
не сработает, потому что это ValueError
Чтобы фильтровать, используя столбец с НАН
ценности. Вместо этого мы должны фильтровать НАН
, затем проверьте наличие правдоподобия. Так же, как с простым питоном условным и
Заявление, условия проверяются слева направо.
clean_df = df[~df['messy_feature'].isna() & df['messy_feature']] clean_df.sample(10)
1049 | 1049 | змея | 1 |
4769 | 4769 | змея | -1 |
4924 | 4924 | кошка | -1 |
6907 | 6907 | змея | -1 |
5914 | 5914 | собака | Истинный |
6291 | 6291 | кошка | 1 |
5872 | 5872 | собака | 1 |
1794 | 1794 | кошка | 1 |
8987 | 8987 | кошка | 1 |
3848 | 3848 | собака | Истинный |
Наменение значений тоже легко:
clean_df = df.copy() clean_df['messy_feature'].fillna(clean_df['str_feature'], inplace=True) clean_df.sample(10)
8063 | 8063 | змея | змея |
3901 | 3901 | кошка | кошка |
3702 | 3702 | собака | -1 |
906 | 906 | собака | 1 |
8039 | 8039 | собака | собака |
2433 | 2433 | собака | -1 |
4996 | 4996 | змея | ЛОЖЬ |
3015 | 3015 | змея | змея |
8307 | 8307 | кошка | 1 |
1904 | 1904 | кошка | кошка |
И мы можем выйти за рамки простого .fillna
:
clean_df = df.copy() clean_df['messy_feature'] = clean_df.apply( lambda row: row['messy_feature'] if row['messy_feature'] == -1 else row['str_feature'], axis=1 ) clean_df.sample(10)
9217 | 9217 | змея | змея |
5018 | 5018 | змея | змея |
6383 | 6383 | собака | собака |
1297 | 1297 | кошка | -1 |
9701 | 9701 | собака | собака |
5192 | 5192 | кошка | кошка |
4018 | 4018 | собака | собака |
7619 | 7619 | змея | змея |
9890 | 9890 | собака | собака |
5102 | 5102 | змея | змея |
Просто будь осторожен с .подать заявление
поскольку он имеет потенциал для значительного снижения производительности. Смотрите: Улучшение производительности в пандах документах.
3. SQL-подобные соединения
Я определенно порекомендовал вам узнать о объединениях SQL, а затем просто сопоставить эти знания в Pandas Joins. Аргументы, такие как на
, Как
, слева
и Правильно
Все имеют смысл, если вы просто помните, как соединения будут работать в SQL.
merged_df = df.copy().join(df.copy(), how='left', lsuffix="_l", rsuffix="_r") merged_df
0 | 0 | 0 | змея | змея | ЛОЖЬ | ЛОЖЬ |
1 | 1 | 1 | кошка | кошка | НАН | НАН |
2 | 2 | 2 | змея | змея | ЛОЖЬ | ЛОЖЬ |
3 | 3 | 3 | собака | собака | ЛОЖЬ | ЛОЖЬ |
4 | 4 | 4 | змея | змея | Никто | Никто |
… | … | … | … | … | … | … |
9995 | 9995 | 9995 | змея | змея | ЛОЖЬ | ЛОЖЬ |
9996 | 9996 | 9996 | собака | собака | Истинный | Истинный |
9997 | 9997 | 9997 | змея | змея | НАН | НАН |
9998 | 9998 | 9998 | собака | собака | НАН | НАН |
9999 | 9999 | 9999 | собака | собака | 1 | 1 |
10000 рядов × 6 столбцов
4. SQL-подобная группа
Как и совет по объединению SQL, я настоятельно рекомендую узнать о SQL Group и позволить этому быть основой ваших Pandas .groupby
знания. Вот простая группа по сумме в пандах
df.groupby('str_feature').sum()
кошка | 16476822 |
собака | 16729175 |
змея | 16789003 |
Обратите внимание, что .группа по
должен быть вызван методом, который объединяет группировку. Посмотрим, что произойдет, если мы пренебрегаем этой частью …
df.groupby('str_feature')
.groupby
особенно мощный в сочетании с .apply
Метод … но мое более раннее предупреждение о производительности .подать заявление
все еще держит.
# Simple sum/size calculation df.groupby(['str_feature']).apply(lambda x: x.sum()/x.size)
кошка | 2485.191855 | 0.088688 |
собака | 2509.627213 | 0.086709 |
змея | 2504.326223 | 0.089499 |
Движущиеся окна
Часто мы хотим вычислять значения прокатки (например, суммирование катания):
df['rolling'] = df.rolling(3).sum() df.head(10)
НАН | 0 | 0 | змея | ЛОЖЬ |
НАН | 1 | 1 | кошка | НАН |
3.0 | 2 | 2 | змея | ЛОЖЬ |
6.0 | 3 | 3 | собака | ЛОЖЬ |
9.0 | 4 | 4 | змея | Никто |
12.0 | 5 | 5 | змея | НАН |
15.0 | 6 | 6 | змея | Никто |
18.0 | 7 | 7 | кошка | -1 |
21.0 | 8 | 8 | кошка | НАН |
24.0 | 9 | 9 | змея | НАН |
Индексация по времени
Связанная задача состоит в том, чтобы использовать DateTime, которое мы выполняем, используя Pd.to_dateTime
. Есть вспомогательные методы, такие как .astype
Для кастинга для типов DateTime, а также .set_index
Если мы хотим использовать DateTime
столбец как наш индекс.
import time df['Datetime'] = pd.to_datetime([time.time() - (86400*x) for x in range(df.shape[0])], unit='s') # Round to nearest day # see https://stackoverflow.com/a/13788301/10553976 df['Datetime'] = np.round(df['Datetime'].astype(np.int64), -9).astype('datetime64[ns]') # Set as index df = df.set_index('Datetime')
Ввод
Одной из самых привлекательных особенностей Pandas являются надежные возможности ввода -вывода. Весь список Здесь Но давайте посмотрим на два важных, CSV и SQL.
CSV
Написание CSV в пандах не может быть проще:
df.to_csv('./data.csv')
Чтение его обратно так же просто:
df = pd.read_csv('./data.csv')
Но подождите: давайте посмотрим на наш df
в настоящее время…
df.head()
НАН | 2019-10-08 21:51:50 | 0 | 0 | змея | ЛОЖЬ |
НАН | 2019-10-07 21:51:50 | 1 | 1 | кошка | НАН |
3.0 | 2019-10-06 21:51:50 | 2 | 2 | змея | ЛОЖЬ |
6.0 | 2019-10-05 21:51:50 | 3 | 3 | собака | ЛОЖЬ |
9.0 | 2019-10-04 21:51:50 | 4 | 4 | змея | НАН |
Мы не можем сделать вывод, что какой -либо конкретный столбец является индексом, так что .read_csv
создал DataFrame
с простым индексом вместо использования DateTimeIndex
Мы установили перед написанием в файл. Конечно, мы всегда могли бы использовать .set_index
После факта, но в сценариях с ограниченными ресурсами Мы хотим контролировать, насколько велик созданный DataFrame
будет. Поиск решения, мы приходим к .read_csv
документация и сила этого инструмента сразу становится очевидной:
- Читать CSV от URL (!!!!!), Путь или файл, как
- Укажите точное окно в файле CSV, чтобы читать с Kwargs, как
Заголовок
,index_col
,USECOLS
,NROS
,Skiprows
иСкипфутер
- Управление
НАН
с Kwargs, какna_filter
иna_values
Sep
для обработкиTSV
а также Другие ситуации разделителя- Kwargs, чтобы помочь сохранить память, как
low_memory
,memory_map
иdtype
Вооружившись множеством новых знаний о том, как читать файлы CSV с помощью Pandas, мы можем с уверенностью прочитать наши data.csv
Файл с DateTimeIndex
указано.
df = pd.read_csv('./data.csv', index_col='Datetime') df.head()
НАН | 2019-10-08 21:51:50 | 0 | змея | ЛОЖЬ |
НАН | 2019-10-07 21:51:50 | 1 | кошка | НАН |
3.0 | 2019-10-06 21:51:50 | 2 | змея | ЛОЖЬ |
6.0 | 2019-10-05 21:51:50 | 3 | собака | ЛОЖЬ |
9.0 | 2019-10-04 21:51:50 | 4 | змея | НАН |
Просто так, как это было изначально! Давайте очистим этот файл:
import os os.remove('./data.csv')
Примечание. Если вы не используете функции Pandas для манипуляции с данными, рассмотрите возможность использования stdlib CSV Библиотека для ввода/вывода вместо панд. Объекты Pandas создают некоторые накладные расходы в памяти, а сама Pandas – это нетривиальная зависимость.
SQL
Если вы хотите прочитать и записать данные, которые живет в базе данных SQL, Pandas использует Pd.read_sql
и .to_sql
Методы взаимодействия с базами данных. В этом примере мы будем использовать stdlib SQLite3
DBAPI, но панды могут интегрироваться с любым видом DB, который поддерживается SQLALCHEMY Анкет Также обратите внимание, что мы собираемся использовать Попробуйте ... наконец
Блок для обеспечения соблюдения .Закрыть
всегда называется.
import sqlite3 # DB operations try: # Connect to db conn = sqlite3.connect('data.db') # Write dataframe to table 'df' df.to_sql('df', conn, if_exists='replace') # Read a SELECT * query of the same table, specifying index column sqldf = pd.read_sql('SELECT * FROM df', conn, index_col='Datetime') # Ensure the connection closes finally: conn.close() # Cleanup file os.remove('data.db')
sqldf.head()
НАН | 2019-10-08 21:51:50 | 0 | змея | ЛОЖЬ |
НАН | 2019-10-07 21:51:50 | 1 | кошка | Никто |
3.0 | 2019-10-06 21:51:50 | 2 | змея | ЛОЖЬ |
6.0 | 2019-10-05 21:51:50 | 3 | собака | ЛОЖЬ |
9.0 | 2019-10-04 21:51:50 | 4 | змея | Никто |
Еще раз наши данные совершили переход к памяти на диск и обратно. Молодец, панд и молодец, дорогой читатель!
Завершая
В этой статье вы узнали, как Pandas реализует некоторые основные задачи по сбору данных, в частности:
- Извлечение метаданных и описательной статистики
- Фильтрация, вменение, соединение и группировка данных
- Оконные функции (например, щитка)
- Использование данных DateTime и индексация по времени
- Чтение и написание данных в файлы и базы данных
Счастливого спора!
Изображение по Cimberley на Pixabay
Оригинал: “https://dev.to/charlesdlandau/data-munging-with-common-pandas-operations-2il4”