数据清洗和准备

处理缺失数据

string_data = pd.Series(['aardvark', 'artichoke', np.nan, 'avocado'])
string_data
string_data.isnull()   #判断每个值是不是缺失值
string_data[0] = None
string_data.isnull()

滤除缺失数据

from numpy import nan as NA
data = pd.Series([1, NA, 3.5, NA, 7])
data.dropna()  #丢弃含有缺失值的行
data[data.notnull()]
#第一种方法是主流
data = pd.DataFrame([[1., 6.5, 3.], [1., NA, NA],
                     [NA, NA, NA], [NA, 6.5, 3.]])
cleaned = data.dropna()    #丢弃所有含有缺失值的行 
data
cleaned
data.dropna(how='all')  #丢弃全为NA的行,用这种方法丢弃列只需输入参数axis=1

data[4] = NA  #增加一个名为4的列
data
data.dropna(axis=1, how='all')
df = pd.DataFrame(np.random.randn(7, 3))
df.iloc[:4, 1] = NA
df.iloc[:2, 2] = NA
df
df.dropna()   #滤除所有带缺失值德行
df.dropna(thresh=2)  #滤除带缺失值的前两行

填充缺失数据

对于大多数情况来说fillna是最好的方法

df.fillna(0)   #所有缺失值都换成0
df.fillna({1: 0.5, 2: 0})    #列1的缺失值换成0.5,列2的缺失值换成0
_ = df.fillna(0, inplace=True)    #添加参数inplace对现有对象就地修改,不产生新对象
df

#对reindexing有效的插值方法也可以用于fillna
df = pd.DataFrame(np.random.randn(6, 3))
df.iloc[2:, 1] = NA
df.iloc[4:, 2] = NA
df
df.fillna(method='ffill')   #缺失值出插值与上一行一样的数
df.fillna(method='ffill', limit=2)  #对于有缺失值的行,只插值前两行
data = pd.Series([1., NA, 3.5, NA, 7])
data.fillna(data.mean())   #缺失值处传入Series的平均值

7.2数据转换

7.2.1移除重复数据

data = pd.DataFrame({'k1': ['one', 'two'] * 3 + ['two'],
                     'k2': [1, 1, 2, 3, 3, 4, 4]})
data
data.duplicated()    #判断各行是不是重复行,返回的是布尔型的Series格式
data.drop_duplicates()     #返回一个DataFrame,并且只输出非重复行  
data['v1'] = range(7)    #增加名为V1的列
data.drop_duplicates(['k1'])    #输出K1列不重复的所在的行
data.drop_duplicates(['k1', 'k2'], keep='last')    #duplicated和drop_duplicates默认保留的是第⼀个出现的值组合。传⼊keep='last'则保留最后⼀个。

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

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
meat_to_animal = {
  'bacon': 'pig',
  'pulled pork': 'pig',
  'pastrami': 'cow',
  'corned beef': 'cow',
  'honey ham': 'pig',
  'nova lox': 'salmon'
}

#Series的map⽅法可以接受⼀个函数或含有映射关系的字典型对象,但是这⾥有⼀个⼩问题,即有些⾁类的⾸字⺟⼤写了,⽽另⼀些则没有。因此,我们还需要使⽤Series的str.lower⽅法,将各个值转换为⼩写

lowercased = data['food'].str.lower()    #得到上面DataFrame中的列‘food',并且格式是Series,然后把首字母变成小写
lowercased
data['animal'] = lowercased.map(meat_to_animal)
data
data['food'].map(lambda x: meat_to_animal[x.lower()])

7.2.3替换值

data = pd.Series([1., -999., 2., -999., -1000., 3.])
data
data.replace(-999, np.nan)    #把-999替换为缺失值NA
data.replace([-999, -1000], np.nan)   #把-999和-1000替换为缺失值NA
data.replace([-999, -1000], [np.nan, 0])     #把-999替换为缺失值NA,把-1000替换为0
data.replace({-999: np.nan, -1000: 0})    #和上一个效果一样,只是写为了字典的形式

data.replace⽅法与data.str.replace不同,后者做的是字符串的元素级替换

7.2.4重命名轴索引


data = pd.DataFrame(np.arange(12).reshape((3, 4)),
                    index=['Ohio', 'Colorado', 'New York'],
                    columns=['one', 'two', 'three', 'four'])
transform = lambda x: x[:4].upper()
data.index.map(transform)  
data.index = data.index.map(transform)    #换index
data
data.rename(index=str.title, columns=str.upper)    #创建新的,不修改原始数据
data.rename(index={'OHIO': 'INDIANA'},
            columns={'three': 'peekaboo'})      #结合字典,修改部分轴标签
data.rename(index={'OHIO': 'INDIANA'}, inplace=True)   #加入Inplace=True参数,就可以就地修改数据
data

7.2.5离散化和面元划分

为了便于分析,连续数据常常被离散化或拆分为“⾯元”(bin)

ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]
bins = [18, 25, 35, 60, 100]
cats = pd.cut(ages, bins)
cats
#将数据划分为“18到25”、 “26到35”、 “35到60”以及“60以上”⼏个⾯元。
pd.value_counts(cats)  #pandas.cut结果的面元计数
pd.cut(ages, [18, 26, 36, 61, 100], right=False)   #添加参数right,变成左闭右开
group_names = ['Youth', 'YoungAdult', 'MiddleAged', 'Senior']
pd.cut(ages, bins, labels=group_names)  #labels参数为面元设置名称
data = np.random.rand(20)
pd.cut(data, 4, precision=2)    # 4表示面元的数量,参数precision=2表示保留小数点后两位

qcut是⼀个⾮常类似于cut的函数,它可以根据样本分位数对数据
进⾏⾯元划分。根据数据的分布情况, cut可能⽆法使各个⾯元
中含有相同数量的数据点。⽽qcut由于使⽤的是样本分位数,因
此可以得到⼤⼩基本相等的⾯元。

data = np.random.randn(1000)  # Normally distributed
cats = pd.qcut(data, 4)  # Cut into quartiles
cats
pd.value_counts(cats)
pd.qcut(data, [0, 0.1, 0.5, 0.9, 1.]) #传入了自定义的分位数

7.2.6检验和过滤异常值

data = pd.DataFrame(np.random.randn(1000, 4))
data.describe()   #描述性统计
col = data[2]
col[np.abs(col) > 3]
#得到列2中绝对值大于3的值
data[(np.abs(data) > 3).any(1)]  #选出含有绝对值大于3的数的行
data[np.abs(data) > 3] = np.sign(data) * 3  #将值限制在-3到3之间
data.describe()
np.sign(data).head()  #根据数值的正负,可以生成1或-1

7.2.7排列和随机采样

利⽤numpy.random.permutation函数可以轻松实现对Series或DataFrame的列的排列⼯作

df = pd.DataFrame(np.arange(5 * 4).reshape((5, 4)))
sampler = np.random.permutation(5)    #随机排0到4五个数,得到的是一个数组
sampler
df
df.take(sampler)   #按上面得到的顺序重拍DataFrame的index行
df.sample(n=3)     #不可重复的抽取3行
choices = pd.Series([5, 7, -1, 6, 4])
draws = choices.sample(n=10, replace=True)   #添加参数replace,变成可重复抽样·
draws

7.2.8计算指标/哑变量

⼀种常⽤于统计建模或机器学习的转换⽅式是:将分类变量(categorical variable)转换为“哑变量”或“指标矩阵”。如果DataFrame的某⼀列中含有k个不同的值,则可以派⽣出⼀个k列矩阵或DataFrame(其值全为1和0)。 pandas有⼀个get_dummies函数可以实现该功能。

df = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'],
                   'data1': range(6)})
pd.get_dummies(df['key'])    #每一个值对应的列的值变为1,其他列为0

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

dummies = pd.get_dummies(df['key'], prefix='key')    #参数prefix给列名添加前缀
df_with_dummy = df[['data1']].join(dummies)    #.join用于和其他数据合并,多增加几列
df_with_dummy

mnames = ['movie_id', 'title', 'genres']
movies = pd.read_table('datasets/movielens/movies.dat', sep='::',
                       header=None, names=mnames)
movies[:10]
all_genres = []
for x in movies.genres:
    all_genres.extend(x.split('|'))
genres = pd.unique(all_genres)
genres 
#提取generes中所有不同的值

zero_matrix = np.zeros((len(movies), len(genres)))
dummies = pd.DataFrame(zero_matrix, columns=genres)
gen = movies.genres[0]    
gen.split('|')   #这个DataFrame中genres的第一行电影属于的种类有3种
dummies.columns.get_indexer(gen.split('|'))   #pd.Index(unique_vals).get_indexer(to_match)
#to_match中各元素在索引数组中的位置
for i, gen in enumerate(movies.genres):
    indices = dummies.columns.get_indexer(gen.split('|'))
    dummies.iloc[i, indices] = 1
movies_windic = movies.join(dummies.add_prefix('Genre_'))
movies_windic = movies.join(dummies.add_prefix('Genre_'))
movies_windic.iloc[0]

对于很⼤的数据,⽤这种⽅式构建多成员指标变量就会变得⾮常慢。最好使⽤更低级的函数,将其写⼊NumPy数组,然后结果包装在DataFrame中。⼀个对统计应⽤有⽤的秘诀是:结合get_dummies和诸如cut之类的离散化函数:

np.random.seed(12345)
values = np.random.rand(10)
values
bins = [0, 0.2, 0.4, 0.6, 0.8, 1]
pd.get_dummies(pd.cut(values, bins))

7.3字符串操作

⼤部分⽂本运算都直接做成了字符串对象的内置⽅法。对于更为复杂的模式匹配和⽂本操作,则可能需要⽤到正则表达式。 pandas对此进⾏了加强,它使你能够对整组数据应⽤字符串表达式和正则表达式,⽽且能处理烦⼈的缺失数据。

7.3.1字符串现象方法

val = 'a,b,  guido'
val.split(',')   #按逗号切割字符串
pieces = [x.strip() for x in val.split(',')]   #x.strip去除两边的空格
pieces
'::'.join(pieces)   #把::加到每两个字符之间
'guido' in val
val.index(',')   #逗号第一次出现的位置,如果不存在则返回错误
val.find(':')    # :不存在,返回-1,这点也与index不同
val.index(':')   #  返回错误
val.count(',')     #统计逗号出现的次数
val.replace(',', '::')    #用::代替逗号
val.replace(',', '')

7.3.2正则表达式

正则表达式提供了⼀种灵活的在⽂本中搜索或匹配(通常⽐前者
复杂)字符串模式的⽅式。re模块的函数可以分为三个⼤类:模式匹配、替换以及拆分。

import re
text = "foo    bar\t baz  \tqux"
re.split('\s+', text)  #分隔符为数量不定的⼀组空⽩符(制表符、空格、换⾏符等)。描述⼀个或多个空⽩符的regex是\s+:
regex = re.compile('\s+')
regex.split(text)
#⽤re.compile⾃⼰编译regex以得到⼀个可重⽤的regex对象也是可以的,并且如果打算对许多字符串应⽤同⼀条正则表达式,强烈建议通过
re.compile创建regex对象。这样将可以节省⼤量的CPU时间。

7.3.3pandas的矢量化字符串函数

清理待分析的散乱数据时,常常需要做⼀些字符串规整化⼯作。更为复杂的情况是,含有字符串的列有时还含有缺失数据。

data = {'Dave': 'dave@google.com', 'Steve': 'steve@gmail.com',
        'Rob': 'rob@gmail.com', 'Wes': np.nan}
data = pd.Series(data)
data
data.isnull()   #判断哪个值是缺失值
data.str.contains('gmail')    #用str.comtains方法判断每个值是否含有gmail
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值