Панды: Как я могу использовать функцию apply () для одного столбца?

260

У меня есть кадр данных панд с двумя столбцами. Мне нужно изменить значения первого столбца, не затрагивая второй, и получить обратно весь фрейм данных только с измененными значениями первого столбца. Как я могу сделать это, используя apply в пандах?

Амани
источник
4
Пожалуйста, опубликуйте некоторые входные данные и желаемый результат.
Фабио Ламанна
Вы не должны почти никогда использовать applyв такой ситуации. Вместо этого используйте столбец напрямую.
Тед Петру
Как сказал Тед Петру, избегайте использовать applyкак можно больше. Если вы не уверены, что вам нужно его использовать, скорее всего, нет. Я рекомендую взглянуть на то, когда я хочу использовать pandas apply () в своем коде? ,
cs95
Вопрос не совсем ясен: применяется ли функция к каждому элементу столбца или применяется функция к столбцу в целом (например: перевернуть столбец)?
Пьер Альбарде

Ответы:

339

Дан примерный кадр данных dfкак:

a,b
1,2
2,3
3,4
4,5

что вы хотите это:

df['a'] = df['a'].apply(lambda x: x + 1)

что возвращает:

   a  b
0  2  2
1  3  3
2  4  4
3  5  5
Фабио Ламанна
источник
9
applyникогда не должен использоваться в такой ситуации
Тед Петру
5
@TedPetrou Вы совершенно правы, это был просто пример того, как применить общую функцию к одному столбцу, как спросил ОП.
Фабио
14
Когда я пытаюсь сделать это, я получаю следующее предупреждение: «Значение пытается быть установлено для копии среза из DataFrame. Попробуйте вместо этого использовать .loc [row_indexer, col_indexer] = value»
dagrun
24
Из любопытства: почему не следует применять в этой ситуации? Какова ситуация точно?
Дядя Бен Бен
19
@UncleBenBen обычно applyиспользует внутренний цикл над строками, который намного медленнее, чем векторизованные функции, например, например df.a = df.a / 2(см. Ответ Mike Muller).
Фабио Ламанна
66

Для одного столбца лучше использовать map(), например так:

df = pd.DataFrame([{'a': 15, 'b': 15, 'c': 5}, {'a': 20, 'b': 10, 'c': 7}, {'a': 25, 'b': 30, 'c': 9}])

    a   b  c
0  15  15  5
1  20  10  7
2  25  30  9



df['a'] = df['a'].map(lambda a: a / 2.)

      a   b  c
0   7.5  15  5
1  10.0  10  7
2  12.5  30  9
Георгий Петров
источник
78
Почему map()лучше, чем apply()для одного столбца?
ChaimG
2
Это было очень полезно. Я использовал его для извлечения имен файлов из путей, хранящихся в столбцеdf['file_name'] = df['Path'].map(lambda a: os.path.basename(a))
mmann1123
46
map () для Series (то есть для отдельных столбцов) и работает с одной ячейкой за раз, тогда как apply () для DataFrame и работает с целой строкой за раз.
jpcgt
3
@jpcgt Означает ли это, что карта быстрее, чем применяется в этом случае?
Viragos
@ChaimG Я вижу, что это хорошо объясняет: stackoverflow.com/a/19798528/571828
道 嘉 道
41

Вам не нужна функция вообще. Вы можете работать над целым столбцом напрямую.

Пример данных:

>>> df = pd.DataFrame({'a': [100, 1000], 'b': [200, 2000], 'c': [300, 3000]})
>>> df

      a     b     c
0   100   200   300
1  1000  2000  3000

Половина всех значений в столбце a:

>>> df.a = df.a / 2
>>> df

     a     b     c
0   50   200   300
1  500  2000  3000
Майк Мюллер
источник
Что, если я хочу разделить каждый элемент в столбце с помощью "/" и принять первую часть?
K47
12

Хотя данные ответы являются правильными, они изменяют начальный фрейм данных, что не всегда желательно (и, учитывая, что OP запросил примеры «использования apply», возможно, им нужна версия, которая возвращает новый фрейм данных, как это applyпроисходит).

Это возможно с помощью assign: он действителен для assignсуществующих столбцов, как указано в документации (выделено мое):

Назначьте новые столбцы в DataFrame.

Возвращает новый объект со всеми исходными столбцами в дополнение к новым. Существующие столбцы, которые будут переназначены, будут перезаписаны .

Коротко:

In [1]: import pandas as pd

In [2]: df = pd.DataFrame([{'a': 15, 'b': 15, 'c': 5}, {'a': 20, 'b': 10, 'c': 7}, {'a': 25, 'b': 30, 'c': 9}])

In [3]: df.assign(a=lambda df: df.a / 2)
Out[3]: 
      a   b  c
0   7.5  15  5
1  10.0  10  7
2  12.5  30  9

In [4]: df
Out[4]: 
    a   b  c
0  15  15  5
1  20  10  7
2  25  30  9

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

Тибо Дюберне
источник
9

Если вы действительно обеспокоены скоростью выполнения вашей функции применения, и у вас есть огромный набор данных для работы, вы можете использовать swifter для ускорения выполнения, вот пример для swifter на панде dataframe:

import pandas as pd
import swifter

def fnc(m):
    return m*3+4

df = pd.DataFrame({"m": [1,2,3,4,5,6], "c": [1,1,1,1,1,1], "x":[5,3,6,2,6,1]})

# apply a self created function to a single column in pandas
df["y"] = df.m.swifter.apply(fnc)

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

Durjoy
источник
1

Позвольте мне попробовать сложное вычисление, используя datetime и принимая во внимание нули или пустые места. Я сокращаю 30 лет для столбца datetime и использую applyметод, а также lambdaконвертирую формат datetime. Line if x != '' else xпозаботится обо всех пустых местах или нулях соответственно.

df['Date'] = df['Date'].fillna('')
df['Date'] = df['Date'].apply(lambda x : ((datetime.datetime.strptime(str(x), '%m/%d/%Y') - datetime.timedelta(days=30*365)).strftime('%Y%m%d')) if x != '' else x)
Harry_pb
источник