【问题标题】:read_csv get the line where exception occuredread_csv 获取发生异常的行
【发布时间】:2018-10-07 08:02:53
【问题描述】:

我尝试使用 pandas 分析的 HTTP 日志文件有时会出现意外的行。这是我加载数据的方式:

df = pd.read_csv('mylog.log',
            sep=r'\s(?=(?:[^"]*"[^"]*")*[^"]*$)(?![^\[]*\])', 
            engine='python', na_values=['-'], header=None,
            usecols=[0, 3, 4, 5, 6, 7, 8,10],
            names=['ip', 'time', 'request', 'status', 'size', 
                'referer','user_agent','req_time'], 
                converters={'status': int, 'size': int, 'req_time': int})

它适用于我拥有的大多数日志(来自同一台服务器)。但是,在加载一些日志时,会引发异常: 要么

 TypeError: int() argument must be a string, a bytes-like object or a  number, not 'NoneType'

ValueError: invalid literal for int() with base 10: '"GET /agent/10577/bdl HTTP/1.1"'

为了示例,这里是触发第二个异常的行:

22.111.117.229, 22.111.117.229 - - [19/Sep/2018:22:17:40 +0200] "GET /agent/10577/bdl HTTP/1.1" 204 - "-" "okhttp/3.8.0" apibackend.site.fr 429282

为了找到犯罪行的编号,我使用了以下(非常慢)函数:

def search_error_dichotomy(path):    
        borne_inf = 0
        log = open(path)
        borne_sup = len(log.readlines())
        log.close()
        while borne_sup - borne_inf>1:
            exceded = False
            search_index = (borne_inf + borne_sup) // 2
            try:
                pd.read_csv(path,...,...,nrows=search_index)
            except:
                exceded = True
            if exceded:
                borne_sup = search_index
            else:
                borne_inf = search_index

        return search_index

我想要的是这样的:

try:
    pd.read_csv(..........................)
except MyError as e:
    print(e.row_number)

其中 e.row_number 是凌乱行的编号。

提前谢谢你。

解决方案 所有的功劳归功于 devssh,他的建议不仅让这个过程更快,而且让我可以一次得到所有意想不到的线路。这是我所做的:

  1. 在没有转换器的情况下加载数据帧。

    df = pd.read_csv(path,
                     sep=r'\s(?=(?:[^"]*"[^"]*")*[^"]*$)(?![^\[]*\])', 
                     engine='python', na_values=['-'], header=None,
                     usecols=[0, 3, 4, 5, 6, 7, 8,10],
                     names=['ip', 'time', 'request', 'status', 'size',
                     'referer', 'user_agent', 'req_time'])
    
  2. 使用 .reset_index() 添加一个“索引”列。

    df = df.reset_index()
    
  3. 编写自定义函数(与apply一起使用),如果可能,转换为int,否则保存 字典中的条目和“索引” wrong_lines

    wrong_lines = {}
    def convert_int_feedback_index(row,col):
        try:
            ans = int(row[col])
        except:
            wrong_lines[row['index']] = row[col]
            ans = pd.np.nan
        return ans
    
  4. 在我要转换的列上使用 apply(例如 col = 'status'、'size' 或 'req_time')

    df[col] = df.apply(convert_int_feedback_index, axis=1, col=col)
    

【问题讨论】:

  • 有什么异常?
  • @devssh :是的,我尝试使用小 nrows,错误发生在第 271119 行。至于删除转换器并使用 .astype 或 .apply,这听起来是个好主意,但我不知道如何检索发生异常的行号。
  • 你看到我对reset_index的建议了吗?它将为您提供带有行号的 index 列。将它与apply 结合到多个列中,您可以自定义所有内容。您现在似乎默认使用这种替代方法获取行号,对吗?
  • @devssh :哇,使用 'df.reset_index()' 确实可以解决问题!非常感谢!

标签: python pandas exception


【解决方案1】:

您尝试pd.read_csv(..., nrows=10) 看看它是否适用于甚至 10 行?

也许您不应该使用converters 来指定dtypes
加载 DataFrame,然后将 dtype 应用于 df["column"] = df["column"].astype(np.int64) 之类的列或 df["column"]=df["column"].apply(lambda x: convert_type(x)) 之类的自定义函数,并在函数 convert_type 中自己处理错误。
最后,通过调用 df.to_csv("preprocessed.csv", headers=True, index=False) 更新 csv。
我认为您无法从pd.read_csv 本身获取行号。该分隔符本身看起来太复杂了。

或者您可以尝试将 csv 作为单列 DataFrame 读取并使用df["column"].str.extract 使用正则表达式来提取列。这样您就可以控制如何引发异常或处理错误的默认值。

df.reset_index() 会将行号作为一列提供给您。这样,如果您apply to two columns,您也将获得行号。它将为您提供带有行号的索引列。将其与应用于多列相结合,您可以自定义所有内容。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-04
    • 2018-11-14
    • 2012-09-11
    相关资源
    最近更新 更多