V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
warcraft1236
V2EX  ›  Python

如果有 33W 行文字,去重应该怎么写呢?

  •  
  •   warcraft1236 · 2017-12-17 00:22:49 +08:00 · 6480 次点击
    这是一个创建于 2566 天前的主题,其中的信息可能已经有所发展或是发生改变。

    比如有个 txt 文本,一共有 33W 行文字,以行作为单位,去重,我应该怎么写效率会比较高呢?

    我目前用的普通的方法,发现耗时比较长

    我列出我目前的方法

        with open('/Users/lizhao/Downloads/aboutchinese.dict.yaml') as f:
    
            for i in f.readlines():
                if i == '\n':
                    continue
                if i not in oldList:
                    oldList.append(i)
    
        with open('tmp.txt','w') as g:
            g.writelines(oldList)
    

    代码渣,请轻喷

    44 条回复    2017-12-20 21:07:00 +08:00
    l00t
        1
    l00t  
       2017-12-17 00:25:37 +08:00
    为啥不用 dict
    realpg
        2
    realpg  
       2017-12-17 00:47:29 +08:00   ❤️ 1
    33 万行 这么点数据还考虑啥性能……
    wspsxing
        3
    wspsxing  
       2017-12-17 00:48:05 +08:00
    hash
    ihciah
        4
    ihciah  
       2017-12-17 00:48:19 +08:00 via iPhone
    list 查找是线性的似乎…换成 dict 或者 set 会好
    fy
        5
    fy  
       2017-12-17 00:55:19 +08:00
    蛐蛐 33w 行,pandas 读入,drop_duplicates,导出,收工。
    vimiix
        6
    vimiix  
       2017-12-17 00:57:25 +08:00
    我的话,可能会先考虑用集合 set,下面是伪代码思路:
    values = set()
    vals_len = 0

    dest_file = open('dest_file', 'w')
    with open txt:
    for line in readlines():
    val = hash(line)
    vals_len = len(values)
    values.add(val) #主要在这一步借助 set 的不重复特性,判断集合长度是否增长
    if len(values) == vals_len+1:
    dest_file.write(line)
    dest.file.close()

    仅供参考,没试验过。
    Guaidaodl
        7
    Guaidaodl  
       2017-12-17 01:12:59 +08:00
    list 的查找是 O(n) 的吧,整个复杂度是 O(n^2)。去重的时候不要用 list, 换成 set 就好了。
    em70
        8
    em70  
       2017-12-17 01:15:02 +08:00   ❤️ 1
    直接 EmEditor 打开 TXT,工具->插件->删除重复行,搞定
    rrfeng
        9
    rrfeng  
       2017-12-17 01:32:45 +08:00   ❤️ 11
    这个题我最喜欢了:

    awk '!a[$0]++' file.txt
    ytterbium
        10
    ytterbium  
       2017-12-17 03:46:04 +08:00 via Android
    这么小数据量,hash, map 都行啊。linux 的话就直接 sort -u,虽然不是最快的,不过简单省心。
    ytterbium
        11
    ytterbium  
       2017-12-17 03:52:11 +08:00 via Android
    @ytterbium 之前做 nlp 数据预处理去重过 5 亿多行的数据,偷懒用的 sort -u,多线程 1 天不到也就跑完了。30 几万行几秒的事。
    param
        12
    param  
       2017-12-17 03:53:15 +08:00 via Android
    转成 set 再转成 list 不知道行不行
    ytterbium
        13
    ytterbium  
       2017-12-17 03:57:16 +08:00 via Android
    @ytterbium 50 亿
    param
        14
    param  
       2017-12-17 03:57:47 +08:00 via Android
    list(set(open('/Users/lizhao/Downloads/aboutchinese.dict.yaml'))) 这样子?不知道性能如何。
    cljnnn
        15
    cljnnn  
       2017-12-17 05:56:29 +08:00 via Android
    用 set,set 自带去重
    ioven
        16
    ioven  
       2017-12-17 08:01:46 +08:00
    如果对流程没太多需要直接用 awk 更简单
    swulling
        17
    swulling  
       2017-12-17 08:27:26 +08:00 via iPhone
    awk dict
    wweir
        18
    wweir  
       2017-12-17 08:28:21 +08:00 via iPhone
    除了 hash,还可以考虑用排序的方法来做
    如果 33w 仅仅是代表大(内存不允许)的话,还可以考虑利用一些磁盘排序的算法
    secsilm
        19
    secsilm  
       2017-12-17 08:56:22 +08:00 via Android
    这也不大吧
    cxbig
        20
    cxbig  
       2017-12-17 08:59:46 +08:00
    sort -u dict.yml
    awk '!a[$0]++' dict.yml
    ceclinux
        21
    ceclinux  
       2017-12-17 09:01:01 +08:00 via Android
    要是对结果没有顺序要求的,不要想太多,直接 sort 然后 uniq 即可
    fyibmsd
        22
    fyibmsd  
       2017-12-17 10:31:45 +08:00
    cat old | uniq | tee new
    artandlol
        23
    artandlol  
       2017-12-17 10:44:36 +08:00   ❤️ 3
    一个 uniq 也能发一贴
    root@x:~# cat test
    247
    214
    209
    228
    216
    216
    root@x:~# cat test|uniq
    247
    214
    209
    228
    216
    root@x:~# cat test|sort -u
    209
    214
    216
    228
    247
    root@x:~# cat test|awk '!a[$0]++'
    247
    214
    209
    228
    216
    ericls
        24
    ericls  
       2017-12-17 10:47:05 +08:00   ❤️ 1
    from collections import OrderedDict
    '\n'.join(OrderedDict.fromkeys(string.splitlines()))
    selfAccomplish
        25
    selfAccomplish  
       2017-12-17 10:51:26 +08:00 via Android
    如果楼主的数据是在 window 下面呢,没有 linux 的机子或者数据量大拷不进 linux 的虚拟机。
    cdwyd
        26
    cdwyd  
       2017-12-17 10:53:50 +08:00 via Android
    list(set(f.readlines()))
    takeoffyoung
        27
    takeoffyoung  
       2017-12-17 11:08:28 +08:00
    cat xxx.txt | sort | uniq 这样??
    msg7086
        28
    msg7086  
       2017-12-17 11:23:32 +08:00
    @selfAccomplish 那就在 Windows 下用 awk 啊。
    anexplore
        29
    anexplore  
       2017-12-17 11:41:29 +08:00
    1、for i in f.readlines() 改成 for i in f: 是不是更好;
    2、查重用 dict 性能好,如果每一行都比较长 建议把 line 换算一下再 put 到 dict 中,要保证顺序就 orderdict
    young6
        30
    young6  
       2017-12-17 12:58:22 +08:00 via Android
    要是对行顺序没要求,用 cat,sort,uniq 吧,还写啥代码啊。要是行顺序要求不变,python 有有序字典
    leavic
        31
    leavic  
       2017-12-17 16:31:34 +08:00
    有序字典吧,set 虽然可以去重,但你恢复的时候就无序了。
    wizardoz
        32
    wizardoz  
       2017-12-17 16:35:42 +08:00
    dict 可破,用文本当 key,用行号当 value
    dict 在判断 in 的时候效率应该吊打 list。
    jyf007
        33
    jyf007  
       2017-12-17 20:55:58 +08:00
    @msg7086
    @artandlol
    @young6
    @selfAccomplish
    @swulling
    @ioven
    @rrfeng

    别忘了用这个 https://frippery.org/busybox/
    ,开一个批处理写成 busybox ash 就有坠吼的环境了,(其实没什么大功能)
    我原来写了一个在 windows 处理 netstat 输出的就是这么骚操作.
    artandlol
        34
    artandlol  
       2017-12-17 21:04:36 +08:00 via iPhone
    @jyf007
    cmder
    +gow
    或者用在线 docker-ce 再通过用 websocket 映射到浏览器
    估计你要百度一晚上了
    jyf007
        35
    jyf007  
       2017-12-17 21:42:58 +08:00
    @artandlol 说起来 unix 的好还是只能体现在 busybox 上了,其他不能算是原始的 unix 了
    我当时是直接操作 cmd 的窗口就这 vi 写脚本,或者开记事本搞,
    完整的 msys2 我装完有 45G 这么大,不够轻盈,
    我觉得安利 unix 必须提 busybox 和 dropbear.
    warcraft1236
        36
    warcraft1236  
    OP
       2017-12-17 21:47:41 +08:00
    @wizardoz

    能给个示例吗?我不知道该怎么用 dict 去重
    ipwx
        37
    ipwx  
       2017-12-17 23:18:59 +08:00
    24L 是 Python 最优雅的解决方案。
    Lpl
        38
    Lpl  
       2017-12-17 23:56:50 +08:00
    vim 文件

    然后 :sort u
    会先排序然后取相同行第一行
    lylijincheng
        39
    lylijincheng  
       2017-12-18 09:15:55 +08:00
    ```
    Array.from(new Set(longlongtext.split(''))).join('')
    ```
    wizardoz
        40
    wizardoz  
       2017-12-18 09:45:31 +08:00
    @warcraft1236 把你的 list 换成 dict 就可以了!

    buffer = {}
    sort_key = 0
    with open('file.txt', 'r') as fp:
    for line in fp.readlines():
    if line not in buffer:
    sort_key += 1
    buffer[line] = sort_key


    # 完了用 sort_key 排序,保持原来顺序
    ...
    warcraft1236
        41
    warcraft1236  
    OP
       2017-12-18 09:56:19 +08:00
    @wizardoz
    多谢,不过再问一个基础的问题,怎么根据 dict 的 values 排序写入文件呢?
    araraloren
        42
    araraloren  
       2017-12-18 11:09:21 +08:00
    .say for f.lines.unique(:with(&[eqv])) (in Perl6, f is IO::Handle)
    wizardoz
        43
    wizardoz  
       2017-12-18 13:51:32 +08:00
    @warcraft1236
    # 排序并丢弃行号信息,得到的是按照原本顺序的文本数据
    sorted_lines = sorted([item[0] for item in buffer.items()], cmp=lambda a, b: a[1] - b[1])

    # 输出
    with open("output.txt", "w") as fp:
    for line in sorted_lines:
    print(line, file=fp)
    shawndev
        44
    shawndev  
       2017-12-20 21:07:00 +08:00
    cat file | uniq
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1643 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 81ms · UTC 16:49 · PVG 00:49 · LAX 08:49 · JFK 11:49
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.