Pandas.at против .loc
Я изучал, как оптимизировать свой код, и пробежал метод pandas
.at
. В документации
Быстрый сканирующий аксессуар на основе меток
Аналогично loc, при предоставлении скалярных запросов на основе ярлыков. Вы также можете установить эти индексы.
Итак, я запустил несколько образцов:
Настройка
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
где as print sdf.at[:, :]
приводит к TypeError: unhashable type
Таким образом, очевидно, что это не так, даже если намерение должно быть аналогичным.
Тем не менее, кто может дать указания о том, что можно и не может сделать с помощью метода .at
?
Ответы
Ответ 1
Обновление: 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
, но также некоторые типы проверки на клавишах.
Ответ 2
Как вы спросили об ограничениях .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'
Однако это сработает, если использовать строку, в которой int()
действительно работает, как отмечено @n1k31t4 в комментариях, например
df.at[5, 'A'] = '123'
A B C
4 0 2 3
5 123 4 1
6 10 20 30