Stel dat ik een df
heb met kolommen van 'ID', 'col_1', 'col_2'
. En ik definieer een functie :
f = lambda x, y : my_function_expression
.
Nu wil ik de f
toepassen op de twee kolommen van df
'col_1', 'col_2'
om elementsgewijs een nieuwe kolom 'col_3'
, ongeveer zoals :
df['col_3'] = df[['col_1','col_2']].apply(f)
# Pandas gives : TypeError: ('<lambda>() takes exactly 2 arguments (1 given)'
Hoe te doen?
**Voeg detailvoorbeeld toe zoals hieronder ***
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']
Antwoord 1, autoriteit 100%
Hier is een voorbeeld van het gebruik van apply
op het dataframe, dat ik aanroep met axis = 1
.
Merk op dat het verschil is dat in plaats van te proberen twee waarden door te geven aan de functie f
, de functie moet worden herschreven om een pandas Series-object te accepteren en vervolgens de Series te indexeren om de benodigde waarden te krijgen.
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
Afhankelijk van uw gebruikssituatie is het soms handig om een pandas group
-object te maken en vervolgens apply
op de groep te gebruiken.
Antwoord 2, autoriteit 87%
Er is een duidelijke, eenregelige manier om dit te doen in Panda’s:
df['col_3'] = df.apply(lambda x: f(x.col_1, x.col_2), axis=1)
Hierdoor kan f
een door de gebruiker gedefinieerde functie zijn met meerdere invoerwaarden, en worden (veilige) kolomnamen gebruikt in plaats van (onveilige) numerieke indices om toegang te krijgen tot de kolommen.
Voorbeeld met gegevens (op basis van oorspronkelijke vraag):
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)
Uitvoer van 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]
Als uw kolomnamen spaties bevatten of een naam delen met een bestaand dataframe-kenmerk, kunt u indexeren met vierkante haken:
df['col_3'] = df.apply(lambda x: f(x['col 1'], x['col 2']), axis=1)
Antwoord 3, autoriteit 30%
Een eenvoudige oplossing is:
df['col_3'] = df[['col_1','col_2']].apply(lambda x: f(*x), axis=1)
Antwoord 4, autoriteit 11%
Een interessante vraag! mijn antwoord zoals hieronder:
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
Uitvoer:
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]
Ik heb de kolomnaam gewijzigd in ID,J1,J2,J3 om ID < J1 < J2 < J3, dus de kolom wordt in de juiste volgorde weergegeven.
Nog een korte versie:
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
Antwoord 5, autoriteit 7%
De methode die u zoekt is Series.combine.
Het lijkt er echter op dat er enige zorg moet worden besteed aan datatypes.
In jouw voorbeeld zou je (zoals ik deed bij het testen van het antwoord) naïef bellen
df['col_3'] = df.col_1.combine(df.col_2, func=get_sublist)
Dit geeft echter de fout:
ValueError: setting an array element with a sequence.
Mijn beste gok is dat het lijkt te verwachten dat het resultaat van hetzelfde type is als de serie die de methode (DF.col_1 hier) oproept. De volgende werken:
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]
Antwoord 6, Autoriteit 4%
Een lijst retourneren van apply
is een gevaarlijke werking als het resulterende object is niet gegarandeerd ofwel een serie of een dataframe. En uitzonderingen kunnen in bepaalde gevallen worden verhoogd. Laten we door een eenvoudig voorbeeld lopen:
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
Er zijn drie mogelijke uitkomsten met het retourneren van een lijst van apply
1) Als de lengte van de geretourneerde lijst niet gelijk is aan het aantal kolommen, wordt dan een reeks lijsten geretourneerd.
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) Wanneer de lengte van de geretourneerde lijst gelijk is aan het aantal
Kolommen Dan wordt een dataframe geretourneerd en elke kolom krijgt de
Corresponderende waarde in de lijst.
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)Als de lengte van de geretourneerde lijst gelijk is aan het aantal kolommen voor de eerste rij, maar ten minste één rij heeft waar de lijst een ander aantal elementen heeft dan het aantal kolommen, is er een ValueError verhoogd.
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)
Het probleem beantwoorden zonder te solliciteren
Het gebruik van apply
met axis=1 is erg traag. Het is mogelijk om veel betere prestaties te krijgen (vooral op grotere datasets) met eenvoudige iteratieve methoden.
Maak een groter dataframe
df1 = df.sample(100000, replace=True).reset_index(drop=True)
Timingen
# 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 antwoord
%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)
Antwoord 7, autoriteit 3%
Zoals je hebt geschreven, heeft het twee ingangen nodig. Als je naar de foutmelding kijkt, staat er dat je geen twee ingangen geeft aan f, maar één. De foutmelding is correct.
De mismatch is omdat df[[‘col1′,’col2’]] een enkel dataframe retourneert met twee kolommen, niet twee afzonderlijke kolommen.
Je moet je f veranderen zodat er een enkele invoer voor nodig is, het bovenstaande dataframe als invoer behouden en het dan opsplitsen in x,y binnende functietekst. Doe dan wat je nodig hebt en retourneer een enkele waarde.
Je hebt deze functiehandtekening nodig omdat de syntaxis .apply(f) is
Dus f moet het enige ding = dataframe nemen en niet twee dingen, wat je huidige f verwacht.
Aangezien je de hoofdtekst van f niet hebt verstrekt, kan ik je niet meer in detail helpen – maar dit zou de uitweg moeten bieden zonder je code fundamenteel te veranderen of andere methoden te gebruiken in plaats van toe te passen
Antwoord 8, autoriteit 3%
Ik ga stemmen voor np.vectorize. Hiermee kun je gewoon meer dan x aantal kolommen fotograferen en niet omgaan met het dataframe in de functie, dus het is geweldig voor functies die je niet beheert of iets doet zoals het verzenden van 2 kolommen en een constante naar een functie (dwz 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]
Antwoord 9, autoriteit 2%
Ik weet zeker dat dit niet zo snel is als de oplossingen met Pandas- of Numpy-bewerkingen, maar als je je functie niet wilt herschrijven, kun je map gebruiken. De originele voorbeeldgegevens gebruiken –
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
We zouden zoveel argumenten kunnen passeren zoals we op deze manier in de functie wilden. De uitvoer is wat we wilden
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]
Antwoord 10
Mijn voorbeeld naar uw vragen:
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')
Antwoord 11
Als u een enorme dataset hebt, kunt u een eenvoudige maar snellere (uitvoeringstijd) manier gebruiken om dit te doen met Swifter:
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)
Antwoord 12
Ik veronderstel dat u niet wilt wijzigen get_sublist
-functie, en wilt u gewoon dataframe gebruiken apply
-methode toepassen om de taak te doen. Om het gewenste resultaat te krijgen, heb je twee hulpfuncties geschreven: get_sublist_list
en unlist
. Naarmate de functienaam suggereert, ontvang eerst de lijst met sublist, tweede uittreksel die sublist uit die lijst. Ten slotte moeten we u bellen met apply
-functie om die twee functies toe te passen op de df[['col_1','col_2']]
DATAFRAME.
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
Als u niet gebruikt []
om de get_sublist
-functie bij te voegen, dan zal de get_sublist_list
-functie een duidelijke lijst retourneren, deze ‘ LL verhogen ValueError: could not broadcast input array from shape (3) into shape (2)
, zoals @TED PETRU had genoemd.