Панды.at против.loc

Я изучал, как оптимизировать мой код и столкнулся с pandas.at метод. Согласно документации

Быстрый скалярный метод доступа на основе меток

Аналогично loc, at обеспечивает скалярный поиск на основе меток. Вы также можете установить с помощью этих индексаторов.

Итак, я запустил несколько образцов:

Настроить

import pandas as pd
import numpy as np
from string import letters, lowercase, uppercase

lt = list(letters)
lc = list(lowercase)
uc = list(uppercase)

def gdf(rows, cols, seed=None):
    """rows and cols are what you'd pass
    to pd.MultiIndex.from_product()"""
    gmi = pd.MultiIndex.from_product
    df = pd.DataFrame(index=gmi(rows), columns=gmi(cols))
    np.random.seed(seed)
    df.iloc[:, :] = np.random.rand(*df.shape)
    return df

seed = [3, 1415]
df = gdf([lc, uc], [lc, uc], seed)

print df.head().T.head().T

df похоже:

            a                                        
            A         B         C         D         E
a A  0.444939  0.407554  0.460148  0.465239  0.462691
  B  0.032746  0.485650  0.503892  0.351520  0.061569
  C  0.777350  0.047677  0.250667  0.602878  0.570528
  D  0.927783  0.653868  0.381103  0.959544  0.033253
  E  0.191985  0.304597  0.195106  0.370921  0.631576

Давайте использовать .at а также .loc и убедитесь, что я получаю то же самое

print "using .loc", df.loc[('a', 'A'), ('c', 'C')]
print "using .at ", df.at[('a', 'A'), ('c', 'C')]

using .loc 0.37374090276
using .at  0.37374090276

Тест скорости с помощью .loc

%%timeit
df.loc[('a', 'A'), ('c', 'C')]

10000 loops, best of 3: 180 µs per loop

Тест скорости с помощью .at

%%timeit
df.at[('a', 'A'), ('c', 'C')]

The slowest run took 6.11 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 8 µs per loop

Это выглядит как огромное увеличение скорости. Даже на этапе кеширования 6.11 * 8 намного быстрее чем 180

Вопрос

Каковы ограничения .at? Я мотивирован, чтобы использовать это. Документация говорит, что это похоже на .loc но это не ведет себя так же. Пример:

# small df
sdf = gdf([lc[:2]], [uc[:2]], seed)

print sdf.loc[:, :]

          A         B
a  0.444939  0.407554
b  0.460148  0.465239

в то время как print sdf.at[:, :] результаты в TypeError: unhashable type

Так что, очевидно, не то же самое, даже если намерение должно быть похожим.

Тем не менее, кто может дать рекомендации о том, что можно и нельзя делать с .at метод?

5 ответов

Решение

Обновить: df.get_value устарело с версии 0.21.0. С помощью df.at или же df.iat это рекомендуемый метод в будущем.


df.at может получить доступ только к одному значению за раз.

df.loc Можно выбрать несколько строк и / или столбцов.

Обратите внимание, что есть также df.get_value, что может быть даже быстрее при доступе к отдельным значениям:

In [25]: %timeit df.loc[('a', 'A'), ('c', 'C')]
10000 loops, best of 3: 187 µs per loop

In [26]: %timeit df.at[('a', 'A'), ('c', 'C')]
100000 loops, best of 3: 8.33 µs per loop

In [35]: %timeit df.get_value(('a', 'A'), ('c', 'C'))
100000 loops, best of 3: 3.62 µs per loop

Под капотом, df.at[...] звонки df.get_value, но также выполняет некоторую проверку типов ключей.

Как вы спросили об ограничениях .at Вот одна вещь, с которой я недавно столкнулся (используя панд 0,22). Давайте использовать пример из документации:

df = pd.DataFrame([[0, 2, 3], [0, 4, 1], [10, 20, 30]], index=[4, 5, 6], columns=['A', 'B', 'C'])
df2 = df.copy()

    A   B   C
4   0   2   3
5   0   4   1
6  10  20  30

Если я сейчас сделаю

df.at[4, 'B'] = 100

результат выглядит как ожидалось

    A    B   C
4   0  100   3
5   0    4   1
6  10   20  30

Тем не менее, когда я пытаюсь сделать

 df.at[4, 'C'] = 10.05

Кажется, что .at пытается сохранить тип данных (здесь: int ):

    A    B   C
4   0  100  10
5   0    4   1
6  10   20  30

Это похоже на разницу .loc:

df2.loc[4, 'C'] = 10.05

дает желаемый

    A   B      C
4   0   2  10.05
5   0   4   1.00
6  10  20  30.00

В приведенном выше примере рискованно то, что это происходит тихо (преобразование из float до int`). Когда кто-то пытается сделать то же самое со строками, он выдаст ошибку:

df.at[5, 'A'] = 'a_string'

ValueError: недопустимый литерал для int() с основанием 10: 'a_string'

В дополнение к вышесказанному, документация Pandas дляat функция заявляет:

Доступ к одному значению для пары меток строки / столбца.

Подобно loc в том, что оба обеспечивают поиск на основе меток. Используйте at, если вам нужно получить или установить только одно значение в DataFrame или Series.

Для настройки данных loc а также at похожи, например:

df = pd.DataFrame({'A': [1,2,3], 'B': [11,22,33]}, index=[0,0,1])

Обе loc а также at даст тот же результат

df.at[0, 'A'] = [101,102]
df.loc[0, 'A'] = [101,102]

    A   B
0   101 11
0   102 22
1   3   33

df.at[0, 'A'] = 103
df.loc[0, 'A'] = 103

    A   B
0   103 11
0   103 22
1   3   33

Кроме того, для доступа к одному значению оба одинаковы

df.loc[1, 'A']   # returns a single value (<class 'numpy.int64'>)
df.at[1, 'A']    # returns a single value (<class 'numpy.int64'>)

3

Однако при сопоставлении нескольких значений loc вернет группу строк / столбцов из DataFrame, пока at вернет массив значений

df.loc[0, 'A']  # returns a Series (<class 'pandas.core.series.Series'>)

0    103
0    103
Name: A, dtype: int64

df.at[0, 'A']   # returns array of values (<class 'numpy.ndarray'>)

array([103, 103])

И более того, loc может использоваться для сопоставления группы строк / столбцов и может иметь только индекс, в то время как at должен получить столбец

df.loc[0]  # returns a DataFrame view (<class 'pandas.core.frame.DataFrame'>)

    A   B
0   103 11
0   103 22


# df.at[0]  # ERROR: must receive column

.at это оптимизированный метод доступа к данным по сравнению с .loc.

.locфрейма данных выбирает все элементы, расположенные indexed_rows и labeled_columns, как указано в его аргументе. Insetad,.at выбирает конкретный элемент кадра данных, расположенный в заданных indexed_row и labeled_column.

Также, .at принимает одну строку и один столбец в качестве входного аргумента, тогда как .locможет занимать несколько строк и столбцов. Выход с использованием.at является одним элементом и использует .loc может быть серия или DataFrame.

Еще одно ограничение заключается в том, что.locпринимать другие входные данные (в обмен на индекс), такие как условия, тогда как.atне :

      > df = pd.DataFrame([[1, 2], 
                   [0, 0],
                   ], 
                   columns=["A", "B"])

> df["A"] > 0 # is a pd.Series of bool values

> df.loc[df["A"] > 0, "B"] # provides the first line in this example

> df.at[df["A"] > 0, "B"] # using .at will raise InvalidIndexError
Другие вопросы по тегам