数据帧的 Pandas 函数的包装器,它从文件中读取 csv 作为数据帧 - df 未定义错误

问题描述

我经常编写处理数据帧的函数,并带有附加参数。 我想编写一个通用函数,我可以将这种函数包装起来,它将加载一个 .csv 文件作为数据帧,然后在函数中使用该数据帧。我想在某些情况下,还可以选择将输出另存为另一个 .csv 文件,从而为函数提供保存 .csv 的文件位置。

我遇到的问题是这不是一个装饰器函数,因为它包含额外的参数,即文件位置(用于加载 .csv,有时用于保存一个)。但是我也不想为我想要使用的每个函数编写唯一的函数在这种情况下,我只需将包含函数的所有参数传递给包装函数)。

我目前的尝试如下。我在 jupyter notebook 中运行它,所以它只是将 .csv 保存在主目录中并从那里加载。

import pandas as pd

a=[1,2,3,4]
b=[5,7,2]
testdf=pd.DataFrame(list(zip(a,b)),columns=['A','B'])

file_in_location='test.csv'
testdf.to_csv(file_in_location)

def open_file_and_run_wrapper(func,file_in_location,file_out_location='',save_output=False,delimiter=','):
    '''
    Function that opens a file as a dataframe and runs it through the given function
    '''
    if save_output==True:
        if file_out_location=='':
            # raise exception
            print('error: must have file output location')

    df=pd.read_csv(file_in_location,delimiter=delimiter)

    if save_output==True:
        df.to_csv(file_out_location,delimiter=delimiter)

    return func(df=df,*args,**kwargs)

def df_function(df,add_colname,value):
    df[add_colname]=value
    return df

open_file_and_run_wrapper(
    df_function(df,'C',4),)

这会返回以下错误

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-3-d174cd4d8bbc> in <module>
     29 
     30 open_file_and_run_wrapper(
---> 31     df_function(df,32     file_in_location,33 )

NameError: name 'df' is not defined

这并不奇怪,因为在我开始运行此函数时未定义数据帧。但是,它将由包装函数定义。 如何创建允许附加参数的通用包装器/装饰器函数

解决方法

以下是编写(和调用)包装器的方法:

# notice the additional *args and **kwargs
def open_file_and_run_wrapper(func,file_in_location,*args,file_out_location='',save_output=False,delimiter=',',**kwargs):
    '''
    Function that opens a file as a dataframe and runs it through the given function
    '''
    if save_output==True:
        if file_out_location=='':
            # raise exception
            print('error: must have file output location')

    df=pd.read_csv(file_in_location,delimiter=delimiter)

    if save_output==True:
        df.to_csv(file_out_location,delimiter=delimiter)

    # note how we pass the additional parameters
    # in `df_function` `df` is not a keyword argument
    # we call it as such
    return func(df,**kwargs)

def df_function(df,add_colname,value):
    df[add_colname]=value
    return df

现在,我们可以使用附加参数作为关键字参数调用包装器

open_file_and_run_wrapper(
    df_function,add_colname='C',value=4
)

或者我们也可以使用位置参数调用,但这会降低可读性

open_file_and_run_wrapper(
    df_function,'C',4       # positional arguments here
)

输出:

   Unnamed: 0  A  B  C
0           0  1  5  4
1           1  2  3  4
2           2  3  7  4
3           3  4  2  4
,

您可以像这样处理它,将函数作为对象传递,然后将位置参数和关键字参数作为类似列表和字典的形式传递。它看起来像这样:

def open_file_and_run_wrapper(
    func,func_args=[],func_kwargs={},file_out_location=None,delimiter=",",):
    """
    Function that opens a file as a dataframe and runs it through the given function
    """

    df = pd.read_csv(file_in_location,delimiter=delimiter)
    processed_df = func(df,*func_args,**func_kwargs)

    if file_out_location is not None:
        processed_df.to_csv(file_out_location,delimiter=delimiter)

    return processed_df


def df_function(df,value):
    df[add_colname] = value
    return df


open_file_and_run_wrapper(
    df_function,func_args=["C"],func_kwargs={"value": 5}
)

我对您的代码进行了一些更改,因此希望我没有更改您所期望的。

  • func_args 接受一个列表或元组(实际上是任何序列),然后作为位置参数传递给函数
  • func_kwargs 接受一个类似字典的参数并作为关键字参数传递给函数
  • 删除了save_output,赞成检查file_out_location是否存在以保存函数的输出(如果没有提供file_out_location,则不会将输出保存为文件)。立>
  • 将调用移至 to_csv 以保存新创建的数据帧,而不是保存从文件中读取的相同数据帧
,

你想要的是一个对象,而不是一个函数

class DataWrapper:

    def run(self,df):
        raise NotImplementedError

    def open_and_run(self,'):
        df = pd.read_csv(file_in_location,delimiter=delimiter)
        return self.run(df)

    def open_run_and_save(self,file_out_location,'):
        df_result = self.open_and_run(file_in_location,delimiter)
        df_result.to_csv(file_out_location,delimiter=delimiter)

您的包装函数将在 run 方法中实现,参数将在初始化程序中传递

class AddConstantColumnWrapper(DataWrapper):

    def __init__(self,colname,value):
        super().__init__()
        self.colname = colname
        self.value = value
 
    def run(self,df):
        df[self.colname] = self.value
        return df

然后你可以调用对象来执行你需要的

wrapper = AddConstantColumnWrapper('C',4)
df_result = wrapper.open_and_run(file_in_location)

将参数字典作为参数传递通常表示需要面向对象