【发布时间】:2021-04-10 14:30:02
【问题描述】:
动机:
我经常面临需要检查数据框的值是否正确的任务。基于它,我可以使用基于第一个数据帧的 True/False 值创建另一个数据帧。我在下面演示了此类任务的示例。我有一个带有日志的数据框,我需要检查每个值是否对应于某个范围。如果它在范围内,则算法在新数据帧的相应单元格中返回 True。本质上,该算法可用于数据帧中的异常检测。在简单的示例中,使用 apply 方法 和内置 NumPy 函数非常容易。 Bellow 是一个重要的例子,我正在努力寻找快速实现。
输入:
我试图了解如何通过将 for 循环转换为矢量化操作来使代码运行得更快。
期望的输出:
基于这个数据框,我想创建一个新的数据框,其中每个值都是 True 或 False 基于一些特定规则
数据:
数据以json格式呈现data.json:
[
{
"Med": "9/20/2020 8:50",
"KE": 1,
"SL": 154
},
{
"Med": "9/20/2020 8:50",
"KE": 2,
"SL": 123
},
{
"Med": "9/20/2020 8:50",
"KE": 3,
"SL": 132
},
{
"Med": "9/20/2020 8:57",
"KE": 1,
"SL": 141
},
{
"Med": "9/20/2020 8:57",
"KE": 8,
"SL": 151
},
{
"Med": "9/20/2020 8:57",
"KE": 2,
"SL": 155
},
{
"Med": "9/20/2020 9:12",
"KE": 1,
"SL": 151
},
{
"Med": "9/20/2020 9:12",
"KE": 5,
"SL": 154
},
{
"Med": "9/20/2020 9:12",
"KE": 3,
"SL": 144
},
{
"Med": "9/20/2020 9:20",
"KE": 1,
"SL": 134
},
{
"Med": "9/20/2020 9:20",
"KE": 4,
"SL": 155
},
{
"Med": "9/20/2020 9:20",
"KE": 3,
"SL": 153
}
]
我的实现:
我上传数据如下:
def upload_data(file):
df = pd.read_json(file)
df['Med'] = pd.to_Medtime(df['Med'], format="%Y-%d-%m %H:%M:%S")
df['EQE'] = np.nan
return df
df = upload_data('data.json')
接下来,我创建一个额外的行。我设法以矢量化的方式做到了:
df['EQE'] = (df['Med'] != df['Med'].shift()).cumsum()
最后我用结果创建数据框:
def create_df_with_reslts(df):
df_results = pd.DataFrame().reindex_like(df)
df_results['Pred'] = np.nan
return df_results
df_results = create_df_with_reslts(df)
现在我正在研究如何加速和矢量化代码的主要部分
def check_df(df, df_results):
# check Med format
Med_format = '%Y-%m-%d %H:%M:%S'
for index, row in df.iterrows():
Med_string = row['Med']
try:
df_results['Med'][index] = True
except ValueError:
df_results['Med'][index] = False
# checking that number of KEs is between 1 and 500
df_results['KE'] = np.where((df['KE'] >=1) & (df['KE'] <=500), True, False)
# finding bordes for EQE's spans
previous_row = df['Med'].astype(str)[0]
EQE_index = 0
EQE_list = []
for index, row in df.iterrows():
# for same EQE
if row['Med'] == previous_row:
EQE_index += 1
previous_row = row['Med']
# for next EQE in table
else:
EQE_list.append(EQE_index)
EQE_index = 1
previous_row = row['Med']
EQE_list.append(EQE_index)
# checking whether or not borders are correct
k=0
for i in range(len(EQE_list)):
if EQE_list[i] == 6 or EQE_list[i] == 8:
for j in range(EQE_list[i]):
df_results['EQE'][j+k] = True
else:
for j in range(EQE_list[i]):
df_results['EQE'][j+k] = False
j=EQE_list[i]
k=k+j
# Values of SL corresponds to uniform distribution with epsilon 10%
list_of_columns = ['SL']
for n in range(len(list_of_columns)):
# find the highest number for each EQE (EQE_MAX)
k=0
max_list = []
for i in range(len(EQE_list)):
X = df[list_of_columns[n]][k:k+EQE_list[i]+1]
max_list.append(X[X == X.max()].iloc[0])
k=k+EQE_list[i]
# check that each value in [max_list*10/100, max_list]
k=0
for i in range(len(EQE_list)):
for j in range(EQE_list[i]):
if float(df[list_of_columns[n]][j+k]) >= float(max_list[i])*90/100 and float(df[list_of_columns[n]][j+k]) <= float(max_list[i]):
df_results[list_of_columns[n]][j+k] = True
else:
df_results[list_of_columns[n]][j+k] = False
j=EQE_list[i]
k=k+j
# final results for each column
df_results['Pred'] = df_results.prod(axis=1).astype(bool)
# return max_list
return df_results
%timeit check_df(df, df_results)
# 19.5 ms ± 2.84 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
我将数据帧的迭代更改为 iterrows 的迭代:range(len(df)) 更改为 df.iterrows()。它只提供了约 20% 的加速。还有一个 for 循环通过矢量化操作 (np.where) 进行了更改。我不知道如何对其他 for 循环执行相同操作。
我的问题:
是否可以加速和矢量化代码的其他部分?
【问题讨论】:
-
这对于 StackOverflow 问题来说非常广泛。我将专注于您尝试矢量化的一个小节,并询问有关如何做到这一点的问题。我投票结束这个问题,因为它属于:codereview.stackexchange.com
-
@DavidErickson 虽然这可能是 CR 的主题,但在未来,请不要以 Code Review 站点的存在作为关闭问题的理由。评估请求并使用需要更多关注(就像我在这里所做的那样)、主要基于意见等原因。然后您可以向 OP 提及它如果是on-topic,可以在 Code Review 上发布。请参阅Does being on-topic at another Stack Exchange site automatically make a question off-topic for Stack Overflow?
标签: python pandas for-loop time-complexity vectorization