如何用熊猫打开德国的csv文件?

时间:2020-07-13 09:10:13

标签: python pandas csv

问题

用熊猫打开德国csv文件的最佳方法是什么?

我有一个德国csv文件,其中包含以下列:

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

我的预期输出是:

            Umlaute      Zahlen
Datum                          
2020-01-01  Rüdiger  1000000.11
2020-01-02  Günther       12.34
2020-01-03   Jürgen      567.89

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


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

    df = pd.read_csv('german_csv_test.csv')

这会引发UnicodeDecodeError

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfc in position 12: invalid start byte

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

  df = pd.read_csv('german_csv_test.csv', sep=';', encoding='latin1')

这不会引发任何错误,但与我期望的输出相距很远:

  • 日期是字符串而不是日期时间。
  • 数字不是浮动的,而是对象。
  • “基准”列不是索引。
        Datum  Umlaute          Zahlen
0  01.01.2020  Rüdiger   1.000.000,11 
1  02.01.2020  Günther          12,34 
2  03.01.2020   Jürgen         567,89 

第三次尝试:清理

df = pd.read_csv('german_csv_test.csv', sep=';', encoding='latin1')
df['Datum'] = pd.to_datetime(df['Datum'])
df = df.set_index('Datum')
df['Zahlen'] = pd.to_numeric(df['Zahlen'])

现在,我有四行代码,但仍然无法正常工作。最后一行抛出错误ValueError: Unable to parse string " 1.000.000,11 " at position 0。如果我将最后一行注释掉,它将起作用。但是日期仍然是错误的,因为日期和月份已切换。

            Umlaute          Zahlen
Datum                              
2020-01-01  Rüdiger   1.000.000,11 
2020-02-01  Günther          12,34 
2020-03-01   Jürgen         567,89 

文件

我的文件german_csv_test.csv如下:

Datum;Umlaute;Zahlen
01.01.2020;Rüdiger; 1.000.000,11 
02.01.2020;Günther; 12,34 
03.01.2020;Jürgen; 567,89 

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

1 个答案:

答案 0 :(得分:4)

解决方案

    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')

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

参数sep=';'

几乎所有德国csv文件都使用分号';'作为分隔符。这适用于大多数欧洲国家。您可能会说这是错误的,因为csv的意思是“逗号分隔的值”。但这不是关于对与错,而是关于惯例。您可能会说csv代表"character separated values"

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

此外,大多数欧洲国家/地区都使用点将千位和逗号分隔成小数。 This great article解释了原因。

参数encoding='latin1'

如果您在Python documentation中查找德语编码,则会看到德语的编解码器“ 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,并使用下划线代替破折号。对这些编码使用备用别名可能会导致执行速度变慢。

为了进一步阅读,请查找this SO postJoel Spolsky's blog

参数converters=converters

大多数熊猫用户对转换器的了解不足。它看起来像一个简单问题的复杂解决方案。为什么在读取文件后不使用pd.to_datetime()?您要将输入与处理数据分开(请参见IPO model)。

我看过(写过)很多次了:

  df = pd.read_csv('test.csv')
  df['Revenue'] = df['Price'] * df['Quantity']  # I don't have to clean up all columns. I just need the revenue.
  (...)  # Some other code

  # Plotting revenue
  df['Revenue'] = df['Revenue'] / 1000
  df['Date'] = pd.to_datetime(df['Date'])  # Oh, the dates are still strings. I can fix this easily before plotting.

在下一次迭代中,您可以向上移动pd.to_datetime()。但也许不是。可能导致某些意外行为。编写此类代码两个月后,您只看到了一系列漫长的非结构化熊猫操作,并且认为“ 这是一团糟。

有几种清除数据框的方法。但是为什么不使用内置转换器呢?如果您为数据框的每一列定义dtypesconverters,则不必回头(生气)。致电pd.read_csv()后,您会站稳脚跟。

请注意,转换器仅接受功能。这就是为什么我在转换器中使用了lambda函数的原因。否则,我无法指定format参数。

documentationthis SO post中了解有关转换器的更多信息

参数index_col='Datum'

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