正确的数据处理脚本布局,所有合并,删除,聚合,重命名都易于跟踪和显示

问题描述 投票:2回答:1

我目前有一个长脚本,其目标是:提取多个csv表数据,将它们合并为一个,同时沿途执行各种计算,然后输出最终的csv表。

我本来就是这种布局的(请参阅布局A),但是发现这使它不得不查看要添加或合并的列,因为清洁和操作方法列在所有内容的下方,因此您必须上下移动查看表如何更改的文件。这是一种尝试,遵循我所读过的全部“保持事物模块化和小的方法”:

import pandas as pd
#...

SOME_MAPPER = {'a':1, 'b':2, ...}
COLUMNS_RENAMER = {'new_col1': 'aaa', ...}

def main():
    df1 = clean_table_1('table1.csv')
    df2 = clean_table_2('table2.csv')
    df3 = clean_table_3('table3.csv')

    df = pd.merge(df1, df2, on='col_a')
    df['new_col1'] = df.apply(lambda r: some_operation(r['x'], r['y'], r['z']), axis=1)
    df['new_col2'] = df['new_col1'].map(SOME_MAPPER)

    df = pd.merge(df, df3, on='new_col2')
    df['new_col3'] = df['something']+df['new_col2']
    df['new_col4'] = df.apply(lambda r: some_other_operation(r['a'], r['b']), axis=1)   

    df = df.rename(columns=COLUMNS_RENAMER)
    return df

def some_operation(x,y,z):
    #<calculations for performing on table column>

def some_other_operation(a,b):
    #<some calculation>

def clean_table_1(fn_1):
    df = pd.read_csv(fn_1)
    df['some_col1'] = 400
    def do_operations_unique_to_table1(df):
        #<operations>
        return df
    df = do_operations_unique_to_table1(df)

    return df

def clean_table_2(fn_2):
    #<similar to clean_table_1>

def clean_table_3(fn_3):
    #<similar to clean_table_1>

if __name__=='__main__':
    main()

我的下一个倾向是将所有功能与主脚本内联,因此显而易见,这是在做什么(请参见布局B)。这使您更容易看到正在执行的操作的线性度,但同时也使操作变得有些混乱,因此您不能只是快速阅读主函数来获得所有正在执行的操作的“概述”。

# LAYOUT B
import pandas as pd
#...

SOME_MAPPER = {'a':1, 'b':2, ...}
COLUMNS_RENAMER = {'new_col1': 'aaa', ...}

def main():
    def clean_table_1(fn_1):
        df = pd.read_csv(fn_1)
        df['some_col1'] = 400
        def do_operations_unique_to_table1(df):
            #<operations>
            return df
        df = do_operations_unique_to_table1(df)

    df1 = clean_table_1('table1.csv')


    def clean_table_2(fn_2):
        #<similar to clean_table_1>

    df2 = clean_table_2('table2.csv')


    def clean_table_3(fn_3):
        #<similar to clean_table_1>

    df3 = clean_table_3('table3.csv')

    df = pd.merge(df1, df2, on='col_a')

    def some_operation(x,y,z):
        #<calculations for performing on table column>

    df['new_col1'] = df.apply(lambda r: some_operation(r['x'], r['y'], r['z']), axis=1)
    df['new_col2'] = df['new_col1'].map(SOME_MAPPER)

    df = pd.merge(df, df3, on='new_col2')

    def some_other_operation(a,b):
        #<some calculation>

    df['new_col3'] = df['something']+df['new_col2']
    df['new_col4'] = df.apply(lambda r: some_other_operation(r['a'], r['b']), axis=1)   

    df = df.rename(columns=COLUMNS_RENAMER)
    return df

if __name__=='__main__':
    main()

所以我想,为什么还要具有这些功能;如果它们都处于同一级别,可能会更容易遵循,就像这样的脚本(LAYOUT C):

# LAYOUT C
import pandas as pd
#...

SOME_MAPPER = {'a':1, 'b':2, ...}
COLUMNS_RENAMER = {'new_col1': 'aaa', ...}

def main():

    df1 = pd.read_csv('table1.csv)
    df1['some_col1'] = 400
    df1 = #<operations on df1>  

    df2 = pd.read_csv('table2.csv)
    df2['some_col2'] = 200
    df2 = #<operations on df2>  

    df3 = pd.read_csv('table3.csv)
    df3['some_col3'] = 800
    df3 = #<operations on df3>  

    df = pd.merge(df1, df2, on='col_a')

    def some_operation(x,y,z):
        #<calculations for performing on table column>

    df['new_col1'] = df.apply(lambda r: some_operation(r['x'], r['y'], r['z']), axis=1)
    df['new_col2'] = df['new_col1'].map(SOME_MAPPER)

    df = pd.merge(df, df3, on='new_col2')

    def some_other_operation(a,b):
        #<some calculation>

    df['new_col3'] = df['something']+df['new_col2']
    df['new_col4'] = df.apply(lambda r: some_other_operation(r['a'], r['b']), axis=1)   

    df = df.rename(columns=COLUMNS_RENAMER)
    return df

if __name__=='__main__':
    main()

问题的症结在于在清楚地记录哪些列正在更新,更改,删除,重命名,合并等之间找到平衡,同时仍然保持足够的模块化以适应“干净代码”的范式。

而且,实际上,此脚本和其他脚本要更长得多,并且有更多的表被合并到混合表中,因此这很快就成为一长串操作。我应该将操作分解为较小的文件并输出中间文件,还是只是要求引入错误?还可以查看沿途所做的所有假设以及它们如何影响最终状态的数据,而不必在文件之间跳转或向上滚动,向下滚动等,以便将数据从A跟踪到B,如果有道理。

[如果有人对如何最好地编写这些类型的数据清理/操作脚本有见解,我希望听到他们。

python python-3.x database pandas dataframe
1个回答
0
投票

这是一个非常主观的话题,但这是我的典型做法/备注/提示:

  • 尽可能优化调试/开发时间并简化操作
  • 将流程拆分成几个脚本(例如,分别为每个表下载,预处理,...,因此,要单独准备合并每个表的内容)]]
  • 尝试在脚本内保持相同的操作顺序(例如,类型更正,填充na,缩放,新列,删除列)
  • 对于每个纠缠脚本,都有加载开始和保存结束
  • 保存至泡菜(避免将日期另存为字符串之类的问题)和小型csv(以便在python之外轻松预览结果)
  • 使用“集成点”作为数据,您可以轻松地组合不同的技术(注意:在这种情况下,通常不使用pickle作为输出,而是使用csv /其他数据格式)
  • 每个脚本都有明确定义的输入/输出,可以单独进行测试和开发,我也对数据框形状使用断言
  • 用于可视化/ EDA的脚本使用了纠缠脚本中的数据,但是它们从来都不是纠缠脚本的一部分,通常它们也是瓶颈
  • 例如组合脚本bash,如果你想简单
  • 保持脚本长度在1页以下*
  • *如果我在将函数封装到函数之前有冗长而复杂的代码,我会检查它是否可以更简单地完成(80%是),但是您需要了解有关熊猫的更多信息,但是您学习了一些新知识,熊猫文档通常会更好,您的代码更具声明性和惯用性
  • *如果没有简单的方法可以解决,并且您在许多地方都使用了此函数,请将其放入utils.py,在文档字符串中放入示例>>> f(input)输出以及有关此函数的一些原理]
  • *如果在许多项目中使用了函数,则值得进行https://github.com/twopirllc/pandas-ta这样的熊猫扩展>
  • *如果我有很多列,我会考虑很多有关层次结构和分组的信息,并将其保存在每个表的单独文件中,现在它只是py文件,但是我开始考虑使用yaml和记录表结构的方法
  • 遵守一种惯例,例如我完全不使用inplace=True
  • 数据框上的链操作*
  • *如果您对子链结果有一个很好的有意义的名称,可以在其他地方使用,则可能是拆分脚本的好地方
  • 删除main函数,如果您按照上述规则保留脚本,则df全局变量中没有任何问题
  • 当我从csv中读取内容时,我总是检查可以直接使用read_csv参数执行的操作,例如解析日期
  • 明显标记为“ TEMPORARY HACKS”,从长期来看,它们会导致意想不到的副作用
© www.soinside.com 2019 - 2024. All rights reserved.