【问题标题】:Pandas and for-loops: vectorizing operations and speeding up a python code [closed]Pandas 和 for 循环:矢量化操作和加速 python 代码 [关闭]
【发布时间】:2021-04-10 14:30:02
【问题描述】:

动机:

我经常面临需要检查数据框的值是否正确的任务。基于它,我可以使用基于第一个数据帧的 True/False 值创建另一个数据帧。我在下面演示了此类任务的示例。我有一个带有日志的数据框,我需要检查每个值是否对应于某个范围。如果它在范围内,则算法在新数据帧的相应单元格中返回 True。本质上,该算法可用于数据帧中的异常检测。在简单的示例中,使用 apply 方法 和内置 NumPy 函数非常容易。 Bellow 是一个重要的例子,我正在努力寻找快速实现。

输入:

我试图了解如何通过将 for 循环转换为矢量化操作来使代码运行得更快。

期望的输出:

基于这个数据框,我想创建一个新的数据框,其中每个值都是 TrueFalse 基于一些特定规则

数据:

数据以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 循环执行相同操作。

我的问题:

是否可以加速和矢量化代码的其他部分?

【问题讨论】:

标签: python pandas for-loop time-complexity vectorization


【解决方案1】:

您可以阅读大量关于使用 .apply() 或 numpy where() 加快代码速度的帖子。当您考虑iterrows() 时,请查看其他方法。我刚刚采用了您的一个 iterrows 函数并将其包装到一个函数中,以便针对其他方法轻松计时(使用 ipython)。

data 只是您在上面发布的 json。

df = pd.DataFrame(data) # 12 rows based on your data
df_results =  pd.DataFrame() # create blank dataframe

def test_iter(): #your code inside a function 
    for index, row in df.iterrows():
        if row['UE'] >= 1 and row['UE'] <= 500:
            df_results['UE'][index] = True        
        else:
            df_results['UE'][index] = False 
%timeit test_iter()
1.42 ms ± 35.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df_results['UE'] = df.apply(lambda x: True if (x['UE'] >=1) & (x['UE'] <=500) else False, axis=1)
538 µs ± 20.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df_results['UE'] = np.where((df['UE'] >=1) & (df['UE'] <=500), True, False)
295 µs ± 12 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

您可以将其他迭代重新编码为类似的功能。正如评论者所说,如果您在重新编码后遇到特定问题,请以非常简洁的方式写下该问题,并提供您在这篇文章中的详细信息。

【讨论】:

    猜你喜欢
    • 2015-05-17
    • 1970-01-01
    • 2020-06-24
    • 1970-01-01
    • 2021-03-04
    • 1970-01-01
    • 2017-02-01
    • 2018-10-15
    • 2019-12-02
    相关资源
    最近更新 更多