1数据清洗
1.1缺失数据处理
isnull检测缺失值;dropna删除缺失值;python内置的none值在对象数组中也可以作为na值
string_data = pd.Series(['aardvark', 'artichoke', np.nan, 'avocado'])
string_data.isnull() # 检测空值
data.dropna() # 删除空值
data[data.notnull()] # 提取出不是空值的数据
DataFrame对象,dropna默认丢弃任何含有缺失值的行(只要有na就删除);传入how=‘all’将只丢弃全为NA的那些行
cleaned = data.dropna()
data.dropna(how='all')
data.dropna(axis=1, how='all') #删除列上全为空的列
df.iloc[:4, 1] = NA #将1-4行,第2列的数据全置为na
df.dropna(thresh=2) # 删除大于等于2个非空值的行
1.2缺失数据填充
填充缺失数据中fillna方法是主要的函数。通过一个常数调用fillna就会将缺失值替换为那个常数值。通过字典调用,可以实现对不同的列填充不同的值
df.fillna(0) # 将na值填充为0
df.fillna({1: 0.5, 2: 0}) # 第2列缺失填0.5,第三列缺失填0
fillna默认会返回新对象,但也可以对现有对象进行就地修改
_ = df.fillna(0, inplace=True)
对reindexing有效的那些插值方法也可用于fillna(如轴标签loc和整数索引iloc及前向填充等)
df.fillna(method='ffill') #对na值用前向填充
df.fillna(method='ffill', limit=2) # 最多前向填充两个
填充series的平均值或中位数
data = pd.Series([1., NA, 3.5, NA, 7])
data.fillna(data.mean()) # 有值就用原值,没有就用有数据的平均数
1.3删除重复数据
data = pd.DataFrame({'k1': ['one', 'two'] * 3 + ['two'], 'k2': [1, 1, 2, 3, 3, 4, 4]})
data.duplicated() # 前向比较,与前项相同就标为true
data.drop_duplicates() # 删除与前项相同的数据
data.drop_duplicates(['k1']) # 根据k1列过滤重复项
data.drop_duplicates(['k1', 'k2'], keep='last')
drop_duplicates方法,它会返回一个DataFrame,重复的数组会标为False;duplicated和drop_duplicates默认保留的是第一个出现的值组合。传入keep=‘last’则保留 后一个
1.4利用函数或映射进行数据转换
根据数组、Series或DataFrame列中的值来实现转换工作
data = pd.DataFrame({'food': ['bacon', 'pulled pork'], 'ounces': [4, 3,]})
添加一列表示该肉类食物来源的动物类型。我们先编写一个不同肉类到动物的映射
meat_to_animal = { 'bacon': 'pig', 'pulled pork': 'pig'}
lowercased = data['food'].str.lower() # 使用Series的str.lower方法;字母转小写
data['animal'] = lowercased.map(meat_to_animal) # 映射
也可以传入一个能够完成全部这些工作的函数
data['food'].map(lambda x: meat_to_animal[x.lower()])
1.5替换
-999这个值可能是一个表示缺失数据的标记值。要将其替换为pandas能够理解的NA值(使用replace)
data = pd.Series([1., -999., 2., -999., -1000., 3.])
data.replace(-999, np.nan)
data.replace([-999, -1000], np.nan) # 一次性替换两种数据
data.replace([-999, -1000], [np.nan, 0]) # 每种数据对应不同的替换类型
data.replace({-999: np.nan, -1000: 0}) # 也是替换两种数据
1.6重命名轴索引
transform = lambda x: x[:4].upper() # 将行的索引切前四个并全部大写
data.index.map(transform) # 替换行的索引
如果想要创建数据集的转换版(而不是修改原始数据)
data.rename(index=str.title, columns=str.upper)
rename可以结合字典型对象实现对部分轴标签的更新;或者就地修改某个数据集,传入inplace=True即可
# 将OHIO换成INDIANA。three换成peekaboo
data.rename(index={'OHIO': 'INDIANA'}, columns={'three': 'peekaboo'})
data.rename(index={'OHIO': 'INDIANA'}, inplace=True) # 修改OHIO为INDIANA
1.7离散化和面元划分
为了便于分析,连续数据常被离散化或拆分为“面元”(bin)。
分隔后的参数默认为前开后闭,可以对cut进行设置right=False使其前闭后开
ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32] #年龄表
bins = [18, 25, 35, 60, 100] # 年龄段划分的断点
cats = pd.cut(ages, bins) # 划分年龄,返回的是年龄对应的每个年龄段
pd.value_counts(cats) # 统计每个区间的人的个数
pd.cut(ages, [18, 26, 36, 61, 100], right=False) # 切割成前闭后开的类型
group_names = ['Youth', 'YoungAdult', 'MiddleAged', 'Senior']
pd.cut(ages, bins, labels=group_names) # 给每个区间设置名称
向cut传入的是面元的数量而不是确切的面元边界,则它会根据数据的最小值和最大值计算等长面元。下面这个例子中,我们将一些均匀分布的数据分成四组,选项precision=2,限定小数只有两位。
qcut是一个非常类似于cut的函数,它可以根据样本分位数对数据进行面元划分。
pd.cut(data, 4, precision=2)
cats = pd.qcut(data, 4)
pd.value_counts(cats) # 统计个数
cut和qcut区别:cut是将区间进行均分,区间间隔相同,每个区间的数值个数不同 ;qcut是将每个区间的数值进行均分的数值处划分;区间间隔不同,每个区间的数值个数相同。
1.8检测和过滤异常值
describe() 统计每一列的个数,平均值,中位数,最大、最小值等;np.sign(data)可以生成1和-1
data = pd.DataFrame(np.random.randn(1000, 4))
data.describe() # 统计每一列的个数,平均值,中位数,最大、最小值等
col = data[2] # 取出第三列
col[np.abs(col) > 3] # 统计这列中绝对值大于3的数
data[(np.abs(data) > 3).any(1)] # 选出全部含有超过-3或3的行
np.sign(data).head() #生成1和-1
1.9排列和随机采样
利用numpy.random.permutation函数可以轻松实现对Series或DataFrame的列的排列工作 (permuting,随机重排序)
df = pd.DataFrame(np.arange(20).reshape((5, 4)))
sampler = np.random.permutation(5)
df.take(sampler) # 按随机生成的sampler中的顺序对df进行重排序
df.sample(n=3) # 随机排列3行(从df中随机取3行)
要通过替换的方式产生样本(允许重复选择),可以传递replace=True到sample
choices = pd.Series([5, 7, -1, 6, 4])
draws = choices.sample(n=10, replace=True)
1.10计算指标
df = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'],'data1': range(6)})
pd.get_dummies(df['key']) # 取key列的数据,并将其作为列的索引,统计每一行出现的位置
dummies = pd.get_dummies(df['key'], prefix='key') # 统计并给索引加前缀key_
1.11从dat文件中读数据并写入数据库
在读取数据前,需要退出ipython环境,并运行conda install pymysql安装依赖库
# 连接数据库
db= sqla.create_engine('mysql+pymysql://root:123456@127.0.0.1/pdmovie?charset=utf8')
# 给读取的数据前索引,必须于数据库中的字段保持一致
mnames = ['movie_id', 'title', 'genres']
# 读取数据
movies = pd.read_table('datasets/movielens/movies.dat', sep='::', header=None,
names=mnames, engine='python')
# 吸入数据库
movies.to_sql('movies', db,index=False, if_exists='append')
all_genres = []
# 遍历读取的数据中的genres列,实质上就是一个serise
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)
通过data.map,所有字符串和正则表达式方法都能被应用于(传入lambda表达式或其他函数)各个值,但是如果存在NA(null)就会报错。为了了解决这个问题,Series有一些能够跳过 NA值的面向数组方法,进行字符串操作。通过Series的str属性即可访问这些方法。例如,我们可以通过str.contains检查各个电子邮件地址是否含有"gmail"
data = {'Dave': 'dave@google.com', 'Steve': 'steve@gmail.com', 'Rob': 'rob@gmail.com', 'Wes': np.nan}
data = pd.Series(data)
data.str.contains('gmail') # 查看是否含有gmail
矢量化字符串函数也可以使用正则表达式,还可以加上任意re选项(如IGNORECASE)
import re
pattern = r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})'
data.str.findall(pattern, flags=re.IGNORECASE) # 按@和.符号将其切分为三个部分
matches = data.str.match(pattern, flags=re.IGNORECASE) # 使用match进行比对
data.str[:5] # 对data中的每个字符串进行切片
网友评论