Diferença entre mapa, applymap e métodos de aplicação no Pandas

466

Você pode me dizer quando usar esses métodos de vetorização com exemplos básicos?

Vejo que mapé um Seriesmétodo, enquanto o resto são DataFramemétodos. Fiquei confuso sobre applye applymapmétodos embora. Por que temos dois métodos para aplicar uma função a um DataFrame? Novamente, exemplos simples que ilustram o uso seriam ótimos!

marillion
fonte
5
Corrija-me se estiver errado, mas acredito que essas funções não são métodos de vetorização, pois envolvem um loop sobre os elementos nos quais são aplicados.
quer
1
Não consigo ver a diferença aqui: gist.github.com/MartinThoma/e320cbb937afb4ff766f75988f1c65e6
Martin Thoma

Respostas:

533

Direto do livro Python for Data Analysis de Wes McKinney , pág. 132 (recomendo vivamente este livro):

Outra operação frequente é a aplicação de uma função em matrizes 1D a cada coluna ou linha. O método de aplicação do DataFrame faz exatamente isso:

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

Muitas das estatísticas de matriz mais comuns (como soma e média) são métodos DataFrame, portanto, o uso de apply não é necessário.

Também é possível usar funções Python em termos de elementos. Suponha que você queira calcular uma sequência formatada de cada valor de ponto flutuante no quadro. Você pode fazer isso com o 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

O motivo do nome applymap é que o Series possui um método de mapa para aplicar uma função em elementos:

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

Resumindo, applyfunciona em uma linha / coluna de um DataFrame, applymapfunciona em elementos em um DataFrame e mapfunciona em elementos em uma Série.

jeremiahbuddha
fonte
31
estritamente falando, applymap internamente é implementado através de aplicar com um pouco de parâmetro de função ao longo do passado wrap-up (rougly falando substituindo funca lambda x: [func(y) for y in x], e aplicando coluna-wise)
alko
5
Obrigada pelo esclarecimento. Como mape applymapambos funcionam em elementos, eu esperaria um único método (ou mapou applymap) que funcionasse tanto para uma série quanto para um DataFrame. Provavelmente existem outras considerações de design, e Wes McKinney decidiu criar dois métodos diferentes.
Marillion
2
Está na página 129 da minha cópia por algum motivo. Não há rótulo para a segunda edição ou algo assim.
Jody
1
Existe uma maneira de fazer isso applymapcom a groupbyfunção nos pandas?
everestial007
Como aplicar uma função em dados agrupados em colunas?
hhh
83

Comparando map, applymape : O contexto importaapply

Primeira grande diferença: DEFINIÇÃO

  • map é definido apenas na série
  • applymap é definido apenas nos DataFrames
  • apply é definido em AMBOS

Segunda grande diferença: ARGUMENTO DE ENTRADA

  • mapaceita dicts Seriesou
  • applymape applyaceitar apenas callables

Terceira grande diferença: COMPORTAMENTO

  • map é elementar para Series
  • applymap é elementwise para DataFrames
  • applytambém funciona de maneira elementar, mas é adequado para operações e agregações mais complexas. O comportamento e o valor de retorno dependem da função.

Quarta maior diferença (a mais importante): USE CASE

  • mapdestina-se ao mapeamento de valores de um domínio para outro, portanto, é otimizado para desempenho (por exemplo, df['A'].map({1:'a', 2:'b', 3:'c'}))
  • applymapé bom para transformações elementares em várias linhas / colunas (por exemplo, df[['A', 'B', 'C']].applymap(str.strip))
  • applyé para aplicar qualquer função que não possa ser vetorizada (por exemplo, df['sentences'].apply(nltk.sent_tokenize))

Resumindo

insira a descrição da imagem aqui

Notas de rodapé

  1. mapquando aprovado, um dicionário / série mapeará elementos com base nas chaves desse dicionário / série. Os valores ausentes serão registrados como NaN na saída.
  2. applymapnas versões mais recentes foi otimizado para algumas operações. Você encontrará applymapum pouco mais rápido do que applyem alguns casos. Minha sugestão é testar os dois e usar o que funciona melhor.

  3. mapé otimizado para mapeamentos e transformação de elementos. Operações que envolvem dicionários ou séries permitirão que os pandas usem caminhos de código mais rápidos para obter melhor desempenho.

  4. Series.applyretorna um escalar para operações de agregação; caso contrário, Series. Da mesma forma para DataFrame.apply. Note-se que applytambém tem fastpaths quando chamado com certas funções Numpy tais como mean, sum, etc.
cs95
fonte
70

Há ótimas informações nessas respostas, mas estou adicionando as minhas para resumir claramente quais métodos funcionam em matriz versus elemento. jeremiahbuddha fez isso principalmente, mas não mencionou Series.apply. Eu não tenho o representante para comentar.

  • DataFrame.apply opera em linhas ou colunas inteiras de cada vez.

  • DataFrame.applymap, Series.applyE Series.mapoperar em um elemento de tempo.

Há muita sobreposição entre os recursos de Series.applye Series.map, o que significa que qualquer um deles funcionará na maioria dos casos. Eles têm algumas pequenas diferenças, algumas das quais foram discutidas na resposta da osa.

MarredCheese
fonte
38

Adicionando às outras respostas, em um Seriestambém há mapa e aplicação .

O Apply pode criar um DataFrame de uma série ; no entanto, o mapa apenas colocará uma série em todas as células de outra série, o que provavelmente não é o que você deseja.

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

Além disso, se eu tivesse uma função com efeitos colaterais, como "conectar-se a um servidor web", provavelmente usaria applyapenas por uma questão de clareza.

series.apply(download_file_for_every_element) 

Mappode usar não apenas uma função, mas também um dicionário ou outra série. Digamos que você queira manipular permutações .

Toma

1 2 3 4 5
2 1 4 5 3

O quadrado dessa permutação é

1 2 3 4 5
1 2 5 3 4

Você pode calcular usando map. Não tenho certeza se a auto-aplicação está documentada, mas funciona 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
fonte
3
Além disso, .apply () permite passar kwargs para a função, enquanto .map () não.
Neilxdims 12/08
19

O @jeremiahbuddha mencionou que o apply funciona em linhas / colunas, enquanto o applymap funciona em elementos. Mas parece que você ainda pode usar o aplicativo para computação por elementos ....

    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
fonte
29
Boa captura com isso. A razão pela qual isso funciona no seu exemplo é porque np.sqrt é um ufunc, ou seja, se você fornecer uma matriz, ela transmitirá a função sqrt para cada elemento da matriz. Portanto, quando o Apply empurra o np.sqrt em cada coluna, o np.sqrt funciona automaticamente em cada um dos elementos das colunas; portanto, você está basicamente obtendo o mesmo resultado que o applymap.
precisa
11

Só queria ressaltar, porque lutei um pouco com isso

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

df.applymap(f)
df.describe()

isso não modifica o próprio quadro de dados, deve ser reatribuído

df = df.applymap(f)
df.describe()
muon
fonte
1
Às vezes, tenho dificuldade em descobrir se é necessário reatribuir ou não depois de fazer algo com o df. É principalmente tentativa e erro para mim, mas aposto que há uma lógica de como funciona (que estou perdendo).
Marillion 13/04
2
em geral, um quadro de dados do pandas é modificado apenas pela reatribuição df = modified_dfou se você definir inplace=Truesinalizador. Também trama de dados vai mudar se você passar uma trama de dados a uma função por referência e as modifica função da trama de dados
múon
1
Isso não é inteiramente verdade, pense .ixou .whereetc. Não sabe ao certo qual é a explicação completa quando você precisa mudar a atribuição e quando não.
Thanos
10

Explicação provavelmente mais simples, a diferença entre apply e applymap:

Apply pega a coluna inteira como um parâmetro e, em seguida, atribua o resultado a esta coluna

applymap usa o valor da célula separado como parâmetro e atribua o resultado novamente a essa célula.

Nota: Se aplicar retorna o valor único, você terá esse valor em vez da coluna após a atribuição e, eventualmente, terá apenas uma linha em vez de matriz.

Kath
fonte
3

Meu entendimento:

Do ponto de vista da função:

Se a função tiver variáveis ​​que precisam ser comparadas em uma coluna / linha, use apply.

por exemplo: lambda x: x.max()-x.mean().

Se a função deve ser aplicada a cada elemento:

1> Se uma coluna / linha estiver localizada, use apply

2> Se aplicável a todo o quadro de dados, use 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)
Vicky Miao
fonte
Forneça o df2 também para melhor clareza, para que possamos testar seu código.
Ashish Anand
1

Com base na resposta de cs95

  • map é definido apenas na série
  • applymap é definido apenas nos DataFrames
  • apply é definido em AMBOS

dê alguns exemplos

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
Alfa
fonte
0

FOMO:

O exemplo a seguir mostra applye applymapaplicado a um DataFrame.

mapfunção é algo que você aplica apenas na série. Você não pode aplicar map no DataFrame.

É importante lembrar que applypode fazer tudo o que applymap puder, mas applytem opções eXtra .

As opções do fator X são: axise result_typeonde result_typesó funciona quando axis=1(para colunas).

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

Como nota de rodapé, a mapfunção Series , não deve ser confundida com a mapfunção Python .

O primeiro é aplicado em Série, para mapear os valores, e o segundo para cada item de um iterável.


Por fim, não confunda o applymétodo dataframe com o applymétodo groupby .

prosti
fonte