Bir işlev Panda'nın veri çerçevesinin iki sütununa nasıl uygulanır?


369

Diyelim ki dfsütunları olan bir tane var 'ID', 'col_1', 'col_2'. Ve bir fonksiyon tanımlarım:

f = lambda x, y : my_function_expression.

Şimdi uygulamak istediğiniz fiçin dfbireyin iki sütun 'col_1', 'col_2'öğeye göre hesapla yeni bir sütun 'col_3'biraz gibi:

df['col_3'] = df[['col_1','col_2']].apply(f)  
# Pandas gives : TypeError: ('<lambda>() takes exactly 2 arguments (1 given)'

Nasıl yapılır ?

** Aşağıdaki gibi detay örnek ekleyin ***

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

#df['col_3'] = df[['col_1','col_2']].apply(get_sublist,axis=1)
# expect above to output df as below 

  ID  col_1  col_2            col_3
0  1      0      1       ['a', 'b']
1  2      2      4  ['c', 'd', 'e']
2  3      3      5  ['d', 'e', 'f']

4
f'yi doğrudan sütunlara uygulayabilir misiniz: df ['col_3'] = f (df ['col_1'], df ['col_2'])
btel

1
ne fyaptığını bilmek yararlı olur
tehmisvh

2
hayır, df ['col_3'] = f (df ['col_1'], df ['col_2']) çalışmıyor. Çünkü f vektör girişlerini değil, sadece skaler girişi kabul eder. Tamam, f = lambda x, y: x + y olduğunu varsayabilirsiniz. (tabii ki, benim gerçek f o kadar basit değil, aksi takdirde doğrudan df ['col_3'] = df ['col_1'] + df ['col_2'])
bigbug

1
URL'nin altında ilgili bir Soru ve Cevap buldum, ancak sorunum 1'den 2 değil, mevcut iki sütunla yeni bir sütun hesaplıyor. stackoverflow.com/questions/12356501/…
bigbug

Yanıtım stackoverflow.com/a/52854800/5447172, bu soruna geçici çözüm veya sayısal dizin oluşturma olmadan en Pythonic / Pandanic şekilde yanıt verdiğini düşünüyorum . Tam olarak örneğinizde gereken çıktıyı üretir.
ajrwhite

Yanıtlar:


291

İşte applyben veriyorum veri çerçevesi üzerinde kullanarak bir örnek axis = 1.

Aradaki fark, işleve iki değer iletmeye çalışmak yerine, fbir pandalar Series nesnesini kabul etmek için işlevi yeniden yazmak ve sonra gerekli değerleri almak için Diziyi dizinlemek olduğunu unutmayın.

In [49]: df
Out[49]: 
          0         1
0  1.000000  0.000000
1 -0.494375  0.570994
2  1.000000  0.000000
3  1.876360 -0.229738
4  1.000000  0.000000

In [50]: def f(x):    
   ....:  return x[0] + x[1]  
   ....:  

In [51]: df.apply(f, axis=1) #passes a Series object, row-wise
Out[51]: 
0    1.000000
1    0.076619
2    1.000000
3    1.646622
4    1.000000

Kullanım durumunuza bağlı olarak, bazen bir panda groupnesnesi oluşturmak ve sonra applygrup üzerinde kullanmak yararlı olabilir .


Evet, uygulamak kullanmaya çalıştım, ancak geçerli sözdizimi ifadesini bulamıyorum. Ve her df satırı benzersizse yine de groupby kullanılsın mı?
bigbug

Cevabıma bir örnek ekledim, umarım bu aradığınızı yapar. Değilse, sumşimdiye kadar önerilen yöntemlerden herhangi biri tarafından başarıyla çözüldüğü için lütfen daha spesifik bir örnek işlev sağlayın .
Aman

1
Lütfen kodunuzu yapıştırır mısınız? İşlevi yeniden yazarım: def get_sublist (x): dönüş mylist [x [1]: x [2] + 1] ve df ['col_3'] = df.apply (get_sublist, axis = 1) 'ValueError: operands olabilir (2) (3) 'ile birlikte yayınlanamaz'
bigbug

3
@Aman: Panda sürüm 0.14.1 (ve muhtemelen daha önce) ile, kullanım lambda ifadesini de kullanabilir. dfTanımladığınız nesneyi verin , başka bir yaklaşım (eşdeğer sonuçlarla) df.apply(lambda x: x[0] + x[1], axis = 1).
Jubbles

2
@CanCeylan, dizin isimleri yerine işlevdeki sütun adlarını kullanabilirsiniz, ardından sipariş değiştirme konusunda endişelenmenize veya dizini adıyla almanıza gerek yoktur, örneğin bkz. Stackoverflow.com/questions/13021654/…
Davos

168

Pandalar'da bunu yapmanın temiz, tek satırlı bir yolu var:

df['col_3'] = df.apply(lambda x: f(x.col_1, x.col_2), axis=1)

Bu, fbirden çok giriş değeri olan kullanıcı tanımlı bir işlev olmasını sağlar ve sütunlara erişmek için (güvenli olmayan) sayısal dizinler yerine (güvenli) sütun adları kullanır.

Verilere sahip örnek (orijinal soruya dayanarak):

import pandas as pd

df = pd.DataFrame({'ID':['1', '2', '3'], 'col_1': [0, 2, 3], 'col_2':[1, 4, 5]})
mylist = ['a', 'b', 'c', 'd', 'e', 'f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

df['col_3'] = df.apply(lambda x: get_sublist(x.col_1, x.col_2), axis=1)

Çıktı print(df):

  ID  col_1  col_2      col_3
0  1      0      1     [a, b]
1  2      2      4  [c, d, e]
2  3      3      5  [d, e, f]

Sütun adlarınız boşluk içeriyorsa veya mevcut bir veri çerçevesi özniteliğiyle bir ad paylaşıyorsa, köşeli parantezlerle dizinleyebilirsiniz:

df['col_3'] = df.apply(lambda x: f(x['col 1'], x['col 2']), axis=1)

2
Eğer axis=1ve you sütunu çağrılırsa name, sütun verilerinizi aslında döndürmeyecektir index. Almak üzere benzer namebir yer groupby(). Bunu sütunumu yeniden adlandırarak çözdüm.
Tom Hemmes

2
BUDUR! Lambdaslara çoklu giriş parametreleri ile kullanıcı tanımlı fonksiyonlar ekleyebileceğinizi bilmiyordum. Series.apply () yerine DF.apply () kullandığınızı belirtmek önemlidir (bence). Bu, istediğiniz iki sütunu kullanarak df'yi dizine eklemenizi ve tüm sütunu işleve geçirmenizi sağlar, ancak Apply () yöntemini kullandığınız için, işlevi tüm sütun boyunca öğe bazında uygular. Parlak! Gönderdiğiniz için teşekkür ederiz!
Veri phile

1
EN SONUNDA! Günümü kurtardın!
Mysterio

Bunu yapmak için önerilen yolu df.loc [:, 'new col'] = df.apply .....
valearner

@valearner Örnekte tercih etmek için herhangi bir neden olduğunu düşünmüyorum .loc. Bunu başka bir sorun ayarına uyarlamanız gerekebilir (örn. Dilimlerle çalışma).
ajrwhite

86

Basit bir çözüm:

df['col_3'] = df[['col_1','col_2']].apply(lambda x: f(*x), axis=1)

1
bu yanıtın sorgudaki yaklaşımdan farkı nedir: df ['col_3'] = df [['col_1', 'col_2']]. (f) 'yi sadece onaylamak için uygulayın, sorudaki yaklaşım işe yaramadı çünkü poster bu ekseni belirtmedi = 1, varsayılan eksen = 0?
Lost1

1
Bu cevap @ Aman'ın cevabı ile karşılaştırılabilir, ancak biraz daha keskindir. Anonim bir fonksiyon inşa ediyor ve f fonksiyonuna geçmeden önce paketini açıyor.
tiao

39

İlginç bir soru! cevabım aşağıdaki gibi:

import pandas as pd

def sublst(row):
    return lst[row['J1']:row['J2']]

df = pd.DataFrame({'ID':['1','2','3'], 'J1': [0,2,3], 'J2':[1,4,5]})
print df
lst = ['a','b','c','d','e','f']

df['J3'] = df.apply(sublst,axis=1)
print df

Çıktı:

  ID  J1  J2
0  1   0   1
1  2   2   4
2  3   3   5
  ID  J1  J2      J3
0  1   0   1     [a]
1  2   2   4  [c, d]
2  3   3   5  [d, e]

Ben ID <J1 <J2 <J3 sağlamak için sütun adını ID, J1, J2, J3 olarak değiştirdim, böylece sütun doğru sırayla görüntülenir.

Bir kısa versiyon daha:

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'J1': [0,2,3], 'J2':[1,4,5]})
print df
lst = ['a','b','c','d','e','f']

df['J3'] = df.apply(lambda row:lst[row['J1']:row['J2']],axis=1)
print df

23

Aradığınız yöntem Series.combine'dir. Ancak, veri tiplerinin etrafına biraz özen gösterilmesi gerektiği görülmektedir. Örneğinizde, (yanıtı test ederken yaptığım gibi) naif olarak

df['col_3'] = df.col_1.combine(df.col_2, func=get_sublist)

Ancak, bu hatayı atar:

ValueError: setting an array element with a sequence.

En iyi tahminim, sonucun yöntemi çağıran diziyle aynı türden olmasını beklediği gibi görünüyor. Ancak, aşağıdakiler çalışır:

df['col_3'] = df.col_1.astype(object).combine(df.col_2, func=get_sublist)

df

   ID   col_1   col_2   col_3
0   1   0   1   [a, b]
1   2   2   4   [c, d, e]
2   3   3   5   [d, e, f]

12

Yazdığınız şekilde iki giriş gerekir. Hata mesajına bakarsanız, f'ye iki giriş sağlamadığınızı söyler, sadece bir tane. Hata mesajı doğrudur.
Uyumsuzluk, df [['col1', 'col2']] 'nin iki ayrı sütun değil, iki sütun içeren tek bir veri çerçevesi döndürmesidir.

F'nizi tek bir giriş alacak şekilde değiştirmeniz, yukarıdaki veri çerçevesini giriş olarak tutmanız, sonra işlev gövdesinin içinde x, y olarak ayırmanız gerekir . Sonra ihtiyacınız olanı yapın ve tek bir değer döndürün.

Sözdizimi .apply (f) olduğu için bu işlev imzasına ihtiyacınız vardır. Bu nedenle f, geçerli f'nizin beklediği iki şeyi değil, tek şey = veri çerçevesi almalıdır.

F gövdesini sağlamadığınız için artık ayrıntılı olarak yardım edemem - ancak bu, kodunuzu temelde değiştirmeden veya uygulamak yerine başka yöntemler kullanmadan çıkış yolu sağlamalıdır.


12

Np.vectorize için oy vereceğim. Sadece x sütun sayısını aşmanıza ve işlevdeki veri çerçevesiyle uğraşmanıza izin vermez, bu nedenle kontrol etmediğiniz veya bir işleve 2 sütun ve sabit gönderme gibi bir şey (ör. Col_1, col_2, 'foo').

import numpy as np
import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

#df['col_3'] = df[['col_1','col_2']].apply(get_sublist,axis=1)
# expect above to output df as below 

df.loc[:,'col_3'] = np.vectorize(get_sublist, otypes=["O"]) (df['col_1'], df['col_2'])


df

ID  col_1   col_2   col_3
0   1   0   1   [a, b]
1   2   2   4   [c, d, e]
2   3   3   5   [d, e, f]

1
Bu gerçekten pandaları kullanarak soruyu cevaplamıyor.
mnky9800n

18
Soru "Pandas veri çerçevesinin iki sütununa bir işlev nasıl uygulanır" değil "Yalnızca Pandas yöntemlerini kullanarak iki Panda veri çerçevesine bir işlev nasıl uygulanır" ve numpy, Pandaların bir bağımlılığıdır, bu yüzden yine de yüklemeniz gerekir, bu garip bir itiraz gibi görünüyor.
Trae Wallace

12

Bir listenin applydöndürülmesi tehlikeli bir işlemdir, çünkü ortaya çıkan nesnenin Seri veya DataFrame olduğu garanti edilmez. Ve bazı durumlarda istisnalar olabilir. Basit bir örneği inceleyelim:

df = pd.DataFrame(data=np.random.randint(0, 5, (5,3)),
                  columns=['a', 'b', 'c'])
df
   a  b  c
0  4  0  0
1  2  0  1
2  2  2  2
3  1  2  2
4  3  0  0

Bir liste döndürerek üç olası sonuç vardır: apply

1) Döndürülen listenin uzunluğu sütun sayısına eşit değilse, bir dizi dizi döndürülür.

df.apply(lambda x: list(range(2)), axis=1)  # returns a Series
0    [0, 1]
1    [0, 1]
2    [0, 1]
3    [0, 1]
4    [0, 1]
dtype: object

2) Döndürülen listenin uzunluğu sütun sayısına eşit olduğunda, bir DataFrame döndürülür ve her sütun listede karşılık gelen değeri alır.

df.apply(lambda x: list(range(3)), axis=1) # returns a DataFrame
   a  b  c
0  0  1  2
1  0  1  2
2  0  1  2
3  0  1  2
4  0  1  2

3) Döndürülen listenin uzunluğu ilk satır için sütun sayısına eşitse, ancak listenin bir ValueError öğesinin yükseltildiği sütun sayısından farklı sayıda öğeye sahip olduğu en az bir satıra sahipse.

i = 0
def f(x):
    global i
    if i == 0:
        i += 1
        return list(range(3))
    return list(range(4))

df.apply(f, axis=1) 
ValueError: Shape of passed values is (5, 4), indices imply (5, 3)

Sorunu başvurmadan cevaplama

applyEksen = 1 ile kullanmak çok yavaştır. Temel yinelemeli yöntemlerle (özellikle daha büyük veri kümelerinde) daha iyi performans elde etmek mümkündür.

Daha büyük veri çerçevesi oluştur

df1 = df.sample(100000, replace=True).reset_index(drop=True)

zamanlamalar

# apply is slow with axis=1
%timeit df1.apply(lambda x: mylist[x['col_1']: x['col_2']+1], axis=1)
2.59 s ± 76.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

# zip - similar to @Thomas
%timeit [mylist[v1:v2+1] for v1, v2 in zip(df1.col_1, df1.col_2)]  
29.5 ms ± 534 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

@Thomas cevap

%timeit list(map(get_sublist, df1['col_1'],df1['col_2']))
34 ms ± 459 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

1
Öğrenmenin mümkün olduğu yerlerden çok detaylı cevaplar görmek güzel.
Andrea Moro

7

Eminim ki bu, Pandalar veya Numpy işlemlerini kullanan çözümler kadar hızlı değildir, ancak işlevinizi yeniden yazmak istemiyorsanız haritayı kullanabilirsiniz. Orijinal örnek verileri kullanma -

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

df['col_3'] = list(map(get_sublist,df['col_1'],df['col_2']))
#In Python 2 don't convert above to list

Fonksiyona bu şekilde istediğimiz kadar argüman aktarabiliriz. Çıktı istediğimiz şey

ID  col_1  col_2      col_3
0  1      0      1     [a, b]
1  2      2      4  [c, d, e]
2  3      3      5  [d, e, f]

1
Bu aslında çok daha hızlı olan cevaplar o kullanımıdır applyileaxis=1
Ted Petrou

2

Sorularınıza örnek:

def get_sublist(row, col1, col2):
    return mylist[row[col1]:row[col2]+1]
df.apply(get_sublist, axis=1, col1='col_1', col2='col_2')

2

Çok büyük bir veri kümeniz varsa, bunu daha hızlı ve kolay bir şekilde (yürütme süresi) kullanarak bunu hızlı bir şekilde yapabilirsiniz:

import pandas as pd
import swifter

def fnc(m,x,c):
    return m*x+c

df = pd.DataFrame({"m": [1,2,3,4,5,6], "c": [1,1,1,1,1,1], "x":[5,3,6,2,6,1]})
df["y"] = df.swifter.apply(lambda x: fnc(x.m, x.x, x.c), axis=1)

1

get_sublistFonksiyonu değiştirmek istemediğinizi ve sadece applyişi yapmak için DataFrame'in yöntemini kullanmak istediğinizi varsayalım . İstediğiniz sonucu elde etmek için iki yardım işlevi yazdım: get_sublist_listve unlist. İşlev adından da anlaşılacağı gibi, önce alt listenin listesini alın, ikinci olarak o listeden o listeyi çıkarın. Son olarak, applybu iki işlevi daha sonra df[['col_1','col_2']]DataFrame'e uygulamak için işlevi çağırmamız gerekir .

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

def get_sublist_list(cols):
    return [get_sublist(cols[0],cols[1])]

def unlist(list_of_lists):
    return list_of_lists[0]

df['col_3'] = df[['col_1','col_2']].apply(get_sublist_list,axis=1).apply(unlist)

df

İşlevi []kapsamak için kullanmazsanız , get_sublistişlev get_sublist_listdüz bir liste döndürür ValueError: could not broadcast input array from shape (3) into shape (2), @Ted Petrou'nun belirttiği gibi yükselir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.