как проверить dtype столбца в python pandas

135

Мне нужно использовать разные функции для обработки числовых и строковых столбцов. То, что я делаю сейчас, действительно глупо:

allc = list((agg.loc[:, (agg.dtypes==np.float64)|(agg.dtypes==np.int)]).columns)
for y in allc:
    treat_numeric(agg[y])    

allc = list((agg.loc[:, (agg.dtypes!=np.float64)&(agg.dtypes!=np.int)]).columns)
for y in allc:
    treat_str(agg[y])    

Есть ли более элегантный способ сделать это? Например

for y in agg.columns:
    if(dtype(agg[y]) == 'string'):
          treat_str(agg[y])
    elif(dtype(agg[y]) != 'string'):
          treat_numeric(agg[y])
Джеймс Бонд
источник
2
stringне dtype
Дэвид Робинсон

Ответы:

124

Вы можете получить доступ к типу данных столбца с помощью dtype:

for y in agg.columns:
    if(agg[y].dtype == np.float64 or agg[y].dtype == np.int64):
          treat_numeric(agg[y])
    else:
          treat_str(agg[y])
Дэвид Робинсон
источник
1
Привет, Дэвид! Можете ли вы прокомментировать, почему вы включили == np.float64? Разве мы не пытаемся преобразовать в числа с плавающей запятой? Спасибо.
Райан Чейз
@RyanChase OP в этом вопросе никогда не говорил, что он преобразовывает в числа с плавающей запятой, ему просто нужно было знать, использовать ли (неуказанную) treat_numericфункцию. Поскольку он включал agg.dtypes==np.float64в качестве опции, я тоже.
Дэвид Робинсон
3
В numpy больше числовых типов, чем этих двух, все ниже number: docs.scipy.org/doc/numpy-1.13.0/reference/arrays.scalars.html Общее решениеis_numeric_dtype(agg[y])
Аттила
96

В pandas 0.20.2это можно сделать:

from pandas.api.types import is_string_dtype
from pandas.api.types import is_numeric_dtype

is_string_dtype(df['A'])
>>>> True

is_numeric_dtype(df['B'])
>>>> True

Итак, ваш код становится:

for y in agg.columns:
    if (is_string_dtype(agg[y])):
        treat_str(agg[y])
    elif (is_numeric_dtype(agg[y])):
        treat_numeric(agg[y])
danthelion
источник
1
Есть ли альтернатива более старым версиям панд? Я получаю сообщение об ошибке: Нет модуля с именем api.types.
рф 02
2
pandas.core.common.is_numeric_dtypeсуществует с Pandas 0.13, и он делает то же самое, но pandas.api.types.is_numeric_dtype, я думаю , он устарел в пользу
версии
Это самый родной ответ. Но здесь следует помнить о некоторых оговорках .
BeforeFlight 02
46

Я знаю, что это немного старая тема, но с pandas 19.02 вы можете:

df.select_dtypes(include=['float64']).apply(your_function)
df.select_dtypes(exclude=['string','object']).apply(your_other_function)

http://pandas.pydata.org/pandas-docs/version/0.19.2/generated/pandas.DataFrame.select_dtypes.html

Майк
источник
1
хороший ответ, хотя я бы, вероятно, сделал include[np.number](чтобы также включить целые числа и 32-битные числа с плавающей запятой) для первой строки и exclude[object]для второй строки. Строки являются объектами в том, что касается типов. Фактически, включение строки с объектом дает мне ошибку.
JohnE
1
кажется, что "строка" больше не поддерживается, вместо нее нужно использовать "объект". Но однозначно правильный ответ :)
Бертран
Также следует отметить, что 'period'dtype пока растет NotImplementedError(pandas 0.24.2). Так что может потребоваться ручная постобработка.
BeforeFlight 02
22

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

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

1. Сравнение типов напрямую через ==(принятый ответ).

Несмотря на то, что это принятый ответ и наибольшее количество положительных голосов, я думаю, что этот метод не следует использовать вообще. Потому что на самом деле этот подход не рекомендуется в Python, как упоминалось здесь несколько раз .
Но если один все еще хотите использовать его - должны быть осведомлены о некоторых панды специфических dtypes , как pd.CategoricalDType, pd.PeriodDtypeили pd.IntervalDtype. Здесь нужно использовать extra type( ), чтобы правильно распознать dtype:

s = pd.Series([pd.Period('2002-03','D'), pd.Period('2012-02-01', 'D')])
s
s.dtype == pd.PeriodDtype   # Not working
type(s.dtype) == pd.PeriodDtype # working 

>>> 0    2002-03-01
>>> 1    2012-02-01
>>> dtype: period[D]
>>> False
>>> True

Еще одно предостережение: необходимо точно указать тип:

s = pd.Series([1,2])
s
s.dtype == np.int64 # Working
s.dtype == np.int32 # Not working

>>> 0    1
>>> 1    2
>>> dtype: int64
>>> True
>>> False

2. isinstance()подход.

Этот метод пока не упоминается в ответах.

Так что если прямое сравнение типов не очень хорошая идея - давайте попробуем для этого встроенную функцию python, а именно - isinstance().
Он терпит неудачу только в начале, потому что предполагает, что у нас есть некоторые объекты, но pd.Seriesили pd.DataFrameмогут использоваться как просто пустые контейнеры с предопределенными, dtypeно без объектов в нем:

s = pd.Series([], dtype=bool)
s

>>> Series([], dtype: bool)

Но если кто-то как-то преодолеет эту проблему и захочет получить доступ к каждому объекту, например, в первой строке, и проверит его dtype примерно так:

df = pd.DataFrame({'int': [12, 2], 'dt': [pd.Timestamp('2013-01-02'), pd.Timestamp('2016-10-20')]},
                  index = ['A', 'B'])
for col in df.columns:
    df[col].dtype, 'is_int64 = %s' % isinstance(df.loc['A', col], np.int64)

>>> (dtype('int64'), 'is_int64 = True')
>>> (dtype('<M8[ns]'), 'is_int64 = False')

Это будет вводить в заблуждение в случае смешанного типа данных в одном столбце:

df2 = pd.DataFrame({'data': [12, pd.Timestamp('2013-01-02')]},
                  index = ['A', 'B'])
for col in df2.columns:
    df2[col].dtype, 'is_int64 = %s' % isinstance(df2.loc['A', col], np.int64)

>>> (dtype('O'), 'is_int64 = False')

И последнее, но не менее важное - этот метод не может напрямую распознавать Categorydtype. Как указано в документах :

При возврате одного элемента из категориальных данных также будет возвращено значение, а не категориальное значение длины «1».

df['int'] = df['int'].astype('category')
for col in df.columns:
    df[col].dtype, 'is_int64 = %s' % isinstance(df.loc['A', col], np.int64)

>>> (CategoricalDtype(categories=[2, 12], ordered=False), 'is_int64 = True')
>>> (dtype('<M8[ns]'), 'is_int64 = False')

Так что этот метод тоже практически неприменим.

3. df.dtype.kindподход.

Этот метод еще может работать с пустым pd.Seriesили pd.DataFramesимеет другие проблемы.

Во-первых, он не может различать некоторые типы:

df = pd.DataFrame({'prd'  :[pd.Period('2002-03','D'), pd.Period('2012-02-01', 'D')],
                   'str'  :['s1', 's2'],
                   'cat'  :[1, -1]})
df['cat'] = df['cat'].astype('category')
for col in df:
    # kind will define all columns as 'Object'
    print (df[col].dtype, df[col].dtype.kind)

>>> period[D] O
>>> object O
>>> category O

Во-вторых, что для меня на самом деле все еще неясно, это даже возвращается на некоторых типах dtypes None .

4. df.select_dtypesподход.

Это почти то, что мы хотим. Этот метод разработан внутри pandas, поэтому он обрабатывает большинство угловых случаев, упомянутых ранее - пустые DataFrames, хорошо различаются dtypes numpy или pandas. Он хорошо работает с одиночным типом dtype .select_dtypes('bool'). Его можно использовать даже для выбора групп столбцов на основе dtype:

test = pd.DataFrame({'bool' :[False, True], 'int64':[-1,2], 'int32':[-1,2],'float': [-2.5, 3.4],
                     'compl':np.array([1-1j, 5]),
                     'dt'   :[pd.Timestamp('2013-01-02'), pd.Timestamp('2016-10-20')],
                     'td'   :[pd.Timestamp('2012-03-02')- pd.Timestamp('2016-10-20'),
                              pd.Timestamp('2010-07-12')- pd.Timestamp('2000-11-10')],
                     'prd'  :[pd.Period('2002-03','D'), pd.Period('2012-02-01', 'D')],
                     'intrv':pd.arrays.IntervalArray([pd.Interval(0, 0.1), pd.Interval(1, 5)]),
                     'str'  :['s1', 's2'],
                     'cat'  :[1, -1],
                     'obj'  :[[1,2,3], [5435,35,-52,14]]
                    })
test['int32'] = test['int32'].astype(np.int32)
test['cat'] = test['cat'].astype('category')

Примерно так, как указано в документах :

test.select_dtypes('number')

>>>     int64   int32   float   compl   td
>>> 0      -1      -1   -2.5    (1-1j)  -1693 days
>>> 1       2       2    3.4    (5+0j)   3531 days

Может показаться, что здесь мы видим первые неожиданные (бывшие для меня: вопрос ) результаты - TimeDeltaвключается в вывод DataFrame. Но , как ответил в наоборот это должно быть так, но один должен знать об этом. Обратите внимание , что boolDTYPE пропускается, что может быть также нежелателен для кого - то, но это из - за boolи numberв различных « поддеревьев » из Numpy dtypes. В случае с bool мы можем использовать test.select_dtypes(['bool'])здесь.

Следующее ограничение этого метода заключается в том, что для текущей версии pandas (0.24.2) этот код: test.select_dtypes('period')будет повышаться NotImplementedError.

И еще одно: отличить строки от других объектов невозможно:

test.select_dtypes('object')

>>>     str     obj
>>> 0    s1     [1, 2, 3]
>>> 1    s2     [5435, 35, -52, 14]

Но это, во-первых, уже упоминалось в документации. И второе - проблема не в этом методе, а в способе хранения строк DataFrame. Но в любом случае это дело требует постобработки.

5. df.api.types.is_XXX_dtypeподход.

Предполагается, что это самый надежный и собственный способ достижения распознавания dtype (путь к модулю, в котором находятся функции, говорит сам по себе), как я полагаю. И он работает почти идеально, но все же имеет как минимум одно предостережение и все же нужно как-то различать строковые столбцы .

Кроме того, это может быть субъективным, но этот подход также имеет более «понятную для numberчеловека» групповую обработку dtypes по сравнению с .select_dtypes('number'):

for col in test.columns:
    if pd.api.types.is_numeric_dtype(test[col]):
        print (test[col].dtype)

>>> bool
>>> int64
>>> int32
>>> float64
>>> complex128

Нет timedeltaи boolв комплекте. Отлично.

Мой конвейер использует именно эту функциональность в данный момент плюс небольшую пост-ручную обработку.

Вывод.

Надеюсь, я смог аргументировать главное - что все обсуждаемые подходы могут быть использованы, но только pd.DataFrame.select_dtypes()и pd.api.types.is_XXX_dtypeдолжны действительно рассматриваться как применимые.

BeforeFlight
источник
1
Отличный и хорошо сформулированный ответ. :-)
Оливер
8

Если вы хотите пометить тип столбца фрейма данных как строку, вы можете сделать:

df['A'].dtype.kind

Пример:

In [8]: df = pd.DataFrame([[1,'a',1.2],[2,'b',2.3]])
In [9]: df[0].dtype.kind, df[1].dtype.kind, df[2].dtype.kind
Out[9]: ('i', 'O', 'f')

Ответ для вашего кода:

for y in agg.columns:
    if(agg[y].dtype.kind == 'f' or agg[y].dtype.kind == 'i'):
          treat_numeric(agg[y])
    else:
          treat_str(agg[y])
Том
источник
4

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

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

def printColumnInfo(df):
    template="%-8s %-30s %s"
    print(template % ("Type", "Column Name", "Example Value"))
    print("-"*53)
    for c in df.columns:
        print(template % (df[c].dtype, c, df[c].iloc[1]) )

Иллюстративный вывод:

Type     Column Name                    Example Value
-----------------------------------------------------
int64    Age                            49
object   Attrition                      No
object   BusinessTravel                 Travel_Frequently
float64  DailyRate                      279.0
ePi272314
источник