Разница между картой, applymap и методами применения в Pandas

468

Можете ли вы сказать мне, когда использовать эти методы векторизации с основными примерами?

Я вижу, что mapэто Seriesметод, а остальные DataFrameметоды. Я запутался applyи в applymapметодах, хотя. Почему у нас есть два метода для применения функции к DataFrame? Опять же, простые примеры, которые иллюстрируют использование было бы здорово!

Marillion
источник
5
Поправьте меня, если я ошибаюсь, но я считаю, что эти функции не являются векторизованными методами, поскольку все они включают цикл над элементами, к которым они применяются.
Танги
1
Я не вижу здесь никакой
Мартин Тома

Ответы:

534

Прямо из книги Уэста МакКинни « Питон для анализа данных» , стр. 132 (я очень рекомендую эту книгу):

Другая частая операция - применение функции к 1D-массивам для каждого столбца или строки. Метод apply DataFrame делает именно это:

In [116]: frame = DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])

In [117]: frame
Out[117]: 
               b         d         e
Utah   -0.029638  1.081563  1.280300
Ohio    0.647747  0.831136 -1.549481
Texas   0.513416 -0.884417  0.195343
Oregon -0.485454 -0.477388 -0.309548

In [118]: f = lambda x: x.max() - x.min()

In [119]: frame.apply(f)
Out[119]: 
b    1.133201
d    1.965980
e    2.829781
dtype: float64

Многие из наиболее распространенных статистических данных массива (например, сумма и среднее) являются методами DataFrame, поэтому использование применять не обязательно.

Поэлементные функции Python также могут быть использованы. Предположим, вы хотите вычислить форматированную строку из каждого значения с плавающей запятой в кадре. Вы можете сделать это с applymap:

In [120]: format = lambda x: '%.2f' % x

In [121]: frame.applymap(format)
Out[121]: 
            b      d      e
Utah    -0.03   1.08   1.28
Ohio     0.65   0.83  -1.55
Texas    0.51  -0.88   0.20
Oregon  -0.49  -0.48  -0.31

Причина имени applymap заключается в том, что в Series есть метод map для применения поэлементной функции:

In [122]: frame['e'].map(format)
Out[122]: 
Utah       1.28
Ohio      -1.55
Texas      0.20
Oregon    -0.31
Name: e, dtype: object

Подводя итог, applyработает на основе строки / столбца DataFrame, applymapработает поэлементно на DataFrame и поэтапно mapработает на серии.

jeremiahbuddha
источник
31
строго говоря, applymap внутренне осуществляются через применять с небольшим наматываются вверх над переданным параметром функции (rougly говоря замену funcк lambda x: [func(y) for y in x], и применяя столбцы)
ALKO
5
Спасибо за объяснение. Так как mapи applymapоба работают поэтапно, я бы ожидал один метод (или, mapили applymap), который будет работать как для Series, так и для DataFrame. Вероятно, есть и другие конструктивные соображения, и Уэс МакКинни решил придумать два разных метода.
13
2
Это на странице 129 в моем экземпляре по какой-то причине. Там нет лейбла для второго издания или что-нибудь.
Джоди
1
Есть ли способ сделать applymapвместе с groupbyфункцией в пандах?
everestial007
Как применить функцию к сгруппированным столбцовым данным?
чпч
84

Сравнивая map, applymapи : Контекст Mattersapply

Первое существенное отличие: ОПРЕДЕЛЕНИЕ

  • map определяется ТОЛЬКО для серии
  • applymap определяется ТОЛЬКО в DataFrames
  • apply определяется ОБА

Второе главное отличие: входной аргумент

  • mapпринимает dicts Series, или вызывается
  • applymapи applyпринимать только вызываемые

Третье главное отличие: ПОВЕДЕНИЕ

  • map поэлементно для серии
  • applymap поэлементно для DataFrames
  • applyтакже работает поэлементно, но подходит для более сложных операций и агрегации. Поведение и возвращаемое значение зависят от функции.

Четвертое основное отличие (самое важное): случай использования

  • mapпредназначен для отображения значений из одного домена в другой, поэтому оптимизирован для выполнения (например, df['A'].map({1:'a', 2:'b', 3:'c'}))
  • applymapподходит для поэлементных преобразований в нескольких строках / столбцах (например, df[['A', 'B', 'C']].applymap(str.strip))
  • applyдля применения любой функции, которая не может быть векторизована (например, df['sentences'].apply(nltk.sent_tokenize))

Подведение итогов

введите описание изображения здесь

Сноски

  1. mapпри передаче словарь / серия отображает элементы на основе ключей в этом словаре / серии. Недостающие значения будут записаны как NaN на выходе.
  2. applymapв более поздних версиях была оптимизирована для некоторых операций. Вы найдете applymapнемного быстрее, чем applyв некоторых случаях. Мое предложение состоит в том, чтобы проверить их обоих и использовать то, что работает лучше.

  3. mapоптимизирован для поэлементного отображения и преобразования. Операции, в которых используются словари или серии, позволят пандам использовать более быстрые пути к коду для повышения производительности.

  4. Series.applyвозвращает скаляр для агрегирующих операций, Series в противном случае. Аналогично для DataFrame.apply. Обратите внимание, что applyпри вызове некоторых функций NumPy, таких как mean, sumи т. Д. , Также имеются быстрые пути.
cs95
источник
70

В этих ответах содержится много информации, но я добавляю свой собственный, чтобы четко подытожить, какие методы работают по массивам, а не по элементам. Джеремиабудда в основном делал это, но не упомянул Series.apply. У меня нет представителя, чтобы комментировать.

  • DataFrame.apply работает с целыми строками или столбцами одновременно.

  • DataFrame.applymap, Series.applyИ Series.mapработают на одном элементе в момент времени.

Существует много совпадений между возможностями Series.applyи Series.map, что означает, что любой из них будет работать в большинстве случаев. У них действительно есть некоторые небольшие различия, некоторые из которых были обсуждены в ответе osa.

MarredCheese
источник
38

Добавляя к другим ответам, в также Seriesесть карта и применяются .

Применить может сделать DataFrame из серии ; однако карта просто поместит ряд в каждую ячейку другого ряда, что, вероятно, не то, что вам нужно.

In [40]: p=pd.Series([1,2,3])
In [41]: p
Out[31]:
0    1
1    2
2    3
dtype: int64

In [42]: p.apply(lambda x: pd.Series([x, x]))
Out[42]: 
   0  1
0  1  1
1  2  2
2  3  3

In [43]: p.map(lambda x: pd.Series([x, x]))
Out[43]: 
0    0    1
1    1
dtype: int64
1    0    2
1    2
dtype: int64
2    0    3
1    3
dtype: int64
dtype: object

Также, если бы у меня была функция с побочными эффектами, такая как «подключение к веб-серверу», я бы, вероятно, использовал ее applyтолько для ясности.

series.apply(download_file_for_every_element) 

MapМожно использовать не только функцию, но и словарь или другую серию. Допустим, вы хотите манипулировать перестановками .

принимать

1 2 3 4 5
2 1 4 5 3

Квадрат этой перестановки

1 2 3 4 5
1 2 5 3 4

Вы можете вычислить это используя map. Не уверен, что самоприменение задокументировано, но оно работает в 0.15.1.

In [39]: p=pd.Series([1,0,3,4,2])

In [40]: p.map(p)
Out[40]: 
0    0
1    1
2    4
3    2
4    3
dtype: int64
OSA
источник
3
Также .apply () позволяет передавать в функцию kwargs, а .map () - нет.
neilxdims
19

@jeremiahbuddha упомянул, что apply работает со строками / столбцами, а applymap работает поэлементно. Но, похоже, вы все еще можете использовать apply для поэлементного вычисления ....

    frame.apply(np.sqrt)
    Out[102]: 
                   b         d         e
    Utah         NaN  1.435159       NaN
    Ohio    1.098164  0.510594  0.729748
    Texas        NaN  0.456436  0.697337
    Oregon  0.359079       NaN       NaN

    frame.applymap(np.sqrt)
    Out[103]: 
                   b         d         e
    Utah         NaN  1.435159       NaN
    Ohio    1.098164  0.510594  0.729748
    Texas        NaN  0.456436  0.697337
    Oregon  0.359079       NaN       NaN
user2921752
источник
29
Хороший улов с этим. Причина, по которой это работает в вашем примере, заключается в том, что np.sqrt является ufunc, то есть, если вы передадите ему массив, он передаст функцию sqrt на каждый элемент массива. Поэтому, когда apply применяет толчок np.sqrt к каждому столбцу, np.sqrt работает сам с каждым из элементов столбцов, так что вы, по сути, получаете тот же результат, что и applymap.
Иеремия Будда
11

Просто хотел отметить, как я боролся с этим немного

def f(x):
    if x < 0:
        x = 0
    elif x > 100000:
        x = 100000
    return x

df.applymap(f)
df.describe()

это не изменяет сам фрейм данных, должен быть переназначен

df = df.applymap(f)
df.describe()
мюонов
источник
1
Иногда мне трудно понять, нужно ли вам переназначать или нет после выполнения каких-либо действий с df. Для меня это в основном метод проб и ошибок, но держу пари, что есть логика, как это работает (что я упускаю).
marillion
2
в общем случае, кадр данных pandas изменяется только путем переназначения df = modified_dfили если вы установите inplace=Trueфлаг. Кроме того, датафрейм изменится, если вы передадите фрейм данных в функцию по ссылке, и функция изменит фрейм данных
muon
1
Это не совсем верно, подумайте о чем- .ixлибо .whereи т. Д. Не уверен, что такое полное объяснение, когда вам нужно переназначить, а когда нет.
Танос
10

Вероятно, самое простое объяснение разницы между apply и applymap:

Применить принимает весь столбец в качестве параметра, а затем присваивает результат этому столбцу

applymap принимает значение отдельной ячейки в качестве параметра и присваивает результат этой ячейке.

NB. Если метод apply возвращает одно значение, после присвоения вам будет присвоено это значение вместо столбца, и в конечном итоге вместо матрицы будет только строка.

Ката
источник
3

Мое понимание:

С функциональной точки зрения:

Если функция имеет переменные, которые нужно сравнивать в столбце / строке, используйте apply.

например: lambda x: x.max()-x.mean().

Если функция должна применяться к каждому элементу:

1> Если столбец / строка находится, используйте apply

2> Если применимо ко всему фрейму данных, используйте applymap

majority = lambda x : x > 17
df2['legal_drinker'] = df2['age'].apply(majority)

def times10(x):
  if type(x) is int:
    x *= 10 
  return x
df2.applymap(times10)
Вики Мяо
источник
Пожалуйста, предоставьте df2 для большей ясности, чтобы мы могли протестировать ваш код.
Ашиш Ананд
1

На основании ответа CS95

  • map определяется ТОЛЬКО для серии
  • applymap определяется ТОЛЬКО в DataFrames
  • apply определяется ОБА

привести несколько примеров

In [3]: frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])

In [4]: frame
Out[4]:
            b         d         e
Utah    0.129885 -0.475957 -0.207679
Ohio   -2.978331 -1.015918  0.784675
Texas  -0.256689 -0.226366  2.262588
Oregon  2.605526  1.139105 -0.927518

In [5]: myformat=lambda x: f'{x:.2f}'

In [6]: frame.d.map(myformat)
Out[6]:
Utah      -0.48
Ohio      -1.02
Texas     -0.23
Oregon     1.14
Name: d, dtype: object

In [7]: frame.d.apply(myformat)
Out[7]:
Utah      -0.48
Ohio      -1.02
Texas     -0.23
Oregon     1.14
Name: d, dtype: object

In [8]: frame.applymap(myformat)
Out[8]:
            b      d      e
Utah     0.13  -0.48  -0.21
Ohio    -2.98  -1.02   0.78
Texas   -0.26  -0.23   2.26
Oregon   2.61   1.14  -0.93

In [9]: frame.apply(lambda x: x.apply(myformat))
Out[9]:
            b      d      e
Utah     0.13  -0.48  -0.21
Ohio    -2.98  -1.02   0.78
Texas   -0.26  -0.23   2.26
Oregon   2.61   1.14  -0.93


In [10]: myfunc=lambda x: x**2

In [11]: frame.applymap(myfunc)
Out[11]:
            b         d         e
Utah    0.016870  0.226535  0.043131
Ohio    8.870453  1.032089  0.615714
Texas   0.065889  0.051242  5.119305
Oregon  6.788766  1.297560  0.860289

In [12]: frame.apply(myfunc)
Out[12]:
            b         d         e
Utah    0.016870  0.226535  0.043131
Ohio    8.870453  1.032089  0.615714
Texas   0.065889  0.051242  5.119305
Oregon  6.788766  1.297560  0.860289
Альфа
источник
0

СЧ:

В следующем примере показано applyи applymapприменяется к DataFrame.

mapФункция - это то, что вы применяете только в серии. Вы не можете подать заявку map на DataFrame.

Следует помнить, что applyможно делать все что угодноapplymap , но applyесть опции eXtra .

Опции X-фактора: axisи result_typeгде result_typeработает только когда axis=1(для столбцов).

df = DataFrame(1, columns=list('abc'),
                  index=list('1234'))
print(df)

f = lambda x: np.log(x)
print(df.applymap(f)) # apply to the whole dataframe
print(np.log(df)) # applied to the whole dataframe
print(df.applymap(np.sum)) # reducing can be applied for rows only

# apply can take different options (vs. applymap cannot)
print(df.apply(f)) # same as applymap
print(df.apply(sum, axis=1))  # reducing example
print(df.apply(np.log, axis=1)) # cannot reduce
print(df.apply(lambda x: [1, 2, 3], axis=1, result_type='expand')) # expand result

В качестве описания mapфункции Series не следует путать ее с mapфункцией Python .

Первый применяется к Серии, чтобы отобразить значения, а второй - к каждому элементу итерируемого.


Наконец, не путайте applyметод dataframe с applyметодом groupby .

прости
источник