当前位置: 首页 > 科技观察

使用Python分析14亿个单词的数据

时间:2023-03-16 01:32:51 科技观察

使用pytubes、numpy和matplotlibGoogleNgram查看器是一个有趣且有用的工具,它使用Google从书籍中扫描的大量数据来绘制单词使用情况随时间的变化。例如,单词Python(区分大小写):这张图来自:books.google.com/ngrams/grap…绘制了单词“Python”随时间的使用情况。它由Google的n-gram数据集提供支持,该数据集记录了GoogleBooks图书每年印刷的特定单词或短语的使用量。虽然这并不完整(它不包括曾经出版的每一本书!),但数据集中有数百万本书,涵盖从16世纪到2008年的时间段。可以从此处免费下载数据集。我决定使用Python和我的新数据加载库PyTubes,看看重新生成上面的图表有多么容易。challenge1-gram的数据集在硬盘上可以扩展到27Gb的数据,读入python是一个很大的数据量级。Python可以轻松地一次处理千兆字节的数据,但是当数据被破坏和处理时,它会很慢并且内存效率低下。总的来说,这14亿条记录(1,430,727,243)分布在38个源文件中,总计2400万(24,359,460)个单词(和词性标签,见下文),从1505年到2008年计算。它很快变慢当处理10亿行数据时。并且原生Python并未针对处理这方面的数据进行优化。幸运的是,numpy非常擅长处理大量数据。使用一些简单的技巧,我们可以使用numpy使这种分析变得可行。在python/numpy中处理字符串很复杂。python中字符串的内存开销很大,而numpy只能处理已知且固定长度的字符串。基于此,大多数单词都有不同的长度,所以这并不理想。加载数据下面的所有代码/示例都在具有8GBRAM的2016MacbookPro上运行。如果硬件或云实例有更好的内存配置,性能会更好。1-gram的数据以tab键划分的形式存储在文件中,如下所示:Python158742Python162111Python165122Python165911每条数据包含以下字段:那就是:Whatareweinterestedin这个词?该词在发表年份被使用的总次数,通过提取这些信息,忽略了处理不同长度的字符串数据的额外成本,但我们仍然需要比较不同字符串的值来区分哪一行data是我们感兴趣的字段。这是pytube可以做的:).split().tsv(headers=False).multi(lambdarow:(row.get(0)).equals(WORD.encode('utf-8')),row.get(1).to(int),row.get(2).to(int))))将近170秒(3分钟)后,onegrams_是一个包含近14亿行的numpy数组,它看起来像这样(为说明添加了标题):╒══════════╤════════╤═════════╕│IS_word│year│count│╞══??═══╪═════╡│0│1799│2│├──────────────────────────────────┼──────────┤│0│1804│1│├────────────┼──────────┼────────────┤│0│1805│1│├────────────┼──────────┼────────────┤│0│1811│1│├────────────┼──────────┼────────────┤│0│1820│...│╘═══════════╧════════╧═════════╛numpy:一个词出现的百分比(一个词在一年中出现的次数/一年中所有词出现的总次数),取其多比仅仅计算原始单词有用。要计算这个百分比,我们需要知道单词总数是多少。幸运的是,numpy使这变得非常简单:last_year=2008YEAR_COL='1'COUNT_COL='2'year_totals,bins=np.histogram(one_grams[YEAR_COL],density=False,range=(0,last_year+1),bins=last_year+1,weights=one_grams[COUNT_COL])画这个图是为了显示谷歌每年收集了多少单词:很明显在1800之前总数据量下降很快,所以这次歪曲了最终结果,并且会隐藏规律我们感兴趣。为了避免这个问题,我们只导入1800之后的数据:1).to(int).gt(1799).multi(lambdarow:(row.get(0).equals(word.encode('utf-8')),row.get(1).to(int),row.get(2).to(int))))这会返回13亿行数据(1800年之前只有3.7%)Python每年的百分比获取python每年的百分比现在特别简单。使用一个简单的技巧,创建基于年份的数组,长度为2008个元素意味着每个年份的索引等于年份的数字,因此,例如1995只是获取1995的元素的问题。这些都不是值得用numpy做:word_rows=one_grams[IS_WORD_COL]word_counts=np.zeros(last_year+1)for_,year,countinone_grams[word_rows]:word_counts[year]+=(100*count)/year_totals[year]绘制结果ofword_counts:形状看起来和谷歌的版本相似,实际百分比不匹配,我认为这是因为下载的数据集,它以不同的方式包含单词(例如:Python_VERB)。该数据集在google页面上没有得到很好的解释,并提出了几个问题:人们如何将Python用作动词?“Python”的计算总数是否包括“Python_VERB”?etc.幸运的是,我们知道我使用的方法产生了一个类似谷歌的图标,相关的趋势没有受到影响,所以对于这次探索,我没有打算尝试修复它。性能Google在大约1秒内生成图像,与此脚本的8分钟相比,这也是合理的。Google单词计算的背景将从明显的准备好的数据集视图开始工作。例如,提前计算前一年的总单词使用量并将其存储在单独的查找表中可以节省大量时间。此外,将单词用法保存在单独的数据库/文件中,然后索引第一列将减少几乎所有的处理时间。这种探索确实表明,使用带有标准商品硬件和Python的numpy和初出茅庐的pytubes,可以在合理的时间内从数十亿行的数据集中加载、处理和提取任意统计数据,语言大战用于稍微演示这个概念更复杂的例子,我决定比较提到的三种相关编程语言:Python、Pascal和Perl。源数据是嘈杂的(它包含所有使用的英文单词,而不仅仅是编程语言参考,例如,python也有非技术含义!),为了适应这一点,我们做了两件事:只匹配名称的大写形式(Python,不是python)per一种语言的总提及次数已转换为从1800年到1960年的百分比平均值,考虑到Pascal在1970年首次被提及,这应该给出一个合理的基线。结果:与谷歌相比(没有任何基线调整):运行时间:10多分钟代码:gist.github.com/stestagg/91...未来的PyTubes改进在这个阶段,pytubes只有一个整数的概念,即64位。这意味着pytubes生成的numpy数组对所有整数使用i8dtypes。在某些地方(如ngrams数据),8位整数是多余的并且浪费内存(总的ndarray是38Gb,dtypes可以轻松地将其减少60%)。我计划添加一些1类、2类和4位整数支持(github.com/stestagg/py…)不)能力。在某些用例中,这可以更快地减少加载数据的大小。更好的字符串匹配-像这样简单的测试:startswith、endswith、contains、isoneof可以很容易地添加,以显着提高加载字符串数据的效率。一如既往,补丁非常受欢迎!