如何用 pandas 打开德语 csv 文件?

abo*_*vel 2 python csv pandas

问题

\n

用 pandas 打开德语 csv 文件的最佳方法是什么?

\n

我有一个包含以下列的德语 csv 文件:

\n
    \n
  • 数据:格式为“DD.MM.YYYY”的日期
  • \n
  • Umlaute:带有特定于德语的特殊字符的德语名字
  • \n
  • Zahlen:格式为“000.000,00”的数字
  • \n
\n

我的预期输出是:

\n
            Umlaute      Zahlen\nDatum                          \n2020-01-01  R\xc3\xbcdiger  1000000.11\n2020-01-02  G\xc3\xbcnther       12.34\n2020-01-03   J\xc3\xbcrgen      567.89\n
Run Code Online (Sandbox Code Playgroud)\n

下面提供了示例数据(参见文件)。

\n
\n

第一次尝试:使用不带参数的 pd.read_csv()

\n
            Umlaute      Zahlen\nDatum                          \n2020-01-01  R\xc3\xbcdiger  1000000.11\n2020-01-02  G\xc3\xbcnther       12.34\n2020-01-03   J\xc3\xbcrgen      567.89\n
Run Code Online (Sandbox Code Playgroud)\n

这会抛出一个UnicodeDecodeError

\n
UnicodeDecodeError: \'utf-8\' codec can\'t decode byte 0xfc in position 12: invalid start byte\n
Run Code Online (Sandbox Code Playgroud)\n

第二次尝试:使用 pd.read_csv 指定编码和分隔

\n
  df = pd.read_csv(\'german_csv_test.csv\', sep=\';\', encoding=\'latin1\')\n
Run Code Online (Sandbox Code Playgroud)\n

这不会引发错误,但它与我想要的输出相去甚远:

\n
    \n
  • 日期是字符串而不是日期时间。
  • \n
  • 这些数字不是浮动的,而是对象。
  • \n
  • 列“Datum”不是索引。
  • \n
\n
        Datum  Umlaute          Zahlen\n0  01.01.2020  R\xc3\xbcdiger   1.000.000,11 \n1  02.01.2020  G\xc3\xbcnther          12,34 \n2  03.01.2020   J\xc3\xbcrgen         567,89 \n
Run Code Online (Sandbox Code Playgroud)\n

第三次尝试:清理

\n
df = pd.read_csv(\'german_csv_test.csv\', sep=\';\', encoding=\'latin1\')\ndf[\'Datum\'] = pd.to_datetime(df[\'Datum\'])\ndf = df.set_index(\'Datum\')\ndf[\'Zahlen\'] = pd.to_numeric(df[\'Zahlen\'])\n
Run Code Online (Sandbox Code Playgroud)\n

现在,我有四行代码,但它仍然不起作用。最后一行抛出错误ValueError: Unable to parse string " 1.000.000,11 " at position 0。如果我注释掉最后一行,它就会起作用。但日期仍然是错误的,因为日期和月份互换了。

\n
            Umlaute          Zahlen\nDatum                              \n2020-01-01  R\xc3\xbcdiger   1.000.000,11 \n2020-02-01  G\xc3\xbcnther          12,34 \n2020-03-01   J\xc3\xbcrgen         567,89 \n
Run Code Online (Sandbox Code Playgroud)\n
\n

文件

\n

我的文件german_csv_test.csv如下所示:

\n
Datum;Umlaute;Zahlen\n01.01.2020;R\xc3\xbcdiger; 1.000.000,11 \n02.01.2020;G\xc3\xbcnther; 12,34 \n03.01.2020;J\xc3\xbcrgen; 567,89 \n
Run Code Online (Sandbox Code Playgroud)\n

它被编码为“cp1252”。我使用“CSV (MS-DOS)”选项将其保存在 Windows 上。

\n

abo*_*vel 5

解决方案

    converters = {'Datum': lambda x: pd.to_datetime(x, format='%d.%m.%Y')}
    df1 = pd.read_csv('german_csv_test.csv', sep=';', thousands='.', decimal=',', encoding='latin1',
                      converters=converters, index_col='Datum')
Run Code Online (Sandbox Code Playgroud)

德国 csv 文件很棘手,因为乍一看它们看起来不错,但数据类型都是错误的,而且月份和日期之间的切换可能会令人沮丧。上述参数适用于各种欧洲 csv 文件。下面我将解释每个参数。

范围sep=';'

几乎所有德语 csv 文件都使用分号“;” 作为分隔符。这适用于大多数欧洲国家。您可能会说这是错误的,因为 csv 意味着“逗号分隔值”。但这不是对错的问题,而是惯例的问题。您可以说 csv 代表“字符分隔值”

参数thousands='.'decimal=','

此外,大多数欧洲国家使用点来对千位进行分组,并使用逗号来分隔小数。这篇很棒的文章解释了原因。

范围encoding='latin1'

如果您在Python 文档中查找德语编码,您将看到德语的编解码器“cp273”。它很少被使用。对于西欧,您应该可以使用“latin1”。使用此编解码器受益于 CPython 中的内部优化:

CPython 实现细节:一些常见的编码可以绕过编解码器查找机制以提高性能。这些优化机会仅被 CPython 识别为一组有限的(不区分大小写)别名:utf-8、utf8、latin-1、latin1、iso-8859-1、iso8859-1、mbcs(仅限 Windows)、ascii、us -ascii、utf-16、utf16、utf-32、utf32,同样使用下划线而不是破折号。对这些编码使用替代别名可能会导致执行速度变慢。

如需进一步阅读,请查看这篇 SO 文章Joel Spolsky 的博客

范围converters=converters

大多数 pandas 用户都低估了转换器的重要性。它看起来像是一个简单问题的复杂解决方案。pd.to_datetime()为什么读完文件后不使用呢?您希望将输入与数据处理分开(请参阅IPO 模型)。

我已经多次看到(并写过)这样的东西:

    converters = {'Datum': lambda x: pd.to_datetime(x, format='%d.%m.%Y')}
    df1 = pd.read_csv('german_csv_test.csv', sep=';', thousands='.', decimal=',', encoding='latin1',
                      converters=converters, index_col='Datum')
Run Code Online (Sandbox Code Playgroud)

在下一次迭代中,你可能会晋升pd.to_datetime()。但也许不是。这可能会导致一些意想不到的行为。编写此类代码两个月后,您只会看到一长串非结构化的 pandas 操作,并且您会认为“这真是一团糟。

有多种方法可以清理数据框。但为什么不使用内置转换器呢?如果您为数据帧的每一列定义dtypesconverters,您就不必回头(愤怒地)。打电话后你就站稳了脚跟pd.read_csv()

请注意,转换器仅接受函数。这就是我在转换器中使用 lambda 函数的原因。否则我无法指定格式参数。

在文档这篇 SO 帖子中了解有关转换器的更多信息

范围index_col='Datum'

这只是定义了索引列。它很方便,因为替代方案df = df.set_index('Datum')并不那么漂亮。此外,它还有助于 - 像转换器一样 - 将输入块与数据处理分开。