Рубрики
Без рубрики

Данные, разбирающиеся с обычными операциями Pandas

Почему? Почему я пишу миллиардную статью о Munging Datas Pandas? Разве не другое искусство … Tagged with Python, начинающие, учебник.

Почему?

Почему я пишу миллиардную статью о Munging Datas Pandas? Разве другие статьи не достаточно? Может быть! Если вы уже прочитали несколько из них и чувствуете, что много знаете о пандах, возможно, ваше время будет лучше потратить на некоторые из этих материалов:

Начиная

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

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 Используя следующие шаблоны:

  1. DF [состояние]
  2. df.iloc и df.loc
  3. 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

Это работает нормально, пока:

  1. Ваша функция названа моя супер -функция и вы пытаетесь получить доступ DF.MY Super Featural
  2. Вы забываете, что обратная операция незаконна.

Это верно, вы не можете сделать это:

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 собака

Манипуляция

Давайте выполним некоторые общие задачи, чтобы манипулировать нашими данными. А именно:

  1. Значения фильтра
  2. Обрабатывать отсутствующие или неверные значения
  3. SQL-подобный присоединение к двум DataFrame s
  4. 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 реализует некоторые основные задачи по сбору данных, в частности:

  1. Извлечение метаданных и описательной статистики
  2. Фильтрация, вменение, соединение и группировка данных
  3. Оконные функции (например, щитка)
  4. Использование данных DateTime и индексация по времени
  5. Чтение и написание данных в файлы и базы данных

Счастливого спора!

Изображение по Cimberley на Pixabay

Оригинал: “https://dev.to/charlesdlandau/data-munging-with-common-pandas-operations-2il4”