题记:
我们可能会遇到需要对一个很大的文件的某一行进行去重,排序,或者对两个文件进行合并等。本文主要测试了几种不同的方案,并进行比较。
场景: 对4百万数据的CSV文件进行去重
测试环境:MacBook Pro Intel Core i9 16GB Memory
测试文件:
# total lines
$ wc -l test.csv
42013405 test.csv
$ ls -lh test.csv
-rw-r--r--@ 1 haofan staff 448M Nov 6 22:29 test.csv
$ head -4 test.csv
id
202105261
202105261
202105262
- 方案一:linux 命令, 用时:186s
-
time sort -u test.csv > deup.csv 186.88s user 11.05s system 97% cpu 3:22.14 total
-
- 方案二: Python pandas, 代码如下, 大概耗时60s, 其中大部分时间花在写数据到磁盘上,因为是I/O bound。
-
import pandas as pd import logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s') logging.info("Start reading files...") table_file = "~/Desktop/test/test.csv" test_csv = pd.read_csv(table_file, encoding='utf_8', low_memory=False) logging.info("Start running dedup...") test_csv = test_csv.drop_duplicates() logging.info("Start output...") test_csv.to_csv('dedup.csv', sep=" ", header=False, index=False) # np.savetxt("numpytotxt.csv", test_csv.values, fmt='%d') # take 37s. # test_csv.to_pickle('dedup.csv') # take 1s test_csv.reset_index().to_feather('dedup.csv') # take 2s logging.info("Finish all...") # 运行结果:用时大概60s 主要耗时在写数据到disk上, disk 写入大概耗时30s python test.py 2021-11-06 22:57:11,552 - test.py[line:10] - INFO: Start reading files... 2021-11-06 22:57:16,900 - test.py[line:15] - INFO: Start running dedup... 2021-11-06 22:57:29,343 - test.py[line:19] - INFO: Start output... 2021-11-06 22:58:02,648 - test.py[line:21] - INFO: Finish all...
也查过其他写数据到磁盘上更快的方式,测试代码如上, 结果如下。因为feather或pickle是经过压缩的数据格式,输出的文件是二进制文件,必须用python才可读。但是因为平台不支持这种feather或者pickle的数据格式,只能用csv的数据格式。
-
to_feather: 只耗时2s,就可以把数据写入磁盘
-
to_pickle: 只耗时1s,就可以把数据写入磁盘。
-
-
-
方案三:导入本地mysql,通过sql命令去执行。发现import data 超级慢大概要十几分钟,import data后,执行一次count的query,要耗时1分钟。
-
# 启动mysql docker run --name mysql -v /Users/haofan/code/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=haofan -d mysql # login mysql docker exec -it mysql bash # create DB create DATABASE test; use test # create table CREATE TABLE IF NOT EXISTS `users`( `id` VARCHAR(40) NOT NULL, PRIMARY KEY ( `id` ) )ENGINE=InnoDB DEFAULT CHARSET=utf8; # allow import data from disk SET GLOBAL local_infile=1; docker exec -it mysql bash # import data mysqlimport --ignore-lines=1 \ --fields-terminated-by='\t' \ --local -u root \ -p test \ users.csv mysql> select count(*) from users; +----------+ | count(*) | +----------+ | 42013405 | +----------+ 1 row in set (53.22 sec)
-
-
终极方案:导入大数据平台,hadoop.
总结
- 如果对小文件的内容进行排序,最方便直接用shell sort.
- 再大点的文件可以用python pandas,pandas最耗时的部分是写文件到磁盘。所以最好用pickle后者feather的数据格式。
- 再大的文件,如果追求高效率,只能用大数据平台。