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

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

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

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


person Community    schedule 08.04.2013    source источник


Ответы (10)


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

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

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

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

  4. convert_dtypes() - преобразовать столбцы DataFrame в наилучший из возможных dtype, который поддерживает pd.NA (объект pandas для указания отсутствующего значения).

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


1. to_numeric()

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

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

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

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

Обычно это то, что вам нужно, но что, если вы хотите сэкономить немного памяти и использовать более компактный 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() позволяет четко указать dtype, который должен иметь ваш DataFrame или Series. Он очень универсален, так как вы можете пробовать переходить от одного типа к другому.

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

Просто выберите тип: вы можете использовать dtype NumPy (например, np.int16), некоторые типы Python (например, bool) или типы, специфичные для pandas (например, категориальный 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, вы получите сообщение об ошибке при попытке преобразовать его в целое число.

Начиная с pandas 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') вместо этого может помочь предотвратить эту ошибку.


3. infer_objects()

Версия 0.21.0 pandas представила метод _51 _ для преобразования столбцов 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).


4. convert_dtypes()

Версия 1.0 и выше включает метод convert_dtypes() для преобразования столбцов Series и DataFrame в наилучший из возможных dtype, поддерживающий pd.NA отсутствующее значение.

Здесь лучший из возможных означает тип, наиболее подходящий для хранения значений. Например, это целочисленный тип pandas, если все значения являются целыми числами (или пропущенными значениями): столбец объекта целочисленных объектов Python преобразуется в Int64, столбец значений NumPy int32 станет pandas dtype Int32.

С нашим object DataFrame df мы получаем следующий результат:

>>> df.convert_dtypes().dtypes                                             
a     Int64
b    string
dtype: object

Поскольку столбец «a» содержал целочисленные значения, он был преобразован в тип Int64 (который может содержать отсутствующие значения, в отличие от int64).

Столбец 'b' содержал строковые объекты, поэтому был изменен на pandas 'string dtype.

По умолчанию этот метод определяет тип на основе значений объекта в каждом столбце. Мы можем изменить это, передав infer_objects=False:

>>> df.convert_dtypes(infer_objects=False).dtypes                          
a    object
b    string
dtype: object

Теперь столбец 'a' остался столбцом объекта: pandas знает, что его можно описать как столбец 'integer' (внутри он работал _ 70_), но не понял, какой именно тип целого числа должен иметь, поэтому не преобразовал его. Столбец «b» снова был преобразован в «строковый» dtype, поскольку он был распознан как содержащий «строковые» значения.

person Alex Riley    schedule 21.02.2015
comment
Кроме того, в отличие от .astype (float), это преобразует строки в NaN вместо того, чтобы вызывать ошибку. - person Rob; 04.08.2015
comment
.convert_objects устарело с 0.17 - используйте вместо этого df.to_numeric - person Matti Lyra; 31.10.2015
comment
есть ли способ error=coerce в astype()? - person fogx; 07.05.2021

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

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
person hernamesbarbara    schedule 21.04.2013
comment
Да! pd.DataFrame имеет dtype аргумент, который может позволить вам делать то, что вы ищете. df = pd.DataFrame (a, columns = ['one', 'two', 'three'], dtype = float) In [2]: df.dtypes Out [2]: один объект два float64 три float64 dtype: object - person hernamesbarbara; 09.12.2013
comment
Когда я пытаюсь выполнить предложенную операцию, я получаю предупреждение 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. Возможно, это было введено в более новую версию pandas, и в результате я не вижу ничего плохого, но мне просто интересно, о чем это предупреждение. Любая идея? - person orange; 06.06.2014
comment
@orange предупреждение должно предупредить пользователей о потенциально запутанном поведении с цепными операциями и с пандами, возвращающими копии, а не редактирующие фреймы данных. см. stackoverflow.com/questions/ 20625582 / и родственные. - person A.Wan; 27.06.2014
comment
Это хороший метод, но он не работает, когда в столбце есть NaN. Понятия не имею, почему NaN просто не может оставаться NaN при приведении float к int: ValueError: Cannot convert NA to integer - person Vitaly Isaev; 21.01.2015
comment
@VitalyIsaev: поскольку numpy ints не имеют представления для NaN: pandas.pydata.org/pandas-docs/stable/ - person Pietro Battiston; 22.10.2015
comment
@hernamesbarbara, можно ли передавать разные dtype для разных столбцов? - person Gill Bates; 07.06.2016
comment
@GillBates да, в словаре. df = pd.DataFrame(a, columns=['one', 'two', 'three'], dtype={'one': str, 'two': int, 'three': float}). Однако мне сложно найти спецификацию для принятых значений dtype. Список был бы хорош (сейчас я делаю dict(enumerate(my_list))). - person FichteFoll; 07.07.2016
comment
@FichteFoll Не может работать. Поднимает ValueError: entry not a 2- or 3- tuple - person wyx; 11.04.2017
comment
Это также преобразует дату и время. - person Sören; 15.02.2018

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

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

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

person Akash Nayak    schedule 15.11.2017
comment
Имейте в виду, что при применении этого к столбцу, содержащему строки `` 'True' '' 'и ``' 'False' '`` с использованием data_type bool, все изменяется на True. - person H. Vabri; 06.01.2018
comment
Эту опцию вы также можете преобразовать в категорию типа - person neves; 22.09.2018

Когда мне нужно было указать только определенные столбцы, и я хочу быть явным, я использовал (per МЕСТОПОЛОЖЕНИЕ ДОКУМЕНТОВ):

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'})
person Thom Ives    schedule 12.10.2018

Вот функция, которая принимает в качестве аргументов 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'])
person Harry Stevens    schedule 14.06.2017
comment
что, если вы хотите использовать индексы столбцов вместо имен столбцов? - person jvalenti; 05.07.2019

панды> = 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                                                     
person cs95    schedule 18.02.2020

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

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-м столбце.

person MikeyE    schedule 11.07.2017

Начиная с pandas 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
person Sohail    schedule 06.04.2020

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

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

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

person SarahD    schedule 01.02.2019

person    schedule
comment
Мне нравится, как df.info () указывает использование памяти в последней строке. - person BSalita; 08.05.2021