Rom*_*man 1551 python rows dataframe pandas
我有一只DataFrame熊猫:
import pandas as pd
inp = [{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}]
df = pd.DataFrame(inp)
print df
Run Code Online (Sandbox Code Playgroud)
输出:
c1 c2
0 10 100
1 11 110
2 12 120
Run Code Online (Sandbox Code Playgroud)
现在我想迭代这个帧的行.对于每一行,我希望能够通过列的名称访问其元素(单元格中的值).例如:
for row in df.rows:
print row['c1'], row['c2']
Run Code Online (Sandbox Code Playgroud)
是否有可能在熊猫中做到这一点?
我发现了类似的问题.但它没有给我我需要的答案.例如,建议使用:
for date, row in df.T.iteritems():
Run Code Online (Sandbox Code Playgroud)
要么
for row in df.iterrows():
Run Code Online (Sandbox Code Playgroud)
但我不明白row对象是什么以及如何使用它.
wai*_*kuo 2141
DataFrame.iterrows是一个生成索引和行的生成器
import pandas as pd
import numpy as np
df = pd.DataFrame([{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}])
Run Code Online (Sandbox Code Playgroud)
vid*_*k13 332
要在pandas中迭代DataFrame的行,可以使用:
for index, row in df.iterrows():
print row["c1"], row["c2"]
Run Code Online (Sandbox Code Playgroud)for row in df.itertuples(index=True, name='Pandas'):
print getattr(row, "c1"), getattr(row, "c2")
Run Code Online (Sandbox Code Playgroud)itertuples() 应该比...更快 iterrows()
但请注意,根据文档(目前的pandas 0.21.1):
iterrows:dtype可能在行与行之间不匹配
因为iterrows为每一行返回一个Series,所以它不会保留行中的dtypes(dtypes保留在DataFrames的列之间).
iterrows:不要修改行
你永远不应该修改你正在迭代的东西.这并不能保证在所有情况下都有效.根据数据类型,迭代器返回副本而不是视图,并且写入它将不起作用.
new_df = df.apply(lambda x: x * 2)
Run Code Online (Sandbox Code Playgroud)itertuples:
如果列名称是无效的Python标识符,重复或以下划线开头,则列名称将重命名为位置名称.使用大量列(> 255)时,将返回常规元组.
cs9*_*s95 206
如何在Pandas的DataFrame中的行上进行迭代?
大熊猫中的迭代是一种反模式,只有在穷尽所有其他可能选项后才应该这样做。您不应该考虑将iter名称中带有“ ”的任何函数用于超过数千行的记录,否则您将不得不习惯很多等待。
您要打印一个DataFrame吗?使用DataFrame.to_string()。
您要计算吗?在这种情况下,请按以下顺序搜索方法(列表从此处修改):
for循环)DataFrame.apply():i)可以在cython中执行的约简操作,ii)在python空间中进行迭代 DataFrame.itertuples() 和 iteritems()DataFrame.iterrows()iterrows并且itertuples(在该问题的答案中都获得很多票)应该在非常罕见的情况下使用,例如生成行对象/命名元以进行顺序处理,这实际上是这些功能唯一有用的东西。
呼吁授权迭代中
的docs页面上有一个巨大的红色警告框,指出:
遍历熊猫对象通常很慢。在许多情况下,不需要手动在行上进行迭代。
熊猫(通过NumPy或通过Cythonized函数)对许多基本操作和计算进行了“向量化”。这包括算术,比较,(大部分)归约,整形(例如透视),联接和groupby操作。浏览有关基本基本功能的文档,以找到适合您问题的矢量化方法。
如果不存在,请使用自定义cython扩展名自行编写。
如果1)没有可用的向量化解决方案,2)性能很重要,但不够重要,不足以经历对代码进行cythonize的麻烦,并且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
result = [f(row[0], ..., row[n]) for row in df[['col1', ...,'coln']].values]
Run Code Online (Sandbox Code Playgroud)
如果可以将业务逻辑封装到一个函数中,则可以使用调用它的列表理解。您可以通过原始python的简单性和速度来使任意复杂的事情起作用。
让我们用添加两个pandas column的简单示例来演示差异A + B。这是可向量化的操作数,因此很容易对比上述方法的性能。
但是,我应该指出的是,并非总是如此。有时,“什么是最佳操作方法”的答案是“取决于您的数据”。我的建议是在建立数据之前先测试一下数据的不同方法。
增强性能 -有关增强标准熊猫操作的文档入门
熊猫中的for循环真的不好吗?我什么时候应该在意?-我对列表理解及其对各种操作(主要是涉及非数字数据的操作)的适用性进行了详细的撰写
我何时应该在代码中使用pandas apply()?- apply速度很慢(但现在和iter*家庭一样慢。但是,apply在某些情况下,人们可以(或应该)将其视为一系列替代选择,尤其是在某些GroupBy手术中)。
*熊猫字符串方法是“矢量化的”,因为它们在系列中已指定但可在每个元素上操作。底层机制仍然是迭代的,因为字符串操作本来就很难向量化。
Wes*_*ney 190
你应该用df.iterrows().虽然逐行迭代不是特别有效,因为必须创建Series对象.
e9t*_*e9t 144
虽然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
Run Code Online (Sandbox Code Playgroud)
che*_*ard 82
您还可以使用df.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)
Run Code Online (Sandbox Code Playgroud)
PJa*_*Jay 71
您可以使用df.iloc函数,如下所示:
for i in range(0, len(df)):
print df.iloc[i]['c1'], df.iloc[i]['c2']
Run Code Online (Sandbox Code Playgroud)
Rom*_*ron 56
如果您真的必须迭代 Pandas 数据帧,您可能希望避免使用 iterrows()。有不同的方法,通常的iterrows()方法远非最好的。itertuples() 可以快 100 倍。
简而言之:
df.itertuples(name=None). 特别是当您有固定数量的列且少于 255 列时。见点(3)df.itertuples()除非您的列具有特殊字符(例如空格或“-”),否则请使用。见点(2)itertuples()使用最后一个例子,即使你的数据帧有奇怪列。见点(4)iterrows()当您无法使用以前的解决方案时才使用。见点(1)生成一百万行和 4 列的随机数据框:
df = pd.DataFrame(np.random.randint(0, 100, size=(1000000, 4)), columns=list('ABCD'))
print(df)
Run Code Online (Sandbox Code Playgroud)
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))
Run Code Online (Sandbox Code Playgroud)
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))
Run Code Online (Sandbox Code Playgroud)
3) 默认itertuples()使用 name=None 更快,但不是很方便,因为您必须为每列定义一个变量。
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))
Run Code Online (Sandbox Code Playgroud)
4) 最后,nameditertuples()比前一点慢,但是您不必为每列定义一个变量,它可以与列名一起使用,例如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))
Run Code Online (Sandbox Code Playgroud)
输出:
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
Run Code Online (Sandbox Code Playgroud)
这篇文章是iterrows和itertuples的一个很有趣的对比
Sac*_*hin 43
我们有多种选择来做同样的事情,很多人都分享了他们的答案。
我发现以下两种方法简单有效:
例子:
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)
Run Code Online (Sandbox Code Playgroud)
注意:itertuples() 应该比 iterrows() 更快
Luc*_*s B 30
我一直在寻找如何迭代行和列,并在此结束:
for i, row in df.iterrows():
for j, column in row.iteritems():
print(column)
Run Code Online (Sandbox Code Playgroud)
piR*_*red 17
您可以编写自己的实现迭代器 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)
Run Code Online (Sandbox Code Playgroud)
这可以直接比较pd.DataFrame.itertuples.我的目标是以更高的效率执行相同的任务.
对于具有my function的给定数据帧:
list(myiter(df))
[MyTuple(c1=10, c2=100), MyTuple(c1=11, c2=110), MyTuple(c1=12, c2=120)]
Run Code Online (Sandbox Code Playgroud)
或者pd.DataFrame.itertuples:
list(df.itertuples(index=False))
[Pandas(c1=10, c2=100), Pandas(c1=11, c2=110), Pandas(c1=12, c2=120)]
Run Code Online (Sandbox Code Playgroud)
全面测试
我们测试使所有列可用并对列进行子集化.
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);
Run Code Online (Sandbox Code Playgroud)
Gab*_*les 15
要点:
DataFrame不迭代的情况下迭代 Pandas经过几周的研究这个答案,我得出了以下结论:
这里有13 种迭代 PandasDataFrame的技术。正如您所看到的,所花费的时间差异很大。最快的技术比最慢的技术快约 1363 倍!正如 @cs95 所说,关键的一点是不要迭代!请改用矢量化(“数组编程”)。所有这些真正意味着您应该直接在数学公式中使用数组,而不是尝试手动迭代数组。当然,底层对象必须支持这一点,但 Numpy 和 Pandas都支持。
在 Pandas 中使用矢量化的方法有很多,您可以在下面的绘图和示例代码中看到。当直接使用数组时,底层循环仍然会发生,但在(我认为)非常优化的底层 C 代码中,而不是通过原始 Python。
测试了 13 种技术,编号为 1 至 13。技术编号和名称位于每个条形下方。总计算时间位于每个柱上方。下面是乘数,显示它比最右边最快的技术花费了多长时间:
来自pandas_dataframe_iteration_vs_vectorization_vs_list_comprehension_speed_tests.svg我的eRCaGuy_hello_world存储库(由此代码生成)。
列表理解和向量化(可能使用布尔索引)是您真正需要的。
使用列表理解(好)和向量化(最好)。我认为纯矢量化总是可能的,但在复杂的计算中可能需要额外的工作。在此答案中搜索“布尔索引”、“布尔数组”和“布尔掩码”(这三个都是同一件事),以查看可以使用纯矢量化的一些更复杂的情况。
8_pure_vectorization__with_df.loc[]_boolean_array_indexing_for_if_statment_corner_case6_vectorization__with_apply_for_if_statement_corner_case7_vectorization__with_list_comprehension_for_if_statment_corner_case11_list_comprehension_w_zip_and_direct_variable_assignment_calculated_in_place10_list_comprehension_w_zip_and_direct_variable_assignment_passed_to_func12_list_comprehension_w_zip_and_row_tuple_passed_to_func5_itertuples_in_for_loop13_list_comprehension_w__to_numpy__and_direct_variable_assignment_passed_to_func9_apply_function_with_lambda1_raw_for_loop_using_regular_df_indexing2_raw_for_loop_using_df.loc[]_indexing4_iterrows_in_for_loop3_raw_for_loop_using_df.iloc[]_indexing.loc[]或 ).iloc[]使这些技术变得糟糕,而是它们所处的循环使for它们变得糟糕!.loc[]例如,我使用最快的(纯矢量化)方法!因此,这里有 3 种最慢的技术,千万不要使用:
3_raw_for_loop_using_df.iloc[]_indexing4_iterrows_in_for_loop2_raw_for_loop_using_df.loc[]_indexing1_raw_for_loop_using_regular_df_indexing,但如果您要使用原始 for 循环,它比其他循环更快。.apply()( 9_apply_function_with_lambda) 还可以,但一般来说,我也会避免使用它。然而,技术6_vectorization__with_apply_for_if_statement_corner_case确实表现得比 更好7_vectorization__with_list_comprehension_for_if_statment_corner_case,这很有趣。if这意味着函数内部可以有非常复杂的语句和内容。因此,这里的权衡是,它通过使用外部计算函数为您提供了真正可读和可重用的代码的强大多功能性,同时仍然为您提供了很高的速度!.apply()或列表理解,同时仍然可以轻松地对其余部分使用矢量化。if使纯向量化起作用,但会增加额外的工作并降低可读性。因此,您可以选择仅针对那些边缘情况使用列表理解(通常是最好的)或.apply()(通常较慢,但并非总是如此),同时仍对其余计算使用向量化。例如:参见技术和。7_vectorization__with_list_comprehension_for_if_statment_corner_case6_vectorization__with_apply_for_if_statement_corner_case假设我们有以下 Pandas DataFrame。它有 200 万行,4 列(A、B、C和D),每列都有从-1000到的随机值1000:
df =
A B C D
0 -365 842 284 -942
1 532 416 -102 888
2 397 321 -296 -616
3 -215 879 557 895
4 857 701 -157 480
... ... ... ... ...
1999995 -101 -233 -377 -939
1999996 -989 380 917 145
1999997 -879 333 -372 -970
1999998 738 982 -743 312
1999999 -306 -103 459 745
Run Code Online (Sandbox Code Playgroud)
我这样生成了这个 DataFrame:
df =
A B C D
0 -365 842 284 -942
1 532 416 -102 888
2 397 321 -296 -616
3 -215 879 557 895
4 857 701 -157 480
... ... ... ... ...
1999995 -101 -233 -377 -939
1999996 -989 380 917 145
1999997 -879 333 -372 -970
1999998 738 982 -743 312
1999999 -306 -103 459 745
Run Code Online (Sandbox Code Playgroud)
我想证明所有这些技术都可以在非平凡的函数或方程上实现,因此我故意使他们正在计算的方程要求:
if声明我们将为每一行计算的方程是这样的。我随意编造了它,但我认为它包含足够的复杂性,您将能够扩展我所做的工作,以在完全矢量化的 Pandas 中执行您想要的任何方程:
在Python中,上面的方程可以写成这样:
import numpy as np
import pandas as pd
# Create an array (numpy list of lists) of fake data
MIN_VAL = -1000
MAX_VAL = 1000
# NUM_ROWS = 10_000_000
NUM_ROWS = 2_000_000 # default for final tests
# NUM_ROWS = 1_000_000
# NUM_ROWS = 100_000
# NUM_ROWS = 10_000 # default for rapid development & initial tests
NUM_COLS = 4
data = np.random.randint(MIN_VAL, MAX_VAL, size=(NUM_ROWS, NUM_COLS))
# Now convert it to a Pandas DataFrame with columns named "A", "B", "C", and "D"
df_original = pd.DataFrame(data, columns=["A", "B", "C", "D"])
print(f"df = \n{df_original}")
Run Code Online (Sandbox Code Playgroud)
或者,你可以这样写:
# Calculate and return a new value, `val`, by performing the following equation:
val = (
2 * A_i_minus_2
+ 3 * A_i_minus_1
+ 4 * A
+ 5 * A_i_plus_1
# Python ternary operator; don't forget parentheses around the entire
# ternary expression!
+ ((6 * B) if B > 0 else (60 * B))
+ 7 * C
- 8 * D
)
Run Code Online (Sandbox Code Playgroud)
其中任何一个都可以包装到一个函数中。前任:
# Calculate and return a new value, `val`, by performing the following equation:
if B > 0:
B_new = 6 * B
else:
B_new = 60 * B
val = (
2 * A_i_minus_2
+ 3 * A_i_minus_1
+ 4 * A
+ 5 * A_i_plus_1
+ B_new
+ 7 * C
- 8 * D
)
Run Code Online (Sandbox Code Playgroud)
完整的代码可以下载并在我的eRCaGuy_hello_worldpython/pandas_dataframe_iteration_vs_vectorization_vs_list_comprehension_speed_tests.py存储库中的文件中运行。
技巧一: 1_raw_for_loop_using_regular_df_indexing
def calculate_val(
A_i_minus_2,
A_i_minus_1,
A,
A_i_plus_1,
B,
C,
D):
val = (
2 * A_i_minus_2
+ 3 * A_i_minus_1
+ 4 * A
+ 5 * A_i_plus_1
# Python ternary operator; don't forget parentheses around the
# entire ternary expression!
+ ((6 * B) if B > 0 else (60 * B))
+ 7 * C
- 8 * D
)
return val
Run Code Online (Sandbox Code Playgroud)
技巧2: 2_raw_for_loop_using_df.loc[]_indexing
val = [np.NAN]*len(df)
for i in range(len(df)):
if i < 2 or i > len(df)-2:
continue
val[i] = calculate_val(
df["A"][i-2],
df["A"][i-1],
df["A"][i],
df["A"][i+1],
df["B"][i],
df["C"][i],
df["D"][i],
)
df["val"] = val # put this column back into the dataframe
Run Code Online (Sandbox Code Playgroud)
技巧3: 3_raw_for_loop_using_df.iloc[]_indexing
val = [np.NAN]*len(df)
for i in range(len(df)):
if i < 2 or i > len(df)-2:
continue
val[i] = calculate_val(
df.loc[i-2, "A"],
df.loc[i-1, "A"],
df.loc[i, "A"],
df.loc[i+1, "A"],
df.loc[i, "B"],
df.loc[i, "C"],
df.loc[i, "D"],
)
df["val"] = val # put this column back into the dataframe
Run Code Online (Sandbox Code Playgroud)
技巧4: 4_iterrows_in_for_loop
# column indices
i_A = 0
i_B = 1
i_C = 2
i_D = 3
val = [np.NAN]*len(df)
for i in range(len(df)):
if i < 2 or i > len(df)-2:
continue
val[i] = calculate_val(
df.iloc[i-2, i_A],
df.iloc[i-1, i_A],
df.iloc[i, i_A],
df.iloc[i+1, i_A],
df.iloc[i, i_B],
df.iloc[i, i_C],
df.iloc[i, i_D],
)
df["val"] = val # put this column back into the dataframe
Run Code Online (Sandbox Code Playgroud)
对于接下来的所有示例,我们必须首先通过添加具有上一个值和下一个值的列来准备数据框:A_(i-2)、A_(i-1)和A_(i+1)。DataFrame 中的这些列将分别命名为A_i_minus_2、A_i_minus_1和A_i_plus_1:
val = [np.NAN]*len(df)
for index, row in df.iterrows():
if index < 2 or index > len(df)-2:
continue
val[index] = calculate_val(
df["A"][index-2],
df["A"][index-1],
row["A"],
df["A"][index+1],
row["B"],
row["C"],
row["D"],
)
df["val"] = val # put this column back into the dataframe
Run Code Online (Sandbox Code Playgroud)
运行上面的矢量化代码来生成这 3 个新列总共花费了0.044961 秒。
现在讨论其余的技术:
技巧5: 5_itertuples_in_for_loop
df_original["A_i_minus_2"] = df_original["A"].shift(2) # val at index i-2
df_original["A_i_minus_1"] = df_original["A"].shift(1) # val at index i-1
df_original["A_i_plus_1"] = df_original["A"].shift(-1) # val at index i+1
# Note: to ensure that no partial calculations are ever done with rows which
# have NaN values due to the shifting, we can either drop such rows with
# `.dropna()`, or set all values in these rows to NaN. I'll choose the latter
# so that the stats that will be generated with the techniques below will end
# up matching the stats which were produced by the prior techniques above. ie:
# the number of rows will be identical to before.
#
# df_original = df_original.dropna()
df_original.iloc[:2, :] = np.NAN # slicing operators: first two rows,
# all columns
df_original.iloc[-1:, :] = np.NAN # slicing operators: last row, all columns
Run Code Online (Sandbox Code Playgroud)
技巧6: 6_vectorization__with_apply_for_if_statement_corner_case
val = [np.NAN]*len(df)
for row in df.itertuples():
val[row.Index] = calculate_val(
row.A_i_minus_2,
row.A_i_minus_1,
row.A,
row.A_i_plus_1,
row.B,
row.C,
row.D,
)
df["val"] = val # put this column back into the dataframe
Run Code Online (Sandbox Code Playgroud)
技巧7: 7_vectorization__with_list_comprehension_for_if_statment_corner_case
def calculate_new_column_b_value(b_value):
# Python ternary operator
b_value_new = (6 * b_value) if b_value > 0 else (60 * b_value)
return b_value_new
# In this particular example, since we have an embedded `if-else` statement
# for the `B` column, pure vectorization is less intuitive. So, first we'll
# calculate a new `B` column using
# **`apply()`**, then we'll use vectorization for the rest.
df["B_new"] = df["B"].apply(calculate_new_column_b_value)
# OR (same thing, but with a lambda function instead)
# df["B_new"] = df["B"].apply(lambda x: (6 * x) if x > 0 else (60 * x))
# Now we can use vectorization for the rest. "Vectorization" in this case
# means to simply use the column series variables in equations directly,
# without manually iterating over them. Pandas DataFrames will handle the
# underlying iteration automatically for you. You just focus on the math.
df["val"] = (
2 * df["A_i_minus_2"]
+ 3 * df["A_i_minus_1"]
+ 4 * df["A"]
+ 5 * df["A_i_plus_1"]
+ df["B_new"]
+ 7 * df["C"]
- 8 * df["D"]
)
Run Code Online (Sandbox Code Playgroud)
技巧8: 8_pure_vectorization__with_df.loc[]_boolean_array_indexing_for_if_statment_corner_case
这使用布尔索引(又名:布尔掩码)来完成if等式中语句的等效操作。这样,整个方程可以使用纯矢量化,从而最大限度地提高性能和速度。
# In this particular example, since we have an embedded `if-else` statement
# for the `B` column, pure vectorization is less intuitive. So, first we'll
# calculate a new `B` column using **list comprehension**, then we'll use
# vectorization for the rest.
df["B_new"] = [
calculate_new_column_b_value(b_value) for b_value in df["B"]
]
# Now we can use vectorization for the rest. "Vectorization" in this case
# means to simply use the column series variables in equations directly,
# without manually iterating over them. Pandas DataFrames will handle the
# underlying iteration automatically for you. You just focus on the math.
df["val"] = (
2 * df["A_i_minus_2"]
+ 3 * df["A_i_minus_1"]
+ 4 * df["A"]
+ 5 * df["A_i_plus_1"]
+ df["B_new"]
+ 7 * df["C"]
- 8 * df["D"]
)
Run Code Online (Sandbox Code Playgroud)
技巧9: 9_apply_function_with_lambda
# If statement to evaluate:
#
# if B > 0:
# B_new = 6 * B
# else:
# B_new = 60 * B
#
# In this particular example, since we have an embedded `if-else` statement
# for the `B` column, we can use some boolean array indexing through
# `df.loc[]` for some pure vectorization magic.
#
# Explanation:
#
# Long:
#
# The format is: `df.loc[rows, columns]`, except in this case, the rows are
# specified by a "boolean array" (AKA: a boolean expression, list of
# booleans, or "boolean mask"), specifying all rows where `B` is > 0. Then,
# only in that `B` column for those rows, set the value accordingly. After
# we do this for where `B` is > 0, we do the same thing for where `B`
# is <= 0, except with the other equation.
#
# Short:
#
# For all rows where the boolean expression applies, set the column value
# accordingly.
#
# GitHub CoPilot first showed me this `.loc[]` technique.
# See also the official documentation:
# https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.loc.html
#
# ===========================
# 1st: handle the > 0 case
# ===========================
df["B_new"] = df.loc[df["B"] > 0, "B"] * 6
#
# ===========================
# 2nd: handle the <= 0 case, merging the results into the
# previously-created "B_new" column
# ===========================
# - NB: this does NOT work; it overwrites and replaces the whole "B_new"
# column instead:
#
# df["B_new"] = df.loc[df["B"] <= 0, "B"] * 60
#
# This works:
df.loc[df["B"] <= 0, "B_new"] = df.loc[df["B"] <= 0, "B"] * 60
# Now use normal vectorization for the rest.
df["val"] = (
2 * df["A_i_minus_2"]
+ 3 * df["A_i_minus_1"]
+ 4 * df["A"]
+ 5 * df["A_i_plus_1"]
+ df["B_new"]
+ 7 * df["C"]
- 8 * df["D"]
)
Run Code Online (Sandbox Code Playgroud)
技巧10: 10_list_comprehension_w_zip_and_direct_variable_assignment_passed_to_func
df["val"] = df.apply(
lambda row: calculate_val(
row["A_i_minus_2"],
row["A_i_minus_1"],
row["A"],
row["A_i_plus_1"],
row["B"],
row["C"],
row["D"]
),
axis='columns' # same as `axis=1`: "apply function to each row",
# rather than to each column
)
Run Code Online (Sandbox Code Playgroud)
技巧11: 11_list_comprehension_w_zip_and_direct_variable_assignment_calculated_in_place
df["val"] = [
# Note: you *could* do the calculations directly here instead of using a
# function call, so long as you don't have indented code blocks such as
# sub-routines or multi-line if statements.
#
# I'm using a function call.
calculate_val(
A_i_minus_2,
A_i_minus_1,
A,
A_i_plus_1,
B,
C,
D
) for A_i_minus_2, A_i_minus_1, A, A_i_plus_1, B, C, D
in zip(
df["A_i_minus_2"],
df["A_i_minus_1"],
df["A"],
df["A_i_plus_1"],
df["B"],
df["C"],
df["D"]
)
]
Run Code Online (Sandbox Code Playgroud)
技巧12: 12_list_comprehension_w_zip_and_row_tuple_passed_to_func
df["val"] = [
2 * A_i_minus_2
+ 3 * A_i_minus_1
+ 4 * A
+ 5 * A_i_plus_1
# Python ternary operator; don't forget parentheses around the entire
# ternary expression!
+ ((6 * B) if B > 0 else (60 * B))
+ 7 * C
- 8 * D
for A_i_minus_2, A_i_minus_1, A, A_i_plus_1, B, C, D
in zip(
df["A_i_minus_2"],
df["A_i_minus_1"],
df["A"],
df["A_i_plus_1"],
df["B"],
df["C"],
df["D"]
)
]
Run Code Online (Sandbox Code Playgroud)
技巧13: 13_list_comprehension_w__to_numpy__and_direct_variable_assignment_passed_to_func
df["val"] = [
# Note: you *could* do the calculations directly here instead of using a
# function call, so long as you don't have indented code blocks such as
# sub-routines or multi-line if statements.
#
# I'm using a function call.
calculate_val(
A_i_minus_2,
A_i_minus_1,
A,
A_i_plus_1,
B,
C,
D
) for A_i_minus_2, A_i_minus_1, A, A_i_plus_1, B, C, D
# Note: this `[[...]]` double-bracket indexing is used to select a
# subset of columns from the dataframe. The inner `[]` brackets
# create a list from the column names within them, and the outer
# `[]` brackets accept this list to index into the dataframe and
# select just this list of columns, in that order.
# - See the official documentation on it here:
# https://pandas.pydata.org/docs/user_guide/indexing.html#basics
# - Search for the phrase "You can pass a list of columns to [] to
# select columns in that order."
# - I learned this from this comme
Ped*_*ito 14
要遍历a中的所有行,dataframe您可以使用:
for x in range(len(date_example.index)):
print date_example['Date'].iloc[x]
Run Code Online (Sandbox Code Playgroud)
Gra*_*015 14
恕我直言,最简单的决定
for ind in df.index:
print df['c1'][ind], df['c2'][ind]
Run Code Online (Sandbox Code Playgroud)
Joh*_*hnE 13
正如这里的许多答案正确指出的那样,Pandas 中的默认计划应该是编写矢量化代码(及其隐式循环),而不是自己尝试显式循环。但问题仍然是您是否应该在Pandas 中编写循环,如果是的话,在这些情况下循环的最佳方法是什么。
我相信至少在一种一般情况下循环是合适的:当您需要以某种复杂的方式计算某个依赖于其他行中的值的函数时。在这种情况下,循环代码通常比矢量化代码更简单、更易读并且更不容易出错。
循环代码甚至可能更快,如下所示,因此在速度至关重要的情况下循环可能有意义。但实际上,这些只是您可能应该一开始就使用 numpy/numba(而不是 Pandas)的情况的子集,因为优化的 numpy/numba 几乎总是比 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
Run Code Online (Sandbox Code Playgroud)
这是一个很好的例子,你当然可以编写一行 Pandas 来实现这一点,尽管它不是特别可读,特别是如果你对 Pandas 还没有相当的经验的话:
df.groupby( (df.y==0).cumsum() )['x'].cumsum()
Run Code Online (Sandbox Code Playgroud)
对于大多数情况来说,这已经足够快了,尽管您也可以通过避免使用 来编写更快的代码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() )
Run Code Online (Sandbox Code Playgroud)
诚然,将 DataFrame 列转换为 NumPy 数组需要一些开销,但核心代码只是一行代码,即使您对 Pandas 或 NumPy 一无所知,您也可以阅读它:
if y[i] > 0: x_sum[i] = x_sum[i-1] + x[i]
Run Code Online (Sandbox Code Playgroud)
而且这个代码实际上比矢量化代码更快。在一些 100,000 行的快速测试中,上述方法比groupby方法快大约 10 倍。请注意,速度的关键是 numba,这是可选的。如果没有“@nb.jit”行,循环代码实际上比groupby方法慢大约 10 倍。
显然,这个示例非常简单,您可能更喜欢一行 pandas,而不是编写一个具有相关开销的循环。然而,这个问题还有更复杂的版本,对于这些版本,NumPy/numba 循环方法的可读性或速度可能是有意义的。
Jor*_*rdy 13
我建议使用df.at[row, column]( source ) 来迭代所有 pandas 单元。
例如 :
for row in range(len(df)):
print(df.at[row, 'c1'], df.at[row, 'c2'])
Run Code Online (Sandbox Code Playgroud)
输出将是:
10 100
11 110
12 120
Run Code Online (Sandbox Code Playgroud)
您还可以使用 修改单元格的值df.at[row, column] = newValue。
for row in range(len(df)):
print(df.at[row, 'c1'], df.at[row, 'c2'])
Run Code Online (Sandbox Code Playgroud)
输出将是:
data-10 100
data-11 110
data-12 120
Run Code Online (Sandbox Code Playgroud)
Hos*_*ein 10
对于查看和修改值,我会使用iterrows(). 在 for 循环中并通过使用元组解包(参见示例:)i, row,我使用rowfor 仅查看值并在我想修改值时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'
Run Code Online (Sandbox Code Playgroud)
这里的rowin 循环是该行的副本,而不是它的视图。因此,你不应该写类似的东西row['A'] = 'New_Value',它不会修改数据帧。但是,您可以使用i和loc指定 DataFrame 来完成这项工作。
Zei*_*ist 10
有一种方法可以在获取 DataFrame 的同时迭代抛出行,而不是 Series。我没有看到有人提到您可以将索引作为列表传递给要作为 DataFrame 返回的行:
for i in range(len(df)):
row = df.iloc[[i]]
Run Code Online (Sandbox Code Playgroud)
注意双括号的使用。这将返回一个带有单行的 DataFrame。
And*_*rdo 10
更新: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]
Run Code Online (Sandbox Code Playgroud)
可能是最优雅的解决方案(但肯定不是最有效的):
for row in df.values:
c2 = row[1]
print(row)
# ...
for c1, c2 in df.values:
# ...
Run Code Online (Sandbox Code Playgroud)
注意:
尽管如此,我认为这个选项应该包含在此处,作为解决(人们应该认为的)琐碎问题的直接解决方案。
除了上面的答案,有时一个有用的模式是:
# 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)
Run Code Online (Sandbox Code Playgroud)
结果如下:
{'col1':1.0, 'col2':0.1}
{'col1':2.0, 'col2':0.2}
Run Code Online (Sandbox Code Playgroud)
小智 7
有很多方法可以迭代 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]])
Run Code Online (Sandbox Code Playgroud)
您还可以进行 NumPy 索引以获得更高的速度。它并不是真正的迭代,但对于某些应用程序来说比迭代效果更好。
subset = row['c1'][0:5]
all = row['c1'][:]
Run Code Online (Sandbox Code Playgroud)
您可能还想将其转换为数组。这些索引/选择应该像 NumPy 数组一样工作,但我遇到了问题并且需要强制转换
np.asarray(all)
imgs[:] = cv2.resize(imgs[:], (224,224) ) # Resize every image in an hdf5 file
Run Code Online (Sandbox Code Playgroud)
要循环使用 a中的所有行dataframe并方便地使用每行的值,可以将其转换为s.例如:namedtuplesndarray
df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]}, index=['a', 'b'])
Run Code Online (Sandbox Code Playgroud)
迭代行:
for row in df.itertuples(index=False, name='Pandas'):
print np.asarray(row)
Run Code Online (Sandbox Code Playgroud)
结果是:
[ 1. 0.1]
[ 2. 0.2]
Run Code Online (Sandbox Code Playgroud)
请注意,如果index=True,索引被添加为元组的第一个元素,这对于某些应用程序可能是不合需要的.
最简单的方法,使用apply函数
def print_row(row):
print row['c1'], row['c2']
df.apply(lambda row: print_row(row), axis=1)
Run Code Online (Sandbox Code Playgroud)
df.index并访问通过at[]一种非常可读的方法是迭代索引(如@Grag2015建议)。但是,为了提高效率,不要使用链式索引at:
for ind in df.index:
print(df.at[ind, 'col A'])
Run Code Online (Sandbox Code Playgroud)
此方法的优点for i in range(len(df))是即使索引不正确,它也能工作RangeIndex。请参见以下示例:
df = pd.DataFrame({'col A': list('ABCDE'), 'col B': range(5)}, index=list('abcde'))
for ind in df.index:
print(df.at[ind, 'col A'], df.at[ind, 'col B']) # <---- OK
df.at[ind, 'col C'] = df.at[ind, 'col B'] * 2 # <---- can assign values
for ind in range(len(df)):
print(df.at[ind, 'col A'], df.at[ind, 'col B']) # <---- KeyError
Run Code Online (Sandbox Code Playgroud)
如果需要一行的整数位置(例如,获取前一行的值),请将其包装为enumerate():
for i, ind in enumerate(df.index):
prev_row_ind = df.index[i-1] if i > 0 else df.index[i]
df.at[ind, 'col C'] = df.at[prev_row_ind, 'col B'] * 2
Run Code Online (Sandbox Code Playgroud)
get_loc配合使用itertuples()尽管它比 快得多iterrows(),但 的一个主要缺点itertuples()是,如果列标签中包含空格(例如'col C'变成_1等),它会破坏列标签,这使得在迭代中访问值变得困难。
您可以使用df.columns.get_loc()获取列标签的整数位置并使用它来索引命名元组。请注意,每个命名元组的第一个元素是索引标签,因此要通过整数位置正确访问列,您必须向返回的内容加 1,get_loc或者在开头解压元组。
df = pd.DataFrame({'col A': list('ABCDE'), 'col B': range(5)}, index=list('abcde'))
for row in df.itertuples(name=None):
pos = df.columns.get_loc('col B') + 1 # <---- add 1 here
print(row[pos])
for ind, *row in df.itertuples(name=None):
# ^^^^^^^^^ <---- unpacked here
pos = df.columns.get_loc('col B') # <---- already unpacked
df.at[ind, 'col C'] = row[pos] * 2
print(row[pos])
Run Code Online (Sandbox Code Playgroud)
dict_items循环数据帧的另一种方法是将其转换为字典 inorient='index'并迭代dict_itemsor dict_values。
df = pd.DataFrame({'col A': list('ABCDE'), 'col B': range(5)})
for row in df.to_dict('index').values():
# ^^^^^^^^^ <--- iterate over dict_values
print(row['col A'], row['col B'])
for index, row in df.to_dict('index').items():
# ^^^^^^^^ <--- iterate over dict_items
df.at[index, 'col A'] = row['col A'] + str(row['col B'])
Run Code Online (Sandbox Code Playgroud)
这不会破坏像这样的数据类型iterrows,不会破坏像这样的列标签,itertuples并且不知道列数(zip(df['col A'], df['col B'], ...)如果有很多列,很快就会变得很麻烦)。
最后,作为@cs95提到的,尽可能避免循环。特别是如果您的数据是数字,如果您稍微挖掘一下,库中就会有针对您的任务的优化方法。
也就是说,在某些情况下迭代比矢量化操作更有效。一个常见的此类任务是将 pandas 数据帧转储到嵌套的 json 中。至少从 pandas 1.5.3 开始,在这种情况下,itertuples()循环比涉及方法的任何向量化操作要快得多。groupby.apply
| 归档时间: |
|
| 查看次数: |
1987524 次 |
| 最近记录: |