Сообщить об ошибке.

Объект DataFrame модуля pandas в Python

DataFrame - двумерная структура данных pandas

Синтаксис:

import pandas as pd

df = pd.DataFrame(data=None, index=None, columns=None, dtype=None, copy=None)

Параметры:

  • data=None - принимает numpy.ndarray (структурированный или однородный), Iterable, dict или DataFrame. Словарь dict может содержать серии, массивы, константы, классы данных или объекты, подобные спискам. Если данные представляют собой словарь, то порядок столбцов соответствует порядку вставки. Если словарь содержит серии, то для которых определен индекс, он выравнивается по индексу. Это выравнивание также происходит, если данные представляют собой серию или сам DataFrame. Выравнивание выполняется для входных данных Series/DataFrame.
  • index=None - принимает pandas.Index() или массив. Метки строк, которые будут использоваться для результирующего DataFrame. По умолчанию будет RangeIndex(0, 1, 2, …, n), если во входных данных отсутствует информация об индексировании (не указан индекс).
  • columns=None - принимает pandas.Index() или массив. Метки столбцов, которые будут использоваться для результирующего DataFrame, когда данные их не содержат. По умолчанию используется RangeIndex(0, 1, 2, …, n). Если данные содержат метки столбцов, то будет выполнен выбор этих меток.
  • dtype=None - принимает dtype из numpy. Тип данных для принудительного использования во всем DataFrame. Допускается только один тип dtype. Если это неприемлемо, то лучше оставить None.
  • copy=None - принимает bool. Копирует входные данные. Для данных из dict, при значении по умолчанию None ведет себя как copy=True. Для данных DataFrame или 2d numpy.ndarray при значении по умолчанию None ведет себя как copy=False. Если данные представляют собой словарь, содержащий одну или несколько серий (возможно, разных типов), copy=False гарантирует, что эти входные данные не будут скопированы.

    Аргумент copy изменит поведение в pandas 3.0. Копирование при записи будет включено по умолчанию, а это означает, что все методы с аргументом copy будут использовать механизм отложенного копирования и игнорировать аргумент copy. Ключевой аргумент copy будет удален в будущей версии pandas. Можно уже сейчас получить будущее поведение и улучшения, включив копирование при записи pd.options.mode.copy_on_write = True

Возвращаемое значение:

  • двумерную структуру данных DataFrame

Описание:

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

Структура DataFrame также содержит помеченные оси (строки и столбцы). Арифметические операции выравниваются как по меткам строк, так и по столбцам. DataFrame можно рассматривать как dict-подобный контейнер для объектов pandas.Series.

Работа с двумерной структурой данных DataFrame

DataFrame - это двумерная помеченная структура данных со столбцами потенциально разных типов. О ней можно думать как об электронной таблице, таблице SQL или наборе объектов Series. Как правило, это наиболее часто используемый объект библиотеки panda. Как и Series, DataFrame принимает множество различных типов входных данных:

  • Словарь состоящий из:
    • 1d numpy.ndarray;
    • списков;
    • других словарей;
    • серий Series;
  • 2d numpy.ndarray;
  • Структурированный массив numpy.ndarray - это массив, тип данных которых представляет собой композицию более простых типов данных, организованных в виде последовательности;
  • pandas.Series;
  • pandas.DataFrame.

Вместе с данными можно дополнительно передать индексы (метки строк) и столбца (метки столбца) при помощи соответствующих аргументов index и columns. Если передать индексы, то гарантируется установка переданных меток строк и столбцов результирующего DataFrame. Таким образом, сочетание Series с определенным индексом отбросит все данные, не соответствующие переданному индексу. Результирующий индекс будет объединением индексов различных серий. Смотрим о чем идет речь:

>>> import pandas as pd
>>> import numpy as np

# допусти есть словарь, содержащий серии 
# с различным количеством элементов
d = {
    "one": pd.Series([1.0, 2.0, 3.0], index=["a", "b", "c"]),
    "two": pd.Series([1.0, 2.0, 3.0, 4.0], index=["a", "b", "c", "d"]),
}

# создаем `DataFrame`
>>> df = pd.DataFrame(d)
# обратите внимание как объединились серии
>>> df
#    one  two
# a  1.0  1.0
# b  2.0  2.0
# c  3.0  3.0
# d  NaN  4.0

# при создании `DataFrame` передадим `index` 
# произойдет выравнивание данных по меткам строк 
>>> pd.DataFrame(d, index=["d", "b", "a"])
#    one  two
# d  NaN  4.0
# b  2.0  2.0
# a  1.0  1.0

# теперь дополнительно передадим метки столбцов, 
# произойдет выравнивание данных по меткам строк и столбцов 
>>> pd.DataFrame(d, index=["d", "b", "a"], columns=["two", "three"])
#    two three
# d  4.0   NaN
# b  2.0   NaN
# a  1.0   NaN

Если метки осей не переданы, то они будут созданы из входных данных на основе правил здравого смысла.

Доступ к меткам строк и столбцов можно получить соответственно, обратившись к атрибутам индекса df.index и столбца df.columns:

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

>>> df.index
# Index(['a', 'b', 'c', 'd'], dtype='object')
>>> df.columns
# Index(['one', 'two'], dtype='object')

Создание DataFrame из разных структур данных

DataFrame из словаря содержащего numpy.ndarray или списки

Все numpy.ndarray должны быть одинаковой длины. Если передается индекс, то он также должен быть той же длины, что и массивы. Если индекс не передается, то результатом будет диапазон (n), где n - длина массива.

>>> d = {"one": [1.0, 2.0, 3.0, 4.0], "two": [4.0, 3.0, 2.0, 1.0]}
# метки строк создаются автоматически
>>> pd.DataFrame(d)
#    one  two
# 0  1.0  4.0
# 1  2.0  3.0
# 2  3.0  2.0
# 3  4.0  1.0

# метки строк передаются при создании
>>> pd.DataFrame(d, index=["a", "b", "c", "d"])
#    one  two
# a  1.0  4.0
# b  2.0  3.0
# c  3.0  2.0
# d  4.0  1.0

В качестве альтернативы можно использовать метод DataFrame.from_dict(), который принимает принимает словарь, состоящий из словарей или состоящий из массивоподобных последовательностей. Он работает как конструктор DataFrame, за исключением аргумента orient, который по умолчанию имеет значение 'columns', но для которого можно установить значение 'index', с целью использования ключей словаря в качестве меток строк.

pd.DataFrame.from_dict(dict([("A", [1, 2, 3]), ("B", [4, 5, 6])]))
#    A  B
# 0  1  4
# 1  2  5
# 2  3  6

Если передать orient='index', то ключи будут метками строк. В этом случае еще можно передать нужные имена столбцов:

>>> pd.DataFrame.from_dict(
...     dict([("A", [1, 2, 3]), ("B", [4, 5, 6])]),
...     orient="index",
...     columns=["one", "two", "three"],
... )
#    one  two  three
# A    1    2      3
# B    4    5      6

DataFrame из структурированного массива или массива записей

>>> import numpy as np
>>> data = np.zeros((2,), dtype=[("A", "i4"), ("B", "f4"), ("C", "a10")])
>>> data[:] = [(1, 2.0, "Hello"), (2, 3.0, "World")]
>>> pd.DataFrame(data)
#    A    B         C
# 0  1  2.0  b'Hello'
# 1  2  3.0  b'World'

>>> pd.DataFrame(data, index=["first", "second"])
#         A    B         C
# first   1  2.0  b'Hello'
# second  2  3.0  b'World'

>>> pd.DataFrame(data, columns=["C", "A", "B"])
#           C  A    B
# 0  b'Hello'  1  2.0
# 1  b'World'  2  3.0

Замечание. DataFrame не предназначен для работы точно так же, как двумерный numpy.ndarray.

В качестве альтернативы можно использовать метод DataFrame.from_records(), который принимает список кортежей или numpy.ndarray со структурированным типом dtype. Он работает аналогично обычному конструктору DataFrame, за исключением того, что результирующий индекс DataFrame может быть конкретным полем структурированного типа dtype.

>>> data
# array([(1, 2., b'Hello'), (2, 3., b'World')],
#       dtype=[('A', '<i4'), ('B', '<f4'), ('C', 'S10')])

>>> pd.DataFrame.from_records(data, index="C")
#           A    B
# C               
# b'Hello'  1  2.0
# b'World'  2  3.0

DataFrame из списка словарей

>>> data2 = [{"a": 1, "b": 2}, {"a": 5, "b": 10, "c": 20}]
# При создании `DataFrame` данные выравниваются
>>> pd.DataFrame(data2)
#    a   b     c
# 0  1   2   NaN
# 1  5  10  20.0

# создаем метки строк согласно аргументу `index`
>>> pd.DataFrame(data2, index=["first", "second"])
#         a   b     c
# first   1   2   NaN
# second  5  10  20.0

# данные в `DataFrame` попадут согласно 
# меткам запрашиваемых в `columns`
>>> pd.DataFrame(data2, columns=["a", "b"])
#    a   b
# 0  1   2
# 1  5  10

DataFrame из словаря кортежей

Можно автоматически создать многоиндексный фрейм, передав словарь кортежей.

>>> pd.DataFrame(
...     {
...         ("a", "b"): {("A", "B"): 1, ("A", "C"): 2},
...         ("a", "a"): {("A", "C"): 3, ("A", "B"): 4},
...         ("a", "c"): {("A", "B"): 5, ("A", "C"): 6},
...         ("b", "a"): {("A", "C"): 7, ("A", "B"): 8},
...         ("b", "b"): {("A", "D"): 9, ("A", "B"): 10},
...     }
... )
#        a              b      
#        b    a    c    a     b
# A B  1.0  4.0  5.0  8.0  10.0
#   C  2.0  3.0  6.0  7.0   NaN
#   D  NaN  NaN  NaN  NaN   9.0

DataFrame из Series

Результатом будет DataFrame с тем же индексом, что и входная серия, и с одним столбцом, имя которого является исходным именем серии (только если не указано другое имя столбца).

>>> ser = pd.Series(range(3), index=list("abc"), name="ser")
>>> ser
# a    0
# b    1
# c    2
# Name: ser, dtype: int64

>>> pd.DataFrame(ser)
#    ser
# a    0
# b    1
# c    2

DataFrame из списка именованных кортежей

Имена полей первого именованного кортежа в списке определяют столбцы DataFrame. Остальные именованные кортежи (или кортежи) просто распаковываются, и их значения передаются в строки DataFrame. Если какой-либо из этих кортежей короче первого именованного кортежа, то последующие столбцы в соответствующей строке помечаются как пропущенные значения NaN. Если какой-либо из них длиннее первого именованного кортежа, возникает ошибка ValueError.

>>> from collections import namedtuple
>>> Point = namedtuple("Point", "x y")
>>> pd.DataFrame([Point(0, 0), Point(0, 3), (2, 3)])
#    x  y
# 0  0  0
# 1  0  3
# 2  2  3

>>> Point3D = namedtuple("Point3D", "x y z")
>>> pd.DataFrame([Point3D(0, 0, 0), Point3D(0, 3, 5), Point(2, 3)])
#    x  y    z
# 0  0  0  0.0
# 1  0  3  5.0
# 2  2  3  NaN

DataFrame из списка классов данных

Классы данных можно передавать в конструктор DataFrame. Передача списка классов данных эквивалентна передаче списка словарей.

Необходимо иметь в виду, что все значения в списке должны быть классами данных, смешивание типов в списке приведет к ошибке TypeError.

>>> from dataclasses import make_dataclass
>>> Point = make_dataclass("Point", [("x", int), ("y", int)])
>>> pd.DataFrame([Point(0, 0), Point(0, 3), Point(2, 3)])
#    x  y
# 0  0  0
# 1  0  3
# 2  2  3

Выбор, добавление, удаление столбцов

Можно относиться к DataFrame как к набору объектов Series с одинаковым индексом строк. Получение, установка и удаление столбцов работает с тем же синтаксисом, что и аналогичные операции словарей dict:

>>> df["one"]
# a    1.0
# b    2.0
# c    3.0
# d    NaN
# Name: one, dtype: float64

# создание столбца "three", значение которого будут
# результатом векторного умножения df["one"] * df["two"]
>>> df["three"] = df["one"] * df["two"]
# создание столбца "flag", значение которого логическими
# т.е. результат векторного сравнения df["one"] > 2
>>> df["flag"] = df["one"] > 2
# смотрим
>>> df
#    one  two  three   flag
# a  1.0  1.0    1.0  False
# b  2.0  2.0    4.0  False
# c  3.0  3.0    9.0   True
# d  NaN  4.0    NaN  False

Столбцы можно удалять или выталкивать, как с помощью операций dict:

# удаление столбца "two"
>>> del df["two"]
# удаление столбца "three" с 
# сохранением в переменную
>>> three = df.pop("three")
>>> df
#    one   flag
# a  1.0  False
# b  2.0  False
# c  3.0   True
# d  NaN  False

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

>>> df["foo"] = "bar"
>>> df
   one   flag  foo
a  1.0  False  bar
b  2.0  False  bar
c  3.0   True  bar
d  NaN  False  bar

При вставке серии pandas.Series, которая не имеет того же индекса, что и DataFrame, она Series будет приведена в соответствие с индексом DataFrame:

# добавим столбец "one_trunc", значения которому 
# присваиваются из среза строк столбца "one"
>>> df["one_trunc"] = df["one"][:2]
# видим пропущенные значения
>>> df
   one   flag  foo  one_trunc
a  1.0  False  bar        1.0
b  2.0  False  bar        2.0
c  3.0   True  bar        NaN
d  NaN  False  bar        NaN

Можно вставлять необработанные массивы ndarray, но их длина должна соответствовать длине индекса DataFrame. По умолчанию столбцы вставляются в конец. Метод DataFrame.insert(loc, column, value) вставляет столбцы в определенное место. Метод принимает loc - индекс местоположения (0 <= loc <= len(column)); column - индексную метку нового столбца; value - массив значений или скаляр.

Смотрим:

>>> df.insert(1, "bar", df["one"])
>>> df
#    one  bar   flag  foo  one_trunc
# a  1.0  1.0  False  bar        1.0
# b  2.0  2.0  False  bar        2.0
# c  3.0  3.0   True  bar        NaN
# d  NaN  NaN  False  bar        NaN

DataFrame.assign() добавление новых столбцов в цепочках методов

Pandas имеет метод DataFrame.assign(), который позволяет легко создавать новые столбцы, которые потенциально могут быть производными от существующих столбцов. DataFrame.assign() всегда возвращает КОПИЮ ДАННЫХ, оставляя исходный DataFrame нетронутым.

# загрузим данные по URL
url = (
    "https://raw.githubusercontent.com/pandas-dev"
    "/pandas/main/pandas/tests/io/data/csv/tips.csv"
)
>>> tips = pd.read_csv(url)
>>> tips.head()
#    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

>>> tips.assign(sepal_ratio=tips["total_bill"] / tips["size"]).head()
#    total_bill   tip     sex smoker  day    time  size  sepal_ratio
# 0       16.99  1.01  Female     No  Sun  Dinner     2     8.495000
# 1       10.34  1.66    Male     No  Sun  Dinner     3     3.446667
# 2       21.01  3.50    Male     No  Sun  Dinner     3     7.003333
# 3       23.68  3.31    Male     No  Sun  Dinner     2    11.840000
# 4       24.59  3.61  Female     No  Sun  Dinner     4     6.147500

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

>>> tips.assign(sepal_ratio=lambda x: (x["total_bill"] / x["size"])).head()
#    total_bill   tip     sex smoker  day    time  size  sepal_ratio
# 0       16.99  1.01  Female     No  Sun  Dinner     2     8.495000
# 1       10.34  1.66    Male     No  Sun  Dinner     3     3.446667
# 2       21.01  3.50    Male     No  Sun  Dinner     3     7.003333
# 3       23.68  3.31    Male     No  Sun  Dinner     2    11.840000
# 4       24.59  3.61  Female     No  Sun  Dinner     4     6.147500

Передача вызываемого объекта, в отличие от фактического значения, которое нужно вставить, полезна, например, когда нет ссылки на фрейм данных. Это часто встречается при использовании метода DataFrame.assign() в цепочке операций. Например, можно ограничить DataFrame только теми наблюдениями, длина которых превышает 5, вычислить соотношение и сразу построить график:

# Внимание! абстрактный код.
iris = pd.read_csv(iris_data)

(
    iris.query("SepalLength > 5")
    .assign(
        SepalRatio=lambda x: x.SepalWidth / x.SepalLength,
        PetalRatio=lambda x: x.PetalWidth / x.PetalLength,
    ).plot(kind="scatter", x="SepalRatio", y="PetalRatio")
)

Так как передается функция, она вычисляется в назначенном DataFrame. Важно отметить, что это DataFrame, который был отфильтрован по строкам с длиной более 5. Сначала происходит фильтрация, а затем расчеты соотношения. Это пример, когда не было доступной ссылки на отфильтрованный DataFrame.

Сигнатура функции для DataFrame.assign() - это просто **kwargs. Ключи - это имена столбцов для новых полей, а значения - это либо значение, которое нужно вставить (например, массив Series или NumPy), либо функция с одним аргументом, которая вызывается во фрейме данных. Возвращается копия исходного фрейма данных со вставленными новыми значениями.

Порядок **kwargs сохраняется. Это допускает зависимое присваивание, когда выражение, указанное позже в **kwargs, может ссылаться на столбец, созданный ранее в том же DataFrame.assign().

>>> dfa = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
# круто, не правда ли
>>> dfa.assign(C=lambda x: x["A"] + x["B"], D=lambda x: x["A"] + x["C"])
#    A  B  C   D
# 0  1  4  5   6
# 1  2  5  7   9
# 2  3  6  9  12

Во втором выражении x['C'] будет ссылаться на вновь созданный столбец, который равен dfa['A'] + dfa['B'].

Индексация / выборка DataFrame

Основы индексации заключаются в следующем:

ОперацияСинтаксисРезультат
Выбор столбцовdf[`col`]Series
Выбор строк по индексным меткамdf.loc[label]Series
Выбор строки по позицииdf.iloc[loc]Series
Срез строк по позицииdf[5:10]DataFrame
Выбор строк по логическому векторуdf[bool_vec]DataFrame

Например, выбор строки возвращает Series, индексом которой являются столбцы DataFrame:

>>> df
#    one  bar   flag  foo  one_trunc
# a  1.0  1.0  False  bar        1.0
# b  2.0  2.0  False  bar        2.0
# c  3.0  3.0   True  bar        NaN
# d  NaN  NaN  False  bar        NaN

>>> df.loc["b"]
# one            2.0
# bar            2.0
# flag         False
# foo            bar
# one_trunc      2.0
# Name: b, dtype: object

>>> df.iloc[2]
# one           3.0
# bar           3.0
# flag         True
# foo           bar
# one_trunc     NaN
# Name: c, dtype: object

Выравнивание данных и арифметика

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

>>> df = pd.DataFrame(np.random.randn(10, 4), columns=["A", "B", "C", "D"])
>>> df2 = pd.DataFrame(np.random.randn(7, 3), columns=["A", "B", "C"])
>>> df + df2
#           A         B         C   D
# 0 -0.328227  1.804025  0.125411 NaN
# 1 -0.670840  2.067624 -1.108924 NaN
# 2  0.831945 -0.426104 -0.575880 NaN
# 3 -2.709065  1.140954  2.316253 NaN
# 4  0.433693  0.745088  0.804108 NaN
# 5 -1.205184 -0.616827 -1.709180 NaN
# 6 -0.021662  1.724195  0.893563 NaN
# 7       NaN       NaN       NaN NaN
# 8       NaN       NaN       NaN NaN
# 9       NaN       NaN       NaN NaN

При выполнении операции между DataFrame и Series поведение по умолчанию заключается в выравнивании индекса Series по столбцам DataFrame, таким образом передавая данные по строкам. Например:

>>> df - df.iloc[0]
#           A         B         C         D
# 0  0.000000  0.000000  0.000000  0.000000
# 1  0.508564  0.662213 -1.113077  2.258814
# 2  0.963024 -0.474567 -0.292452  1.672575
# 3 -0.609690 -0.416193  0.516814  0.072799
# 4  2.090974  1.114655  0.564536  2.688766
# 5 -0.271611  0.655948 -1.346594  1.657681
# 6  1.227515  0.933819 -0.191704  2.524008
# 7  1.250461 -0.798301 -0.467560  2.218241
# 8 -0.199686 -1.912055 -0.020415  1.306960
# 9  0.814666 -0.254829 -0.154157  3.829802

Арифметические операции со скалярами выполняются поэлементно:

>>> df * 5 + 2
#           A         B         C          D
# 0 -2.188748  2.875122  1.913159  -6.576318
# 1  0.354075  6.186185 -3.652227   4.717752
# 2  2.626371  0.502287  0.450899   1.786558
# 3 -5.237199  0.794154  4.497227  -6.212324
# 4  8.266122  8.448398  4.735839   6.867513
# 5 -3.546803  6.154862 -4.819811   1.712087
# 6  3.948828  7.544215  0.954639   6.043723
# 7  4.063558 -1.116385 -0.424642   4.514885
# 8 -3.187178 -6.685155  1.811085  -0.041519
# 9  1.884584  1.600975  1.142376  12.572693
>>> 1 / df
#            A          B          C          D
# 0  -1.193674   5.713491 -57.576821  -0.583001
# 1  -3.037805   1.194405  -0.884607   1.839756
# 2   7.982486  -3.338424  -3.227678 -23.425568
# 3  -0.690875  -4.146468   2.002221  -0.608841
# 4   0.797942   0.775386   1.827593   1.027219
# 5  -0.901420   1.203410  -0.733158 -17.366332
# 6   2.565645   0.901841  -4.783037   1.236484
# 7   2.423000  -1.604423  -2.062160   1.988162
# 8  -0.963915  -0.575695 -26.466883  -2.449157
# 9 -43.321462 -12.530547  -5.830058   0.472916
>>> df ** 4
#               A         B             C          D
# 0  4.925573e-01  0.000938  9.099327e-08   8.656125
# 1  1.174250e-02  0.491353  1.633045e+00   0.087289
# 2  2.462903e-04  0.008051  9.213805e-03   0.000003
# 3  4.389368e+00  0.003383  6.222317e-02   7.277536
# 4  2.466694e+00  2.766478  8.963598e-02   0.898149
# 5  1.514576e+00  0.476811  3.461062e+00   0.000011
# 6  2.307882e-02  1.511750  1.910667e-03   0.427805
# 7  2.901259e-02  0.150912  5.529816e-02   0.064002
# 8  1.158363e+00  9.103960  2.037928e-06   0.027793
# 9  2.839145e-07  0.000041  8.655825e-04  19.992290

Логические операторы также работают поэлементно:

>>> df1 = pd.DataFrame({"a": [1, 0, 1], "b": [0, 1, 1]}, dtype=bool)
>>> df2 = pd.DataFrame({"a": [0, 1, 1], "b": [1, 1, 0]}, dtype=bool)
>>> df1 & df2
 #       a      b
 # 0  False  False
 # 1  False   True
 # 2   True  False
>>> df1 | df2
 #       a     b
 # 0  True  True
 # 1  True  True
 # 2  True  True
>>> df1 ^ df2
 #        a      b
 # 0   True   True
 # 1   True  False
 # 2  False   True
>>> -df1
 #        a      b
 # 0  False   True
 # 1   True  False
 # 2  False  False

Транспонирование данных DataFrame

Чтобы транспонировать данные DataFrame, необходимо обратится к атрибуту DataFrame.T или методу DataFrame.transpose(), аналогичному numpy.ndarray:

# покажем только первые 5 строк
>>> df[:5].T
#           0         1         2         3         4
# A -0.837750 -0.329185  0.125274 -1.447440  1.253224
# B  0.175024  0.837237 -0.299543 -0.241169  1.289680
# C -0.017368 -1.130445 -0.309820  0.499445  0.547168
# D -1.715264  0.543550 -0.042688 -1.642465  0.973503

Совместимость DataFrame с функциями NumPy

Большинство функций NumPy можно вызывать непосредственно в Series и DataFrame.

>>> np.exp(df).head()
#           A         B         C         D
# 0  0.432683  1.191275  0.982782  0.179916
# 1  0.719510  2.309976  0.322889  1.722110
# 2  1.133459  0.741157  0.733579  0.958210
# 3  0.235172  0.785709  1.647807  0.193502
# 4  3.501615  3.631623  1.728351  2.647201

# 
>>> np.asarray(df)
# array([[-0.83774952,  0.17502433, -0.0173681 , -1.71526364],
#        [-0.3291851 ,  0.837237  , -1.13044531,  0.54355038],
#        [ 0.12527426, -0.29954257, -0.30982022, -0.0426884 ],
#        [-1.44743974, -0.24116912,  0.49944541, -1.64246485],
#        [ 1.25322432,  1.28967954,  0.54716788,  0.97350267],
#        [-1.10936066,  0.83097233, -1.36396227, -0.05758268],
#        [ 0.38976552,  1.1088429 , -0.20907218,  0.80874457],
#        [ 0.41271156, -0.62327699, -0.48492845,  0.50297708],
#        [-1.03743561, -1.73703107, -0.03778307, -0.40830374],
#        [-0.02308325, -0.07980498, -0.17152488,  2.11453869]])

DataFrame не предназначен для замены numpy.ndarray, т.к. его семантика индексации и модель данных местами сильно отличаются от n-мерного массива.

В свою очередь Series реализует __array_ufunc__, что позволяет ему работать с универсальными функциями NumPy.

Ufunc применяется к базовому массиву последовательно.

>>> ser = pd.Series([1, 2, 3, 4])
>>> np.exp(ser)
0     2.718282
1     7.389056
2    20.085537
3    54.598150
dtype: float64

Когда в ufunc передается несколько рядов, то они выравниваются перед выполнением операции.

Pandas автоматически выравнивает помеченные входные данные как часть ufunc с несколькими входными данными. Например, использование numpy.remain() для двух рядов с по-разному упорядоченными метками приведет к выравниванию перед операцией.

>>> ser1 = pd.Series([1, 2, 3], index=["a", "b", "c"])
>>> ser2 = pd.Series([1, 3, 5], index=["b", "a", "c"])
>>> np.remainder(ser1, ser2)
# a    1
# b    0
# c    3
# dtype: int64

Берется объединение двух индексов, и непересекающиеся значения заполняются пропущенными значениями NaN.

>>> ser3 = pd.Series([2, 4, 6], index=["b", "c", "d"])
>>> np.remainder(ser1, ser3)
# a    NaN
# b    0.0
# c    3.0
# d    NaN
# dtype: float64

Когда двоичный ufunc применяется к pandas.Series и pandas.Index, реализация Series имеет приоритет, и возвращается pandas.Series.

>>> ser = pd.Series([1, 2, 3])
>>> idx = pd.Index([4, 5, 6])
>>> np.maximum(ser, idx)
# 0    4
# 1    5
# 2    6
# dtype: int64

Вывод данных DataFrame на консоль

По умолчанию pandas обрезает вывод больших фреймов данных и показывает несколько первых и последних строк. Такое поведение можно переопределить, используя метод DataFrame.head(n) для вывода на экран n первых строк или DataFrame.tail(n) последних строк. Для отрицательных значений n эти методы покажут строки, кроме первых/последних n строк в зависимости от метода.

# данные `tips` были загружены ранее
# Отобразим первые 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.to_string() вернет строковое представление DataFrame в табличной форме, хотя оно не всегда будет соответствовать ширине консоли.

Можно получить сводку по данным, используя метод 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', 'size']].mean()
# total_bill    19.785943
# tip            2.998279
# size           2.569672
# dtype: float64

Атрибуты pandas.DataFrame и лежащие в их основе данные

  • DataFrame.T: транспонирование DataFrame.

    >>> df = pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]})
    >>> df
    #    col1  col2
    # 0     1     3
    # 1     2     4
    
    >>> df.T
    #       0  1
    # col1  1  2
    # col2  3  4
    
  • DataFrame.index: индекс (индексные метки строк) DataFrame;

  • DataFrame.columns: метки столбцов DataFrame.

  • DataFrame.dtypes: возвращает dtypes в DataFrame.

  • DataFrame.values: возвращает представление Numpy.

  • DataFrame.axes: возвращает список, представляющий оси DataFrame.

  • DataFrame.ndim: возвращает целое число, представляющее количество осей/измерений массива.

  • DataFrame.size: возвращает целое число, представляющее количество элементов в этом объекте.

  • DataFrame.shape: возвращает кортеж, представляющий размерность DataFrame.

  • DataFrame.empty: проверяет, пуст ли DataFrame.

  • DataFrame.info(verbose=None, max_cols=None, memory_usage=None, show_counts=None): печатает краткую сводку DataFrame.

    Принимаемые аргументы:

    • verbose=None - следует ли распечатывать полную сводку. Принимает bool;

    • max_cols=None - целое число, когда следует переключаться с подробного вывода на усеченный. Если DataFrame содержит более max_cols столбцов, используются усеченные выходные данные. По умолчанию соответствует параметру pandas.options.display.max_info_columns;

    • memory_usage=None - определяет, следует ли отображать общее использование памяти элементами DataFrame (включая индекс). По умолчанию соответствует параметру pandas.options.display.memory_usage;

      • True всегда показывает использование памяти.
      • False никогда не показывает использование памяти.
      • 'deep' - эквивалентно высказыванию "True с глубоким самоанализом".

      Использование памяти отображается в удобочитаемых единицах (представление по основанию 2). Без глубокого самоанализа оценка памяти производится на основе столбца dtype и количества строк, предполагая, что значения потребляют одинаковый объем памяти для соответствующих dtypes. При глубокой интроспекции памяти выполняется расчет реального использования памяти за счет вычислительных ресурсов.

    • show_counts=None - следует ли отображать числа, отличные от NULL. По умолчанию это отображается только в том случае, если DataFrame меньше, чем pandas.options.display.max_info_rows и pandas.options.display.max_info_columns. Значение True всегда показывает счетчики, а False никогда не показывает счетчики.

    >>> import pandas as pd
    >>> int_values = [1, 2, 3, 4, 5]
    >>> text_values = ['alpha', 'beta', 'gamma', 'delta', 'epsilon']
    >>> float_values = [0.0, 0.25, 0.5, 0.75, 1.0]
    >>> df = pd.DataFrame({"int_col": int_values, "text_col": text_values, "float_col": float_values})
    >>> df
    #     int_col text_col  float_col
    # 0        1    alpha       0.00
    # 1        2     beta       0.25
    # 2        3    gamma       0.50
    # 3        4    delta       0.75
    # 4        5  epsilon       1.00
    

    Печатает информацию по всем столбцам:

    >>> df.info(verbose=True)
    # <class 'pandas.core.frame.DataFrame'>
    # RangeIndex: 5 entries, 0 to 4
    # Data columns (total 3 columns):
    #  #   Column     Non-Null Count  Dtype
    # ---  ------     --------------  -----
    #  0   int_col    5 non-null      int64
    #  1   text_col   5 non-null      object
    #  2   float_col  5 non-null      float64
    # dtypes: float64(1), int64(1), object(1)
    # memory usage: 248.0+ bytes
    

    Выводит сводку о количестве столбцов и их dtypes, но не сведения о каждом столбце:

    >>> df.info(verbose=False)
    # <class 'pandas.core.frame.DataFrame'>
    # RangeIndex: 5 entries, 0 to 4
    # Columns: 3 entries, int_col to float_col
    # dtypes: float64(1), int64(1), object(1)
    # memory usage: 248.0+ bytes
    

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

    >>> import numpy as np
    
    df = pd.DataFrame({
        'column_1': np.random.choice(['a', 'b', 'c'], 10 ** 6),
        'column_2': np.random.choice(['a', 'b', 'c'], 10 ** 6),
        'column_3': np.random.choice(['a', 'b', 'c'], 10 ** 6)
    })
    
    >>> df.info()
    # <class 'pandas.core.frame.DataFrame'>
    # RangeIndex: 1000000 entries, 0 to 999999
    # Data columns (total 3 columns):
    #  #   Column    Non-Null Count    Dtype
    # ---  ------    --------------    -----
    #  0   column_1  1000000 non-null  object
    #  1   column_2  1000000 non-null  object
    #  2   column_3  1000000 non-null  object
    # dtypes: object(3)
    # memory usage: 22.9+ MB
    
    >>> df.info(memory_usage='deep')
    # <class 'pandas.core.frame.DataFrame'>
    # RangeIndex: 1000000 entries, 0 to 999999
    # Data columns (total 3 columns):
    #  #   Column    Non-Null Count    Dtype
    # ---  ------    --------------    -----
    #  0   column_1  1000000 non-null  object
    #  1   column_2  1000000 non-null  object
    #  2   column_3  1000000 non-null  object
    # dtypes: object(3)
    # memory usage: 165.9 MB
    
  • DataFrame.select_dtypes(include=None, exclude=None): возвращает подмножество столбцов DataFrame на основе dtypes. Должен быть указан хотя бы один из аргументов.

    • Чтобы выбрать все числовые типы, используйте np.number или 'number'.
    • Чтобы выбрать строки, нужно использовать object, при этом будут возвращены все столбцы dtype: object.
    • Чтобы выбрать дату и время, используйте np.datetime64, 'datetime' или 'datetime64'.
    • Чтобы выбрать временные разницы, используйте np.timedelta64, 'timedelta' или 'timedelta64'.
    • Чтобы выбрать категориальные типы данных Pandas, используйте 'category'.
    • Чтобы выбрать типы данных datetimetz Pandas, используйте 'datetimetz' или 'datetime64[ns, tz]'.
    df = pd.DataFrame({'a': [1, 2] * 3,
                       'b': [True, False] * 3,
                       'c': [1.0, 2.0] * 3})
    >>> df
    #         a      b  c
    # 0       1   True  1.0
    # 1       2  False  2.0
    # 2       1   True  1.0
    # 3       2  False  2.0
    # 4       1   True  1.0
    # 5       2  False  2.0
    
    >>> df.select_dtypes(include='bool')
    #    b
    # 0  True
    # 1  False
    # 2  True
    # 3  False
    # 4  True
    # 5  False
    
    >>> df.select_dtypes(include=['float64'])
    #    c
    # 0  1.0
    # 1  2.0
    # 2  1.0
    # 3  2.0
    # 4  1.0
    # 5  2.0
    
    >>> df.select_dtypes(exclude=['int64'])
    #        b    c
    # 0   True  1.0
    # 1  False  2.0
    # 2   True  1.0
    # 3  False  2.0
    # 4   True  1.0
    # 5  False  2.0
    
  • DataFrame.memory_usage([index, deep]): возвращает использование памяти каждым столбцом в байтах. Это значение отображается в DataFrame.info() по умолчанию. Его можно подавить, установив для pandas.options.display.memory_usage=False.

    >>> dtypes = ['int64', 'float64', 'complex128', 'object', 'bool']
    >>> data = dict([(t, np.ones(shape=5000, dtype=int).astype(t)) for t in dtypes])
    >>> df = pd.DataFrame(data)
    >>> df.head()
    #    int64  float64            complex128  object  bool
    # 0      1      1.0              1.0+0.0j       1  True
    # 1      1      1.0              1.0+0.0j       1  True
    # 2      1      1.0              1.0+0.0j       1  True
    # 3      1      1.0              1.0+0.0j       1  True
    # 4      1      1.0              1.0+0.0j       1  True
    
    >>> df.memory_usage()
    # Index           128
    # int64         40000
    # float64       40000
    # complex128    80000
    # object        40000
    # bool           5000
    # dtype: int64
    
    # без учета индекса
    >>> df.memory_usage(index=False)
    # int64         40000
    # float64       40000
    # complex128    80000
    # object        40000
    # bool           5000
    # dtype: int64
    

    Объем памяти, занимаемый столбцами dtype: object, по умолчанию игнорируется:

    >>> df.memory_usage(deep=True)
    # Index            128
    # int64          40000
    # float64        40000
    # complex128     80000
    # object        180000
    # bool            5000
    # dtype: int64
    

    Используйте категориальный тип для эффективного хранения столбца c типом dtype: object с множеством повторяющихся значений.

    >>> df['object'].astype('category').memory_usage(deep=True)
    # 5244
    
  • DataFrame.flags: получает свойства, связанные с этим объектом pandas.

    Доступные флаги: pandas.Flags.allows_duplicate_labels. Установка pandas.Flags.allows_duplicate_labels=False гарантирует, что индекс (и столбцы DataFrame) будут уникальными. Большинство методов, принимающих и возвращающих DataFrame, распространяют значение allows_duplicate_labels.

    >>> df = pd.DataFrame({"A": [1, 2]}, index=['a', 'a'])
    >>> df.flags.allows_duplicate_labels
    # True
    >>> df.flags.allows_duplicate_labels = False
    #   Traceback (most recent call last):
    #       ...
    #   pandas.errors.DuplicateLabelError: Index has duplicates.
    #         positions
    #   label
    #   a        [0, 1]