برنامه نویسی

ساخت مجموعه داده تجزیه و تحلیل فایل با پایتون

سال گذشته راه هایی برای تجزیه و تحلیل تاریخچه و ساختار کد به صورت تصویری ابداع کردم، اما چیز زیادی از آن را مستند نکردم تا با جامعه به اشتراک بگذارم. این مقاله فرآیندی را که من برای ساختن یک فایل CSV استفاده کردم که شامل مسیر، پسوند، پروژه و تعداد خط کد در همه فایل‌های منبع پروژه است، بررسی می‌کند.

توجه: اگر ایده دریافت داده از یک مخزن git برای شما جالب است، مقاله من در مورد استفاده از PyDriller برای استخراج اطلاعات git را بررسی کنید.

اگر بخواهید فایل های کدی را که روی هارد دیسک خود دارید به سرعت تجزیه و تحلیل کنید و یک مجموعه داده برای تجزیه و تحلیل بیشتر و تجسم داده ها بسازید، این کد موثر است.

بسیاری از کدهای پایتون که من به اشتراک می گذارم، به شدت از پاسخ های مختلف StackOverflow با ترفندها و تغییرات خودم وام گرفته شده است. من سعی کردم به همه منابعی که استفاده کردم اعتبار بدهم، اما این کد یک سال پیش ساخته شده است و ممکن است یکی دو منبع مفید را فراموش کرده باشم.

وابستگی ها

من این کد را با استفاده از پایتون 3.8 نوشتم که روی نوت بوک Jupyter با پایتون 3.8 اجرا می شود. با این حال، نوت بوک های Jupyter برای عملکرد این کد مورد نیاز نیستند.

کد متکی بر os کتابخانه، که استاندارد پایتون است، و همچنین pandas کتابخانه ای برای تجزیه و تحلیل داده های جدولی

import pandas as pd
import os
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

کد

آنچه در ادامه می‌آید، تفکیک متوالی توابع مورد نیاز برای انجام وظیفه استخراج داده‌ها از کد است.

شناسایی فایل ها

تجزیه و تحلیل فایل ها بدون تعیین اینکه چه فایل هایی باید تجزیه و تحلیل شوند انجام نمی شود.

من این کار را با استفاده از os کتابخانه listdir متد به همراه چندین روش مسیر و دایرکتوری دیگر برای پیمایش بازگشتی درخت دایرکتوری که از یک دایرکتوری مشخص شروع می شود.

من همچنین عمداً دایرکتوری هایی را نادیده می گیرم که حاوی تعداد زیادی فایل نادیده گرفته می شوند، مانند دایرکتوری های گزارش، دایرکتوری git. و دایرکتوری های مورد استفاده در محیط توسعه.

# Some directories should be ignored as their output is not helpful
ignored_directories = ['.git', '.vs', 'obj', 'ndependout', 'bin', 'debug']

def get_file_list(dir_path, paths=None):
    """
    Gets a list of files in this this directory and all contained directories
    """

    files = list()
    contents = os.listdir(dir_path)

    for entry in contents:
        path = os.path.join(dir_path, entry)
        if os.path.isdir(path):
            # Ignore build and reporting directories
            if entry.lower() in ignored_directories:
                continue

            # Maintain an accurate, but separate, hierarchy array
            if paths is None:
                p = [entry]
            else:
                p = paths[:]
                p.append(entry)

            files = files + get_file_list(path, p)
        else:
            files.append((path, paths))

    return files
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تعیین اینکه آیا یک فایل حاوی کد منبع است یا خیر

به منظور تعیین اینکه آیا یک فایل یک فایل کد منبع است و باید در نتایج گنجانده شود، رویکرد ساده‌ای را انتخاب کردم که در آن فقط به فایل‌هایی با پسوندهای خاص توجه می‌کنم.

این کد می تواند از فهرست جامع تری یا حتی شاید برخی تحلیل های پیشرفته از محتویات فایل ها بهره مند شود. با این حال، نگاه کردن به برنامه افزودنی برای اهداف من خوب بود و باید برای اکثر خوانندگان به خوبی خدمت کند.

# Common source file extensions. This list is very incomplete. Intentionally not including JSON / XML
source_extensions = [
    '.cs', 
    '.vb', 
    '.java', 
    '.r', 
    '.agc', 
    '.fs', 
    '.js', 
    '.cpp', 
    '.go', 
    '.aspx', 
    '.jsp', 
    '.do', 
    '.php', 
    '.ipynb', 
    '.sh', 
    '.html', 
    '.lua', 
    '.css'
    ]

def is_source_file(file_label):
    """
    Defines what a source file is.
    """
    file, _ = file_label
    _, ext = os.path.splitext(file)

    return ext.lower() in source_extensions
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

شمارش خطوط کد

پس از آن، باید بتوانم به سرعت طول یک فایل را تعیین کنم.

من از پاسخ Stack Overflow برای تعیین نحوه خواندن موثر تعداد خطوط در یک فایل با استفاده از کد زیر استفاده کردم:

def count_lines(path):
    """
    Reads the file at the specified path and returns the number of lines in that file
    """

    def _make_gen(reader):
        b = reader(2 ** 16)
        while b:
            yield b
            b = reader(2 ** 16)

    with open(path, "rb") as f:
        count = sum(buf.count(b"\n") for buf in _make_gen(f.raw.read))

    return count
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این کد از یک خواننده بافر برای خواندن سریع بافر فایل و برگرداندن تعداد کل استفاده می کند.

Geeks for Geeks پیاده سازی آهسته تر اما قابل درک تر را برای علاقه مندان ارائه می دهد:

def count_lines_simple(path):
    with open(path, 'r') as fp:
        return sum(1 for line in fp)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

دریافت معیارهای فایل

پس از این، من تابعی ساختم که می توانست چندین فایل را بگیرد و لیستی از اشیاء جزئیات را برای همه فایل های موجود در لیست ایجاد کند.

هر شی جزئیات فایل شامل:

  • دایرکتوری ریشه ای که تجزیه و تحلیل در آن آغاز شد
  • مسیر کامل فایل
  • پروژه ای که فایل در آن قرار دارد (دایرکتوری پایه در پروژه ای که فایل در داخل آن قرار دارد)
  • مسیر فایل نسبت به
  • نام فایل
  • پسوند فایل
  • تعداد خطوط آن فایل

این کار با حلقه زدن روی همه فایل‌ها و پوشه‌ها در پارامتر فایل‌های ورودی، و سپس شمارش خطوط فایل‌ها با استفاده از count_lines تابع، شمارش هر پوشه، و ساخت اطلاعات مسیر در هر فایل.

هنگامی که همه اطلاعات شناخته شدند، یک شی جزئیات فایل ایجاد می شود و به لیست حاصل اضافه می شود که پس از تجزیه و تحلیل همه فایل ها برگردانده می شود.

def get_file_metrics(files, root):
    """
    This function gets all metrics for the files and returns them as a list of file detail objects
    """
    results = []

    for file, folders in files:
        lines = count_lines(file) # Slow as it actually reads the file
        _, filename = os.path.split(file)
        _, ext = os.path.splitext(filename)

        fullpath = ''

        if folders != None and len(folders) > 0:
            project = folders[0]
            for folder in folders[1:]:
                if len(fullpath) > 0:
                    fullpath += "https://dev.to/"
                fullpath += folder
        else:
            project = ''

        if len(fullpath) <= 0:
            fullpath = '.'

        id = root + "https://dev.to/" + project + "https://dev.to/" + fullpath + "https://dev.to/" + filename

        file_details = {
                        'fullpath': id,
                        'root': root,
                        'project': project,
                        'path': fullpath,
                        'filename': filename,
                        'ext': ext,
                        'lines': lines,
                        }
        results.append(file_details)

    return results
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

همه اش را بگذار کنار هم

در نهایت، من یک تابع مرکزی برای شروع تجزیه و تحلیل منبع در یک مسیر مشخص ساختم:

def get_source_file_metrics(path):
    """
    This function gets all source files and metrics associated with them from a given path
    """
    source_files = filter(is_source_file, get_file_list(path))
    return get_file_metrics(list(source_files), path)    
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این تابع به سایر توابع فراخوانی می کند تا لیستی از جزئیات فایل را ایجاد کند.

می‌توانم این روش را با اعلام فهرستی از دایرکتوری‌هایی که به آن‌ها علاقه‌مندم، و سپس فراخوانی آن در آن فهرست‌ها، فراخوانی کنم.

# Paths should be one or more directories of interest
paths = ['C:/Dev/MachineLearning/src', 'C:/Dev/MachineLearning/test']

# Pull Source file metrics
files = []
for path in paths:
    files.extend(get_source_file_metrics(path))
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در اینجا من تمام پروژه های موجود در آن را تجزیه و تحلیل می کنم src و test دایرکتوری های مخزن ML.NET من انتخاب کردم که اینها را به عنوان مسیرهای مجزا بگنجانم زیرا آنها دو گروه بندی متفاوت از پروژه ها را در این مخزن نشان می دهند.

ذخیره نتایج

از وقتی که files لیست پر شده است، به راحتی می توان از آن برای ایجاد یک Pandas DataFrame برای تجزیه و تحلیل جدولی استفاده کرد. DataFrame همچنین یک روش آسان برای سریال کردن داده ها به یک فایل CSV مانند شکل زیر ارائه می دهد:

# Grab the file metrics for source files and put them into a data frame
df = pd.DataFrame(files)
df = df.sort_values('lines', ascending=False)

# Write to a file for other analyses processes
df.to_csv('filesizes.csv')
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در نهایت، می‌توانیم 5 ردیف اول آن مجموعه داده را با استفاده از پیش‌نمایش نماییم df.head() روش.

در مجموعه داده نمونه من، این نتایج زیر را ایجاد می کند:

مراحل بعدی

اکنون که داده های سلسله مراتبی را در یک فایل CSV ذخیره کرده اید، و من یک روش جداگانه برای دریافت اطلاعات از یک مخزن git نشان داده ام، مراحل بعدی شامل ادغام این داده ها با یکدیگر برای تجزیه و تحلیل و تجسم است.

منتظر به روز رسانی های آینده در این مجموعه در Visualizing Code باشید.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا