python pandas — общие способы работы с запятыми в строке для преобразования с плавающей запятой с помощью astype()

есть ли общий способ указать пандам использовать запятую (,) в качестве десятичных разделителей для преобразования типа из строки в число с плавающей запятой и т. д.?

import pandas as pd
from datetime import datetime

data = {
    "col_str": ["a", "b", "c"],
    "col_int": ["1", "2", "3"],
    "col_float": ["1,2", "3,2342", "97837,8277"],
    "col_float2": ["13,2", "3234,2342", "263,8277"],
    "col_date": [datetime(2020, 8, 1, 0, 3, 4).isoformat(),
                 datetime(2020, 8, 2, 2, 4, 5).isoformat(),
                 datetime(2020, 8, 3, 6, 8, 4).isoformat()
                 ]
}

conversion_dict = {
    "col_str": str,
    "col_int": int,
    "col_float": float,
    "col_float2": float,
    "col_date": "datetime64"
}

df = pd.DataFrame(data=data)

print(df.dtypes)
df = df.astype(conversion_dict, errors="ignore")
print(df.dtypes)
print(df)

Приведенный выше пример возвращает столбцы объектов для col_float и col_float2 или выдает ошибку, если установлено значение errors.

Я хотел бы использовать метод astype() напрямую, не заменяя вручную запятые точками. Источник данных обычно возвращает числа с запятой в качестве десятичного разделителя, поскольку языковой стандарт установлен на немецкий.

Есть ли общий способ указать pandas как тип, что запятые в числах с плавающей запятой - или любой другой числовой тип данных с десятичными знаками - в порядке и должны быть преобразованы автоматически?

PS: я не могу использовать read_csv, где вы можете напрямую указать разделитель, потому что это база данных.

Заранее спасибо.


person Salfii    schedule 20.08.2020    source источник
comment
Знаете ли вы, какие столбцы имеют проблему, или выяснение этого является частью проблемы? Наиболее простым может быть замена, а затем преобразование: pd.to_numeric(df['col_float'].str.replace(',', '.')). IMO методы pd.to_XXX лучше подходят для преобразования типов   -  person ALollz    schedule 20.08.2020
comment
Привет @ALollz, выяснение того, в каких колонках есть эта ткань, является частью проблемы в реальном приложении.   -  person Salfii    schedule 20.08.2020


Ответы (2)


Я исправил проблему следующим обходным путем. Это все еще может сломаться в некоторых случаях, но я не нашел способа сообщить pands astype(), что запятая в порядке. Если у кого-то есть другое решение только для панд, сообщите мне:

import locale
from datetime import datetime
import pandas as pd

data = {
    "col_str": ["a", "b", "c"],
    "col_int": ["1", "2", "3"],
    "col_float": ["1,2", "3,2342", "97837,8277"],
    "col_float2": ["13,2", "3234,2342", "263,8277"],
    "col_date": [datetime(2020, 8, 1, 0, 3, 4).isoformat(),
                 datetime(2020, 8, 2, 2, 4, 5).isoformat(),
                 datetime(2020, 8, 3, 6, 8, 4).isoformat()
                 ]
}

conversion_dict = {
    "col_str": str,
    "col_int": int,
    "col_float": float,
    "col_float2": float,
    "col_date": "datetime64"
}

df = pd.DataFrame(data=data)
throw_error = True

try:
    df = df.astype(conversion_dict, errors="raise")
except ValueError as e:
    error_message = str(e).strip().upper()
    error_search = "COULD NOT CONVERT STRING TO FLOAT:"
    # compare error messages to only get the string to float error because pandas only throws ValueError´s which
    # are not datatype specific. This might be quite hacky because error messages could change.
    if error_message[:len(error_search)] == error_search:
        # convert everything else and ignore errors for the float columns
        df = df.astype(conversion_dict, errors="ignore")
        # go over the conversion dict
        for key, value in conversion_dict.items():
            # print(str(key) + ":" + str(value) + ":" + str(df[key].dtype))
            # only apply to convert-to-float-columns which are not already in the correct pandas type float64
            # if you don´t check for correctly classified types, .str.replace() throws an error
            if (value == float or value == "float") and df[key].dtype != "float64":
                # df[key].apply(locale.atof) or anythin locale related is plattform dependant and therefore bad
                # in my opinion
                # locale settings for atof
                # WINDOWS: locale.setlocale(locale.LC_ALL, 'deu_deu')
                # UNIX: locale.setlocale(locale.LC_ALL, 'de_DE')
                df[key] = pd.to_numeric(df[key].str.replace(',', '.'))
    else:
        if throw_error:
            # or do whatever is best suited for your use case
            raise ValueError(str(e))
        else:
            df = df.astype(conversion_dict, errors="ignore")

print(df.dtypes)
print(df)
person Salfii    schedule 20.08.2020

Вы можете использовать библиотеку locale, чтобы решить эту проблему общим способом с помощью apply() и locale.atof. Просто замените на соответствующую локаль. В данном случае я использовал de_DE, потому что они используют десятичную дробь.

import locale
from datetime import datetime

import pandas as pd

locale.setlocale(locale.LC_ALL, locale="de_DE")


data = {
    "col_str": ["a", "b", "c"],
    "col_int": ["1", "2", "3"],
    "col_float": ["1,2", "3,2342", "97837,8277"],
    "col_float2": ["13,2", "3234,2342", "263,8277"],
    "col_date": [datetime(2020, 8, 1, 0, 3, 4).isoformat(),
                 datetime(2020, 8, 2, 2, 4, 5).isoformat(),
                 datetime(2020, 8, 3, 6, 8, 4).isoformat()
                 ]
}

conversion_dict = {
    "col_str": str,
    "col_int": int,
    "col_float": str,
    "col_float2": str,
    "col_date": "datetime64"
}

df = pd.DataFrame(data=data)

print(df.dtypes)
df = df.astype(conversion_dict, errors="ignore")
df["col_float"] = df["col_float"].apply(locale.atof)
df["col_float2"] = df["col_float2"].apply(locale.atof)
print(df.dtypes)
print(df)

Выход:

col_str       object
col_int       object
col_float     object
col_float2    object
col_date      object
dtype: object
col_str               object
col_int                int64
col_float            float64
col_float2           float64
col_date      datetime64[ns]
dtype: object
  col_str  col_int   col_float  col_float2            col_date
0       a        1      1.2000     13.2000 2020-08-01 00:03:04
1       b        2      3.2342   3234.2342 2020-08-02 02:04:05
2       c        3  97837.8277    263.8277 2020-08-03 06:08:04
person Nikolaus Gullotta    schedule 20.08.2020
comment
Спасибо за ваш ответ. К сожалению, это не общее решение, которое я ищу, потому что мне нужно явно указать столбцы и применить к ним метод, отличный от astype(). Было бы здорово, если бы astype учитывал настройки локали, а не просто требовал эту точку. - person Salfii; 20.08.2020
comment
Кроме того, имейте в виду, что локаль зависит от платформы, а это означает, что приведенный выше код, по моему мнению, не будет работать в Windows. WINDOWS: locale.setlocale(locale.LC_ALL, 'deu_deu') UNIX: locale.setlocale(locale.LC_ALL, 'de_DE') Этот фрагмент кода теперь запрещает вашему коду беговая кросс-платформа. - person Salfii; 20.08.2020
comment
Вы всегда можете добавить проверку запятых в данных для каждого столбца. Кроме того, это не делает код неработоспособным на других платформах; вы можете проверить, работаете ли вы в Windows/Unix, используя os.name, и определить соответствующую локаль оттуда. - person Nikolaus Gullotta; 20.08.2020