Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/327.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python中用于数千个大型表的外部联接_Python_Python 2.7_Numpy_Pandas - Fatal编程技术网

Python中用于数千个大型表的外部联接

Python中用于数千个大型表的外部联接,python,python-2.7,numpy,pandas,Python,Python 2.7,Numpy,Pandas,所以,我有大约4000个CSV文件,我需要加入所有这些文件。每个文件有两列(一个字符串和一个浮点),行数介于10000-1000000行之间,我希望通过第一列(即字符串变量)进行连接 我尝试了numpy.lib.recfunctions.join_by,但速度非常慢。我切换到pandas.merge,速度快了很多,但考虑到我拥有的表的数量(和大小),速度还是太慢了。而且它似乎真的是内存密集型的——当被合并的文件有数十万行时,机器变得不可用(我主要使用的是MacBook Pro,2.4GHz,4G

所以,我有大约4000个CSV文件,我需要加入所有这些文件。每个文件有两列(一个字符串和一个浮点),行数介于10000-1000000行之间,我希望通过第一列(即字符串变量)进行连接

我尝试了
numpy.lib.recfunctions.join_by
,但速度非常慢。我切换到pandas.merge,速度快了很多,但考虑到我拥有的表的数量(和大小),速度还是太慢了。而且它似乎真的是内存密集型的——当被合并的文件有数十万行时,机器变得不可用(我主要使用的是MacBook Pro,2.4GHz,4GB)

所以,现在我正在寻找替代方案——是否还有其他我遗漏的潜在解决方案?Python还有哪些其他外部连接实现?是否有讨论和比较每个实现的时间复杂性的论文/网站?如果我只是让Python调用,比如说sqlite3,然后让sqlite3进行连接,效率会更高吗?字符串键是问题所在吗?如果我可以用一个数字键,它应该更快吗

如果这有助于让您更具体地了解我试图实现的目标,下面是我使用
pandas.merge的代码:

import os
import pandas as pd

def load_and_merge(file_names, path_to_files, columns):
    '''
    seq, str, dict -> pandas.DataFrame
    '''
    output = pd.DataFrame(columns = ['mykey']) # initialize output DataFrame
    for file in file_names:

        # load new data
        new_data = pd.read_csv(path + file,
                               usecols = [col for col in columns.keys()],
                               dtype = columns,
                               names = ['mykey', file.replace('.csv', '')],
                               header = None)

        # merge with previous data
        output = pd.merge(output, new_data, on = 'mykey', how = 'outer')
        output = output.fillna(0) # kill NaNs

    return output

path = '/Users/username/data/'
files_list = [file for file in os.listdir(path) if '.csv' in file]
merged_table = load_and_merge(files_list, path, {0: 'S30', 1: 'float'})

(Mac OS X 10.6.8和Python 2.7.5;Ubuntu 12.04和Python 2.7.3)

下面是我如何解决这个问题的

不要迭代合并。您正在将较小的帧(称为“合并”)与较大的帧(称为“合并”)合并。然后重复这个过程,导致“合并”变得更大,并且有更多的行

相反,您可以执行重复的层次合并。假设您将合并编号为1-4000

合并1和2以形成1_2

然后重复,然后合并1_2和3_4,形成1_2_3_4

这不会改变您正在做的工作量,但会使每次合并更加简单,从而降低内存障碍和花费的时间(因为它必须通过键的笛卡尔积)。将合并顺序随机化可能是有意义的

此外,这是完全并行的,因为您可以让独立的流程处理这个问题,生成中间合并。这本质上变成了一个map-reduce类型的问题


您还可以将中间合并存储在HDF5文件中(使用
HDFStore
),这将使存储非常高效。请注意,这些文件应该是独立的,以避免使用多个进程(HDF5不支持)写入同一个文件。

好的,这里是Jeff方法的部分实现(参见上面的答案),如果我理解正确的话。我发布这篇文章是为了防止其他人尝试做类似的事情。此外,如果有人可以帮助改进或“美化”这段代码(现在这是一个漫长而难看的代码流……我想我可能应该以某种方式对其进行模块化)

这是一个部分实现,因为我没有并行化合并。我尝试过,使用Python的
多处理
模块,但显然我没有足够的计算机内存来实现这一点——两个同时进行的过程足以冻结一切(或者可能我只是做了一些完全愚蠢的事情——很有可能,因为我以前从未使用过
多处理
)。但剩下的就在这里:分层合并和HDF5(用于存储中间文件)

编辑


仍然不好:中间矩阵最终变得太大,超出了计算机内存,代码崩溃(
tables.exceptions.HDF5ExtError:创建数组时出现问题。
)。我试过sqlite3,但也没用,所以我想我只能继续找了。

下一步我会试试SQL。我不知道有哪种连接的Python实现能打败pandas。如果将数据帧转储到持久性存储(hdf5代表insistance)中,然后将其加载回一个新的Python实例中,是否会有相同的内存占用?i、 e.将加载100个文件时的内存占用与从新python实例中的存储加载的结果数据帧进行比较好的问题,我没有尝试过。下一步我会做的。你最终决定做什么?你接受杰夫的建议了吗?使用SQL?似乎是SQL数据库要解决的问题。我同意Jeff的建议,但我还没有完成它的实现。太棒了!事实上,我没有充分的理由进行迭代合并。然后我将尝试实现分层合并,并将数据存储在HDF5文件中。如果这还不够快,我也会尝试MapReduce选项。嘿,你找到这个问题的解决方案了吗?没有。我最终诉诸暴力(我大学的超级计算机)。
#!/usr/bin/env python

import os
import sys
import pandas as pd

def merge_csv(folder, cols_info):
    '''
    str, dict -> pandas.DataFrame

    This function outer joins all CSV files in the specified folder. It 
    joins them hierarchically and stores the intermediate files in an HDF5 
    container. The first parameter is the path to the folder and the second 
    parameter is a dictionary mapping each column to the corresponding data 
    type. You can only specify two columns.

    Example: 

    merge_csv('/Users/username/data/', {0: 'S30', 2: 'float'})

    Dependencies:

    - HDF5
    - PyTables
    - pandas
    '''

    # ensure that user is specifying only two columns
    if len(cols_info) != 2:
        sys.exit('Error: You can only specify two columns.')

    # ensure that path to input folder ends with '/'
    folder = folder + '/' if folder[-1] != '/' else folder

    # create HDF5 file to store intermediate files
    store = pd.HDFStore(folder + 'store.h5', mode = 'w')

    # load CSV files and write data to HDF5 file
    flist = [file for file in os.listdir(folder) if file[-4:] == '.csv']
    if len(flist) == 0:
        sys.exit('There are no CSV files in the specified folder.')
    for file in flist:
        case = file.replace('.csv', '')
        store[case] = pd.read_csv(folder + file, 
                                  usecols = [col for col in cols_info], 
                                  names = ['key', case], 
                                  dtype = cols_info)
        store.flush()

    # start merge routine
    flist = store.keys()
    counter = 0
    while len(flist) > 1:
        counter += 1

        # merge current set of files, two by two
        length = (len(flist) - 1) if (len(flist) % 2 == 1) else len(flist)
        for i in range(length)[0:length:2]:
            merged_data = pd.merge(store[flist[i]], store[flist[i + 1]], 
                                   on = 'key', 
                                   how = 'outer',
                                   sort = False)
            outputfile = 'file' + str(i) + str(i + 1)

            # if number of files is odd, make last pair a trio
            if (i == len(flist) - 3) and (len(flist) % 2 == 1):
                merged_data = pd.merge(merged_data, store[flist[i + 2]], 
                                       on = 'key',
                                       how = 'outer', 
                                       sort = False)
                outputfile += str(i + 2)

            # save merged pair (or trio) to HDF5 file
            merged_data = merged_data.fillna(0)
            store.put('/tmp' + str(counter) + '/' + outputfile, merged_data)
            store.flush()

        # clean up
        to_remove = [file for file in store.keys() 
                     if 'tmp' + str(counter) + '/' not in file]
        for file in to_remove:
            store.remove(file)

        # move on to next set of intermediate files
        flist = store.keys()

    # wrap up
    store.close()
    return merged_data