【问题标题】:Elegant way to refer to files in data science project在数据科学项目中引用文件的优雅方式
【发布时间】:2018-11-30 15:55:18
【问题描述】:

最近几天我一直在学习如何构建数据科学项目,以使其保持简单、可重用和 Python 风格。坚持this guideline 我已经创建了my_project。你可以在下面看到它的结构。

├── README.md          
├── data
│   ├── processed          <-- data files
│   └── raw                            
├── notebooks  
|   └── notebook_1                             
├── setup.py              
|
├── settings.py            <-- settings file   
└── src                
    ├── __init__.py    
    │
    └── data           
        └── get_data.py    <-- script  

我定义了一个从.data/processed 加载数据的函数。我想在其他脚本以及 .notebooks 中的 jupyter 笔记本中使用此功能。

def data_sample(code=None):
    df = pd.read_parquet('../../data/processed/my_data')
    if not code:
        code = random.choice(df.code.unique())
    df = df[df.code == code].sort_values('Date')
    return df

显然,除非我直接在定义它的脚本中运行它,否则这个函数在任何地方都不起作用。 我的想法是在我声明的地方创建settings.py

from os.path import join, dirname

DATA_DIR = join(dirname(__file__), 'data', 'processed')

所以现在我可以写了:

from my_project import settings
import os

def data_sample(code=None):
    file_path = os.path.join(settings.DATA_DIR, 'my_data')
    df = pd.read_parquet(file_path)
    if not code:
        code = random.choice(df.code.unique())
    df = df[df.code == code].sort_values('Date')
    return df

问题:

  1. 以这种方式引用文件是常见的做法吗? settings.DATA_DIR 看起来有点丑。

  2. 这到底是应该如何使用settings.py?它应该放在这个目录中吗?我在repo .samr/settings.py 下的不同位置看到它

我知道可能没有“一个正确的答案”,我只是试图找到处理这些事情的合乎逻辑、优雅的方式。

【问题讨论】:

  • 请不要将问题帖用作放置 cmets 的地方。 Stack Overflow 不像论坛。我们希望构建问题及其答案,以便未来的访问者可以快速查看他们是否有同样的问题,并且答案也适用于他们的情况。他们不会关心这个过程最终是如何得出这些答案的;关于答案的对话应该留给 cmets。
  • 罗杰,我会在以后的问题中坚持下去。

标签: python python-3.x setuptools directory-structure


【解决方案1】:

不,只有在使用 Django 时才使用 settings.py。至于以这种方式引用数据目录,这取决于您是否希望用户能够更改此值。设置更改值的方式需要编辑 settings.py 文件。如果您希望用户拥有默认值,但也能够在他们使用您的函数时轻松更改它,只需内联创建基本路径值并将其设置为 def data_sample(..., datadir=filepath):.

【讨论】:

    【解决方案2】:

    只要您没有提交大量数据,并且清楚地区分不受控制的外部世界的快照和您自己的派生数据(代码 + raw)== 状态。有时使用 append-only-ish raw 并考虑符号链接步骤(如 raw/interesting_source/2018.csv.gz -&gt; raw_appendonly/interesting_source/2018.csv.gz.20180401T12:34:01 或类似模式)来建立“使用最新”输入结构有时很有用。尝试清楚地分离可能需要根据 env 更改的配置设置(my_project/__init__.pyconfig.pysettings.py 或其他)(想象将 fs 换成 blobstore 或其他)。 setup.py 通常位于顶级 my_project/setup.py 以及与 my_project/my_project 中的可运行内容(不是文档,示例不确定)相关的任何内容。在一个地方定义一个_mydir = os.path.dirname(os.path.realpath(__file__)) (config.py) 并依靠它来避免重构的痛苦。

    【讨论】:

      【解决方案3】:

      我正在维护一个基于 DataDriven Cookiecutter 的经济数据项目,我觉得这是一个很棒的模板。

      分离数据文件夹和代码对我来说似乎是一个优势,允许将您的工作视为定向的转换流('DAG'),从不可变的初始数据开始,到中间和最终结果。

      最初,我查看了pkg_resources,但拒绝使用它(语法冗长且对创建包的理解不足),而是使用自己的帮助函数/类来导航目录。

      本质上,助手做两件事

      1。在常量中保留项目根文件夹和其他一些路径:

      # shorter version 
      ROOT = Path(__file__).parents[3]
      
      # longer version
      def find_repo_root():
          """Returns root folder for repository.
          Current file is assumed to be:
              <repo_root>/src/kep/helper/<this file>.py
          """
          levels_up = 3
          return Path(__file__).parents[levels_up]
      
      ROOT = find_repo_root()
      DATA_FOLDER = ROOT / 'data' 
      UNPACK_RAR_EXE = str(ROOT / 'bin' / 'UnRAR.exe')
      XL_PATH = str(ROOT / 'output' / 'kep.xlsx')
      

      这类似于您对DATA_DIR 所做的事情。一个可能的弱点是我在这里 手动硬编码帮助文件相对于项目根目录的相对位置。如果帮助文件位置移动,则需要进行调整。但是,嘿,这与 Django 中的做法相同。

      2。允许访问rawinterimprocessed 文件夹中的特定数据。

      这可以是一个简单的函数,通过文件夹中的文件名返回完整路径,例如:

      def interim(filename):
          """Return path for *filename* in 'data/interim folder'."""
          return str(ROOT / 'data' / 'interim' / filename)
      

      在我的项目中,我有 interimprocessed 目录的年月子文件夹,我按年、月甚至频率来寻址数据。对于这个数据结构,我有 InterimCSVProcessedCSV 类提供参考特定路径,例如:

      from . helper import ProcessedCSV, InterimCSV
       # somewhere in code
       csv_text = InterimCSV(self.year, self.month).text()
       # later in code
       path = ProcessedCSV(2018,4).path(freq='q')
      

      助手is here 的代码。此外,如果这些类不存在,则创建子文件夹(我希望这个用于临时目录中的 unittest),并且有一些方法可以检查文件是否存在并读取它们的内容。

      在您的示例中,您可以轻松地将根目录固定在setting.py, 但我认为你可以在抽象数据方面更进一步。

      目前data_sample() 混合了文件访问和数据转换,这不是一个好兆头,而且还使用全局名称,这是函数的另一个坏兆头。我建议你可以考虑以下:

      # keep this in setting.py
      def processed(filename):
         return os.path.join(DATA_DIR, filename)
      
      # this works on a dataframe - your argument is a dataframe,
      # and you return a dataframe
      def transform_sample(df: pd.DataFrame, code=None) -> pd.DataFrame:
          # FIXME: what is `code`?
          if not code:
              code = random.choice(df.code.unique())
          return df[df.code == code].sort_values('Date')
      
      # make a small but elegant pipeline of data transfomation
      file_path = processed('my_data')
      df0 = pd.read_parquet(file_path)
      df = transform_sample(df0)
      

      【讨论】:

        【解决方案4】:

        您可以使用open() 打开一个文件并将其保存在一个变量中,然后在您希望引用该文件的任何地方继续使用该变量。

        with open('Test.txt','r') as f:
        

        f=open('Test.txt','r')
        

        并使用f 来引用该文件。 如果您希望文件既可读又可写,可以使用r+ 代替r

        【讨论】:

          猜你喜欢
          • 2014-09-01
          • 2019-08-05
          • 1970-01-01
          • 2018-01-12
          • 2012-08-11
          • 2015-04-18
          • 1970-01-01
          • 2011-10-29
          • 2019-10-12
          相关资源
          最近更新 更多