Ik heb een dataframe
van Pandas:
import pandas as pd
inp = [{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}]
df = pd.DataFrame(inp)
print df
Uitvoer:
c1 c2
0 10 100
1 11 110
2 12 120
Nu wil ik de rijen van dit frame herhalen. Voor elke rij wil ik toegang hebben tot de elementen (waarden in cellen) door de naam van de kolommen. Bijvoorbeeld:
for row in df.rows:
print row['c1'], row['c2']
Is het mogelijk om dat te doen in Panda’s?
Ik heb deze vergelijkbare vraag gevonden . Maar het geeft me niet het antwoord dat ik nodig heb. Er wordt bijvoorbeeld voorgesteld om het volgende te gebruiken:
for date, row in df.T.iteritems():
of
for row in df.iterrows():
Maar ik begrijp niet wat het row
-object is en hoe ik ermee kan werken.
Antwoord 1, autoriteit 100%
DataFrame.iterrows
is een generator die zowel de index als de rij oplevert (als een serie):
import pandas as pd
df = pd.DataFrame({'c1': [10, 11, 12], 'c2': [100, 110, 120]})
for index, row in df.iterrows():
print(row['c1'], row['c2'])
10 100
11 110
12 120
Antwoord 2, autoriteit 35%
Hoe itereren over rijen in een DataFrame in Panda’s?
Antwoord: NIET*!
Iteratie in Panda’s is een anti-patroon en is iets wat je alleen moet doen als je alle andere opties hebt uitgeput. U mag geen enkele functie gebruiken met "iter
" in zijn naam voor meer dan een paar duizend rijen of je zult moeten wennen aan veel wachten.
Wilt u een DataFrame afdrukken? Gebruik DataFrame.to_string()
.
Wilt u iets berekenen? Zoek in dat geval naar methoden in deze volgorde (lijst aangepast van hier):
- Vectorisering
- Cython-routines
- Lijst met begrippen (vanille
for
lus) DataFrame.apply()
: i) Reducties die kunnen worden uitgevoerd in Cython, ii) Iteratie in Python-ruimteDataFrame.itertuples()
eniteritems()
DataFrame.iterrows()
iterrows
en itertuples
(beiden ontvangen veel stemmen in antwoorden op deze vraag) moeten in zeer zeldzame omstandigheden worden gebruikt, zoals het genereren van rij-objecten/nametules voor sequentiële verwerking, dat is eigenlijk het enige waarvoor deze functies nuttig zijn.
Beroep op autoriteit
De documentatiepagina over iteratie heeft een enorme rood waarschuwingsvenster met de tekst:
Het doorlopen van panda-objecten is over het algemeen traag. In veel gevallen is handmatig herhalen van de rijen niet nodig […].
* Het is eigenlijk een beetje ingewikkelder dan “niet doen”. df.iterrows()
is het juiste antwoord op deze vraag, maar "vectoriseer uw ops" is de betere. Ik geef toe dat er omstandigheden zijn waarin iteratie niet kan worden vermeden (bijvoorbeeld sommige bewerkingen waarbij het resultaat afhangt van de waarde die voor de vorige rij is berekend). Het vereist echter enige bekendheid met de bibliotheek om te weten wanneer. Als u niet zeker weet of u een iteratieve oplossing nodig heeft, is dat waarschijnlijk niet het geval. PS: Ga helemaal naar beneden om meer te weten te komen over mijn reden voor het schrijven van dit antwoord.
Sneller dan looping: vectorisatie, Cython
Een groot aantal basisbewerkingen en berekeningen zijn “gevectoriseerd” door panda’s (ofwel via NumPy, ofwel via Cythonized-functies). Dit omvat rekenkunde, vergelijkingen, (de meeste) reducties, omvormen (zoals draaien), samenvoegen en groupby-bewerkingen. Bekijk de documentatie over Essentiële basisfunctionaliteit om een geschikte gevectoriseerde methode voor uw probleem te vinden.
Als er geen bestaat, kunt u uw eigen schrijven met aangepaste Cython-extensies.
Volgende beste: Lijstbegrippen*
Lijstbegrippen zouden uw volgende aanloophaven moeten zijn als 1) er geen gevectoriseerde oplossing beschikbaar is, 2) prestaties belangrijk zijn, maar niet belangrijk genoeg om het gedoe van het cythoniseren van uw code te doorstaan, en 3) u probeert voer elementgewijze transformatie uit op uw code. Er is een goede hoeveelheid bewijs om te suggereren dat de lijst begrip zijn voldoende snel (en soms zelfs sneller) voor veel voorkomende Panda’s-taken.
De formule is eenvoudig,
# Iterating over one column - `f` is some function that processes your data
result = [f(x) for x in df['col']]
# Iterating over two columns, use `zip`
result = [f(x, y) for x, y in zip(df['col1'], df['col2'])]
# Iterating over multiple columns - same data type
result = [f(row[0], ..., row[n]) for row in df[['col1', ...,'coln']].to_numpy()]
# Iterating over multiple columns - differing data type
result = [f(row[0], ..., row[n]) for row in zip(df['col1'], ..., df['coln'])]
Als u uw bedrijfslogica in een functie kunt inkapselen, kunt u een lijstbegrip gebruiken dat deze aanroept. Je kunt willekeurig complexe dingen laten werken door de eenvoud en snelheid van onbewerkte Python-code.
Voorbehoud
Lijstbegrippen gaan ervan uit dat uw gegevens gemakkelijk zijn om mee te werken – wat dat betekent is dat uw gegevenstypen consistent zijn en dat u geen NaN’s heeft, maar dit kan niet altijd worden gegarandeerd.
- De eerste is meer voor de hand liggend, maar als je met NaN’s te maken hebt, geef dan de voorkeur aan ingebouwde panda’s-methoden als die bestaan (omdat ze veel betere logica voor het afhandelen van hoekgevallen hebben), of zorg ervoor dat je bedrijfslogica de juiste NaN-verwerkingslogica bevat.
- Als je te maken hebt met gemengde gegevenstypen, moet je herhalen over
zip(df['A'], df['B'], ...)
in plaats vandf[['A', 'B']].to_numpy()
aangezien de laatste impliciet gegevens upcast naar het meest voorkomende type. Als A bijvoorbeeld numeriek is en B een string is, zalto_numpy()
de hele array naar string casten, wat misschien niet is wat je wilt. Gelukkig is hetzip
je kolommen samen pingen de meest eenvoudige oplossing hiervoor.
*Uw kilometerstand kan variëren om de redenen die worden beschreven in het gedeelte Voorbehouden hierboven.
Een voor de hand liggend voorbeeld
Laten we het verschil demonstreren met een eenvoudig voorbeeld van het toevoegen van twee panda-kolommen A + B
. Dit is een vectoriseerbare bewerking, dus het zal gemakkelijk zijn om de prestaties van de hierboven besproken methoden te vergelijken.
Benchmarkingcode, ter referentie. De regel onderaan meet een functie geschreven in numpanda’s, een stijl van Panda’s die sterk vermengt met NumPy om maximale prestaties uit te persen. Het schrijven van numerieke code moet worden vermeden, tenzij je weet wat je doet. Blijf bij de API waar je kunt (d.w.z. geef de voorkeur aan vec
boven vec_numpy
).
Ik moet er echter bij vermelden dat het niet altijd zo kort en droog is. Soms is het antwoord op "wat is de beste methode voor een operatie" is “het hangt af van uw gegevens”. Mijn advies is om verschillende benaderingen van uw gegevens te testen voordat u er een kiest.
Verder lezen
-
10 minuten tot panda’s, en Essentiële basisfunctionaliteit – Handige links die u kennis laten maken met Pandas en zijn bibliotheek van gevectoriseerde*/gecythoniseerde functies.
-
Prestaties verbeteren – Een inleiding uit de documentatie over het verbeteren van standaard Panda’s-operaties
-
Zijn for-loops in panda’s echt slechte? Wanneer moet ik me zorgen maken? – een gedetailleerde beschrijving door mij over het begrip van de lijst en hun geschiktheid voor verschillende bewerkingen (voornamelijk die met niet-numerieke gegevens)
-
Wanneer moet ik panda’s apply() wel (niet) in mijn code gebruiken? –
apply
is traag (maar niet zo traag als deiter*
-familie. Er zijn echter situaties waarin menapply
als een serieus alternatief kan (of moet) beschouwen, vooral bij sommigegroupby
-bewerkingen).
* String-methoden voor panda’s zijn "gevectoriseerd" in die zin dat ze op de serie zijn gespecificeerd, maar op elk element werken. De onderliggende mechanismen zijn nog steeds iteratief, omdat stringbewerkingen inherent moeilijk te vectoriseren zijn.
Waarom ik dit antwoord heb geschreven
Een veelvoorkomende trend die ik opmerk bij nieuwe gebruikers is om vragen te stellen in de vorm “Hoe kan ik mijn df herhalen om X te doen?”. Code tonen die iterrows()
aanroept terwijl er iets wordt gedaan in een for
-lus. Hier is waarom. Een nieuwe gebruiker van de bibliotheek die nog niet kennis heeft gemaakt met het concept van vectorisatie, zal zich waarschijnlijk de code voorstellen die hun probleem oplost als iteratie van hun gegevens om iets te doen. Omdat ze niet weten hoe ze een DataFrame moeten herhalen, is het eerste wat ze doen Google het en eindigen hier, bij deze vraag. Ze zien dan het geaccepteerde antwoord dat hen vertelt hoe ze dat moeten doen, en ze sluiten hun ogen en voeren deze code uit zonder zich ooit af te vragen of iteratie niet het juiste is om te doen.
Het doel van dit antwoord is om nieuwe gebruikers te helpen begrijpen dat iteratie niet noodzakelijk de oplossing is voor elk probleem, en dat er betere, snellere en meer idiomatische oplossingen kunnen bestaan, en dat het de moeite waard is om tijd te investeren in het verkennen ervan. Ik probeer niet een oorlog van iteratie versus vectorisatie te beginnen, maar ik wil dat nieuwe gebruikers geïnformeerd worden bij het ontwikkelen van oplossingen voor hun problemen met deze bibliotheek.
Antwoord 3, autoriteit 12%
Overweeg eerst of u echt moet herhalen over rijen in een DataFrame. Zie dit antwoord voor alternatieven.
Als u nog steeds over rijen moet herhalen, kunt u onderstaande methoden gebruiken. Let op enkele belangrijke kanttekeningen die in geen van de andere antwoorden worden genoemd.
-
for index, row in df.iterrows(): print(row["c1"], row["c2"])
-
for row in df.itertuples(index=True, name='Pandas'): print(row.c1, row.c2)
itertuples()
zou sneller moeten zijn dan iterrows()
Maar let op, volgens de documenten (panda’s 0.24.2 op dit moment):
-
iterrows:
dtype
komt mogelijk niet overeen van rij tot rijOmdat iterrows een reeks retourneert voor elke rij, behoudt het geen dtypes over de rijen (dtypes worden bewaard over kolommen voor DataFrames). Om dtypes te behouden tijdens het itereren over de rijen, is het beter om iteruples() te gebruiken die benoemde tuples van de waarden retourneert en dat over het algemeen veel sneller is dan iterrows()
-
iterrows: rijen niet wijzigen
Je moet nooit iets wijzigen waar je over herhaalt. Het is niet gegarandeerd dat dit in alle gevallen werkt. Afhankelijk van de gegevenstypen retourneert de iterator een kopie en geen weergave, en het schrijven ernaar heeft geen effect.
Gebruik DataFrame.apply() in plaats daarvan:
new_df = df.apply(lambda x: x * 2)
-
itertuples:
De kolomnamen worden hernoemd naar positionele namen als ze ongeldige Python-ID’s zijn, worden herhaald of beginnen met een onderstrepingsteken. Met een groot aantal kolommen (>255), worden reguliere tuples geretourneerd.
Bekijk pandas-documenten over iteratie voor meer details .
Antwoord 4, autoriteit 6%
U moet df.iterrows()
. Hoewel het rij voor rij itereren niet bijzonder efficiënt is, omdat er Series
-objecten moeten worden gemaakt.
Antwoord 5, autoriteit 4%
Hoewel iterrows()
een goede optie is, kan itertuples()
soms veel sneller zijn:
df = pd.DataFrame({'a': randn(1000), 'b': randn(1000),'N': randint(100, 1000, (1000)), 'x': 'x'})
%timeit [row.a * 2 for idx, row in df.iterrows()]
# => 10 loops, best of 3: 50.3 ms per loop
%timeit [row[1] * 2 for row in df.itertuples()]
# => 1000 loops, best of 3: 541 µs per loop
Antwoord 6, autoriteit 3%
Je kunt ook df.apply()
gebruiken om rijen te doorlopen en toegang te krijgen tot meerdere kolommen voor een functie.
def valuation_formula(x, y):
return x * y * 0.5
df['price'] = df.apply(lambda row: valuation_formula(row['x'], row['y']), axis=1)
Antwoord 7, autoriteit 3%
U kunt de df.iloc-functie als volgt gebruiken:
for i in range(0, len(df)):
print df.iloc[i]['c1'], df.iloc[i]['c2']
Antwoord 8
Efficiënt itereren
Als je echt een Pandas-dataframe moet herhalen, wil je waarschijnlijk het gebruik van iterrows() vermijden. Er zijn verschillende methoden en de gebruikelijke iterrows()
is verre van de beste. itertuples() kunnen 100 keer sneller zijn.
Kortom:
- Gebruik als algemene regel
df.itertuples(name=None)
. In het bijzonder wanneer u een vast aantal kolommen heeft en minder dan 255 kolommen. Zie punt (3) - Gebruik anders
df.itertuples()
behalve als uw kolommen speciale tekens bevatten, zoals spaties of ‘-‘. Zie punt (2) - Het is mogelijk om
itertuples()
te gebruiken, zelfs als je dataframe vreemde kolommen heeft door het laatste voorbeeld te gebruiken. Zie punt (4) - Gebruik alleen
iterrows()
als je de vorige oplossingen niet kunt gebruiken. Zie punt (1)
Verschillende methoden om rijen in een Pandas-dataframe te herhalen:
Genereer een willekeurig dataframe met een miljoen rijen en 4 kolommen:
df = pd.DataFrame(np.random.randint(0, 100, size=(1000000, 4)), columns=list('ABCD'))
print(df)
1) De gebruikelijke iterrows()
is handig, maar verdomd traag:
start_time = time.clock()
result = 0
for _, row in df.iterrows():
result += max(row['B'], row['C'])
total_elapsed_time = round(time.clock() - start_time, 2)
print("1. Iterrows done in {} seconds, result = {}".format(total_elapsed_time, result))
2) De standaard itertuples()
is al veel sneller, maar werkt niet met kolomnamen zoals My Col-Name is very Strange
(je zou vermijd deze methode als uw kolommen worden herhaald of als een kolomnaam niet eenvoudig kan worden geconverteerd naar een Python-variabelenaam).:
start_time = time.clock()
result = 0
for row in df.itertuples(index=False):
result += max(row.B, row.C)
total_elapsed_time = round(time.clock() - start_time, 2)
print("2. Named Itertuples done in {} seconds, result = {}".format(total_elapsed_time, result))
3) De standaard itertuples()
met name=None is nog sneller, maar niet echt handig omdat je een variabele per kolom moet definiëren.
start_time = time.clock()
result = 0
for(_, col1, col2, col3, col4) in df.itertuples(name=None):
result += max(col2, col3)
total_elapsed_time = round(time.clock() - start_time, 2)
print("3. Itertuples done in {} seconds, result = {}".format(total_elapsed_time, result))
4) Ten slotte is de naam itertuples()
langzamer dan het vorige punt, maar je hoeft geen variabele per kolom te definiëren en het werkt met kolomnamen zoals My Col-Name is very Strange
.
start_time = time.clock()
result = 0
for row in df.itertuples(index=False):
result += max(row[df.columns.get_loc('B')], row[df.columns.get_loc('C')])
total_elapsed_time = round(time.clock() - start_time, 2)
print("4. Polyvalent Itertuples working even with special characters in the column name done in {} seconds, result = {}".format(total_elapsed_time, result))
Uitvoer:
A B C D
0 41 63 42 23
1 54 9 24 65
2 15 34 10 9
3 39 94 82 97
4 4 88 79 54
... .. .. .. ..
999995 48 27 4 25
999996 16 51 34 28
999997 1 39 61 14
999998 66 51 27 70
999999 51 53 47 99
[1000000 rows x 4 columns]
1. Iterrows done in 104.96 seconds, result = 66151519
2. Named Itertuples done in 1.26 seconds, result = 66151519
3. Itertuples done in 0.94 seconds, result = 66151519
4. Polyvalent Itertuples working even with special characters in the column name done in 2.94 seconds, result = 66151519
Dit artikel is een zeer interessante vergelijking tussen iterrows en iteruples
Antwoord 9
Ik was op zoek naar Hoe te herhalen op rijen en kolommen en eindigde hier dus:
for i, row in df.iterrows():
for j, column in row.iteritems():
print(column)
Antwoord 10
Je kunt je eigen iterator schrijven die namedtuple
implementeert
from collections import namedtuple
def myiter(d, cols=None):
if cols is None:
v = d.values.tolist()
cols = d.columns.values.tolist()
else:
j = [d.columns.get_loc(c) for c in cols]
v = d.values[:, j].tolist()
n = namedtuple('MyTuple', cols)
for line in iter(v):
yield n(*line)
Dit is direct vergelijkbaar met pd.DataFrame.itertuples
. Ik streef ernaar dezelfde taak efficiënter uit te voeren.
Voor het gegeven dataframe met mijn functie:
list(myiter(df))
[MyTuple(c1=10, c2=100), MyTuple(c1=11, c2=110), MyTuple(c1=12, c2=120)]
Of met pd.DataFrame.itertuples
:
list(df.itertuples(index=False))
[Pandas(c1=10, c2=100), Pandas(c1=11, c2=110), Pandas(c1=12, c2=120)]
Een uitgebreide test
We testen het beschikbaar maken van alle kolommen en het subsetten van de kolommen.
def iterfullA(d):
return list(myiter(d))
def iterfullB(d):
return list(d.itertuples(index=False))
def itersubA(d):
return list(myiter(d, ['col3', 'col4', 'col5', 'col6', 'col7']))
def itersubB(d):
return list(d[['col3', 'col4', 'col5', 'col6', 'col7']].itertuples(index=False))
res = pd.DataFrame(
index=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
columns='iterfullA iterfullB itersubA itersubB'.split(),
dtype=float
)
for i in res.index:
d = pd.DataFrame(np.random.randint(10, size=(i, 10))).add_prefix('col')
for j in res.columns:
stmt = '{}(d)'.format(j)
setp = 'from __main__ import d, {}'.format(j)
res.at[i, j] = timeit(stmt, setp, number=100)
res.groupby(res.columns.str[4:-1], axis=1).plot(loglog=True);
Antwoord 11
for ind in df.index:
print df['c1'][ind], df['c2'][ind]
Antwoord 12
Als u alle rijen in een dataframe
wilt herhalen, kunt u het volgende gebruiken:
for x in range(len(date_example.index)):
print date_example['Date'].iloc[x]
Antwoord 13
Soms is een handig patroon:
# Borrowing @KutalmisB df example
df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]}, index=['a', 'b'])
# The to_dict call results in a list of dicts
# where each row_dict is a dictionary with k:v pairs of columns:value for that row
for row_dict in df.to_dict(orient='records'):
print(row_dict)
Wat resulteert in:
{'col1':1.0, 'col2':0.1}
{'col1':2.0, 'col2':0.2}
Antwoord 14
Kortom
- Gebruik indien mogelijk vectorisatie
- Als een bewerking niet kan worden gevectoriseerd – gebruik lijstbegrippen
- Als je een enkel object nodig hebt dat de hele rij vertegenwoordigt – gebruik iteruples
- Als het bovenstaande te langzaam is, probeer dan swifter.apply
- Als het nog steeds te traag is, probeer dan een Cython-routine
Benchmark
Antwoord 15
Om alle rijen in een dataframe
te herhalen en gebruik waarden van elke rij handig, namedtuples
kan worden geconverteerd naar ndarray
s. Bijvoorbeeld:
df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]}, index=['a', 'b'])
Itereren over de rijen:
for row in df.itertuples(index=False, name='Pandas'):
print np.asarray(row)
resulteert in:
[ 1. 0.1]
[ 2. 0.2]
Houd er rekening mee dat als index=True
, de index wordt toegevoegd als het eerste element van de tuple, wat voor sommige toepassingen ongewenst kan zijn.
Antwoord 16
Voor zowel het bekijken als het wijzigen van waarden, zou ik iterrows()
gebruiken. In een for-lus en door tuple uitpakken te gebruiken (zie het voorbeeld: i, row
), gebruik ik de row
om alleen de waarde te bekijken en gebruik ik i
met de loc
methode wanneer ik waarden wil wijzigen. Zoals vermeld in eerdere antwoorden, moet u hier niet iets wijzigen waarover u itereert.
for i, row in df.iterrows():
df_column_A = df.loc[i, 'A']
if df_column_A == 'Old_Value':
df_column_A = 'New_value'
Hier is de row
in de lus een kopie van die rij, en geen weergave ervan. Daarom moet u NIET iets schrijven als row['A'] = 'New_Value'
, het zal het DataFrame niet wijzigen. U kunt echter i
en loc
gebruiken en het DataFrame specificeren om het werk te doen.
Antwoord 17
Er is een manier om throw-rijen te herhalen terwijl je een DataFrame terugkrijgt, en geen Series. Ik zie niemand zeggen dat je index kunt doorgeven als een lijst voor de rij die moet worden geretourneerd als een DataFrame:
for i in range(len(df)):
row = df.iloc[[i]]
Let op het gebruik van dubbele haakjes. Dit retourneert een DataFrame met een enkele rij.
Antwoord 18
cs95-shows dat Pandas-vectorisatie veel beter presteert dan andere Pandas-methoden voor het berekenen van dingen met dataframes.
Ik wilde hieraan toevoegen dat als je het dataframe eerst converteert naar een NumPy-array en vervolgens vectorisatie gebruikt, het zelfs sneller is dan Panda’s dataframe-vectorisatie (en dat omvat de tijd om het terug te zetten in een dataframereeks).
Als je de volgende functies toevoegt aan de benchmarkcode van cs95, wordt dit vrij duidelijk:
def np_vectorization(df):
np_arr = df.to_numpy()
return pd.Series(np_arr[:,0] + np_arr[:,1], index=df.index)
def just_np_vectorization(df):
np_arr = df.to_numpy()
return np_arr[:,0] + np_arr[:,1]
Antwoord 19
Er zijn zoveel manieren om de rijen in het dataframe van Panda te doorlopen. Een heel eenvoudige en intuïtieve manier is:
df = pd.DataFrame({'A':[1, 2, 3], 'B':[4, 5, 6], 'C':[7, 8, 9]})
print(df)
for i in range(df.shape[0]):
# For printing the second column
print(df.iloc[i, 1])
# For printing more than one columns
print(df.iloc[i, [0, 2]])
Antwoord 20
De gemakkelijkste manier, gebruik de functie apply
def print_row(row):
print row['c1'], row['c2']
df.apply(lambda row: print_row(row), axis=1)
Antwoord 21
Je kunt ook NumPy-indexering doen voor nog grotere snelheden. Het is niet echt iteratief, maar werkt veel beter dan iteratie voor bepaalde toepassingen.
subset = row['c1'][0:5]
all = row['c1'][:]
Misschien wilt u het ook naar een array casten. Deze indexen/selecties zouden al als NumPy-arrays moeten werken, maar ik kwam problemen tegen en moest casten
np.asarray(all)
imgs[:] = cv2.resize(imgs[:], (224,224) ) # Resize every image in an hdf5 file
Antwoord 22
In dit voorbeeld wordt iloc gebruikt om elk cijfer in het gegevensframe te isoleren.
import pandas as pd
a = [1, 2, 3, 4]
b = [5, 6, 7, 8]
mjr = pd.DataFrame({'a':a, 'b':b})
size = mjr.shape
for i in range(size[0]):
for j in range(size[1]):
print(mjr.iloc[i, j])
Antwoord 23
Sommige bibliotheken (bijvoorbeeld een Java-interoperabiliteitsbibliotheek die ik gebruik) vereisen dat waarden in een rij tegelijk worden doorgegeven, bijvoorbeeld bij het streamen van gegevens. Om het streamingkarakter na te bootsen, ‘stream’ ik mijn dataframe-waarden één voor één, ik schreef het onderstaande, wat van tijd tot tijd van pas komt.
class DataFrameReader:
def __init__(self, df):
self._df = df
self._row = None
self._columns = df.columns.tolist()
self.reset()
self.row_index = 0
def __getattr__(self, key):
return self.__getitem__(key)
def read(self) -> bool:
self._row = next(self._iterator, None)
self.row_index += 1
return self._row is not None
def columns(self):
return self._columns
def reset(self) -> None:
self._iterator = self._df.itertuples()
def get_index(self):
return self._row[0]
def index(self):
return self._row[0]
def to_dict(self, columns: List[str] = None):
return self.row(columns=columns)
def tolist(self, cols) -> List[object]:
return [self.__getitem__(c) for c in cols]
def row(self, columns: List[str] = None) -> Dict[str, object]:
cols = set(self._columns if columns is None else columns)
return {c : self.__getitem__(c) for c in self._columns if c in cols}
def __getitem__(self, key) -> object:
# the df index of the row is at index 0
try:
if type(key) is list:
ix = [self._columns.index(key) + 1 for k in key]
else:
ix = self._columns.index(key) + 1
return self._row[ix]
except BaseException as e:
return None
def __next__(self) -> 'DataFrameReader':
if self.read():
return self
else:
raise StopIteration
def __iter__(self) -> 'DataFrameReader':
return self
Wat kan worden gebruikt:
for row in DataFrameReader(df):
print(row.my_column_name)
print(row.to_dict())
print(row['my_column_name'])
print(row.tolist())
En behoudt de waarden/naamtoewijzing voor de rijen die worden herhaald. Het is duidelijk een stuk langzamer dan het gebruik van Apply en Cython zoals hierboven aangegeven, maar is in sommige omstandigheden noodzakelijk.
Antwoord 24
Zoals veel antwoorden hier correct en duidelijk aangeven, moet u over het algemeen niet proberen Panda’s in een lus te plaatsen, maar moet u gevectoriseerde code schrijven. Maar de vraag blijft of je ooit loops moet schrijven in Panda’s, en zo ja, wat de beste manier is om in die situaties loops te maken.
Ik geloof dat er ten minste één algemene situatie is waarin lussen geschikt zijn: wanneer je een functie moet berekenen die op een ietwat complexe manier afhankelijk is van waarden in andere rijen. In dit geval is de luscode vaak eenvoudiger, leesbaarder en minder foutgevoelig dan gevectoriseerde code. De luscode kan zelfs sneller zijn.
Ik zal proberen dit aan te tonen met een voorbeeld. Stel dat u een cumulatieve som van een kolom wilt nemen, maar deze wilt resetten wanneer een andere kolom gelijk is aan nul:
import pandas as pd
import numpy as np
df = pd.DataFrame( { 'x':[1,2,3,4,5,6], 'y':[1,1,1,0,1,1] } )
# x y desired_result
#0 1 1 1
#1 2 1 3
#2 3 1 6
#3 4 0 4
#4 5 1 9
#5 6 1 15
Dit is een goed voorbeeld waarbij je zeker één regel Panda’s zou kunnen schrijven om dit te bereiken, hoewel het niet bijzonder leesbaar is, vooral als je nog niet redelijk ervaren bent met Panda’s:
df.groupby( (df.y==0).cumsum() )['x'].cumsum()
Dat zal voor de meeste situaties snel genoeg zijn, hoewel je ook snellere code zou kunnen schrijven door de groupby
te vermijden, maar het zal waarschijnlijk nog minder leesbaar zijn.
Als alternatief, wat als we dit als een lus schrijven? Je zou iets als het volgende kunnen doen met NumPy:
import numba as nb
@nb.jit(nopython=True) # Optional
def custom_sum(x,y):
x_sum = x.copy()
for i in range(1,len(df)):
if y[i] > 0: x_sum[i] = x_sum[i-1] + x[i]
return x_sum
df['desired_result'] = custom_sum( df.x.to_numpy(), df.y.to_numpy() )
Toegegeven, er is een beetje overhead nodig om DataFrame-kolommen naar NumPy-arrays te converteren, maar het kernstuk van de code is slechts één regel code die je zou kunnen lezen, zelfs als je niets wist over Panda’s of NumPy:
if y[i] > 0: x_sum[i] = x_sum[i-1] + x[i]
En deze code is eigenlijk sneller dan de gevectoriseerde code. In sommige snelle tests met 100.000 rijen is het bovenstaande ongeveer 10x sneller dan de groupby-aanpak. Merk op dat een sleutel tot de snelheid daar numba is, wat optioneel is. Zonder de "@nb.jit" regel, is de luscode eigenlijk ongeveer 10x langzamer dan de groupby-benadering.
Dit voorbeeld is duidelijk zo eenvoudig dat u waarschijnlijk de voorkeur geeft aan de ene regel panda’s boven het schrijven van een lus met de bijbehorende overhead. Er zijn echter complexere versies van dit probleem waarvoor de leesbaarheid of snelheid van de NumPy/numba-lusaanpak waarschijnlijk logisch is.
Antwoord 25
Samen met de geweldige antwoorden in dit bericht ga ik een Verdeel en heers-aanpak voorstellen, ik schrijf dit antwoord niet om de andere geweldige antwoorden af te schaffen, maar om ze te vervullen met een andere aanpak die werkte efficiënt voor mij. Het heeft twee stappen van splitting
en merging
het panda-dataframe:
PRO’S van verdeel en heers:
- U hoeft geen vectorisatie of andere methoden te gebruiken om het type dataframe naar een ander type te casten
- U hoeft uw code niet te Cythoniseren, wat normaal gesproken extra tijd van u kost
- Zowel
iterrows()
alsitertuples()
hadden in mijn geval dezelfde prestaties over het hele dataframe - Afhankelijk van uw keuze voor het snijden van
index
, kunt u de iteratie exponentieel versnellen. Hoe hoger deindex
, hoe sneller uw iteratieproces.
CONS van verdeel en heers:
- Je zou niet afhankelijk moeten zijn van het iteratieproces van hetzelfde dataframe en verschillende slice. Dit betekent dat als je wilt lezen of schrijven vanuit een ander segment, het misschien moeilijk is om dat te doen.
=================== Verdeel en heers aanpak =================
Stap 1: Splitsen/snijden
In deze stap gaan we de iteratie verdelen over het gehele dataframe. Denk dat je een csv-bestand in pandas df gaat lezen en herhaal het dan. In het geval dat ik 5.000.000 records heb, ga ik het opsplitsen in 100.000 records.
OPMERKING: ik moet herhalen, zoals andere runtime-analyses uitgelegd in de andere oplossingen op deze pagina, "aantal records" heeft een exponentieel aandeel van "runtime" bij zoeken op de df. Gebaseerd op de benchmark op mijn gegevens zijn hier de resultaten:
Number of records | Iteration per second
========================================
100,000 | 500 it/s
500,000 | 200 it/s
1,000,000 | 50 it/s
5,000,000 | 20 it/s
Stap 2: Samenvoegen
Dit wordt een gemakkelijke stap, voeg gewoon alle geschreven csv-bestanden samen in één dataframe en schrijf het in een groter csv-bestand.
Hier is de voorbeeldcode:
# Step 1 (Splitting/Slicing)
import pandas as pd
df_all = pd.read_csv('C:/KtV.csv')
df_index = 100000
df_len = len(df)
for i in range(df_len // df_index + 1):
lower_bound = i * df_index
higher_bound = min(lower_bound + df_index, df_len)
# splitting/slicing df (make sure to copy() otherwise it will be a view
df = df_all[lower_bound:higher_bound].copy()
'''
write your iteration over the sliced df here
using iterrows() or intertuples() or ...
'''
# writing into csv files
df.to_csv('C:/KtV_prep_'+str(i)+'.csv')
# Step 2 (Merging)
filename='C:/KtV_prep_'
df = (pd.read_csv(f) for f in [filename+str(i)+'.csv' for i in range(ktv_len // ktv_index + 1)])
df_prep_all = pd.concat(df)
df_prep_all.to_csv('C:/KtV_prep_all.csv')
Referentie:
Efficiënte manier van herhalen datafreeam
Voeg csv-bestanden samen in één Pandas-dataframe
Antwoord 26
Gebruik df.iloc[]
. Gebruik bijvoorbeeld dataframe ‘rows_df’:
Of
Als u waarden uit een specifieke rij wilt halen, kunt u het dataframe converteren naar ndarray.
Selecteer vervolgens de rij- en kolomwaarden als volgt:
Antwoord 27
Gewoon mijn twee cent optellen,
Zoals het geaccepteerde antwoord aangeeft, is de snelste manier om een functie op rijen toe te passen het gebruik van een gevectoriseerde functie, de zogenaamde numpy ufuncs
(universele functies)
Maar wat moet je doen als de functie die je wilt toepassen nog niet is geïmplementeerd in numpy
?
Nou, met behulp van de vectorize
-decorator van numba
, kun je eenvoudig ufuncs rechtstreeks in Python maken als volgt:
from numba import vectorize, float64
@vectorize([float64(float64)])
def f(x):
#x is your line, do something with it, and return a float
De documentatie voor deze functie is hier: https://numba .pydata.org/numba-doc/latest/user/vectorize.html
Antwoord 28
df.iterrows() retourneert tuple(a,b) waarbij a index is en b rij.
Antwoord 29
Waarschijnlijk de meest elegante oplossing (maar zeker niet de meest efficiënte):
for row in df.values:
c2 = row[1]
print(row)
# ...
for c1, c2 in df.values:
# ...
Let op:
- in de documentatie wordt expliciet aanbevolen om
.to_numpy()
in plaats daarvan - de geproduceerde NumPy-array heeft een dtype dat in alle kolommen past, in het ergste geval
object
- er zijn goede redenen om überhaupt geen lus te gebruiken
Toch vind ik dat deze optie hier moet worden opgenomen, als een directe oplossing voor een (men zou moeten denken) triviaal probleem.