Python 随机混合300万行文件的行

Python 随机混合300万行文件的行,python,random,vim,Python,Random,Vim,一切都在标题里。我想知道是否有人知道一种快速且内存需求合理的方法,可以随机混合300万行文件中的所有行。我想用一个简单的vim命令是不可能的,所以任何使用Python的简单脚本都是不可能的。我尝试使用随机数生成器使用python,但没有找到简单的解决方法。在python中只需几秒钟: import random lines = open('3mil.txt').readlines() random.shuffle(lines) open('3mil.txt', 'w').writelines(l

一切都在标题里。我想知道是否有人知道一种快速且内存需求合理的方法,可以随机混合300万行文件中的所有行。我想用一个简单的vim命令是不可能的,所以任何使用Python的简单脚本都是不可能的。我尝试使用随机数生成器使用python,但没有找到简单的解决方法。

在python中只需几秒钟:

import random
lines = open('3mil.txt').readlines()
random.shuffle(lines)
open('3mil.txt', 'w').writelines(lines)

在许多系统上,
sort
shell命令使用
-R
将其输入随机化

import random
with open('the_file','r') as source:
    data = [ (random.random(), line) for line in source ]
data.sort()
with open('another_file','w') as target:
    for _, line in data:
        target.write( line )
应该这样做。300万行将适合大多数机器的内存,除非这些行太大(超过512个字符)。

这是另一个版本

在shell中,使用这个

python decorate.py | sort | python undecorate.py
装点

import sys
import random
for line in sys.stdin:
    sys.stdout.write( "{0}|{1}".format( random.random(), line ) )
import sys
for line in sys.stdin:
    _, _, data= line.partition("|")
    sys.stdout.write( line )
不装饰

import sys
import random
for line in sys.stdin:
    sys.stdout.write( "{0}|{1}".format( random.random(), line ) )
import sys
for line in sys.stdin:
    _, _, data= line.partition("|")
    sys.stdout.write( line )

几乎不使用内存

这与Kugelman先生的相同,但使用vim的内置python接口:

:py import vim, random as r; cb = vim.current.buffer ; l = cb[:] ; r.shuffle(l) ; cb[:] = l
如果您不想将所有内容加载到内存中并在内存中排序,则必须在执行随机排序时将行存储在磁盘上。这将是非常缓慢的

这是一个非常简单,愚蠢和缓慢的版本。请注意,这可能会占用大量磁盘空间,而且速度会非常慢。我用30万行代码运行它,需要几分钟。300万条线路可能需要一个小时。所以:在记忆中做。真正地没那么大

import os
import tempfile
import shutil
import random
tempdir = tempfile.mkdtemp()
print tempdir

files = []
# Split the lines:
with open('/tmp/sorted.txt', 'rt') as infile:
    counter = 0    
    for line in infile:
        outfilename = os.path.join(tempdir, '%09i.txt' % counter)
        with open(outfilename, 'wt') as outfile:
            outfile.write(line)
        counter += 1
        files.append(outfilename)

with open('/tmp/random.txt', 'wt') as outfile:
    while files:
        index = random.randint(0, len(files) - 1)
        filename = files.pop(index)
        outfile.write(open(filename, 'rt').read())

shutil.rmtree(tempdir)

另一个版本是将文件存储在SQLite数据库中,并从该数据库中随机抽取行。这可能比这个要快。

我刚刚在一个有4.3M行的文件上试过这个,最快的是Linux上的'shuf'命令。像这样使用它:

shuf huge_file.txt -o shuffled_lines_huge_file.txt
完成此操作需要2-3秒。

这里是另一种使用方法,这也可以提供一些渐进的内存释放,但使用更糟糕的大O:)


以下Vimscript可用于交换行:

function! Random()                                                       
  let nswaps = 100                                                       
  let firstline = 1                                                     
  let lastline = 10                                                      
  let i = 0                                                              
  while i <= nswaps                                                      
    exe "let line = system('shuf -i ".firstline."-".lastline." -n 1')[:-2]"
    exe line.'d'                                                         
    exe "let line = system('shuf -i ".firstline."-".lastline." -n 1')[:-2]"
    exe "normal! " . line . 'Gp'                                         
    let i += 1                                                           
  endwhile                                                               
endfunction
函数!随机的
设nswaps=100
设firstline=1
设lastline=10
设i=0
而我这会起作用:
我的解决方案甚至不使用random,它还将删除重复项

import sys
lines= list(set(open(sys.argv[1]).readlines()))
print(' '.join(lines))
在壳里

python shuffler.py nameoffilestobeshuffled.txt > shuffled.txt

这不是解决你问题的必要办法。只是为了那些来这里寻找更大文件洗牌解决方案的人而保留它。但它也适用于较小的文件。将
split-b1gb
更改为较小的文件大小,即
split-b100mb
以生成大量文本文件,每个文件大小为100MB

我有一个20GB的文件,其中包含超过15亿个句子。在linux终端中调用
shuf
命令只会淹没我的16GB RAM和同一个交换区。这是我为完成任务而编写的bash脚本。它假设您将bash脚本保存在与大文本文件相同的文件夹中

#!/bin

#Create a temporary folder named "splitted" 
mkdir ./splitted


#Split input file into multiple small(1GB each) files
#This is will help us shuffle the data
echo "Splitting big txt file..."
split -b 1GB ./your_big_file.txt ./splitted/file --additional-suffix=.txt
echo "Done."

#Shuffle the small files
echo "Shuffling splitted txt files..."
for entry in "./splitted"/*.txt
do
  shuf $entry -o $entry
done
echo "Done."

#Concatinate the splitted shuffled files into one big text file
echo "Concatinating shuffled txt files into 1 file..."
cat ./splitted/* > ./your_big_file_shuffled.txt
echo "Done"

#Delete the temporary "splitted" folder
rm -rf ./splitted
echo "Complete."

你可以看到一些想法。“没有找到一个简单的出路。”真的吗?请发布太复杂的代码。应该说“没有找到出路”。我是python新手,所以我只知道一些命令。我想说的是把所有的东西都放在一个向量中,选择一个100万到300万之间的随机数,去掉那一行,然后用一个新的随机数重新开始,这个随机数有一个额外的条件,不包括以前的随机数。因此,我的问题是一个简单的方法(你和其他人提供)。我接受你的,因为你的票数最多。但要感谢每一个人。。。我学到了很多!这里还有一个更深层次的问题没有得到解决:为什么要洗牌这么大的文件?创建一个迭代器来从文件中提取乱序行可能要简单得多。除非我们不太了解混乱的原因,否则实际上不可能给您一个适合于潜在问题的答案(即“好”和回答)。请在下面检查我的答案。它应该是目前为止最快的解决方案,没有任何python代码,只有bash.3百万行(平均每行80个字符)将有大约2.4亿字节,这对于在内存中加载文件来说是巨大的。@Vikram.exe。不是真的。这台机器有4Gb内存。240M算不了什么。@Vikram.exe:使用内存有什么问题?这就是我们购买它的原因。考虑到今天的内存大小,240M并不是那么糟糕。对于
shuffle
来说,大约2000个项目的限制是一个更严重的问题,尽管行是否需要真正随机是一个问题。-1此方法与
random.shuffle
相比绝对没有优势(实现为a,它具有更好的运行时特征(
O(n)
而不是
O(n log(n)
)如上所述,
sort-R
通过一个随机键进行排序。比装饰和取消装饰文件更容易。@Chris B。正如您在上面指出的,
-R
仍将对相同的行进行分组。这不会。因此,如果这是所需的行为,那么这就是方法。正如fuzzyTew在上面指出的,
shuf
将使用每个排列的可能性相同,并且不需要自定义代码。这显然比编写和调试自己的程序要好。请注意,
-R
选项仍然会将相同的行排序在一起,这可能不是理想的行为。
shuf
将随机化行,而不考虑是否相等,这可能是最快的解决方案它当然可以工作,而且工作得很好。它只能生成2**19937个排列是微不足道的,几乎是不相关的。任何基于RNG的洗牌都会有同样的“限制”。基于
sort()
的解决方案如何比
shuffle()更好
?这并不能避免这个假定的问题。@克里斯,你误解了这个答案。无法生成所有可能的排列与根本无法随机排列列表是不一样的。我不喜欢这么争论,但你的警告是不行的