1

我从导入的 CSV 文件 (~500MB) 中提取 4 列用于拟合scikit-learn回归模型。

似乎这个用于提取的函数非常慢。我今天刚学了python,有什么建议可以加快这个函数的速度吗?

可以使用多线程/核心吗?我的系统有 4 个核心。

def splitData(jobs):
    salaries = [jobs[i]['salaryNormalized'] for i, v in enumerate(jobs)]
    descriptions = [jobs[i]['description'] + jobs[i]['normalizedLocation'] + jobs[i]['category'] for i, v in enumerate(jobs)]
    titles = [jobs[i]['title'] for i, v in enumerate(jobs)]

    return salaries, descriptions, titles

打印类型(作业)

<type 'list'>

打印作业[:1]

[{'category': 'Engineering Jobs', 'salaryRaw': '20000 - 30000/annum 20-30K', 'rawLocation': 'Dorking, Surrey, Surrey', 'description': 'Engineering Systems Analyst Dorking Surrey Salary ****K Our client is located in Dorking, Surrey and are looking for Engineering Systems Analyst our client provides specialist software development Keywords Mathematical Modelling, Risk Analysis, System Modelling, Optimisation, MISER, PIONEEER Engineering Systems Analyst Dorking Surrey Salary ****K', 'title': 'Engineering Systems Analyst', 'sourceName': 'cv-library.co.uk', 'company': 'Gregory Martin International', 'contractTime': 'permanent', 'normalizedLocation': 'Dorking', 'contractType': '', 'id': '12612628', 'salaryNormalized': '25000'}]


def loadData(filePath):
    reader = csv.reader( open(filePath) )
    rows = []

    for i, row in enumerate(reader):
        categories = ["id", "title", "description", "rawLocation", "normalizedLocation",
                        "contractType", "contractTime", "company", "category",
                        "salaryRaw", "salaryNormalized","sourceName"]

        # Skip header row
        if i != 0: 
            rows.append( dict(zip(categories, row)) )

    return rows



def splitData(jobs):
    salaries = []
    descriptions = []
    titles = []

    for i in xrange(len(jobs)):
        salaries.append( jobs[i]['salaryNormalized'] )
        descriptions.append( jobs[i]['description'] + jobs[i]['normalizedLocation'] + jobs[i]['category'] )
        titles.append( jobs[i]['title'] )

    return salaries, descriptions, titles



def fit(salaries, descriptions, titles):
    #Vectorize
    vect = TfidfVectorizer()
    vect2 = TfidfVectorizer()
    descriptions = vect.fit_transform(descriptions)
    titles = vect2.fit_transform(titles)

    #Fit
    X = hstack((descriptions, titles))
    y = [ np.log(float(salaries[i])) for i, v in enumerate(salaries) ]

    rr = Ridge(alpha=0.035)
    rr.fit(X, y)

    return vect, vect2, rr, X, y



jobs = loadData( paths['train_data_path'] )
salaries, descriptions, titles = splitData(jobs)
vect, vect2, rr, X_train, y_train = fit(salaries, descriptions, titles)
4

4 回答 4

2

我发现您的代码存在多个问题,直接影响其性能。

  1. enumerate多次列出工作。您只能枚举一次,而是使用枚举列表(存储在变量中)。
  2. 您根本不使用枚举项中的值。您所需要的只是索引,您可以使用内置range函数轻松实现此目的。
  3. 每个列表都是以急切的方式生成的。发生的情况如下:第一个列表阻止程序的执行,并且需要一些时间才能完成。第二个和第三个列表也会发生同样的事情,其中​​的计算完全相同。

我会为您提供的是使用生成器,以便您以惰性方式处理数据。它的性能效率更高,并允许您随时随地提取数据。

def splitData(jobs):
    for job in jobs:
        yield job['salaryNormalized'], job['description'] + job['normalizedLocation'] + job['category'], job['title']
于 2013-05-01T20:16:03.893 回答
1

如果我错了,请纠正我,但它似乎也TfidVectorizer接受迭代器(例如生成器表达式)。这有助于防止在内存中拥有大量数据的多个副本,这可能是它变慢的原因。或者,确保它可以直接处理文件。可以将 csv 转换为单独的文件,然后TfidVectorizer直接将这些文件提供给这些文件,而无需以任何方式将它们保存在内存中。

编辑 1

现在您提供了更多代码,我可以更具体一点。

首先,请注意loadData做的比它需要的多;它复制了csv.DictReader. 如果我们使用它,我们会跳过类别名称的列表。使用另一种打开文件的语法,因为通过这种方式,它们会自动关闭。此外,一些名称已更改为更准确和 Pythonic(下划线样式)。

def data_from_file(filename):
    rows = []
    with open(filename) as f:
        reader = csv.DictReader(f)
        for row in reader:
            rows.append(row)
    return rows

我们现在可以改变这一点,这样我们就不会在内存中构建所有行的列表,而是在我们从文件中读取它之后一次返回一行。如果这看起来很神奇,只需阅读一点关于 Python 中的生成器的信息。

def data_from_file(path):
    with open(filename) as f:
        reader = csv.DictReader(f)
        for row in reader:
            yield row

现在让我们来看看splitData。我们可以像这样写得更干净:

def split_data(jobs):
    salaries = []
    descriptions = []
    titles = []

    for job in jobs:
        salaries.append(job['salaryNormalized'] )
        descriptions.append(job['description'] + job['normalizedLocation'] + 
                            job['category'])
        titles.append(job['title'])

    return salaries, descriptions, titles

但是我们又不想在内存中建立三个巨大的列表。一般来说,这个函数给我们三个不同的东西是不切实际的。所以把它分开:

def extract_salaries(jobs):
    for job in jobs:
        yield job['salaryNormalized']

等等。这有助于我们建立某种处理管道;每次我们从extract_salaries(data_from_file(filename))csv 的单行请求一个值时,都会读取并salary提取该值。下一次,第二行回馈第二行salary。没有必要为这个简单的案例创建函数。相反,您可以使用生成器表达式:

salaries = (job['salaryNormalized'] for job in data_from_file(filename))
descriptions = (job['description'] + job['normalizedLocation'] +
                job['category'] for job in data_from_file(filename))
titles = (job['title'] for job in data_from_file(filename))

您现在可以将这些生成器传递给fit,其中最重要的修改是:

y = [np.log(float(salary)) for salary in salaries]

您不能索引到迭代器(一次给您一个值的东西),所以您只是假设只要有更多,您就会得到一个,并用它做一些事情salarysalaries

最后,您将多次读取整个 csv 文件,但我不认为这会成为瓶颈。否则,需要进行更多的重组。

编辑 2

使用DictReader似乎有点慢。不知道为什么,但你可以坚持自己的实现(修改为生成器)或者更好,使用namedtuples:

def data_from_file(filename):
    with open(filename) as f:
        reader = csv.reader(f)
        header = reader.next()
        Job = namedtuple('Job', header)
        for row in reader:
            yield Job(*row)

然后使用点 ( job.salaryNormalized) 访问属性。但无论如何请注意,您可以从文件中获取列名列表;不要在代码中复制它。

毕竟,您当然可以决定在内存中保留一份文件副本。在这种情况下,请执行以下操作:

data = list(data_from_file(filename))
salaries = (job['salaryNormalized'] for job in data)

功能保持不变。调用list消耗整个生成器并将所有值存储在 a 中list

于 2013-05-01T21:00:20.180 回答
1

一个简单的加速是减少你的列表遍历。您可以构建一个生成器或生成器表达式,为单个字典返回元组,然后压缩生成的可迭代对象:

(salaries, descriptions, titles) = zip(*((j['salaryNormalized'], j['description'] + j['normalizedLocation'] + j['category'], j['title']) for j in jobs))

不幸的是,这仍然会创建三个相当大的内存列表——使用生成器表达式而不是列表推导至少应该可以防止它在压缩之前创建一个完整的三元素元组列表。

于 2013-05-01T20:31:56.817 回答
0

您根本不需要索引。只需使用in. 这节省了额外的元组列表的创建,并删除了间接级别;

salaries = [j['salaryNormalized'] for j in jobs]
descriptions = [j['description'] + j['normalizedLocation'] + j['category'] for j in jobs]
titles = [j['title'] for j in jobs]

这仍然对数据进行了 3 次迭代。

或者,您可以在一个列表理解中获取所有内容,将来自一项工作的相关数据组合在一个元组中;

data = [(j['salaryNormalized'], 
         j['description'] + j['normalizedLocation'] + j['category'],
         j['title']) for j in jobs]

把最好的留到最后; 为什么不直接从 CSV 文件填写列表,而不是先制作一个字典?

import csv

with open('data.csv', 'r') as df:
    reader = csv.reader(df)
    # I made up the row indices...
    data = [(row[1], row[3]+row[7]+row[6], row[2]) for row in reader]
于 2013-05-01T20:24:20.270 回答