Как отследить различные типы пропущенных значений в пандах? - PullRequest
0 голосов
/ 04 сентября 2018

Резюме

Во многих научных приложениях важно отслеживать различные виды недостающих значений. Отсутствует ли значение «еженедельный доход от основной работы», потому что у человека нет работы, или потому что у него есть работа, но он отказался отвечать?

  • Сохранение всех пропущенных значений как NA или NaN теряет эту информацию.
  • Хранение пропущенных меток значений (например, «отсутствует, потому что нет работы», «отсутствует, потому что отказался отвечать») в отдельном столбце означает, что исследователь должен отслеживать два столбца для каждой выполняемой им операции - такие как групповой, переименование и т. д. Это создает бесконечные возможности для ошибок и ошибок.
  • Хранение меток отсутствующих значений в том же столбце (например, в виде отрицательных чисел, как в примере ниже, или очень больших чисел, таких как 99999) означает, что исследователь должен вручную отслеживать, как метки отсутствующих значений кодируется для каждого столбца и создает много других возможностей для ошибок (например, забывая, что столбец содержит пропущенные значения, и вместо использования правильной маски берется среднее значение).

Очень легко решить эту проблему в Stata (см. Ниже), используя тип данных, в котором хранятся как числовые значения, так и метки пропущенных значений, а также функции, которые знают, как обрабатывать этот тип данных. Это очень производительность (тип данных остается числовым, а не строковым или смешанным - подумайте о типах данных NumPy, за исключением того, что вместо NaN у нас есть NaN1, NaN2 и т. Д.) Что такое лучший способ достижения чего-то подобного в пандах?

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

Мотивация / контекст

При работе с любыми данными опроса очень часто бывает несколько видов пропущенных данных.

Вот минимальный пример из правительственной анкеты, используемой для получения официальной статистики занятости:

  • [Q1] У вас есть работа?
  • [Q2] [Если Q1 = Да] Каков ваш еженедельный доход от этой работы?

Вышеуказанное встречается практически во всех государственных обследованиях рабочей силы в мире (например, Обследование рабочей силы в Великобритании , Текущее обследование населения США и т. Д.).

Теперь, для данного респондента, если [Q2] отсутствует, возможно, что (1) они ответили «Нет» на [Q1], и, таким образом, не отвечали требованиям , чтобы их спросили [Q2], или что (2) они ответили «Да» на [Q1], но отказались ответить [Q2] (возможно, потому, что их смутило, сколько / мало они зарабатывают или потому, что они не знали).

Как исследователь, для меня очень важно, произошло ли это (1) или (2). Предположим, моя работа состоит в том, чтобы сообщать о среднем еженедельном доходе работников в Соединенных Штатах. Если в этом столбце [Q2] много пропущенных значений, но все они помечены как «отсутствующие, поскольку респондент ответил« нет »на [Q1]», то я могу с уверенностью взять среднее значение [Q2] - это действительно среднее еженедельный доход людей на работе. (Все пропущенные значения - это люди, у которых не было работы.)

С другой стороны, если все пропущенные значения [Q2] помечены как «отсутствующие, поскольку респондент задал этот вопрос, но отказался отвечать», то я не могу просто сообщить среднее значение [Q2] как средний недельный доход работников , Мне нужно сделать оговорки вокруг моих результатов. Мне нужно проанализировать, какие люди не отвечают (случайно ли они пропадают или люди с более высокими доходами чаще отказываются, например, искажать мои результаты?). Возможно, я попытаюсь вменять пропущенные значения и т. Д.

Theпроблема

Поскольку эти «причины пропажи» очень важны, государственные статистические агентства будут кодировать различные причины в столбце:

enter image description here

Таким образом, столбец, содержащий ответы на [Q2] выше, может содержать значения [1500, -8, 10000, -2, 3000, -1, 6400].

В этом случае «1500», «10000» и т. Д. Являются «верными» ответами на [Q2] (еженедельный доход в размере 1500 долларов США, еженедельный доход в размере 10 000 долларов США и т. Д.); тогда как «-8» означает, что они не имели права отвечать (потому что они ответили «Нет» на [Q1]), «-2» означает, что они имели право отвечать, но отказались сделать это, и т. д.

Теперь, очевидно, если я возьму среднее значение этого столбца, я получу что-то бессмысленное.

С другой стороны, если я просто заменю все отрицательные значения на NaN, тогда я могу взять среднее значение - но я потерял всю эту ценную информацию о том, почему значения отсутствуют. Например, я могу захотеть иметь функцию, которая принимает любой столбец и отчеты, для этого столбца такие статистические данные, как среднее значение и медиана, число приемлемых наблюдений (т. Е. Все, кроме значения = -8), и процент тех , которые не пропали без вести.

Отлично работает в Stata

Выполнить это в Stata чрезвычайно легко. Stata имеет 27 пропущенных числовых категорий: от .a до .z. (Подробнее здесь .) Могу написать:

replace weekly_income = .a if weekly_income == -1 replace weekly_income = .b if weekly_income == -8

и т. Д.

Тогда (в псевдокоде) я могу написать

stats weekly_income if weekly_income!=.b

При сообщении среднего значения, Stata автоматически игнорирует значения, закодированные как отсутствующие (в действительности они теперь не числовые); но это также даст мне статистику пропущенных значений только для наблюдений, которые меня интересуют (в данном случае, те, кто имеет право задавать вопрос, т. е. те, кто изначально не кодировался как '-8').

Как лучше всего справиться с этим в Пандах?

Установка:

>>> import pandas as pd
>>> df = pd.DataFrame.from_dict({
        'income': [1500, -8, 10000, -2, 3000, -1, 6400]})

Желаемый результат:

>>> df.income.missing_dict = {'-1': ['.a', 'Don\'t know'], '-2': ['.b', 'Refused']} # etc.
>>> df
  income
0       1500
1  Inapplic.
2      10000
3    Refused
4       3000
5 Don't know
6       6400

>>> assert df.income.mean() == np.mean([1500, 10000, 3000, 6400])
(passes)

«очевидный» обходной путь

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

Но это крайне неудобно. Эти обследования часто имеют тысячи столбцов, и исследователь вполне может использовать сотни в определенных видах экономического анализа. Наличие двух столбцов для каждого «нижележащего» столбца означает, что исследователь должен отслеживать два столбца для каждой выполняемой ею операции, например, группового режима, переименования и т. Д. Это создает бесконечные возможности для ошибок и ошибок. Это также означает, что отображение таблицы очень расточительно - для любого столбца мне нужно теперь отобразить два столбца, один из которых для любого данного наблюдения всегда избыточен. (Это расточительно как для экранной недвижимости, так и для внимания человеческих аналитиков, когда приходится определять, какие два столбца являются «парой».)

Другие идеи

Две другие мысли, которые приходят мне в голову, обе, вероятно, не идеальные:

(1) Создайте новый тип данных в пандах, который работает аналогично Stata (то есть добавляет «.a», «.b» и т. Д. К допустимым значениям для числовых столбцов).

(2) Используйте решение с двумя столбцами, приведенное выше, но (пере) пишите функции «обертки» в пандах, чтобы «groupby» и т. Д. Отслеживал пары столбцов для меня.

Я подозреваю, что (1) является лучшим решением в долгосрочной перспективе, но, по-видимому, это потребует огромного количества разработки.

С другой стороны, может быть, уже есть пакеты, которые решают эту проблему? Или у людей лучше обходные пути?

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

Pandas недавно представила пользовательский тип массива под названием ExtensionArray, который позволяет определить, что по сути является пользовательским типом столбца, что позволяет (сортировать) использование фактических значений вместе с отсутствующими данными без обращения к двум столбцам , Вот очень, очень грубая реализация, которая едва протестирована:

import numpy as np
import pandas as pd
from pandas.core.arrays.base import ExtensionArray


class StataData(ExtensionArray):
    def __init__(
        self, data, missing=None, factors=None, dtype=None, copy=False
    ):
        def own(array, dtype=dtype):
            array = np.asarray(array, dtype)
            if copy:
                array = array.copy()
            return array

        self.data = own(data)

        if missing is None:
            missing = np.zeros_like(data, dtype=int)
        else:
            missing = own(missing, dtype=int)
        self.missing = missing

        self.factors = own(factors)

    @classmethod
    def _from_sequence(cls, scalars, dtype=None, copy=False):
        return cls(scalars, dtype=dtype, copy=copy)

    @classmethod
    def _from_factorized(cls, data, original):
        return cls(original, None, data)

    def __getitem__(self, key):
        return type(self)(
            self.data[key], self.missing[key], self.factors
        )

    def __setitem__(self, key, value):
        self.data[key] = value
        self.missing[key] = 0

    def __len__(self):
        return len(self.data)

    def __iter__(self):
        return iter(self.data)

    @property
    def dtype(self):
        return self.data.dtype

    @property
    def shape(self):
        return self.data.shape

    @property
    def nbytes(self):
        return self.data.nbytes + self.missing.nbytes + self.factors.nbytes

    def view(self):
        return self

    @property
    def reason_missing(self):
        return self.missing

    def isna(self):
        return self.missing != 0

    def __repr__(self):
        s = {}
        for attr in ['data', 'missing', 'factors']:
            s[attr] = getattr(self, attr)
        return repr(s)

С помощью этой реализации вы можете сделать следующее:

>>> a = StataData([1, 2, 3, 4], [0, 0, 1, 0])
>>> s = pd.Series(a)
>>> print(s[s.isna()])
2    3
dtype: int32
>>> print(s[~s.isna()])
0    1
1    2
3    4
dtype: int32
>>> print(s.isna().values.reason_missing)
array([1])

Надеюсь, кто-то, кто понимает этот API, сможет помочь и улучшить его. Для начинающих a нельзя использовать в DataFrames, только Series.

>>> print(pd.DataFrame({'a': s}).isna())
0  False
1  False
2  False
3  False
0 голосов
/ 05 сентября 2018

Чтобы показать решение, я позволю себе сменить клавиши missing_dict в соответствии с типом данных income.

>>> df
   income
0    1500
1      -8
2   10000
3      -2
4    3000
5      -1
6    6400
>>> df.income.missing_dict
{-8: ['.c', 'Stifled by companion'], -2: ['.b', 'Refused'], -1: ['.a', "Don't know"]}

Теперь, как отфильтровать строки в соответствии со значениями в «пропущенном» списке:

>>> df[(~df.income.isin((df.income.missing_dict)))]
   income
0    1500
2   10000
4    3000
6    6400

Обратите внимание на дополнительные скобки вокруг значений фильтра: мы должны передать tuple значений в isin. Затем примените оператор тильда , побитовое отрицание, чтобы получить серию логических значений.

Наконец, примените mean к результирующему столбцу данных:

>>> df[(~df.income.isin((df.income.missing_dict)))].mean()
income    5225.0
dtype: float64

Это бросает вас в правильном направлении? Отсюда вы можете просто заменить income на соответствующий столбец или имя переменной при необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...