• 7.1 处理缺失数据
    • 滤除缺失数据
    • 填充缺失数据

    7.1 处理缺失数据

    在许多数据分析工作中,缺失数据是经常发生的。pandas的目标之一就是尽量轻松地处理缺失数据。例如,pandas对象的所有描述性统计默认都不包括缺失数据。

    缺失数据在pandas中呈现的方式有些不完美,但对于大多数用户可以保证功能正常。对于数值数据,pandas使用浮点值NaN(Not a Number)表示缺失数据。我们称其为哨兵值,可以方便的检测出来:

    1. In [10]: string_data = pd.Series(['aardvark', 'artichoke', np.nan, 'avocado'])
    2. In [11]: string_data
    3. Out[11]:
    4. 0 aardvark
    5. 1 artichoke
    6. 2 NaN
    7. 3 avocado
    8. dtype: object
    9. In [12]: string_data.isnull()
    10. Out[12]:
    11. 0 False
    12. 1 False
    13. 2 True
    14. 3 False
    15. dtype: bool

    在pandas中,我们采用了R语言中的惯用法,即将缺失值表示为NA,它表示不可用not available。在统计应用中,NA数据可能是不存在的数据或者虽然存在,但是没有观察到(例如,数据采集中发生了问题)。当进行数据清洗以进行分析时,最好直接对缺失数据进行分析,以判断数据采集的问题或缺失数据可能导致的偏差。

    Python内置的None值在对象数组中也可以作为NA:

    1. In [13]: string_data[0] = None
    2. In [14]: string_data.isnull()
    3. Out[14]:
    4. 0 True
    5. 1 False
    6. 2 True
    7. 3 False
    8. dtype: bool

    pandas项目中还在不断优化内部细节以更好处理缺失数据,像用户API功能,例如pandas.isnull,去除了许多恼人的细节。表7-1列出了一些关于缺失数据处理的函数。

    表7-1 NA处理方法

    滤除缺失数据

    过滤掉缺失数据的办法有很多种。你可以通过pandas.isnull或布尔索引的手工方法,但dropna可能会更实用一些。对于一个Series,dropna返回一个仅含非空数据和索引值的Series:

    1. In [15]: from numpy import nan as NA
    2. In [16]: data = pd.Series([1, NA, 3.5, NA, 7])
    3. In [17]: data.dropna()
    4. Out[17]:
    5. 0 1.0
    6. 2 3.5
    7. 4 7.0
    8. dtype: float64

    这等价于:

    1. In [18]: data[data.notnull()]
    2. Out[18]:
    3. 0 1.0
    4. 2 3.5
    5. 4 7.0
    6. dtype: float64

    而对于DataFrame对象,事情就有点复杂了。你可能希望丢弃全NA或含有NA的行或列。dropna默认丢弃任何含有缺失值的行:

    1. In [19]: data = pd.DataFrame([[1., 6.5, 3.], [1., NA, NA],
    2. ....: [NA, NA, NA], [NA, 6.5, 3.]])
    3. In [20]: cleaned = data.dropna()
    4. In [21]: data
    5. Out[21]:
    6. 0 1 2
    7. 0 1.0 6.5 3.0
    8. 1 1.0 NaN NaN
    9. 2 NaN NaN NaN
    10. 3 NaN 6.5 3.0
    11. In [22]: cleaned
    12. Out[22]:
    13. 0 1 2
    14. 0 1.0 6.5 3.0

    传入how=’all’将只丢弃全为NA的那些行:

    1. In [23]: data.dropna(how='all')
    2. Out[23]:
    3. 0 1 2
    4. 0 1.0 6.5 3.0
    5. 1 1.0 NaN NaN
    6. 3 NaN 6.5 3.0

    用这种方式丢弃列,只需传入axis=1即可:

    1. In [24]: data[4] = NA
    2. In [25]: data
    3. Out[25]:
    4. 0 1 2 4
    5. 0 1.0 6.5 3.0 NaN
    6. 1 1.0 NaN NaN NaN
    7. 2 NaN NaN NaN NaN
    8. 3 NaN 6.5 3.0 NaN
    9. In [26]: data.dropna(axis=1, how='all')
    10. Out[26]:
    11. 0 1 2
    12. 0 1.0 6.5 3.0
    13. 1 1.0 NaN NaN
    14. 2 NaN NaN NaN
    15. 3 NaN 6.5 3.0

    另一个滤除DataFrame行的问题涉及时间序列数据。假设你只想留下一部分观测数据,可以用thresh参数实现此目的:

    1. In [27]: df = pd.DataFrame(np.random.randn(7, 3))
    2. In [28]: df.iloc[:4, 1] = NA
    3. In [29]: df.iloc[:2, 2] = NA
    4. In [30]: df
    5. Out[30]:
    6. 0 1 2
    7. 0 -0.204708 NaN NaN
    8. 1 -0.555730 NaN NaN
    9. 2 0.092908 NaN 0.769023
    10. 3 1.246435 NaN -1.296221
    11. 4 0.274992 0.228913 1.352917
    12. 5 0.886429 -2.001637 -0.371843
    13. 6 1.669025 -0.438570 -0.539741
    14. In [31]: df.dropna()
    15. Out[31]:
    16. 0 1 2
    17. 4 0.274992 0.228913 1.352917
    18. 5 0.886429 -2.001637 -0.371843
    19. 6 1.669025 -0.438570 -0.539741
    20. In [32]: df.dropna(thresh=2)
    21. Out[32]:
    22. 0 1 2
    23. 2 0.092908 NaN 0.769023
    24. 3 1.246435 NaN -1.296221
    25. 4 0.274992 0.228913 1.352917
    26. 5 0.886429 -2.001637 -0.371843
    27. 6 1.669025 -0.438570 -0.539741

    填充缺失数据

    你可能不想滤除缺失数据(有可能会丢弃跟它有关的其他数据),而是希望通过其他方式填补那些“空洞”。对于大多数情况而言,fillna方法是最主要的函数。通过一个常数调用fillna就会将缺失值替换为那个常数值:

    1. In [33]: df.fillna(0)
    2. Out[33]:
    3. 0 1 2
    4. 0 -0.204708 0.000000 0.000000
    5. 1 -0.555730 0.000000 0.000000
    6. 2 0.092908 0.000000 0.769023
    7. 3 1.246435 0.000000 -1.296221
    8. 4 0.274992 0.228913 1.352917
    9. 5 0.886429 -2.001637 -0.371843
    10. 6 1.669025 -0.438570 -0.539741

    若是通过一个字典调用fillna,就可以实现对不同的列填充不同的值:

    1. In [34]: df.fillna({1: 0.5, 2: 0})
    2. Out[34]:
    3. 0 1 2
    4. 0 -0.204708 0.500000 0.000000
    5. 1 -0.555730 0.500000 0.000000
    6. 2 0.092908 0.500000 0.769023
    7. 3 1.246435 0.500000 -1.296221
    8. 4 0.274992 0.228913 1.352917
    9. 5 0.886429 -2.001637 -0.371843
    10. 6 1.669025 -0.438570 -0.539741

    fillna默认会返回新对象,但也可以对现有对象进行就地修改:

    1. In [35]: _ = df.fillna(0, inplace=True)
    2. In [36]: df
    3. Out[36]:
    4. 0 1 2
    5. 0 -0.204708 0.000000 0.000000
    6. 1 -0.555730 0.000000 0.000000
    7. 2 0.092908 0.000000 0.769023
    8. 3 1.246435 0.000000 -1.296221
    9. 4 0.274992 0.228913 1.352917
    10. 5 0.886429 -2.001637 -0.371843
    11. 6 1.669025 -0.438570 -0.539741

    对reindexing有效的那些插值方法也可用于fillna:

    1. In [37]: df = pd.DataFrame(np.random.randn(6, 3))
    2. In [38]: df.iloc[2:, 1] = NA
    3. In [39]: df.iloc[4:, 2] = NA
    4. In [40]: df
    5. Out[40]:
    6. 0 1 2
    7. 0 0.476985 3.248944 -1.021228
    8. 1 -0.577087 0.124121 0.302614
    9. 2 0.523772 NaN 1.343810
    10. 3 -0.713544 NaN -2.370232
    11. 4 -1.860761 NaN NaN
    12. 5 -1.265934 NaN NaN
    13. In [41]: df.fillna(method='ffill')
    14. Out[41]:
    15. 0 1 2
    16. 0 0.476985 3.248944 -1.021228
    17. 1 -0.577087 0.124121 0.302614
    18. 2 0.523772 0.124121 1.343810
    19. 3 -0.713544 0.124121 -2.370232
    20. 4 -1.860761 0.124121 -2.370232
    21. 5 -1.265934 0.124121 -2.370232
    22. In [42]: df.fillna(method='ffill', limit=2)
    23. Out[42]:
    24. 0 1 2
    25. 0 0.476985 3.248944 -1.021228
    26. 1 -0.577087 0.124121 0.302614
    27. 2 0.523772 0.124121 1.343810
    28. 3 -0.713544 0.124121 -2.370232
    29. 4 -1.860761 NaN -2.370232
    30. 5 -1.265934 NaN -2.370232

    只要有些创新,你就可以利用fillna实现许多别的功能。比如说,你可以传入Series的平均值或中位数:

    1. In [43]: data = pd.Series([1., NA, 3.5, NA, 7])
    2. In [44]: data.fillna(data.mean())
    3. Out[44]:
    4. 0 1.000000
    5. 1 3.833333
    6. 2 3.500000
    7. 3 3.833333
    8. 4 7.000000
    9. dtype: float64

    表7-2列出了fillna的参考。

    7.1 处理缺失数据 - 图2

    fillna函数参数