Изменить тип данных столбцов в Pandas

Я хочу преобразовать таблицу, представленную как список списков, в Pandas DataFrame. В качестве чрезвычайно упрощенного примера:

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a)

Каков наилучший способ преобразования столбцов в соответствующие типы, в этом случае столбцы 2 и 3 в поплавки? Есть ли способ указать типы при преобразовании в DataFrame? Или лучше сначала создать DataFrame, а затем пропустить столбцы, чтобы изменить тип для каждого столбца? В идеале я хотел бы сделать это динамически, потому что может быть сотни столбцов, и я не хочу точно указывать, какие столбцы имеют тип. Все, что я могу гарантировать, состоит в том, что каждый столбец содержит значения одного и того же типа.

Ответ 1

У вас есть три основных варианта для преобразования типов в пандах:

  1. to_numeric() - предоставляет функциональные возможности для безопасного преобразования нечисловых типов (например, строк) в подходящий числовой тип. (Смотрите также to_datetime() и to_timedelta().)

  2. astype() - преобразует (почти) любой тип в (почти) любой другой тип (даже если это не всегда целесообразно). Также позволяет конвертировать в категориальные типы (очень полезно).

  3. infer_objects() - служебный метод для преобразования столбцов объектов, содержащих объекты Python, в тип панд, если это возможно.

Продолжайте читать для более подробных объяснений и использования каждого из этих методов.


1. to_numeric()

Лучший способ преобразовать один или несколько столбцов DataFrame в числовые значения - это использовать pandas.to_numeric().

Эта функция попытается преобразовать нечисловые объекты (например, строки) в целые числа или числа с плавающей запятой, в зависимости от ситуации.

Основное использование

Входные данные для to_numeric() - это Series или отдельный столбец DataFrame.

>>> s = pd.Series(["8", 6, "7.5", 3, "0.9"]) # mixed string and numeric values
>>> s
0      8
1      6
2    7.5
3      3
4    0.9
dtype: object

>>> pd.to_numeric(s) # convert everything to float values
0    8.0
1    6.0
2    7.5
3    3.0
4    0.9
dtype: float64

Как видите, новая серия возвращается. Не забудьте назначить этот вывод переменной или столбцу для продолжения его использования:

# convert Series
my_series = pd.to_numeric(my_series)

# convert column "a" of a DataFrame
df["a"] = pd.to_numeric(df["a"])

Вы также можете использовать его для преобразования нескольких столбцов DataFrame с помощью метода apply():

# convert all columns of DataFrame
df = df.apply(pd.to_numeric) # convert all columns of DataFrame

# convert just columns "a" and "b"
df[["a", "b"]] = df[["a", "b"]].apply(pd.to_numeric)

Пока ваши значения могут быть преобразованы, это, вероятно, все, что вам нужно.

Обработка ошибок

Но что, если некоторые значения не могут быть преобразованы в числовой тип?

to_numeric() также принимает аргумент ключевого слова errors который позволяет принудительно указывать для нечисловых значений значение NaN или просто игнорировать столбцы, содержащие эти значения.

Вот пример использования серии строк s которая имеет объект dtype:

>>> s = pd.Series(['1', '2', '4.7', 'pandas', '10'])
>>> s
0         1
1         2
2       4.7
3    pandas
4        10
dtype: object

Поведение по умолчанию - повышение, если оно не может преобразовать значение. В этом случае он не может справиться со строкой 'pandas':

>>> pd.to_numeric(s) # or pd.to_numeric(s, errors='raise')
ValueError: Unable to parse string

Вместо того, чтобы терпеть неудачу, мы могли бы хотеть, чтобы 'панды' считались отсутствующим/плохим числовым значением. Мы можем привести недействительные значения к NaN следующим образом, используя аргумент errors:

>>> pd.to_numeric(s, errors='coerce')
0     1.0
1     2.0
2     4.7
3     NaN
4    10.0
dtype: float64

Третий вариант для errors - просто игнорировать операцию, если встречается недопустимое значение:

>>> pd.to_numeric(s, errors='ignore')
# the original Series is returned untouched

Этот последний вариант особенно полезен, когда вы хотите преобразовать весь свой DataFrame, но не знаете, какие из наших столбцов можно надежно преобразовать в числовой тип. В этом случае просто напишите:

df.apply(pd.to_numeric, errors='ignore')

Функция будет применена к каждому столбцу DataFrame. Столбцы, которые можно преобразовать в числовой тип, будут преобразованы, тогда как столбцы, которые не могут (например, содержат нецифровые строки или даты), будут оставлены в покое.

понижающее приведение

По умолчанию преобразование с помощью to_numeric() даст вам int64 или float64 dtype (или любую целую ширину, присущую вашей платформе).

Обычно это то, что вы хотите, но что, если вы хотите сэкономить память и использовать более компактный dtype, такой как float32 или int8?

to_numeric() дает вам возможность понижать до целых чисел, со to_numeric(), без знака, с плавающей точкой. Вот пример для простой серии s целочисленного типа:

>>> s = pd.Series([1, 2, -7])
>>> s
0    1
1    2
2   -7
dtype: int64

Понижение до "целого" использует наименьшее возможное целое число, которое может содержать значения:

>>> pd.to_numeric(s, downcast='integer')
0    1
1    2
2   -7
dtype: int8

Даункастинг до 'float' аналогично выбирает плавающий тип меньше обычного:

>>> pd.to_numeric(s, downcast='float')
0    1.0
1    2.0
2   -7.0
dtype: float32

2. astype()

Метод astype() позволяет вам четко указывать dtype, который вы хотите иметь в своем DataFrame или Series. Он очень универсален в том, что вы можете попробовать перейти от одного типа к другому.

Основное использование

Просто выберите тип: вы можете использовать dum типа NumPy (например, np.int16), некоторые типы Python (например, bool) или типы, специфичные для панд (например, категориальный dtype).

Вызовите метод для объекта, который вы хотите преобразовать, и astype() попытается преобразовать его для вас:

# convert all DataFrame columns to the int64 dtype
df = df.astype(int)

# convert column "a" to int64 dtype and "b" to complex type
df = df.astype({"a": int, "b": complex})

# convert Series to float16 type
s = s.astype(np.float16)

# convert Series to Python strings
s = s.astype(str)

# convert Series to categorical type - see docs for more details
s = s.astype('category')

Заметьте, я сказал "попробуй" - если astype() не знает, как преобразовать значение в Series или DataFrame, это вызовет ошибку. Например, если у вас есть значение NaN или inf вы получите ошибку, пытаясь преобразовать его в целое число.

Начиная с панд 0.20.0, эта ошибка может быть подавлена передачей errors='ignore'. Ваш оригинальный объект будет возвращен нетронутым.

Быть осторожен

astype() является мощным, но иногда он будет преобразовывать значения "неправильно". Например:

>>> s = pd.Series([1, 2, -7])
>>> s
0    1
1    2
2   -7
dtype: int64

Это небольшие целые числа, так как насчет преобразования в 8-битный тип без знака для экономии памяти?

>>> s.astype(np.uint8)
0      1
1      2
2    249
dtype: uint8

Преобразование сработало, но -7 было округлено до 249 (то есть 2 8 - 7)!

Попытка pd.to_numeric(s, downcast='unsigned') использованием pd.to_numeric(s, downcast='unsigned') вместо этого может помочь предотвратить эту ошибку.


3. infer_objects()

Версия 0.21.0 pandas представила метод infer_objects() для преобразования столбцов DataFrame, имеющих тип данных объекта, в более конкретный тип (мягкие преобразования).

Например, вот DataFrame с двумя столбцами типа объекта. Один содержит действительные целые числа, а другой содержит строки, представляющие целые числа:

>>> df = pd.DataFrame({'a': [7, 1, 5], 'b': ['3','2','1']}, dtype='object')
>>> df.dtypes
a    object
b    object
dtype: object

Используя infer_objects(), вы можете изменить тип столбца 'a' на int64:

>>> df = df.infer_objects()
>>> df.dtypes
a     int64
b    object
dtype: object

Столбец "b" остался один, поскольку его значения были строками, а не целыми числами. Если вы хотите попытаться преобразовать оба столбца в целочисленный тип, вместо этого вы можете использовать df.astype(int).

Ответ 2

Как насчет этого?

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])
df
Out[16]: 
  one  two three
0   a  1.2   4.2
1   b   70  0.03
2   x    5     0

df.dtypes
Out[17]: 
one      object
two      object
three    object

df[['two', 'three']] = df[['two', 'three']].astype(float)

df.dtypes
Out[19]: 
one       object
two      float64
three    float64

Ответ 3

этот ниже код изменит тип данных столбца.

df[['col.name1', 'col.name2'...]] = df[['col.name1', 'col.name2'..]].astype('data_type')

вместо типа данных вы можете указать свой тип данных. Что вы хотите, например, str, float, int и т.д.

Ответ 4

Вот функция, которая принимает в качестве своих аргументов DataFrame и список столбцов и коэрцирует все данные в столбцах в числа.

# df is the DataFrame, and column_list is a list of columns as strings (e.g ["col1","col2","col3"])
# dependencies: pandas

def coerce_df_columns_to_numeric(df, column_list):
    df[column_list] = df[column_list].apply(pd.to_numeric, errors='coerce')

Итак, для вашего примера:

import pandas as pd

def coerce_df_columns_to_numeric(df, column_list):
    df[column_list] = df[column_list].apply(pd.to_numeric, errors='coerce')

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a, columns=['col1','col2','col3'])

coerce_df_columns_to_numeric(df, ['col2','col3'])

Ответ 5

Как создать два фрейма данных, каждый с разными типами данных для своих столбцов, а затем добавить их вместе?

d1 = pd.DataFrame(columns=[ 'float_column' ], dtype=float)
d1 = d1.append(pd.DataFrame(columns=[ 'string_column' ], dtype=str))

Результаты

In[8}:  d1.dtypes
Out[8]: 
float_column     float64
string_column     object
dtype: object

После создания фрейма данных вы можете заполнить его переменными с плавающей запятой в 1-м столбце и строки (или любой желаемый тип данных) во втором столбце.

Ответ 6

Когда мне нужно было указать только определенные столбцы, и я хочу быть явным, я использовал (для DOCS LOCATION):

dataframe = dataframe.astype({'col_name_1':'int','col_name_2':'float64', etc. ...})

Итак, используя оригинальный вопрос, но предоставив ему имена столбцов...

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a, columns=['col_name_1', 'col_name_2', 'col_name_3'])
df = df.astype({'col_name_2':'float64', 'col_name_3':'float64'})

Ответ 7

Я думал, что у меня та же проблема, но на самом деле у меня есть небольшая разница, которая облегчает решение проблемы. Для других, смотрящих на этот вопрос, стоит проверить формат вашего входного списка. В моем случае числа изначально плавающие, а не строки, как в вопросе:

a = [['a', 1.2, 4.2], ['b', 70, 0.03], ['x', 5, 0]]

но слишком много обрабатывая список перед созданием кадра данных, я теряю типы, и все становится строкой.

Создание фрейма данных через массив NumPy

df = pd.DataFrame(np.array(a))

df
Out[5]: 
   0    1     2
0  a  1.2   4.2
1  b   70  0.03
2  x    5     0

df[1].dtype
Out[7]: dtype('O')

дает тот же кадр данных, что и в вопросе, где записи в столбцах 1 и 2 рассматриваются как строки. Однако делать

df = pd.DataFrame(a)

df
Out[10]: 
   0     1     2
0  a   1.2  4.20
1  b  70.0  0.03
2  x   5.0  0.00

df[1].dtype
Out[11]: dtype('float64')

на самом деле дает кадр данных со столбцами в правильном формате