从不同的大文件中打乱数据的有效方法

Daw*_*wei 5 python dataframe pandas

举例来说,我已经是df1并且df2在不同的领域:

df1 = pd.DataFrame({"question":["q1","q2"], "answer":["a1","a2"], "domain":"tech"})
df2 = pd.DataFrame({"question":["q3","q4"], "answer":["a3","a4"], "domain":"history"})

print(df1)
  question answer domain
0       q1     a1   tech
1       q2     a2   tech

print(df2)
  question answer   domain
0       q3     a3  history
1       q4     a4  history
Run Code Online (Sandbox Code Playgroud)

我想要的是混洗后的数据:

print(shuffled1)
  question answer   domain
0       q3     a3  history
1       q1     a1     tech
print(shuffled2)
  question answer   domain
0       q2     a2     tech
1       q4     a4  history
Run Code Online (Sandbox Code Playgroud)

在现实世界中,我有来自不同域的 60 多个具有相同结构的 csv 文件。每个文件有 50k 条记录。它们不能同时读入内存。

我想要做的是将这些文件输入到 Bert 模型中进行训练,但是如果模型从“历史”域中学习 10k 步的数据,然后从另外 10k 步的“技术”域中学习,则该模型会做得不好。所以我想打乱文件中的数据,使多个域的数据均匀分布在每个文件中。

小智 1

一种答案是逐一读取每个文件并将这些行分散到N新文件中。这样做,您将获得具有相似行数和与“原始文件”相同比例的“打乱文件”。当然,这在很大程度上取决于您需要什么样的打乱文件。

初始文件的读取可以并行完成,但我们需要协调线程不要同时写入同一文件。我不会在这里描述这一点,因为我认为这对于这里所需要的来说太多了。例如:Python 多处理安全写入文件

除了您拥有和/或想要的文件数量之外,下面的限制部分是洗牌。鉴于您的问题,由于它仅限于 50k 行的文件和机器学习,我认为下面的过程就足够了。50k * 10 的数组大约需要 4 Mb,因此可以将其完全加载到内存中以供np.random.shuffle. 如果它更大,您需要使用另一种方法,请参阅随机播放大型项目列表而不在内存中加载

因此,程序可以是:

  1. 对于原始文件1:
    1. 读取文件
    2. 随机播放文件
    3. 将文件分成N块(考虑N大于行数)
    4. 将块写入打乱的文件中
  2. 转到下一个文件并在 1.1 处重新启动。

首先,我生成了 50 个文件,每个文件有 100,000 行,每行 25 Mb:

import pandas as pd
import numpy as np

for i in range(50):
    arr = np.random.randint(1000, size=(100000,10))
    with open(f'bigfile-{i}', 'w') as f: np.savetxt(f, arr, delimiter=',')
Run Code Online (Sandbox Code Playgroud)

这是一个粗略的代码,但它有效:

originalFiles = [f'bigfile-{i}' for i in range(50)] # paths of your original files
nbShuffled = len( originalFiles ) # number of shuffled files (you can choose)

for i, file in enumerate( originalFiles ):
    # 1. Read the original file
    with open(file, 'r') as f: lines = f.readlines()
    # 2. Shuffle the file
    np.random.shuffle( lines )
    # 3. Estimate number of lines per block
    nbLines = len( lines )
    firstBlocks = int( np.floor( nbLines / nbShuffled ) )
    lastBlock = int( firstBlocks + nbLines % nbShuffled )
    blocks = [firstBlocks] * ( nbShuffled - 1 ) + [lastBlock]
    # 4. Write the blocks
    np.random.shuffle( blocks ) # avoid that the last block is always in the last shuffle file
    x = 0
    for b in range( nbShuffled ):
        with open( f'bigfile_shuffled-{i}', 'a' ) as f: f.writelines( lines[ x : x + blocks[b] ] )
        x += blocks[b]
Run Code Online (Sandbox Code Playgroud)

在我的计算机(Linux 64 位、32 Go RAM、16 CPU)上运行大约需要 13 秒。