Подтвердить что ты не робот

Что такое хорошая эвристика, чтобы определить, является ли столбец в pandas.DataFrame категориальным?

Я разрабатываю инструмент, который автоматически обрабатывает данные в формате pandas.DataFrame. Во время этого этапа предварительной обработки я хочу по-разному рассматривать непрерывные и категориальные данные. В частности, я хочу иметь возможность применять, например, OneHotEncoder только к категориальным данным.

Теперь предположим, что нам предоставлен pandas.DataFrame и нет никакой другой информации о данных в DataFrame. Что такое хорошая эвристика, чтобы определить, является ли столбец в pandas.DataFrame категоричным?

Мои первоначальные мысли:

1) Если в столбце есть строки (например, тип данных столбца равен object), то столбец, скорее всего, содержит категориальные данные

2) Если какой-то процент значений в столбце уникален (например, >= 20%), то столбец, скорее всего, содержит непрерывные данные

Я нашел, что 1) работает нормально, но 2) не очень хорошо работает. Мне нужна лучшая эвристика. Как бы вы решили эту проблему?

Изменить: Кто-то попросил объяснить, почему 2) не работает. Были некоторые примеры тестов, в которых у нас все еще были непрерывные значения в столбце, но в столбце было не так много уникальных значений. Эвристика в 2) явно не удалась в этом случае. Были также проблемы, когда у нас была категориальная колонка с множеством уникальных значений, например, имена пассажиров в наборе данных "Титаник". Проблема с неправильной классификацией такого же столбца.

4b9b3361

Ответ 1

Вот несколько подходов:

  1. Найти отношение количества уникальных значений к общему количеству уникальных значений. Что-то вроде следующего

    likely_cat = {}
    for var in df.columns:
        likely_cat[var] = 1.*df[var].nunique()/df[var].count() < 0.05 #or some other threshold
    
  2. Проверьте, составляют ли верхние n уникальных значений более определенной доли всех значений

    top_n = 10 
    likely_cat = {}
    for var in df.columns:
        likely_cat[var] = 1.*df[var].value_counts(normalize=True).head(top_n).sum() > 0.8 #or some other threshold
    

Подход 1) в целом работал лучше для меня, чем Подход 2). Но подход 2) лучше, если есть "длиннохвостое распределение", где небольшое количество категориальных переменных имеет высокую частоту, а большое количество категориальных переменных имеет низкую частоту.

Ответ 2

Есть много мест, где вы могли бы "украсть" определения форматов, которые можно отличить как "число". ##, # e- # будет одним из таких форматов, просто для иллюстрации. Возможно, вы сможете найти библиотеку, чтобы сделать это. Сначала я пытаюсь отбросить все до чисел и что осталось, ну нет другого пути, кроме как сохранить их как категоричные.

Ответ 3

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

Для данных обследования идея заключалась бы в поиске шкал Ликерта, например. 5-8, либо строки (которые, возможно, нуждаются в жестко закодированных (и переведенных) уровнях, чтобы искать "хорошие", "плохие", ".agree.", "Very. *",...) или значения int в 0-8 диапазон + NA.

Страны и такие вещи также могут быть идентифицируемы...

Также могут работать возрастные группы ( ".-." ).

Ответ 4

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

Если вы не против беспокоить пользователя, возможно, обнаружение двусмысленности и повышение ошибки - это путь.

Если вы не пропустите молчание, тогда ваша эвристика в порядке. Я не думаю, что вы найдете что-нибудь, что значительно лучше. Я думаю, вы могли бы сделать это проблемой обучения, если хотите. Загрузите набор наборов данных, предположите, что они в совокупности являются достойным представлением всех наборов данных в мире и обучают на основе функций по каждому набору данных/столбцу для прогнозирования категориальных и непрерывных.

Но, конечно, ничто не может быть совершенным. Например. это столбец [1, 8, 22, 8, 9, 8], относящийся к часам дня или к собачьим породам?

Ответ 5

Я думал о подобной проблеме, и тем более, что я считаю, кажется, что это сама по себе проблема классификации, которая может извлечь пользу из обучения модели.

Держу пари, если вы проверили кучу наборов данных и извлекли эти функции для каждого столбца / pandas.Series:

  • % floats: процент значений, которые являются float
  • % int: процент значений, которые являются целыми числами
  • % строка: процент значений, которые являются строками
  • % уникальная строка: количество уникальных строковых значений/общее число
  • % уникальных целых чисел: число уникальных целочисленных значений/общее число
  • среднее числовое значение (для этого не считаются числовые значения 0)
  • std отклонение числовых значений

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

Боковое замечание: насколько подходит серия с ограниченным числом числовых значений, кажется, что интересной проблемой будет определение категориального vs ординала; это не больно думать, что переменная является порядковой, если она окажется количественной правильно? Шаги предварительной обработки будут кодировать порядковые значения в любом случае без однократного кодирования.

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

Ответ 6

Вы можете определить, какие типы данных считаются числовыми, а затем исключить соответствующие переменные.

Если исходный фрейм данных df:

numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
dataframe = df.select_dtypes(exclude=numerics)

Ответ 7

Я смотрел на это, подумал, что может быть полезно поделиться тем, что у меня есть. Это основано на ответе @Rishabh Srivastava.

import pandas as pd

def remove_cat_features(X, method='fraction_unique', cat_cols=None, min_fraction_unique=0.05):
    """Removes categorical features using a given method.
       X: pd.DataFrame, dataframe to remove categorical features from."""

    if method=='fraction_unique':
        unique_fraction = X.apply(lambda col: len(pd.unique(col))/len(col)) 
        reduced_X = X.loc[:, unique_fraction>min_fraction_unique]

    if method=='named_columns':
        non_cat_cols = [col not in cat_cols for col in X.columns]
        reduced_X = X.loc[:, non_cat_cols]

    return reduced_X

Затем вы можете вызвать эту функцию, задав pandas df как X и вы можете либо удалить именованные категориальные столбцы, либо вы можете удалить столбцы с небольшим количеством уникальных значений (определяемых min_fraction_unique)