Панды.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