【问题标题】:How to deal with large amount of data in Python如何在 Python 中处理大量数据
【发布时间】:2015-02-24 16:05:49
【问题描述】:

我有一个包含大量数据 (3 GB) 的文本文件。此文本文件的每一行都包含时间、源 IP、目标 IP 和大小。如您所知,IP 地址最后一部分中的数字显示端口地址。我想将这些端口地址带到一个直方图中,我为 10 000 行数据做了它,但我猜到 Python 代码无法为大量数据执行。我简要解释一下我编写的代码。首先,我读取了 10 000 个数据点,然后我将它们拆分并放入一个名为 Everything_list 的列表中。只需忽略 while 循环起作用的条件。后来我把所有的端口地址放在一个列表中,然后画出它们的直方图。 现在假设我有一百万条数据线,我一开始就无法读取它们,更不用说对它们进行分类了。有些人告诉我使用数组,有些人告诉我处理一大块数据,然后再处理另一块数据。我对所有人所说的感到困惑。有人可以帮我解决这个问题吗?

text_file = open("test.data", "r")
a = text_file.read()
text_file.close()

everything_list = a.split()
source_port_list = []
i=0
while 6+7*i<len(everything_list):

    source_element = everything_list[2+7*i]
    source_port_position = source_element.rfind('.')
    source_port_number = int(source_element[source_port_position + 1:])
    source_port_list.append(source_port_number)

    i=i+1


import matplotlib.pyplot as plt
import pylab


numBins = 20
plt.hist(source_port_list, numBins, color='red', alpha=0.8)
plt.show()

这是行格式:

15:42:42.719063 IP 129.241.138.133.47843 > 129.63.27.12.2674: tcp 1460
15:42:42.719205 IP 129.241.138.133.47843 > 129.63.27.12.2674: tcp 1460
15:42:42.719209 IP 129.63.57.175.45241 > 62.85.5.142.55455: tcp 0
15:42:42.719213 IP 24.34.41.8.1236 > 129.63.1.23.443: tcp 394
15:42:42.719217 IP 59.167.148.152.25918 > 129.63.57.40.36075: tcp 0
15:42:42.719260 IP 129.63.223.16.2823 > 80.67.87.25.80: tcp 682
15:42:42.719264 IP 129.63.184.118.2300 > 64.111.215.46.80: tcp 0
15:42:42.719269 IP 129.63.184.118.2300 > 64.111.215.46.80: tcp 0

【问题讨论】:

  • 你有多少内存将决定你可以在内存中存储多少,实际ip中的最后一位数字与端口无关
  • 你能转储你的源文件的一些行,以获得确切的结构吗?
  • @PadraicCunningham 我想他的意思是127.0.0.1:8080 例如
  • 无论如何将整个文件读入内存是错误的方法,遍历文件对象并逐行解析,这将立即节省一些演出
  • 我认为如果你只是遍历文件对象,你的很多问题都会消失。您可以完全按照您的问题添加文件的 sn-p,很难在您的评论中看到实际格式

标签: python text-files histogram large-data


【解决方案1】:

我不知道数据是什么样的,但我认为问题在于您试图一次将其全部保存在内存中。您需要一点一点地进行操作,read the lines one by one 并在进行过程中构建直方图。

histogram = {}
with open(...) as f:
    for line in f:
        ip = ...
        if ip in histogram:
            histogram[ip] += 1
        else:
            histogram[ip] = 1

您现在可以绘制直方图,但使用 plt.plot 而不是 plt.hist,因为您已经在 histogram 字典中找到了频率。

【讨论】:

  • 默认字典会更好
  • 有趣。还有一个计数器字典in there。对我来说,有时简单性胜过性能。
  • 您介意重写整个代码吗?由于我是一个新的python用户,我不知道如何发挥这些变化!
  • 糟糕,最后一行错误,现已修复。它所做的是遍历这些行,记录它看到给定 ip 的次数。直方图字典具有 ips 键和相应计数的值。最后一行只是从 1 开始计算 ip,而 if 语句的另一个分支增加了它在该行中找到的 ip 的计数。其他答案中描述了如何从线路中获取 ip。我留ip = ...给你填写
  • 不要介意前两个 cmets。它们只是对结果使用其他数据结构的可能的性能改进
【解决方案2】:

听起来您应该逐行迭代并使用正则表达式来查找端口。试试这样的:

import re

ports = []
with open("path/to/your/text/file.txt", 'r') as infile:
    for line in infile:
        ports.append(re.findall(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\.(\d+)", line))
        # that regex explained:
        # # re.compile(r"""
        # #     \d{1,3}\.       # 1-3 digits followed by a literal .
        # #     \d{1,3}\.       # 1-3 digits followed by a literal .
        # #     \d{1,3}\.       # 1-3 digits followed by a literal .
        # #     \d{1,3}\.       # 1-3 digits followed by a literal .
        # #     (               # BEGIN CAPTURING GROUP
        # #       \d+           #   1 or more digits
        # #     )               # END CAPTURING GROUP""", re.X)

这是假设您的 IP/端口已按照您在评论中的说明进行格式化

IP.IP.IP.IP.PORT

【讨论】:

  • _ ,port = line.rsplit(".",1) 会好很多,不过我认为这不是真正的问题
  • @PadraicCunningham 当然问题是a = text_file.read(),但我更喜欢在这样的情况下使用正则表达式:不是一点点,因为每行有两个端口,而 OP 的代码只会找到其中一个.
  • 您介意重写整个代码吗?由于我是一个新的 python 用户,我不知道如何应用这些更改!顺便说一句,我不知道你代码的最后一行是做什么的!
  • @CristopherVanPaul 我用正则表达式的扩展定义进行了编辑。它基本上会在您的日志中找到任何看起来像 IP 地址的内容(四次 1-3 位数字,以点分隔),然后分隔端口号并将它们保存在一个列表中。我实际上无法重写您的代码,因为我对 matplotlib 一无所知,所以我不知道它在做什么与它可能做得更好!:)
【解决方案3】:

您可以使用正则表达式并在外部循环编译它。

连同以lazy模式逐行读取文件。

import re
import matplotlib.pyplot as plt
import pylab

r = re.compile(r'(?<=\.)[0-9]{2,5}(?= \>)')
ports = []

for line in open("test.data", "r"):
    ports.append(re.search(r, line).group(0))

# determines the number of lines you want to take into account
i = (len(ports) - 6) // 7

# keeps only the first i elements
ports = ports[0:i]

numBins = 20
plt.hist(ports, numBins, color='red', alpha=0.8)
plt.show()

此代码考虑到您只需要(n-6) / 7 第一项,n 是源文件的行数。如果不完全准确,请尝试使用一些 +1/-1。最后删除不需要的项目可以让您的循环不必为每次迭代检查条件而烦恼。

编辑:

你可以结合上面的几件事来获得更简洁和高效的代码:

import re
import matplotlib.pyplot as plt
import pylab

r = re.compile(r'(?<=\.)[0-9]{2,5}(?= \>)')

ports = [ re.search(r, line).group(0) for line in open("test.data", "r") ]
ports = ports[0:(len(ports) - 6) // 7]

numBins = 20
plt.hist(ports, numBins, color='red', alpha=0.8)
plt.show()

编辑:

如果您认为您的端口列表太大而无法放入 RAM(我认为这不太可能),我的建议是使用端口字典:

ports = {}
for line in open("test.data", "r"):
    port = re.search(r, line).group(0)
    if not ports.get(port, False):
        ports[port] = 0
    ports[port] += 1

这会给你类似的东西:

>>> ports
{
    "8394": 182938,
    "8192": 839288,
    "1283": 9839
}

请注意,在这种情况下,您对plt.hist 的调用将不得不修改。

【讨论】:

  • 但关键是,我将有一长串端口,我可能没有足够的 RAM 容量来存储所有端口。
  • @CristopherVanPaul,您需要大量数据来使用所有 24 gig 的 ram
  • @CristopherVanPaul 您可以将它们放在字典中,其中键是端口号,值是找到的端口数 - 这可能会为您节省大量空间
  • @Jivan,据我所知,如果我想这样做,我不应该通过 hist 来做,因为 hist 需要所有数据然后绘制它。我应该通过处理一大块数据然后处理另一块数据来做到这一点......
  • :@Jivan, my_list[n:]实际上是从n返回到最后没有到n
【解决方案4】:

你可以使用 split 和 defaultdict 会更有效:

from collections import defaultdict

d = defaultdict(int)
with open("a_file.txt") as f:
    for line in f:
         d[line.split()[2].rsplit(".",1)[-1]] += 1 
print(d)

defaultdict(<type 'int'>, {'1236': 1, '2300': 1, '47843': 2, '45241': 1, '25918': 1, '2823': 1})

可能还值得一试不同的绘图方式,matplotlib 不是最有效的:

pyqtgraph、guiqwt、gnuplot.py

【讨论】:

  • 这个split 方法效率更高,但我个人觉得它比正则表达式更不可读,更难维护。
  • 您知道效率提高多少吗?比如说 3M 线?
  • 使用 split 方法解析字符串的速度要快 3-4 倍,我可能会提高效率OP想要
  • 我也会,肯定会更快更干净。
【解决方案5】:

我知道这不是对您的问题的直接回应,但是作为 python 新手,有一个很好的 Coursera 课程来处理这个主题。 “为所有人编程(Python)”它是免费的,不会占用你太多的时间。该课程于 2015 年 2 月 2 日开始。此外,教科书“Python for Informatics: Exploring Information”是免费的知识共享下载。 http://www.pythonlearn.com/book.php 我希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 2020-07-15
    • 2013-04-09
    • 2014-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-24
    • 1970-01-01
    • 2017-01-01
    相关资源
    最近更新 更多