您在 Mysql 表中有大量非常短(<100 个字符)到中等长度(~5k 个字符)的文本项。您需要一个可靠且快速的算法来获得所有项目中的聚合词出现次数。
选定的项目可以少至一个(或无),也可以多至 100 万+,因此必须分析的文本大小可能相差很大。
目前的解决方案包括:
- 将选定记录中的文本读入文本变量
- preg_replacing out everything that is not a "word"getting somewhat cleaned, shorter text (hashtags, @mentions, http(s)://links, number sequences such as phone numbers, etc. are not "words"并被解析出来)
- 将剩下的分解成一个“缓冲区”数组
- 取出所有短于两个单词的所有内容并将其他所有内容转储到“master”数组中
生成的数组然后用 array_count_values 进行转换,排序 (arsort),第一次拼接以获得更易于管理的数组大小,然后根据多种语言的停用词列表进行解析,处理和拼接更多,最后以 JSON 形式输出为50 个最常用词的列表。
通过跟踪操作序列各个步骤的时间,最初明显的瓶颈在查询中,但随着项目计数的增加,它迅速移动到 array_count_values 函数(之后的一切都是或多或少是直接的)。
在约 10k 个项目的测试运行中,从开始到结束的总执行时间约为 3 秒;有 20k 个项目需要 ~7s。
具有 130 万个项目的(相当极端但并非不可能)情况需要 1m 的 mysql 查询,并且能够每分钟解析大约 75000 个项目(因此估计为 17 分钟左右)。
结果应该作为 AJAX 调用的结果可视化,因此在这种时间安排下,用户体验显然被打乱了。我正在寻找尽可能优化所有内容的方法。 30 秒的加载时间可能是可以接受的(但不太现实),10 分钟(或更长时间)则不是。
我已经尝试通过 array_count_values 对 block 进行批处理,然后将生成的计数数组按键添加到主数组中,但这帮助不大 - 各部分的总和相等(或略大于)时间上的总数。
- 我只需要列表顶部出现频率最高的 50 个,因此可能有一些改进的余地,可以删减一些。
最佳答案
- 将列转储到文本文件中。建议
SELECT ... INTO OUTFILE 'x.txt'...
- (如果在 Linux 等上):
tr -s '[:blank:]' '\n' <x.txt | sort | uniq -c
要获得前 50 名:
tr -s '[:blank:]' '\n' <x.txt | sort | uniq -c | sort -nbr | head -50
如果您需要调整“单词”的定义,例如处理标点符号,请参阅 tr
上的文档.您可能在使用单引号中的缩写词和短语时遇到麻烦。
一些文本清理可以(并且应该)在 SQL 中完成:
WHERE col NOT LIKE 'http%'
有些可以(也应该)在 shell 脚本中完成。
例如,这将删除@variables 和电话号码以及 0、1 和 2 个字符的“单词”:
egrep -v '^@|^([-0-9]+$|$|.$|..)$'
它可以在第一个 sort
之前的管道流中.
唯一的限制是磁盘空间。脚本中的每一步都非常快。
测试用例:
表格:176K 行,大部分是文本(包括一些换行符)
$ wc x3.txt
3,428,398 lines 31,925,449 'words' 225,339,960 bytes
$ time tr -s '[:blank:]' '\n' <x.txt | egrep -v '^@|^([-0-9]+$|$|.$|..)$' |
sort | uniq -c | sort -nbr | head -50
658569 the
306135 and
194778 live
175529 rel="nofollow"
161684 you
156377 for
126378 that
121560 this
119729 with
...
real 2m16.926s
user 2m23.888s
sys 0m1.380s
够快吗?
我是通过 top
观看的.似乎最慢的部分是第一个sort
. SELECT
用了不到 2 秒(但是,该表可能缓存在 RAM 中)。
关于php - PHP 中的单词出现次数 - 缩放和优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57191559/