Python 如何跟踪熊猫中不同类型的缺失值? 总结
在许多科学应用中,跟踪不同种类的缺失值是很重要的。“每周主要工作收入”的值是否因为该人没有工作,或者因为他们有工作但拒绝回答而丢失Python 如何跟踪熊猫中不同类型的缺失值? 总结,python,pandas,dataframe,Python,Pandas,Dataframe,在许多科学应用中,跟踪不同种类的缺失值是很重要的。“每周主要工作收入”的值是否因为该人没有工作,或者因为他们有工作但拒绝回答而丢失 将所有缺少的值存储为NA或NaN将丢失此信息 在单独的列中存储缺少的值标签(例如,“因为没有工作而缺少”、“因为拒绝回答而缺少”)意味着研究人员必须跟踪她执行的每个操作的两列,例如分组、重命名等。这为错误和错误创造了无限的机会 将缺失值标签存储在同一列中(例如,作为负数,如下图所示,或非常大的数字,如99999),意味着研究人员必须手动跟踪每列缺失值标签的编码方式
- 将所有缺少的值存储为
或NA
将丢失此信息NaN
- 在单独的列中存储缺少的值标签(例如,“因为没有工作而缺少”、“因为拒绝回答而缺少”)意味着研究人员必须跟踪她执行的每个操作的两列,例如分组、重命名等。这为错误和错误创造了无限的机会
- 将缺失值标签存储在同一列中(例如,作为负数,如下图所示,或非常大的数字,如99999),意味着研究人员必须手动跟踪每列缺失值标签的编码方式,并创造许多其他错误机会(例如,忘记列包含缺少的值,并采用原始平均值,而不是使用正确的掩码)
Stata
(见下文)中,通过使用存储数值和缺失值标签的数据类型,以及知道如何处理此数据类型的函数,可以非常容易地处理此问题。这是一种非常高的性能(数据类型仍然是数字的,而不是字符串或混合的–想想NumPy的数据类型,除了我们有NaN1
,NaN2
,等等之外)在熊猫中实现这一点的最佳方法是什么
注:我是一名经济学家,但这也是政治科学家、流行病学家等——任何处理调查数据的人——的一个非常常见的工作流程。在这种情况下,分析师通过代码簿知道缺失的值是什么,真正关心跟踪它们,并且有数百或数千列需要处理——因此,indeed、 需要一种自动跟踪它们的方法
动机/背景
在处理任何类型的调查数据时,有多种类型的缺失数据是非常常见的
以下是用于编制官方就业统计数据的政府调查问卷中的一个简单示例:
- [Q1]你有工作吗
- [Q2][如果Q1=Yes]你的每周收入是多少
NaN
,那么我可以取平均值,但我已经丢失了所有关于值丢失原因的有价值信息。例如,我可能希望有一个函数,它可以取任何列和报告,对于该列,统计数据如平均值和中值,eligib的数量le观察值(即除值=-8外的所有内容),以及未缺失的百分比
它在斯塔塔非常有效
在Stata
中执行此操作非常简单。Stata
有27个数字缺失类别:'.a'到'.z'。(更多详细信息)。我可以写:
如果周收入=-1,则替换周收入=.a
如果周收入==-8,则替换周收入=.b
等等
然后(用伪代码)我就可以写了
统计每周收入如果每周收入!=.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)
“显而易见”的工作
>>> 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
>>> df[(~df.income.isin((df.income.missing_dict)))].mean()
income 5225.0
dtype: float64
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])
>>> print(pd.DataFrame({'a': s}).isna())
0 False
1 False
2 False
3 False