我有一个数据帧df
,其中包含很长的随机正整数列:
df = pd.DataFrame({'n': np.random.randint(1, 10, size = 10000)})
我想确定列中第一个偶数的索引。一种方法是:
df[df.n % 2 == 0].iloc[0]
但是这涉及很多操作(生成索引f.n % 2 == 0
,在这些索引上评估df
并最终获取第一项)并且非常慢。像这样的循环要快得多:
for j in range(len(df)):
if df.n.iloc[j] % 2 == 0:
break
也因为第一个结果可能在前几行。是否有任何pandas方法以相似的性能执行此操作?谢谢。
注意:此条件(为偶数)只是一个示例。 我正在寻找一种适用于价值观的任何条件的解决方案,例如,快速单线替代:
df[ conditions on df.n ].iloc[0]
答案 0 :(得分:3)
一些时间和是使用发电机通常会给你更快的结果
df = pd.DataFrame({'n': np.random.randint(1, 10, size = 10000)})
%timeit df[df.n % 2 == 0].iloc[0]
%timeit df.iloc[next(k for k,v in df.iterrows() if v.n % 2 == 0)]
%timeit df.iloc[next(t[0] for t in df.itertuples() if t.n % 2 == 0)]
我明白了:
1000 loops, best of 3: 1.09 ms per loop
1000 loops, best of 3: 619 µs per loop # <-- iterrows generator
1000 loops, best of 3: 1.1 ms per loop
10000 loops, best of 3: 25 µs per loop # <--- your solution
但是当你调整大小时:
df = pd.DataFrame({'n': np.random.randint(1, 10, size = 1000000)})
差异消失了:
10 loops, best of 3: 40.5 ms per loop
10 loops, best of 3: 40.7 ms per loop # <--- iterrows
10 loops, best of 3: 56.9 ms per loop
您的解决方案最快,为什么不使用它?
for j in range(len(df)):
if df.n.iloc[j] % 2 == 0:
break
答案 1 :(得分:1)
我决定玩几种可能性。我拿一个数据帧:
MAX = 10**7
df = pd.DataFrame({'n': range(MAX)})
(这次不是随机的。)我想找到第n >= N
个N
的第一行。我有时间以下四个版本:
def getfirst_pandas(condition, df):
return df[condition(df)].iloc[0]
def getfirst_iterrows_loop(condition, df):
for index, row in df.iterrows():
if condition(row):
return index, row
return None
def getfirst_for_loop(condition, df):
for j in range(len(df)):
if condition(df.iloc[j]):
break
return j
def getfirst_numpy_argmax(condition, df):
array = df.as_matrix()
imax = np.argmax(condition(array))
return df.index[imax]
N
=十的权力。当然,numpy(优化的C)代码预计会比python中的for
循环更快,但我想知道N
python循环的哪些值仍然可以。
我计时:
getfirst_pandas(lambda x: x.n >= N, df)
getfirst_iterrows_loop(lambda x: x.n >= N, df)
getfirst_for_loop(lambda x: x.n >= N, df)
getfirst_numpy_argmax(lambda x: x >= N, df.n)
代表N = 1, 10, 100, 1000, ...
。这是性能的对数 - 对数图:
简单的for
循环是可以的,只要&#34;第一个真正的位置&#34;预计会在一开始,但随后变得糟糕。 np.argmax
是最安全的解决方案。
从图中可以看出,pandas
和argmax
的时间保持(几乎)不变,因为它们始终扫描整个数组。拥有np
或pandas
方法并不完美。
答案 2 :(得分:0)
让你迭代行并在你满意时停止的选项是使用 DataFrame.iterrows,这是大熊猫&#39;行迭代器。
在你的情况下,你可以实现这样的事情:
def get_first_row_with(condition, df):
for index, row in df.iterrows():
if condition(row):
return index, row
return None # Condition not met on any row in entire DataFrame
然后,给定一个DataFrame,例如:
df = pd.DataFrame({
'cats': [1,2,3,4],
'dogs': [2,4,6,8]
},
index=['Alice', 'Bob', 'Charlie', 'Eve'])
你可以用作:
def some_condition(row):
return row.cats + row.dogs >= 7
index, row = get_first_row_with(some_condition, df)
# Use results however you like, e.g.:
print('{} is the first person to have at least 7 pets.'.format(index))
print('They have {} cats and {} dogs!'.format(row.cats, row.dogs))
哪个会输出:
Charlie is the first person to have at least 7 pets.
They have 3 cats and 6 dogs!
答案 3 :(得分:0)
Zip
索引和列,然后对其进行循环以提高循环速度。 Zip
提供最快的循环性能,比iterrows()
或itertuples()
快。
for j in zip(df.index,df.n):
if j[1] % 2 == 0:
index_position = j[0]
break
答案 4 :(得分:0)
TLDR:您可以使用next(j for j in range(len(df)) if df.at[j, "n"] % 2 == 0)
我认为完全有可能在一个代码行中编写您的代码。让我们定义一个DataFrame来证明这一点:
df = pd.DataFrame({'n': np.random.randint(1, 10, size = 100000)})
首先,您的代码给出:
for j in range(len(df)):
if df.n.iloc[j] % 2 == 0:
break
% 22.1 µs ± 1.5 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
将其转换为单线可得到:
next(j for j in range(len(df)) if df["n"].iloc[j] % 2 == 0)
% 20.6 µs ± 1.26 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
为了进一步加快计算速度,我们可以使用at
代替iloc
,因为访问单个值时速度更快:
next(j for j in range(len(df)) if df.at[j, "n"] % 2 == 0)
% 8.88 µs ± 617 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)