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

807

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

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

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

социопат
источник
Я видел подходы для преобразования каждого столбца и подходы для преобразования столбцов с конкретными именами, но как насчет определенных столбцов, которые удовлетворяют определенному условию, когда вы не можете перечислить 100 столбцов, которые хотите преобразовать одновременно? Я имею в виду, например, все float64 -> float32 или другую тактику сохранения памяти.
демонголем
@demongolem: вы можете сделать что-то вроде df.apply(pd.to_numeric, downcast="integer", errors="ignore")уменьшения целочисленных столбцов до наименьшего (целочисленного) dtype, который будет содержать значения.
Алекс Райли

Ответы:

1196

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

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

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

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

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


1. to_numeric()

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

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

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

Вход to_numeric()является серия или один столбец 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()даст вам тип a int64или float64dtype (или любую целую ширину, присущую вашей платформе).

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

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()метод позволяет вам четко указать тип d, который вы хотите иметь в своем 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)!

Попытка использовать downcast 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)вместо этого.

Алекс Райли
источник
8
Кроме того, в отличие от .astype (float), это преобразует строки в NaN вместо возникновения ошибки
Роб
11
.convert_objectsсчитается испорченным 0.17- используйте df.to_numericвместо этого
Matti Lyra
4
Спасибо - я должен обновить этот ответ. Возможно, стоит отметить, что pd.to_numericего методы-компаньоны будут работать только с одним столбцом за раз, в отличие от этого convert_objects. Обсуждение функции замены в API, похоже, продолжается ; Я надеюсь, что метод, который работает во всем DataFrame, останется, потому что он очень полезен.
Алекс Райли
Как лучше всего преобразовать все столбцы, скажем, int64в данный момент int32?
RoyalTS
4
@RoyalTS: лучше всего использовать astype(как и в других ответов), то есть .astype(numpy.int32).
Алекс Райли
447

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

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
hernamesbarbara
источник
10
Да! pd.DataFrameесть dtypeаргумент, который может позволить вам сделать, что вы ищете. df = pd.DataFrame (a, columns = ['one', 'two', 'three'], dtype = float) In [2]: df.dtypes Out [2]: один объект два float64 три float64 dtype: object
hernamesbarbara
17
Когда я пытаюсь как предложено, я получаю предупреждение SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_index,col_indexer] = value instead. Возможно, это было введено в более новой версии панд, и я не вижу в этом ничего плохого, но мне просто интересно, что это за предупреждение. Есть идеи?
оранжевый
2
Предупреждение @orange предупреждает пользователей о возможном путанице в поведении с цепочечными операциями и с пандами, которые возвращают копии, а не редактируют кадры данных. см. stackoverflow.com/questions/20625582/… и связанные с ними.
А.Ван
19
Это хороший метод, но он не работает, когда в столбце есть NaN. Понятия не имею, почему NaN просто не может оставаться NaN при приведении float к int:ValueError: Cannot convert NA to integer
Виталий Исаев
7
@GillBates да, в словаре. df = pd.DataFrame(a, columns=['one', 'two', 'three'], dtype={'one': str, 'two': int, 'three': float}), Мне трудно найти спецификацию для принятых значений "dtype". Список был бы хорош (в настоящее время я делаю dict(enumerate(my_list))).
FichteFoll
39

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

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

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

Акаш Наяк
источник
Помните, что при применении этого к столбцу, содержащему строки `` `'True'` `` и `` `'False'` `` с использованием data_type bool, все меняется на True.
Х. Вабри
Эту опцию вы также можете преобразовать в тип "категория"
Neves
17

Когда мне нужно было указать только определенные столбцы, и я хочу быть явным, я использовал (в соответствии с 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'})
Том Айвз
источник
15

Вот функция, которая принимает в качестве аргументов объект 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'])
Гарри Стивенс
источник
Что если вы хотите использовать индексы столбцов вместо имен столбцов?
19
8

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

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-м столбце и строками (или любым другим типом данных) во 2-м столбце.

MikeyE
источник
4

панды> = 1,0

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

введите описание изображения здесь

Преобразование в строку тривиально .astype(str)и не показано на рисунке.

"Жесткие" и "мягкие" преобразования

Обратите внимание, что «преобразования» в этом контексте могут относиться либо к преобразованию текстовых данных в их фактический тип данных (жесткое преобразование), либо к выводу более подходящих типов данных для данных в столбцах объекта (мягкое преобразование). Чтобы проиллюстрировать разницу, взгляните на

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

a    object
b    object
dtype: object

# Actually converts string to numeric - hard conversion
df.apply(pd.to_numeric).dtypes                                             

a    int64
b    int64
dtype: object

# Infers better data types for object data - soft conversion
df.infer_objects().dtypes                                                  

a    object  # no change
b     int64
dtype: object

# Same as infer_objects, but converts to equivalent ExtensionType
df.convert_dtypes().dtypes                                                     
cs95
источник
1

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

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

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

SarahD
источник
0

Стартовые панды 1.0.0 у нас есть pandas.DataFrame.convert_dtypes. Вы даже можете контролировать, какие типы конвертировать!

In [40]: df = pd.DataFrame(
    ...:     {
    ...:         "a": pd.Series([1, 2, 3], dtype=np.dtype("int32")),
    ...:         "b": pd.Series(["x", "y", "z"], dtype=np.dtype("O")),
    ...:         "c": pd.Series([True, False, np.nan], dtype=np.dtype("O")),
    ...:         "d": pd.Series(["h", "i", np.nan], dtype=np.dtype("O")),
    ...:         "e": pd.Series([10, np.nan, 20], dtype=np.dtype("float")),
    ...:         "f": pd.Series([np.nan, 100.5, 200], dtype=np.dtype("float")),
    ...:     }
    ...: )

In [41]: dff = df.copy()

In [42]: df 
Out[42]: 
   a  b      c    d     e      f
0  1  x   True    h  10.0    NaN
1  2  y  False    i   NaN  100.5
2  3  z    NaN  NaN  20.0  200.0

In [43]: df.dtypes
Out[43]: 
a      int32
b     object
c     object
d     object
e    float64
f    float64
dtype: object

In [44]: df = df.convert_dtypes()

In [45]: df.dtypes
Out[45]: 
a      Int32
b     string
c    boolean
d     string
e      Int64
f    float64
dtype: object

In [46]: dff = dff.convert_dtypes(convert_boolean = False)

In [47]: dff.dtypes
Out[47]: 
a      Int32
b     string
c     object
d     string
e      Int64
f    float64
dtype: object
Sohail
источник