python数据清洗

接下来是第三章的学习:数据清洗

在数据分析和建模的过程中,相当多的时间要用在数据准备上:加载、清理、转换以及重塑。这些工作会占到分析师时间的80%或更多。有时,存储在文件和数据库中的数据的格式不适合某个特定的任务。许多研究者都选择使用通用编程语言(如Python、Perl、R或Java)或UNIX文本处理工具(如sed或awk)对数据格式进行专门处理。幸运的是,pandas和内置的Python标准库提供了一组高级的、灵活的、快速的工具,可以让你轻松地将数据规整为想要的格式。

处理缺失数据

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

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

string_data = pd.Series(['aardvark', 'artichoke', np.nan, 'avocado'])
string_data
0   aardvark
1   artichoke
2   NaN
3   avocado
dtype: object

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

一.滤除缺失数据

1.对于Series来说,处理缺失数据会比较简单

from numpy import nan as NA
data = pd.Series([1, NA, 3.5, NA, 7])
data.dropna()
0    1.0
2    3.5
4    7.0
dtype: float64

这其实会等价于data[data.notnull()]

2.但对于DataFrame来说,处理起来就会有点麻烦

2.1、如果你想删除含有NAN的那些行–直接dropna就好

data = pd.DataFrame([[1., 6.5, 3.], [1., NA, NA],
   ....:  [NA, NA, NA], [NA, 6.5, 3.]])
data

0   1   2
0   1.0 6.5 3.0
1   1.0 NaN NaN
2   NaN NaN NaN
3   NaN 6.5 3.0

clean=data.dropna()
clean

     0   1   2
0   1.0 6.5 3.0

2.2、但你也有可能只想删掉所有都是NAN的某一行或者某一列

data.dropna(how='all')
0   1   2
0   1.0 6.5 3.0
1   1.0 NaN NaN
3   NaN 6.5 3.0

data.dropna(axis=1,how='all') #按列来

    0   1   2
0   1.0 6.5 3.0
1   1.0 NaN NaN
2   NaN NaN NaN
3   NaN 6.5 3.0

2.3 当然,有时候你又不想全删完,想留一点数据观察,可以用thresh

df = pd.DataFrame(np.random.randn(7, 3))
df.iloc[:4, 1] = NA
df.iloc[:2, 2] = NA
df

         0       1  2
0   0.578204    NaN NaN
1   0.217969    NaN NaN
2   2.197892    NaN 1.023195
3   -0.894835   NaN 1.823525
4   0.915724    0.198224    0.419659
5   -0.618141   -1.042905   -0.403488
6   0.441870    0.623395    0.108106

对比一下直接dropna和dropna(thresh)的区别

df.dropna()

         0           1         2
4   0.915724    0.198224    0.419659
5   -0.618141   -1.042905   -0.403488
6   0.441870    0.623395    0.108106

df.dropna(thresh=2)  #看他会保留两行NAN的供你观测
         0       1    2
2   2.197892    NaN 1.023195
3   -0.894835   NaN 1.823525
4   0.915724    0.198224    0.419659
5   -0.618141   -1.042905   -0.403488
6   0.441870    0.623395    0.108106

二、填充缺失数据

1.有些时候我们也不想直接删除掉数据,而是希望通过填充缺失数据,对大多数情况而言,fillna是主要的函数

df.fillna(0)#NAN填充0

         0          1           2
0   -0.499572   0.000000    0.000000
1   -1.108934   0.000000    0.000000
2   -0.316295   0.000000    -1.324176
3   -0.371394   0.000000    0.802282
4   0.975481    1.716945    -1.540201
5   0.204285    2.324883    0.117626
6   -0.330108   0.153630    -0.338194

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

df.fillna({1: 0.5, 2: 0})  #第二列NAN填充0,第三列NAN填充1
         0          1           2
0   -0.499572   0.500000    0.000000
1   -1.108934   0.500000    0.000000
2   -0.316295   0.500000    -1.324176
3   -0.371394   0.500000    0.802282
4   0.975481    1.716945    -1.540201
5   0.204285    2.324883    0.117626
6   -0.330108   0.153630    -0.338194

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

_ = df.fillna(0, inplace=True)
df
         0          1            2
0   -0.499572   0.000000    0.000000
1   -1.108934   0.000000    0.000000
2   -0.316295   0.000000    -1.324176
3   -0.371394   0.000000    0.802282
4   0.975481    1.716945    -1.540201
5   0.204285    2.324883    0.117626
6   -0.330108   0.153630    -0.338194

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

df = pd.DataFrame(np.random.randn(6, 3))
df.iloc[2:, 1] = NA
df.iloc[4:, 2] = NA
df
        0            1           2
0   -0.774119   -0.676729   -0.128181
1   0.161477    -1.105764   -1.710922
2   0.937275       NaN       -0.154157
3   -0.347762      NaN       -0.331515
4   0.578991       NaN       NaN
5   -1.993989      NaN       NaN

df.fillna(method='ffill') #向上取等
        0            1          2
0   -0.774119   -0.676729   -0.128181
1   0.161477    -1.105764   -1.710922
2   0.937275    -1.105764   -0.154157
3   -0.347762   -1.105764   -0.331515
4   0.578991    -1.105764   -0.331515
5   -1.993989   -1.105764   -0.331515

df.fillna(method='ffill', limit=2)  #限制两个
      0          1         2
0  0.476985  3.248944 -1.021228
1 -0.577087  0.124121  0.302614
2  0.523772  0.124121  1.343810
3 -0.713544  0.124121 -2.370232
4 -1.860761    NaN    -2.370232
5 -1.265934    NaN    -2.370232

你也可以i在fillna中传入平均值或者中位数等等

三、数据转换

1.移除重复数据

先创建一个DataFrame

data = pd.DataFrame({'k1': ['one', 'two'] * 3 + ['two'],
   ....:  'k2': [1, 1, 2, 3, 3, 4, 4]})
data
    k1  k2
0  one   1
1  two   1
2  one   2
3  two   3
4  one   3
5  two   4
6  two   4

1.1 DataFrame的duplicated方法返回一个布尔型Series,表示各行是否是重复行(前面出现过的行):

data.duplicated()
0    False
1    False
2    False
3    False
4    False
5    False
6    True
dtype: bool

1.2 还有一个与此相关的drop_duplicates方法,它会返回一个DataFrame

data.drop_duplicates()
    k1  k2
0  one   1
1  two   1
2  one   2
3  two   3
4  one   3
5  two   4

1.3 当然你也可以指定列

data.drop_duplicates(['k1'])
    k1  k2
0   one 1
1   two 1

1.4 duplicated和drop_duplicates默认保留的是第一个出现的值组合。传入keep=’last’则保留最后一个

data.drop_duplicates(['k1', 'k2'], keep='last')
    k1  k2  v1
0  one   1   0
1  two   1   1
2  one   2   2
3  two   3   3
4  one   3   4
6  two   4   6

2.利用函数或映射进行数据转换

对于许多数据集,你可能希望根据数组、Series或DataFrame列中的值来实现转换工作。我们来看看下面这组有关肉类的数据:

data = pd.DataFrame({'food': ['bacon', 'pulled pork', 'bacon',
   ....:   'Pastrami', 'corned beef', 'Bacon',
   ....:   'pastrami', 'honey ham', 'nova lox'],
   ....:  'ounces': [4, 3, 12, 6, 7.5, 8, 3, 5, 6]})
data
     food       ounces
0    bacon       4.0
1    pulled pork 3.0
2    bacon1      2.0
3    Pastrami    6.0
4    corned beef 7.5
5    Bacon       8.0
6    pastrami    3.0
7    honey ham   5.0
8    nova lox    6.0

接下来我们希望根据food来加一列动物列表,我们先编写一个不同肉类到动物的映射:

meat_to_animal = {
  'bacon': 'pig',
  'pulled pork': 'pig',
  'pastrami': 'cow',
  'corned beef': 'cow',
  'honey ham': 'pig',
  'nova lox': 'salmon'
}

但是这里有一个小问题,即有些肉类的首字母大写了,而另一些则没有。因此,我们还需要使用Series的str.lower方法,将各个值转换为小写:

lowercased = data['food'].str.lower()
data['animal'] = lowercased.map(meat_to_animal)#每一个meat_to_animal都使用lowercased
data
          food  ounces  animal
0        bacon     4.0     pig
1  pulled pork     3.0     pig
2        bacon    12.0     pig
3     Pastrami     6.0     cow
4  corned beef     7.5     cow
5        Bacon     8.0     pig
6     pastrami     3.0     cow
7    honey ham     5.0     pig
8     nova lox     6.0  salmon

我们也可以传入一个能够完成全部这些工作的函数:

data['food'].map(lambda x: meat_to_animal[x.lower()])

0   pig
1   pig
2   pig
3   cow
4   cow
5   pig
6   cow
7   pig
8  salmon
Name: food, dtype: object

3.替换值

3.1 利用fillna方法填充缺失数据可以看做值替换的一种特殊情况,而replace则提供了一种实现该功能的更简单、更灵活的方式。它可以把一些特殊值改为NAN,我们来看看下面这个Series:

data = pd.Series([1., -999., 2., -999., -1000., 3.])
data
0    1.0
1   -999.0
2    2.0
3   -999.0
4   -1000.0
5    3.0
dtype: float64

3.2 -999这个值可能是一个表示缺失数据的标记值。要将其替换为pandas能够理解的NA值,我们可以利用replace来产生一个新的Series

data.replace(-999, np.nan)
0   1.0
1   NaN
2   2.0
3   NaN
4   -1000.0
5   3.0
dtype: float64

3.3 你也可以一次性替换多个值

data.replace([-999, -1000], np.nan)

要让每个值有不同的替换值,可以传递一个替换列表:

data.replace([-999, -1000], [np.nan, 0])

传入的参数也可以是字典

data.replace({-999: np.nan, -1000: 0})

注意data.replace方法与data.str.replace不同,后者做的是字符串的元素级替换。我们会在后面学习Series的字符串方法。

四.重命名轴索引

1.跟Series中的值一样,轴标签也可以通过函数或映射进行转换,从而得到一个新的不同标签的对象。

data = pd.DataFrame(np.arange(12).reshape((3, 4)),
   ....: index=['Ohio', 'Colorado', 'New York'],
   ....: columns=['one', 'two', 'three', 'four'])
data

           one  two three   four
Ohio        0    1   2      3
Colorado    4    5   6      7
New York    8    9   10     11

跟Series一样,轴索引也有一个map方法:

transform = lambda x: x[:4].upper()  #取字符串的前四个字母大写
data.index.map(transform)
Index(['OHIO', 'COLO', 'NEW '], dtype='object')

这样就可以就地修改

data.index = data.index.map(transform)

2.如果想要创建数据集的转换版(而不是修改原始数据),比较实用的方法是rename:

data.rename(index=str.title, columns=str.upper)
       ONE  TWO  THREE  FOUR
Ohio    0     1    2      3
Colo    4     5    6      7
New     8     9   10     11

2.1 同时,rename可以结合字典型对象实现对部分轴标签的更新,加个inplace=True就可以就地修改

data.rename(index={'OHIO': 'INDIANA'},
   ....: columns={'three': 'peekaboo'})

           one  two  peekaboo  four
INDIANA     0    1       2       3
COLO        4    5       6       7
NEW         8    9      10      11

五、检测和过滤异常值

过滤或变换异常值(outlier)在很大程度上就是运用数组运算。来看一个含有正态分布数据的DataFrame:

data = pd.DataFrame(np.random.randn(1000, 4))
data.describe()
0   1   2   3
count   1000.000000 1000.000000 1000.000000 1000.000000
mean    -0.050093   0.060779    0.015470    -0.006734
std     0.992683    1.032154    0.991869    0.959386
min     -2.881544   -3.037328   -3.172889   -2.907349
25%     -0.700561   -0.605144   -0.659936   -0.639111
50%     -0.051532   0.077689    0.044447    -0.021604
75%     0.626197    0.708148    0.675407    0.620392
max     2.984373    3.104012    3.325806    3.632424

1.假设你想要找出某列中绝对值大小超过3的值:

col = data[2]
col[np.abs(col) > 3]
41    -3.399312
136   -3.745356
Name: 2, dtype: float64

2.要选出全部含有”超过3或-3的值”的行,你可以在布尔型DataFrame中使用any方法:

data[(np.abs(data) > 3).any(1)]
            0         1         2         3
41   0.457246 -0.025907 -3.399312 -0.974657
60   1.951312  3.260383  0.963301  1.201206
136  0.508391 -0.196713 -3.745356 -1.520113
235 -0.242459 -3.056990  1.918403 -0.578828
258  0.682841  0.326045  0.425384 -3.428254
322   1.179227 -3.184377  1.369891 -1.074833
544  -3.548824  1.553205 -2.186301  1.277104
635 -0.578093  0.193299  1.397822  3.366626
782 -0.207434  3.525865  0.283070  0.544635
803 -3.645860  0.255475 -0.549574 -1.907459

3.下面的代码可以将值限制在区间-3到3以内:

data[np.abs(data) > 3] = np.sign(data) * 3  #大于3变成3,小于-3变成-3

六、计算指标/哑变量

另一种常用于统计建模或机器学习的转换方式是:将分类变量(categorical variable)转换为”哑变量”或”指标矩阵”。

1.如果DataFrame的某一列中含有k个不同的值,则可以派生出一个k列矩阵或DataFrame(其值全为1和0)。

df = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'],
   .....:'data1': range(6)})
df
    key data1
0   b   0
1   b   1
2   a   2
3   c   3
4   a   4
5   b   5

pd.get_dummies(df['key'])
    a   b   c
0   0   1   0
1   0   1   0
2   1   0   0
3   0   0   1
4   1   0   0
5   0   1   0

2.有时候,你可能想给指标DataFrame的列加上一个前缀,以便能够跟其他数据进行合并。get_dummies的prefix参数可以实现该功能:

dummies = pd.get_dummies(df['key'], prefix='key')
df_with_dummy = df[['data1']].join(dummies)
df_with_dummy
    data1  key_a  key_b  key_c
0     0      0      1      0
1     1      0      1      0
2     2      1      0      0
3     3      0      0      1
4     4      1      0      0
5     5      0      1      0

如果DataFrame中的某行同属于多个分类,则事情就会有点复杂。看一下MovieLens 1M数据集:

mnames = ['movie_id', 'title', 'genres']
movies = pd.read_table('F:/hellopython/movies.dat', sep='::',header=None, names=mnames)
movies[:10]
   movie_id                               title                        genres
0         1                    Toy Story (1995)   Animation|Children's|Comedy
1         2                      Jumanji (1995)  Adventure|Children's|Fantasy
2         3             Grumpier Old Men (1995)                Comedy|Romance
3         4            Waiting to Exhale (1995)                  Comedy|Drama
4         5  Father of the Bride Part II (1995)                        Comedy
5         6                         Heat (1995)         Action|Crime|Thriller
6         7                      Sabrina (1995)                Comedy|Romance
7         8                 Tom and Huck (1995)          Adventure|Children's
8         9                 Sudden Death (1995)          Action
9        10                    GoldenEye (1995)     Action|Adventure|Thriller

1.首先,我们从数据集中抽取出不同的genre值:

all_genres = []
for x in movies.genres:
    all_genres.extend(x.split('|'))
genres = pd.unique(all_genres)  #这样就提取出来了

构建指标DataFrame的方法之一是从一个全零DataFrame开始:

zero_matrix = np.zeros((len(movies), len(genres)))
dummies = pd.DataFrame(zero_matrix, columns=genres)

2.现在,迭代每一部电影,并将dummies各行的条目设为1。要这么做,我们使用dummies.columns来计算每个类型的列索引:

gen = movies.genres[0]
gen.split('|')
['Animation', "Children's", 'Comedy']  #他所属的类
dummies.columns.get_indexer(gen.split('|'))  #这样就能计算他所属的类在所有的里面是在哪里
array([0, 1, 2])  #在第0,1,2行

for i, gen in enumerate(movies.genres):
    indices = dummies.columns.get_indexer(gen.split('|'))
    dummies.iloc[i, indices] = 1   #写个循环遍历所有的ID
movies_windic = movies.join(dummies.add_prefix('Genre_')) #结合起来
movies_windic.iloc[0]

movie_id                                       1
title                           Toy Story (1995)
genres               Animation|Children's|Comedy
Genre_Animation                                1
Genre_Children's                               1
Genre_Comedy                                   1
Genre_Adventure                                0
Genre_Fantasy                                  0
Genre_Romance                                  0
Genre_Drama                                    0
                                ...

Genre_Crime                                    0
Genre_Thriller                                 0
Genre_Horror                                   0
Genre_Sci-Fi                                   0
Genre_Documentary                              0
Genre_War                                      0
Genre_Musical                                  0
Genre_Mystery                                  0
Genre_Film-Noir                                0
Genre_Western                                  0
Name: 0, Length: 21, dtype: object

七、将值分类

values = np.random.rand(10)
bins = [0, 0.2, 0.4, 0.6, 0.8, 1]
pd.get_dummies(pd.cut(values, bins))
    (0.0, 0.2]  (0.2, 0.4]  (0.4, 0.6]  (0.6, 0.8]  (0.8, 1.0]
0           0           0           0           0           1
1           0           1           0           0           0
2           1           0           0           0           0
3           0           1           0           0           0
4           0           0           1           0           0
5           0           0           1           0           0
6           0           0           0           0           1
7           0           0           0           1           0
8           0           0           0           1           0
9           0           0           0           1           0

Original: https://blog.csdn.net/abc1234564546/article/details/125835836
Author: 平平平安喔
Title: python数据清洗

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/742971/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球