繁体   English   中英

如何在 Pandas 中迭代 DataFrame 中的行

[英]How to iterate over rows in a DataFrame in Pandas

我有一个 pandas dataframe, df

   c1   c2
0  10  100
1  11  110
2  12  120

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

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

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

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

但我不明白 object row是什么以及如何使用它。

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

如何在 Pandas 中遍历 DataFrame 中的行?

答案:不要*

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

你想打印一个DataFrame吗? 使用DataFrame.to_string()

你想计算一些东西吗? 在这种情况下,按此顺序搜索方法(从此处修改的列表):

  1. 矢量化
  2. Cython例程
  3. 列表理解(vanilla for循环)
  4. DataFrame.apply() :i)可以在 Cython 中执行的缩减,ii)Python 空间中的迭代
  5. DataFrame.itertuples()iteritems()
  6. DataFrame.iterrows()

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

向当局上诉

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

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

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


比循环更快: 矢量化Cython

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

如果不存在,请随意使用自定义Cython 扩展编写自己的。


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

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

公式很简单,

# 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,但这并不总是得到保证。

  1. 第一个更明显,但是在处理 NaN 时,如果存在内置的 pandas 方法(因为它们具有更好的极端情况处理逻辑),则更喜欢它们,或者确保您的业务逻辑包含适当的 NaN 处理逻辑。
  2. 在处理混合数据类型时,您应该迭代zip(df['A'], df['B'], ...)而不是df[['A', 'B']].to_numpy()作为后者隐式地将数据向上转换为最常见的类型。 例如,如果 A 是数字而 B 是字符串, to_numpy()会将整个数组转换为字符串,这可能不是您想要的。 幸运的是,将您的列zip在一起是最直接的解决方法。

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


一个明显的例子

让我们通过添加两个 pandas 列A + B的简单示例来演示差异。 这是一个可向量化的操作,因此很容易对比上述方法的性能。

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

然而,我应该提一下,它并不总是这么干脆利落的。 有时,“什么是最佳操作方法”的答案是“这取决于您的数据”。 我的建议是在确定一种方法之前对您的数据测试不同的方法。


我的个人意见*

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

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

对于新手:

矢量化(如果可能) apply() 列出理解; itertuples() / iteritems() ; iterrows() ; 赛通

对于更有经验的人:

矢量化(如果可能) apply() 列出理解; 赛通; itertuples() / iteritems() ; iterrows()

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

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

Cython 在列表中排名较低,因为它需要更多的时间和精力才能正确完成。 您通常永远不需要使用 pandas 编写需要这种性能水平的代码,即使是列表推导也无法满足。

*与任何个人意见一样,请多加盐!


延伸阅读

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


为什么我写这个答案

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

这个答案的目的是帮助新用户理解迭代不一定是所有问题的解决方案,并且可能存在更好、更快和更惯用的解决方案,值得花时间去探索它们。 我并不是要开始一场迭代与矢量化的战争,但我希望新用户在为他们的这个库的问题开发解决方案时被告知。

首先考虑是否真的需要遍历DataFrame 中的行。 有关替代方案,请参见此答案

如果您仍然需要遍历行,可以使用下面的方法。 请注意一些其他答案中未提及的重要警告

itertuples()应该比iterrows()

但请注意,根据文档(目前为 pandas 0.24.2):

  • iterrows: dtype可能与行不匹配

因为 iterrows 为每一行返回一个 Series,所以它不会跨行保留dtypes(dtypes 在 DataFrames 的列中保留)。 要在遍历行时保留 dtypes,最好使用 itertuples(),它返回值的命名元组,通常比 iterrows() 快得多

  • iterrows:不修改行

永远不应该修改你正在迭代的东西。 这不能保证在所有情况下都有效。 根据数据类型,迭代器返回一个副本而不是一个视图,写入它不会有任何效果。

改用DataFrame.apply()

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

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

有关更多详细信息,请参阅关于迭代的 pandas 文档

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

虽然iterrows()是一个不错的选择,但有时itertuples()可以更快:

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

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

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

如何高效迭代

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

简而言之:

  • 作为一般规则,使用df.itertuples(name=None) 特别是当您有固定数量的列且少于 255 列时。 见第 (3) 点
  • 否则,请使用df.itertuples() ,除非您的列有特殊字符,例如空格或“-”。 见点(2)
  • 通过使用最后一个示例,即使您的数据框有奇怪的列,也可以使用itertuples() 见第 (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)默认的itertuples()已经快得多了,但是它不适用于列名,例如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 的默认itertuples()更快,但不是很方便,因为您必须为每列定义一个变量。

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) 最后,命名的itertuples()比前一点要慢,但是您不必为每列定义一个变量,它可以与列名一起使用,例如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 和 itertuples 之间的一个非常有趣的比较

我一直在寻找如何迭代行并在这里结束:

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

您可以编写自己的实现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);

在此处输入图像描述

在此处输入图像描述

要循环dataframe的所有行,您可以使用:

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]

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

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

  1. DataFrame.iterrows()
  2. 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 iterrows method 

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

 #With itertuples method

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

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

有时有用的模式是:

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

更新: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]

在此处输入图像描述

简而言之

  • 尽可能使用矢量化
  • 如果一个操作不能向量化 - 使用列表推导
  • 如果您需要代表整行的单个对象 - 使用 itertuples
  • 如果上述速度太慢 - 尝试swifter.apply
  • 如果它仍然太慢 - 尝试Cython例程

基准

Pandas DataFrame 中行迭代的基准

为了循环数据namedtuples的所有行并方便使用每行的值,可以将dataframe转换为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则将索引添加为 tuple 的第一个元素,这对于某些应用程序可能是不可取的。

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

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

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

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

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

这里循环中的row是该行的副本,而不是它的视图。 因此,你不应该写类似row['A'] = 'New_Value' ,它不会修改 DataFrame。 但是,您可以使用iloc并指定 DataFrame 来完成这项工作。

有很多方法可以遍历 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]])

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

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

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

正如这里的许多答案正确而清楚地指出的那样,您通常不应该尝试在 Pandas 中循环,而应该编写矢量化代码。 但是问题仍然存在,您是否应该在 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]

而且这段代码实际上比矢量化代码更快 在一些 100,000 行的快速测试中,上述方法比groupby方法快 10 倍左右。 请注意,速度的一键是numba,这是可选的。 如果没有“@nb.jit”行,循环代码实际上比groupby方法慢 10 倍。

很明显,这个例子很简单,你可能更喜欢用 pandas 的一行来编写一个带有相关开销的循环。 然而,这个问题有更复杂的版本,NumPy/numba 循环方法的可读性或速度可能是有意义的。

df.iterrows()返回tuple(a, b)其中aindexbrow

您还可以执行 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

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

使用来自 API 的数据扩展数据框

假设您有一个包含不完整用户数据的大型数据框。 现在您必须使用其他列扩展此数据,例如用户的agegender

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

网络请求的成本(等待时间)远远超过了数据帧的迭代。 我们谈论的是数百毫秒的网络往返时间,而使用替代迭代方法的收益微不足道。

每行 1 个昂贵的网络请求

所以在这种情况下,我绝对更喜欢使用迭代方法。 尽管网络请求很昂贵,但可以保证数据帧中的每一行只触发一次。 这是使用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')

此示例使用 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])

某些库(例如,我使用的 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 慢很多,但在某些情况下是必要的。

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

分而治之的优点:

  • 您不需要使用矢量化或任何其他方法将数据框的类型转换为另一种类型
  • 你不需要 Cythonize 你的代码,这通常需要你额外的时间
  • 在我的情况下, iterrows()itertuples()在整个数据帧上都具有相同的性能
  • 取决于您选择的切片index ,您将能够以指数方式加快迭代。 index越高,迭代过程越快。

分而治之的缺点:

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

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

第 1 步:分割/切片

在这一步中,我们将在整个数据帧上划分迭代。 认为您要将 csv 文件读入 pandas df 然后对其进行迭代。 在可能的情况下,我有 5,000,000 条记录,我将把它分成 100,000 条记录。

注意:我需要重申本页其他解决方案中解释的其他运行时分析,“记录数”在 df 上搜索时具有“运行时”的指数比例。 根据我的数据的基准,这里是结果:

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

第 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 数据框

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

但是当你想要应用的功能还没有在 NumPy 中实现时你应该怎么做呢?

好吧,使用来自numbavectorize装饰器,您可以像这样轻松地直接在 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 通用函数

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

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

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

注意:

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

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

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

在此处输入图像描述

或者

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

然后像这样选择行和列值:

在此处输入图像描述

更好的方法是使用zip将数据帧转换为字典,创建键值对,然后通过键访问行值。

我的回答显示了如何使用字典作为 Pandas 的替代品。 有些人认为字典和元组更有效。 您可以轻松地将字典替换为命名元组列表。

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

 for row in inp:
     for (k, v) in zip(row.keys(), row.values()):
         print(k, v)

输出:

c1 10
c2 100
c1 11
c2 110
c1 12
c2 120

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM