我有一个熊猫数据帧,df:

   c1   c2
0  10  100
1  11  110
2  12  120

如何迭代此数据帧的行?对于每一行,我希望能够通过列的名称访问其元素(单元格中的值)。例如:

for row in df.rows:
    print(row['c1'], row['c2'])

我发现了一个类似的问题,建议使用以下任一项:

for date, row in df.T.iteritems():
for row in df.iterrows():

但我不知道row对象是什么,以及如何使用它。


您应该使用df.iterrows()。虽然逐行迭代不是特别有效,因为必须创建Series对象。


DataFrame.iterrows是一个生成索引和行(作为一个系列)的生成器:

import pandas as pd

df = pd.DataFrame({'c1': [10, 11, 12], 'c2': [100, 110, 120]})
df = df.reset_index()  # make sure indexes pair with number of rows

for index, row in df.iterrows():
    print(row['c1'], row['c2'])
10 100
11 110
12 120

您还可以使用df.apply()来迭代行并访问函数的多个列。

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)

虽然iterrows()是一个很好的选项,但有时itertples()会快得多:

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

可以按如下方式使用df.iloc函数:

for i in range(0, len(df)):
    print(df.iloc[i]['c1'], df.iloc[i]['c2'])

首先考虑是否真的需要迭代DataFrame中的行。请参阅此答案以了解备选方案。

如果仍然需要迭代行,可以使用以下方法。请注意其他答案中未提及的一些重要注意事项。

DataFrame.iterrows()对于索引,df.iterrows()中的行:打印(行[“c1”],行[“c2”])DataFrame.itertuples()对于df.itertuples中的行(索引=True,名称=“标准”):打印(第c1行,第c2行)

itertples()应该比iterrows()快

但请注意,根据文件(熊猫目前为0.24.2):

iterrows:dtype可能在行与行之间不匹配

因为iterrows为每一行返回一个Series,所以它不会跨行保留数据类型(数据帧的数据类型跨列保留)。为了在遍历行时保留数据类型,最好使用itertples(),它返回值的namedtuples,通常比iterrows()快得多

iterrows:不修改行

您不应该修改正在迭代的内容。这并不能保证在所有情况下都有效。根据数据类型的不同,迭代器返回的是副本而不是视图,写入它不会产生任何影响。

请改用DataFrame.apply():

    new_df = df.apply(lambda x: x * 2, axis = 1)

迭代:

如果列名是无效的Python标识符、重复或以下划线开头,则将重命名为位置名。对于大量列(>255),将返回常规元组。

有关详细信息,请参阅panda迭代文档。


要循环数据帧中的所有行,可以使用:

for x in range(len(date_example.index)):
    print date_example['Date'].iloc[x]

 for ind in df.index:
     print df['c1'][ind], df['c2'][ind]

您可以编写自己的迭代器来实现namedtuple

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)

这与pd.DataFrame.itertuples直接相当。我的目标是以更高的效率执行相同的任务。


对于具有我的函数的给定数据帧:

list(myiter(df))

[MyTuple(c1=10, c2=100), MyTuple(c1=11, c2=110), MyTuple(c1=12, c2=120)]

或使用pd.DataFrame.itertuples:

list(df.itertuples(index=False))

[Pandas(c1=10, c2=100), Pandas(c1=11, c2=110), Pandas(c1=12, c2=120)]

全面测试我们测试使所有列可用并对列进行子设置。

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


您还可以进行NumPy索引,以实现更高的速度。它不是真正的迭代,但对某些应用程序来说,它比迭代好得多。

subset = row['c1'][0:5]
all = row['c1'][:]

您可能还希望将其强制转换为数组。这些索引/选择本来应该像NumPy数组一样,但我遇到了一些问题,需要转换

np.asarray(all)
imgs[:] = cv2.resize(imgs[:], (224,224) ) # Resize every image in an hdf5 file

我正在寻找如何迭代行和列,并在这里结束:

for i, row in df.iterrows():
    for j, column in row.iteritems():
        print(column)

为了循环数据帧中的所有行并方便地使用每行的值,可以将命名元组转换为ndarray。例如:

df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]}, index=['a', 'b'])

在行上循环:

for row in df.itertuples(index=False, name='Pandas'):
    print np.asarray(row)

结果是:

[ 1.   0.1]
[ 2.   0.2]

请注意,如果index=True,则将索引添加为元组的第一个元素,这对于某些应用程序来说可能是不可取的。


有时,有用的模式是:

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

结果是:

{'col1':1.0, 'col2':0.1}
{'col1':2.0, 'col2':0.2}

在Pandas数据帧中有很多方法可以迭代行。一种非常简单直观的方法是:

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

对于查看和修改值,我将使用iterrows()。在for循环中,通过使用元组解包(参见示例:i,row),我使用行仅查看值,并在需要修改值时使用i和loc方法。正如前面的回答所述,这里您不应该修改正在迭代的内容。

for i, row in df.iterrows():
    df_column_A = df.loc[i, 'A']
    if df_column_A == 'Old_Value':
        df_column_A = 'New_value'  

在这里,循环中的行是该行的副本,而不是它的视图。因此,您不应该编写类似于行['a']='New_Value'的内容,它不会修改DataFrame。但是,您可以使用i和loc并指定DataFrame来完成这项工作。


本例使用iloc隔离数据帧中的每个数字。

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

如何在Pandas中迭代DataFrame中的行

回答:不要*!

Pandas中的迭代是一种反模式,只有在用尽所有其他选项时才应该这样做。您不应该在超过几千行的时间内使用任何名称中带有“iter”的函数,否则您将不得不习惯于大量的等待。

是否要打印DataFrame?使用DataFrame.to_string()。

你想计算什么吗?在这种情况下,按以下顺序搜索方法(从此处修改列表):

矢量化Cython例程列出理解(循环的香草)DataFrame.apply():i)可以在Cython中执行的缩减,ii)Python空间中的迭代DataFrame.itertuples()和itertimes()DataFrame.iterrows()

iterrows和itertules(都在回答这个问题时获得了很多投票)应该在非常罕见的情况下使用,例如为顺序处理生成行对象/nametuples,这实际上是这些函数唯一有用的功能。

向当局提出上诉

迭代的文档页面有一个巨大的红色警告框,上面写着:

遍历熊猫对象通常很慢。在许多情况下,不需要手动迭代行[…]。

*实际上,这比“不”要复杂一点。iterrows()是这个问题的正确答案,但“矢量化操作”是更好的答案。我承认,在某些情况下,迭代是无法避免的(例如,某些操作的结果取决于为前一行计算的值)。然而,需要对图书馆有一定的了解才能知道什么时候。如果您不确定是否需要迭代解决方案,则可能不需要。PS:要了解更多关于我写这个答案的理由,请跳到最底部。


比循环更快:矢量化,Cython

大量的基本操作和计算都由panda“矢量化”(通过NumPy或通过Cythonized函数)。这包括算术、比较、(大多数)归约、重塑(如旋转)、联接和分组操作。查看有关基本功能的文档,找到适合您问题的矢量化方法。

如果没有,请使用自定义Cython扩展编写自己的代码。


下一件最好的事:列出理解*

如果1)没有可用的矢量化解决方案,2)性能很重要,但还不够重要,不足以解决代码代码化的麻烦,3)您正在尝试对代码执行元素化转换,那么列表理解应该是您的下一个选择。有大量证据表明,对于许多常见的熊猫任务,列表理解足够快(有时甚至更快)。

公式简单,

# 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'])]

如果你能将你的业务逻辑封装成一个函数,你就可以使用一个调用它的列表理解。你可以通过原始Python代码的简单性和速度,让任意复杂的事情得以实现。

注意事项

列表理解假设您的数据很容易使用-这意味着您的数据类型是一致的,并且您没有NaN,但这不能总是得到保证。

第一种方法更为明显,但在处理NaN时,如果存在内置panda方法(因为它们具有更好的角案例处理逻辑),请选择内置panda,或者确保您的业务逻辑包含适当的NaN处理逻辑。当处理混合数据类型时,应该迭代zip(df['A'],df['B'],…)而不是df[['A','B'].to_num(),因为后者隐式地将数据上转换为最常见的类型。例如,如果A是数字,B是字符串,to_numpy()将整个数组强制转换为字符串,这可能不是您想要的。幸运的是,将您的专栏压缩在一起是解决此问题的最直接的方法。

*您的里程数可能会因上述“注意事项”部分中概述的原因而有所不同。


一个明显的例子

让我们用一个添加两个panda列a+B的简单示例来演示差异。这是一个可矢量化的操作,因此很容易对比上面讨论的方法的性能。

基准测试代码,供您参考。底部的一行衡量的是用numpanda编写的函数,这是一种Pandas风格,它与NumPy大量混合,以挤出最大的性能。除非你知道自己在做什么,否则应该避免编写numpanda代码。尽可能使用API(即,更喜欢vec而不是vec_numpy)。

然而,我应该提一提,这并不总是那么简单。有时候,“手术的最佳方法是什么”的答案是“这取决于你的数据”。我的建议是在确定数据之前,先测试不同的方法。


我的个人意见*

对iter系列的各种替代品进行的大多数分析都是从性能角度进行的。然而,在大多数情况下,您通常会处理一个大小合理的数据集(只有几千或100K行),性能将仅次于解决方案的简单性/可读性。

这是我在选择解决问题的方法时的个人偏好。

对于新手:

矢量化(如果可能);apply();列出理解;itertples()/iteritems();迭代();赛龙

对于更有经验的人:

矢量化(如果可能);apply();列出理解;Cython;itertples()/iteritems();迭代()

对于任何可以矢量化的问题,矢量化都是最常用的方法。始终寻求矢量化!如果有疑问,请查阅文档,或查看Stack Overflow以了解有关特定任务的现有问题。

我确实倾向于在我的很多帖子中继续谈论申请有多糟糕,但我承认初学者更容易理解申请的内容。此外,在我的这篇文章中解释了许多应用的用例。

Cython在榜单上排名靠后,因为它需要更多的时间和精力才能正确完成。通常情况下,您永远不需要使用panda编写代码,因为它需要这样的性能水平,即使是列表理解也无法满足。

*与任何个人意见一样,请带着大量的盐!


进一步阅读

熊猫10分钟,以及基本功能-有用的链接,可向您介绍熊猫及其矢量化*/细胞化函数库。提高性能-从提高标准熊猫操作的文档中获得的入门知识熊猫的循环真的很糟糕吗?我什么时候该关心?-我对列表的理解及其对各种操作的适用性(主要是涉及非数字数据的操作)的详细记录我什么时候应该(不)在代码中使用pandas apply()?-apply很慢(但没有iter*家族那么慢。但是,在某些情况下,可以(或应该)将apply视为一种严肃的选择,特别是在某些GroupBy操作中)。

*Pandas字符串方法是“矢量化”的,因为它们在序列上指定,但在每个元素上操作。底层机制仍然是迭代的,因为字符串操作本质上很难矢量化。


为什么我写这个答案

我从新用户那里注意到的一个常见趋势是,以“我如何迭代我的df来做X?”的形式提出问题。显示在for循环内执行某些操作时调用iterrows()的代码。原因如下。一个没有被引入矢量化概念的库新用户可能会将解决他们问题的代码设想为迭代他们的数据来做一些事情。不知道如何在DataFrame上迭代,他们做的第一件事就是谷歌搜索它,并在这里结束这个问题。然后,他们看到被接受的答案,告诉他们如何去做,然后闭上眼睛运行这段代码,而不必首先质疑迭代是否正确。

这个答案的目的是帮助新用户理解迭代不一定是每个问题的解决方案,可以存在更好、更快、更地道的解决方案。我并不是想挑起一场迭代与矢量化的战争,但我希望新用户在使用这个库开发解决问题的解决方案时得到通知。


有一种方法可以在返回DataFrame而不是Series时迭代抛出行。我没有看到任何人提到可以将索引作为列表传递给要作为DataFrame返回的行:

for i in range(len(df)):
    row = df.iloc[[i]]

注意双括号的用法。这将返回具有单行的DataFrame。


有些库(例如我使用的Java互操作库)要求一次在一行中传递值,例如,如果是流数据。为了复制流式传输的特性,我将数据帧值逐一“流式传输”,我写了以下内容,这些内容不时会派上用场。

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

可用于:

for row in DataFrameReader(df):
  print(row.my_column_name)
  print(row.to_dict())
  print(row['my_column_name'])
  print(row.tolist())

并保留正在迭代的行的值/名称映射。显然,它比上面提到的使用apply和Cython慢得多,但在某些情况下是必要的。


如何高效迭代

如果您真的需要迭代Pandas数据帧,您可能希望避免使用iterrows()。有不同的方法,通常的iterrows()远远不是最好的。itertples()可以快100倍。

简而言之:

作为一般规则,使用df.itertuples(name=None)。特别是当列数固定且少于255列时。见第(3)点否则,请使用df.itertuples(),除非您的列包含空格或“-”等特殊字符。见第(2)点使用上一个示例,即使数据帧中有奇怪的列,也可以使用itertples()。见第(4)点如果无法使用前面的解决方案,请仅使用iterrows()。见第(1)点

对Pandas数据帧中的行进行迭代的不同方法:

生成具有百万行和4列的随机数据帧:

    df = pd.DataFrame(np.random.randint(0, 100, size=(1000000, 4)), columns=list('ABCD'))
    print(df)

1) 通常的iterrows()很方便,但速度很慢:

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) 默认的itertples()已经快得多,但它不适用于列名称,例如My Col Name is very Strange(我的列名称非常奇怪)(如果列重复或列名称不能简单地转换为Python变量名称,则应避免使用此方法)

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) 使用name=None的默认itertples()甚至更快,但并不方便,因为您必须为每列定义一个变量。

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) 最后,命名的itertples()比上一点慢,但您不必为每列定义变量,它可以处理列名称,例如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))

输出:

         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

本文是iterrows和itertules之间的一个非常有趣的比较


更新:cs95更新了他的答案,包括简单的numpy矢量化。你可以简单地参考他的答案。


cs95表明,Pandas矢量化在使用数据帧计算数据方面远远优于其他Pandas方法。

我想补充一点,如果您首先将数据帧转换为NumPy数组,然后使用矢量化,它甚至比Pandas数据帧矢量化更快(这包括将其转换回数据帧序列的时间)。

如果您将以下函数添加到cs95的基准代码中,这将变得非常明显:

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]


简言之

如果可能,使用矢量化如果操作无法矢量化,请使用列表综合如果需要一个表示整个行的对象,请使用itert元组如果以上步骤太慢,请尝试快速应用如果速度仍然太慢,试试赛马拉松的套路

基准


除了这篇文章中的好答案,我将提出“分而治之”的方法,我写这个答案并不是为了废除其他好答案,而是为了用另一种对我有效的方法来实现它们。它有两个步骤,即拆分和合并熊猫数据帧:

分裂与征服的证明:

您不需要使用矢量化或任何其他方法将数据帧的类型转换为另一种类型您不需要将代码循环化,这通常需要额外的时间在我的例子中,iterrows()和itertples()在整个数据帧上都具有相同的性能根据您对切片索引的选择,您将能够以指数方式加快迭代。索引越高,迭代过程越快。

分裂与征服的缺点:

您不应该依赖于同一数据帧和不同切片的迭代过程。这意味着,如果你想从其他切片读取或写入,那么可能很难做到这一点。

===============分而治之=================

步骤1:分割/切片

在这一步中,我们将在整个数据帧上划分迭代。假设你要将一个CSV文件读入panda df,然后对其进行迭代。在这种情况下,我有5000000条记录,我要将其拆分为100000条记录。

注意:我需要重申,正如本页其他解决方案中解释的其他运行时分析一样,在df上搜索时,“记录数”与“运行时”成指数比例。基于我的数据基准,以下是结果:

Number of records | Iteration rate [per second]
========================================
100,000           | 500
500,000           | 200
1,000,000         | 50
5,000,000         | 20

第2步:合并

这将是一个简单的步骤,只需将所有写入的CSV文件合并到一个数据帧中,然后将其写入一个更大的CSV文件。

以下是示例代码:

# 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')

参考:

数据流迭代的有效方法

将CSV文件连接到一个Pandas数据帧中


最简单的方法是使用apply函数

def print_row(row):
   print row['c1'], row['c2']

df.apply(lambda row: print_row(row), axis=1)

有时循环确实比矢量化代码更好

正如这里的许多答案正确指出的那样,Pandas中的默认计划应该是编写矢量化代码(带有隐式循环),而不是自己尝试显式循环。但问题仍然是你是否应该在Pandas中编写循环,如果是的话,在这些情况下最好的循环方式是什么。

我认为,至少有一种情况下循环是合适的:当您需要以某种复杂的方式计算依赖于其他行中的值的函数时。在这种情况下,循环代码通常比矢量化代码更简单、更可读、更不易出错。

循环代码甚至可能更快,正如您将在下面看到的那样,所以在速度至关重要的情况下,循环可能是有意义的。但实际上,这些只是一些情况的子集,您可能应该首先使用numpy/numa(而不是Pandas),因为优化的numpy/noma几乎总是比Pandas更快。

让我们用一个例子来说明这一点。假设您希望获取一列的累积和,但每当其他列等于零时,将其重置:

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

这是一个很好的例子,你当然可以写一行Pandas来实现这一点,尽管它不是特别可读,特别是如果你还没有对Pandas有足够的经验:

df.groupby( (df.y==0).cumsum() )['x'].cumsum()

对于大多数情况来说,这将足够快,尽管您也可以通过避免groupby来编写更快的代码,但它可能更不可读。

或者,如果我们把它写成一个循环呢?您可以使用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() )

诚然,将DataFrame列转换为NumPy数组需要一些开销,但核心代码只有一行代码,即使您对Pandas或NumPy一无所知,也可以阅读:

if y[i] > 0: x_sum[i] = x_sum[i-1] + x[i]

这段代码实际上比矢量化代码更快。在一些具有100000行的快速测试中,上述方法比groupby方法快大约10倍。注意,速度的一个关键是numba,这是可选的。如果没有“@nb.jit”行,循环代码实际上比groupby方法慢大约10倍。

显然,这个示例非常简单,您可能更喜欢一行panda,而不是编写一个带有相关开销的循环。然而,对于这个问题,有更复杂的版本,NumPy/numa循环方法的可读性或速度可能是有意义的。


使用df.iloc[]。例如,使用数据帧“rows_df”:

Or

要从特定行获取值,可以将数据帧转换为ndarray。

然后选择行和列值,如下所示:


正如公认的答案所述,在行上应用函数的最快方法是使用矢量化函数,即所谓的NumPy-ufuncs(通用函数)。

但是,当您要应用的函数尚未在NumPy中实现时,应该怎么做?

好吧,使用numba的矢量化装饰器,您可以轻松地直接在Python中创建ufunc,如下所示:

from numba import vectorize, float64

@vectorize([float64(float64)])
def f(x):
    #x is your line, do something with it, and return a float

此函数的文档如下:创建NumPy通用函数


iterrows()返回元组(a,b),其中a是索引,b是行。


可能是最优雅的解决方案(但肯定不是最有效的):

for row in df.values:
    c2 = row[1]
    print(row)
    # ...

for c1, c2 in df.values:
    # ...

注意:

文档明确建议改用.to_numpy()在最坏的情况下,生成的NumPy数组将具有适合所有列的dtype对象首先有充分的理由不使用循环

尽管如此,我认为这个选项应该包含在这里,作为一个(人们应该认为)微不足道的问题的直接解决方案。


我们有多种选择来做同样的事情,很多人都分享了他们的答案。

我发现以下两种方法既简单又有效:

DataFrame.iterrows()DataFrame.itertuples()

例子:

 import pandas as pd
 inp = [{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}]
 df = pd.DataFrame(inp)
 print (df)

 # With the iterrows method

 for index, row in df.iterrows():
     print(row["c1"], row["c2"])

 # With the itertuples method

 for row in df.itertuples(index=True, name='Pandas'):
     print(row.c1, row.c2)

注意:itertples()应该比iterrows()快


免责声明:尽管这里有很多答案建议不要使用迭代(循环)方法(我基本同意),但我仍然认为这是一种适用于以下情况的合理方法:

使用API中的数据扩展数据帧

假设您有一个包含不完整用户数据的大型数据帧。现在,您必须使用其他列来扩展此数据,例如,用户的年龄和性别。

这两个值都必须从后端API获取。我假设API不提供“批处理”端点(一次接受多个用户ID)。否则,您应该只调用一次API。

网络请求的成本(等待时间)远远超过了数据帧的迭代。我们讨论的是数百毫秒的网络往返时间,相比之下,使用迭代的替代方法可以忽略不计的小增益。

每行一个昂贵的网络请求

所以在这种情况下,我绝对倾向于使用迭代方法。尽管网络请求很昂贵,但可以保证对数据帧中的每一行只触发一次。以下是使用DataFrame.iterrows的示例:

实例

for index, row in users_df.iterrows():
  user_id = row['user_id']

  # Trigger expensive network request once for each row
  response_dict = backend_api.get(f'/api/user-data/{user_id}')

  # Extend dataframe with multiple data from response
  users_df.at[index, 'age'] = response_dict.get('age')
  users_df.at[index, 'gender'] = response_dict.get('gender')