我有一个df带有很长的随机正整数列的数据帧:
df = pd.DataFrame({'n': np.random.randint(1, 10, size = 10000)})
Run Code Online (Sandbox Code Playgroud)
我想确定列中第一个偶数的索引.一种方法是:
df[df.n % 2 == 0].iloc[0]
Run Code Online (Sandbox Code Playgroud)
但这涉及很多操作(生成指数f.n % 2 == 0,评估df这些指数,最后采取第一项)并且非常缓慢.像这样的循环要快得多:
for j in range(len(df)):
if df.n.iloc[j] % 2 == 0:
break
Run Code Online (Sandbox Code Playgroud)
也因为第一个结果可能在前几行.是否有任何pandas方法以相似的性能执行此操作?谢谢.
注意:这个条件(是一个偶数)只是一个例子. 我正在寻找适用于价值观的任何条件的解决方案,即快速单线替代:
df[ conditions on df.n ].iloc[0]
Run Code Online (Sandbox Code Playgroud)
为了好玩,我决定尝试几种可能性。我拿一个数据框:
MAX = 10**7
df = pd.DataFrame({'n': range(MAX)})
Run Code Online (Sandbox Code Playgroud)
(不是随机的这段时间)。我想找到的第一行这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]
Run Code Online (Sandbox Code Playgroud)
with N= 十的幂。当然,numpy(优化的 C)代码预计比forpython 中的循环更快,但我想看看Npython 循环的哪些值仍然可以。
我对线路进行计时:
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)
Run Code Online (Sandbox Code Playgroud)
为N = 1, 10, 100, 1000, ...。这是性能的日志日志图:
for只要“第一个真实位置”预计在开头,简单循环就可以了,但随后就变得糟糕了。这np.argmax是最安全的解决方案。
你可以从图表中,时间看到pandas和argmax保持(几乎)恒定的,因为他们总是扫描整个阵列。拥有一个没有的nporpandas方法将是完美的。
做了一些计时,是的,使用发电机通常会给你更快的结果
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)]
Run Code Online (Sandbox Code Playgroud)
我得到:
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
Run Code Online (Sandbox Code Playgroud)
但是,当您将其放大时:
df = pd.DataFrame({'n': np.random.randint(1, 10, size = 1000000)})
Run Code Online (Sandbox Code Playgroud)
差异消失:
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
Run Code Online (Sandbox Code Playgroud)
您的解决方案是最快的,为什么不使用它呢?
for j in range(len(df)):
if df.n.iloc[j] % 2 == 0:
break
Run Code Online (Sandbox Code Playgroud)
让您迭代行并在满意时停止的选项是使用 DataFrame.iterrows ,它是 pandas 的行迭代器。
在这种情况下,你可以像这样实现它:
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
Run Code Online (Sandbox Code Playgroud)
然后,给定一个 DataFrame,例如:
df = pd.DataFrame({
'cats': [1,2,3,4],
'dogs': [2,4,6,8]
},
index=['Alice', 'Bob', 'Charlie', 'Eve'])
Run Code Online (Sandbox Code Playgroud)
您可以将其用作:
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))
Run Code Online (Sandbox Code Playgroud)
这会输出:
Charlie is the first person to have at least 7 pets.
They have 3 cats and 6 dogs!
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6302 次 |
| 最近记录: |