Hoe te herhalen over rijen in een DataFrame in Panda’s

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):

  1. Vectorisering
  2. Cython-routines
  3. Lijst met begrippen (vanille for lus)
  4. DataFrame.apply(): i) Reducties die kunnen worden uitgevoerd in Cython, ii) Iteratie in Python-ruimte
  5. DataFrame.itertuples() en iteritems()
  6. 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.

  1. 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.
  2. Als je te maken hebt met gemengde gegevenstypen, moet je herhalen over zip(df['A'], df['B'], ...) in plaats van df[['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, zal to_numpy() de hele array naar string casten, wat misschien niet is wat je wilt. Gelukkig is het zipje 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

* 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.

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 rij

    Omdat 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.

docs: DataFrame.apply()

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);

voer hier de afbeeldingsbeschrijving in

voer hier de afbeeldingsbeschrijving in

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

Benchmark van iteratie over rijen in een Pandas DataFrame

Antwoord 15

Om alle rijen in een dataframe te herhalen en gebruik waarden van elke rij handig, namedtuples kan worden geconverteerd naar ndarrays. 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]

Voer hier de afbeeldingsbeschrijving in

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() als itertuples() 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 de index, 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’:

Voer hier de afbeeldingsbeschrijving in

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:

Voer hier de afbeeldingsbeschrijving in

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.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Other episodes