Python之数据分析
Numpy
Numpy是Python的一种开源的数值计算扩展。可以用来存储和处理大型矩阵,比Python自身的嵌套列表结构(nested list structure)要高效得多。在实际工作中直接使用情况较少。
数组
numpy.array()
import numpy as np a = np.array([1, 2, 3, 4]) print(a) # 输出为:array([1, 2, 3, 4]) # 和 列表 相似,但处理效率要高很多 a[0] = 5 # 得到数组 array([5, 2, 3, 4]) a + 1 # 数组中每个元素都+1,得到数组 array([6, 3, 4, 5]) # 减法、乘除法同理 b = np.array([[1, 2, 3], [4, 5, 6]]) # 可以生成多维数组
查看数组中存储的数据类型:
dtype
print(b.dtype) # 输出为:int32
Pandas
Pandas是基于Numpy的一种工具,是为了解决数据分析任务而创建的。Pandas提供了大量能使我们快速便捷地处理数据的函数和方法。
import pandas as pd
两种数据结构
Series (一维)
s1 = pd.Series([1, 2, 3, 4])
print(s1)
# 输出为:
# 0 1
# 1 2
# 2 3
# 3 4
# dtype: int64
print(s.index)
# 输出为:RangeIndex(start=0, stop=4, step=1)
s1[1]
s2 = pd.Series([1, 2, 3, 4], index = ['a', 'b', 'c', 'd'])
print(s2)
# 输出为:
# a 1
# b 2
# c 3
# d 4
# dtype: int64
s2['b']
print(s2[['a', 'c']])
# 索引多个值 需要用 列表 来表示
# 输出为:
# a 1
# c 3
# dtype: int64
d = {'hunter':'smart', 'peter':'handsome'}
s3 = pd.Series(d)
print(s3)
# 输出为:
# hunter smart
# peter handsome
# dtype: object
s3 = pd.Series(d, index=['hunter', 'peter', 'haha'])
print(s3)
# 输出为:
# hunter smart
# peter handsome
# haha NaN
# dtype: object
# 字符串类型在Series里显示的类型为object
# 可转换类型
s2 = s2.astype('str') # 将 数值类型 转换为 字符串类型
# 将浮点数类型转换为int类型
x_value = titanic['Fare'].astype(int)
DataFrame (二维)
d = {
'name':['qinlu', 'lulu', 'qinqin']
'sex':['male', 'male', 'female']
'age':[18, 18, 25]
}
pd.DataFrame(d)
# 创建一个空的数据框
data = pd.DataFrame()
得到如下二维表格:
name | sex | age | |
---|---|---|---|
0 | qinlu | male | 18 |
1 | lulu | male | 18 |
2 | qinqin | female | 25 |
df = pd.DataFrame([1, 2, 3, 4]) # 效果类似 Series
df
得到如下二维表格:
0 | |
---|---|
0 | 1 |
1 | 2 |
2 | 3 |
3 | 4 |
df = pd.DataFrame([[1, 2, 3, 4], [3, 4, 5, 6]], columns=list('abcd'))
# 提供两行数据和 各列的列名
df
得到如下二维表格:
a | b | c | d | |
---|---|---|---|---|
0 | 1 | 2 | 3 | 4 |
1 | 3 | 4 | 5 | 6 |
DataFrame的列的顺序默认为提供的顺序。如下操作可以调整列的顺序:
df = df[['b', 'd', 'c', 'a']]
df
得到如下二维表格:
b | d | c | a | |
---|---|---|---|---|
0 | 2 | 4 | 3 | 1 |
1 | 4 | 6 | 5 | 3 |
df = pd.DataFrame(d)
# 概览数据
df.info()
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 3 entries, 0 to 2
# Data columns (total 3 columns):
# # Column Non-Null Count Dtype
# --- ------ -------------- -----
# 0 name 3 non-null object
# 1 sex 3 non-null object
# 2 age 3 non-null int64
# dtypes: int64(1), object(2)
# memory usage: 200.0+ bytes
# 查看每个列的数据类型
df.dtypes
# 查看某个列的数据类型
df['name'].dtype
#获取某一行
df.iloc[0] #根据 索引(不论是否自定义了标签名) 读取某一行 (推荐,数据量大了之后更适用)
df.loc[0] # 根据 具体标签名 读取某一行
# 上述两种方式输出相同:
# name lulu
# sex male
# age 18
# Name: 1, dtype: object
# 获取多行
df.iloc[0:2] # 闭区间
df[0:2] # 开区间
#获取某一列
df['age']
#获取多个列
df[['age', 'name']]
df.iloc[:, 0:7] # 所有行和前七列
# 获取二维表格部分列
df[['age', 'name']][1:2] # 开区间
df[['age', 'name']]['age'] # 获取部分列中的指定列
# 读取二维表格部分区域
df.iloc[1:2, 1:2] # 指出 索引,iloc不支持布尔类型的过滤
df.loc['a', ['age', 'name']] # 指出 行和列的 标签名
df.loc[df.age == 19, 'age'] # 指出 符合条件的行的列值 和 列标签
# 更改具体值
df['age'] = 21 # 统一更改整个列
df['age'] += 1 # 统一更改整个列
df['age'] = [21, 18, 18] # 自定义更改列中的值
df['age'][1] = 22 # 更改某个值
df.loc[df.age == 19, 'age'] = 20 # 更改某个筛选范围的值
# 更改标签
df.index = list('abc') # 更改行标签,通常处理的数据量很大,不建议修改
df.columns = list('abcd') # 更改列标签
Series通过reset_index()
可以转换为DataFrame
Series原有的index也是变为一个列
~
代表反转
~ (df.age > 19) #等价于 df.age <= 19
筛选
df[df.age == 18] # 筛选出 年龄=18的 所有行(筛选 结果为True 的行)
df.query('age == 18') # 和上一条代码等价,条件需要引号标注
df[(df.age == 18) | (df.name == 'qinqin')] # 通过 括号和&/|运算符 进行多条件筛选
df.query("(age == 18) | (name == 'qinqin')") # 注意括号不要冲突
增删
pandas中增删的运算效率低,尽量不使用。
del
:将指定列从存储空间中删除
del df.loc[df.age == 19, 'age']
drop()
:删除指定项时返回的是视图,数据仍然在存储空间中df.drop('age', axis=1) # axis=1表示列,axis=0表示行(默认)
Pandas基础命令速查表
读取/写入文件
读取
import pandas as pd
df = pd.read_csv('xxxx.csv', encoding='xxx', sep=',', names='abcdefg')
df = pd.read_table('xxxx', sep='\t')
read_csv()
的参数:
文件名
encoding
:指定编码python默认以utf-8编码读取文件。如果报错无法正确显示文件内容,可以将编码改为gbk等进行读取。
sep
:指定分隔符,默认为逗号,
有的文件由于不标准等原因,采用其他分隔符如
\t
,可通过指定分隔符优化显示。sep='\s+'
:将tab和多个空格都当成一样的分隔符。parse_dates
:将数据解析为日期parse_dates = True
:尝试解析所有可能为日期类型的列;parse_dates = [1, 2]
:尝试解析给定列为日期类型的列。parse_dates = [[1, 2]]
:尝试解析给定列为日期类型的列,并将这些列聚合成为1个列names
指定列名,默认为文件中的第一行。如果自定义列名,文件中的第一行将作为第一行数据显示。
df.info() # 概览数据
df.head(10) # 默认读取前5行
df.tail(20) # 默认读取最后5行
# 根据当前已有的列生成新列
df['avg'] = (df.bottom + df.top) / 2
写入
to_csv()
函数
T
:转置表格df.T
shape
:获取数据框的行数和列数(元组存储)如
df.shape
:可能得到如(10,5)
(10行5列)columns
:获取所有列index
:查看索引信息set_index()
:设置索引crime = crime.set_index('Year')
筛选函数
sort_values()
:按值排序,默认升序参数:
by
:指出排序依据的字段df.sort_values(by = 'avg') # 下述方式效果类似(返回的是avg的有序数组) df.avg.sort_values() # 根据多个条件排序 df.sort_values(by = ['avg', 'city']) # 排序根据unicode而不是拼音,如果想要按照拼音顺序,需要将中文和英文字母关联(可新建一个城市首字母缩写的列)
ascending
:默认为True(升序)inplace
:是否用排序后的数据集替换原来的数据,默认为False
sort_index()
:按索引排序rank()
:给出排名参数:
ascending
:默认为True(升序)method
'average'
(默认,如前5个人分数相同,则排名为**(1+5) / 2** (最大值和最小值的加权平均数))'min'
(取最小值)'max'
'first'
(不考虑并列情况)
df['rank'] = df.avg.rank(ascending=False, method='min') # 按照平均工资排名,将排名作为新的列加入表格
unique()
:去除重复项df.city.unique()
value_counts()
:对Series的每个值进行计数并且排序df.education.value_counts()
describe()
df.avg.describe()
得到:
count 5031.000000 mean 17.111409 std 8.996242 min 1.500000 25% 11.500000 50% 15.000000 75% 22.500000 max 75.000000 Name: avg dtype: float64
count
:计数mean
:平均数std
:标准差
describe()
的统计内容会自动忽略空值count()
max()
min()
last()
mean()
对整个数据框求平均值,结果为按列(默认)分别计算平均值,得到一个Series;
对Series求平均值,结果则为一个平均数
参数:
axis=0
(0为按列,1为按行)sum()
median()
:中位数std()
:标准差var()
:方差cumsum()
:累加pandas.cut()
:分类统计# 4等分 pd.cut(df.avg, bins=4, label=list('abcd')) # 自定义分隔区间 # 为了能够全部包含需要分类的数据区间,最后的值一般一个取极大的值 pd.cut(df.avg, bins=[0, 5, 10, 20, 30, 100], label=['0~5', '5~10', '10~20', '20~30', '30~100'])
pandas.qcut()
isin()
# 找到英格兰(England)、意大利(Italy)和俄罗斯(Russia)的射正率(Shooting Accuracy) euro12.loc[euro12['Team'].isin(['England', 'Italy', 'Russia']), ['Team', 'Shooting Accuracy']]
可以和
~
配合使用,达到不存在的函数isnotin()
的效果。idxmax()
:返回请求轴上第一次出现最大值(不包括``NA/null`)的索引参数:
axis=0
:0对应行,1对应列
聚合函数
MySQL难以完成分组排序。
groupby()
:分组df.groupby(by = 'city') # 得到如下已存入内存的提示: # <pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000012CF16A41C8> # 当前分组下,对各分组中的各列进行统计 df.groupby(by = 'city').count() # 当前分组下,各分组中各列的最大值 df.groupby(by = 'city').max() # 当前分组下,对各分组中某列的最大值 df.groupby(by = 'city')['avg'].max() # 多字段分组 df.groupby(by = ['city', 'workYear']).mean() for k, v in df.groupby(by=['city']): # 打印每个分组中的元素数量 print(len(k[1])) # 打印分组的依据元素(具体的city) print(k) # 打印分割线 print('**' *10) # 20个星号 # 打印分组中元素的详细内容 print(v) # 打印同个城市中,最高薪资和最低薪资的差值 print(max(v.avg) - min(v['avg']))
多表关联
import pandas as pd
position = pd.read_csv('position.csv')
company = pd.read_csv('company.csv')
merge()
函数 —— 根据具体的键值相当于SQL中的JOIN
position.merge(right = company, how='left', on='companyId') # 更改列名 # 方式一:单独更改一个列名 col = list(company.columns) col[0] = 'id' company.columns = col # 方式二:利用rename()更改指定列名 company.rename(columns={'0':'id', '1':'xxx'}, inplace=True) pd.merge(left=position, right=company, how='inner', left_on='companyId', right_on='id')
参数:
left/right
:关联的表how
:关联的方式inner
(默认)outer
left
right
on
:具体的键值(在两个表中对应列名相同时使用)left_on/right_on
:具体的键值(在两个表中对应列名不同时使用,关联之后保留不同名的列)
join()
:根据索引(行号)按照索引关联很有局限性。
- 只要两个表列名不同,不加任何参数就可直接使用
- 如果两个表有重复的列名,需要制定
lsuffix
,rsuffix
参数 - 默认左外连接(LEFT JOIN)
df1.join(df2)
concat()
:堆叠类似于SQL中的UNION,多个表暴力堆叠,新的表包含多个表的所有列,没有对应列值的表项为NaN。
参数:
axis
:- 0:上下堆叠(默认,相同列名会合并 )
- 1:左右堆叠
pd.concat([company, position], axis=1)
区分辨别
Series级别可以直接输入索引标签进行筛选;DataFrame级别,需要通过loc
进行筛选。
position.groupby(by=['city', 'education']).mean().avg['上海']
position.groupby(by=['city', 'education']).mean().loc['上海', '博士']
# reset_index() 重置索引
position.groupby(by=['city', 'education']).mean().reset_index()['上海', '博士']
不借助groupby
,如何设置多重索引:
# sort_values 用于排序,set_index 将列设置为索引
position.sort_values(by=['city', 'education']).set_index(['city', 'education'])
agg()
函数agg()
是聚合函数,得到DataFrame。参数有:func
:实现某种统计功能的函数axis=0
# grouped_user 是通过groupby('user_id')得到的 DataFrameGroupBy 对象 user_life = grouped_user['order_dt'].agg(['first', 'last']) # order_dt列是时间格式,first 和 last 函数能分别得到最早和最迟的时间 user_life.head()
文本函数
str.count()
:统计指定字符出现的次数position.positionLabels.str.count('分析师')
str.find()
:查找指定字符出现的位置position.positionLabels.str.find('数据')
str[1:-1]
:去除表示列表的方括号position.positionLabels.str[1:-1]
str.replace("'", "")
:去除列表中元素的单引号position.positionLabels.str[1:-1].str.replace("'", "")
str.startwith()
:筛选以指定字符开头的字符串euro12['Team'].str.startwith('G')
str.split()
:拆分单元格内容df2 = df['某一列'].str.split('指定分隔符', expand=True) # 默认会删去分隔符 # expand=True 能让拆分后的单元格内容 由 列表类型 变为 DataFrame(默认为False)
str.extract()
:提取单元格中所需内容参数:
pat
:字符串或正则表达式- 只有用
()
包裹的部分才会被保留 - Python中使用正则表达式命名捕获组语法为:
(?P<name>Expression)
,即将name
作为列名
- 只有用
flags
:整型expand
:是否将提取的内容由列表类型变为DataFrame
# 提取 '建筑年代'列 的数字,剥离 年建 df3['建筑年代'] = df3['建筑年代'].str.extract('(\d+)年建')
str(xxx)
:将其他类型的数据(如时间)转为字符串
时间相关
Python中的 datetime模块
from datetime import datetime from datetime import timedelta now = datetime.now() # 获取当前日期和时间 print(now) print(datetime.date.today()) # 获取当前日期 delta = now - datetime(2017,6,27,10,10,10,10) # 获取日期相差值 print(delta) # 得到 xxdays, xx:xx:xx.xxx print(delta.days) # 72天数 print(delta.seconds) # 秒数 print(delta.microseconds) # 微秒数 # 2061年?我们真的有这一年的数据?创建一个函数并用它去修复这个bug # x是一个 YYYY-mm-dd格式 的日期 def fix_century(x): year = x.year if year > 2000: year -= 100 return datetime.date(year, x.month, x.day) text = '2019-09-07' y = datetime.strptime(text, '%Y-%m-%d') # 把字符串转为日期 print(y) # 当前日期的前后n日期 print(datetime.date.today()+timedelta(days=-1)) # 获得某一日期的月初和月末 text='2019-09-07' month_first=datetime.strptime(text[:8]+'01','%Y-%m-%d') print(month_first) #输出结果为:2019-09-01 00:00:00 month_end=datetime.strptime(text[:5] + str(int(text[5:7])+1) + '-01','%Y-%m-%d')+timedelta(days=-1) print(month_end) # 输出结果为:2019-09-30 00:00:00
datetime.now()
:获取当前日期和时间(和MySQL的NOW()
相同)datetime.date.today()
:获取当前日期datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
:生成指定的时间datetime.date(year, month, day)
:构造日期格式,但是数据类型不变datetime.strptime()
:把字符串转为日期(和pandas的to_datetime函数作用相同)
Pandas中的 to_datetime函数
pandas.to_datetime()
:将指定列的 数据类型 转换为 指定格式的 时间类型import pandas as pd crime['Year'] = pandas.to_datetime(crime['Year'], format='%Y') date=['2017-6-26', '2017-6-27'] print(pd.to_datetime(date)) #输出结果为:DatetimeIndex(['2017-06-26', '2017-06-27'], dtype='datetime64[ns]', freq=None)
format参数和数据可视化-模块 datetime 中含义一致
astype()
可以将时间类型的数据更改精度。
pandas.to_datetime()
默认转换的时间类型为datetime64[ns]
,精度为ns(纳秒)级。df['datetime'] = pd.to_datetime(df['datetime'], format='%Y%m%d') df['month'] = df['datetime'].values.astype('datetime64[M]') # 将精度转换为M(月份)级别,日子一律会更改为1日
上述代码,使用
values
将Series内的数值以ndarray
或ndarray-like
的形式返回,因为datetime64[ns]
无法直接转换为datetime64[M]
类型
pandas.date_range()
:创建时间序列参数:
start
:起始时间end
:结束时间periods
:当只声明了起始时间或结束时间时,需要告知时间范围freq
:时间频率(默认为'D'
(Calendar day frequency))
import pandas as pd from datetime import datetime pd.date_range(start=datetime.now(), periods=6, freq='B') # 'B'的含义为 营业日频率(business day frequency) # 得到: # DatetimeIndex(['2020-07-13 20:34:18.990072', '2020-07-14 20:34:18.990072', # '2020-07-15 20:34:18.990072', '2020-07-16 20:34:18.990072', # '2020-07-17 20:34:18.990072', '2020-07-20 20:34:18.990072'], # dtype='datetime64[ns]', freq='B') pd.date_range(start='20130809',periods=8,freq='SM') # 'SM'的含义为 月中和月末(semi-month end frequency) # 得到: # DatetimeIndex(['2020-07-15', '2020-07-31', '2020-08-15', '2020-08-31', # '2020-09-15', '2020-09-30', '2020-10-15', '2020-10-31'], # dtype='datetime64[ns]', freq='SM-15') pd.date_range(start=datetime.now(),periods=5,freq='2H20min') # 时间间隔140min pd.date_range(start='20190623', periods=10, freq='1D10U') # 时间间隔1天10微秒
numpy.timedelta64()
Numpy允许两个Datetime值相减,这个操作产生一个带有时间单位的数字。由于NumPy的核心没有物理量(物理单位)系统,因此创建了
timedelta64
数据类型以补充datetime64
。x = np.datetime64('2017-08-03') - np.datetime64('2017-07-15') # 得到的结果是 x = numpy.timedelta64(19,'D') # 第一个参数为数字,第二个参数为单位 # 利用timedelta64() 消除单位 x / np.timedelta64(1, 'D') # 得到的结果是19.0
以日期时间序列为索引的最多日期相差天数
(df.index.max() - df.index.min()).days
以日期时间序列为索引的数据框中一共有多少个月
d = df.resample('BM').last() len(d.index) # 'DatetimeIndex' object has no attribute 'count',因此不能使用count函数计数
把字符串转成日期
重新采样
重新采样指将时间序列从一个频率转换为另一个频率的过程。
降采样(向下采样)
高频时间序列变为低频,时间粒度变大。如:原有100个时间点,变为10个时间点
升采样(向上采样)
低频时间序列变为高频,时间粒度变小。,
同频之间的切换
比如W-WED(weekly on Wednesday 每周三)转换到W-FRI(每周五)
函数resample()
:
不同于groupby关注特征值的分组操作,它在以时间序列为索引的数据框中使用,对时间索引分组操作来聚合运算。
参数:
rule
:所需采样频率的字符串字符串 含义 B 营业日频率
(business day frequency)C 自定义营业日频率
(custom business day frequency)D 日历日频率
(calendar day frequency)W 每周一次频率
(weekly frequency)M 月末频率
(month end frequency)SM 月中和月末频率
(semi-month end frequency (15th and end of month))BM 营业月末频率
(business month end frequency)CBM 自定义月末频率
(custom business month end frequency)MS 月初频率
(month start frequency)SMS 月初和月中频率
(semi-month start frequency (1st and 15th))BMS 营业月初频率
(business month start frequency)CBMS 自定义营业月初频率
(custom business month start frequency)Q 季度末频率
(quater end frequency)BQ 营业季度末频率
(business quater end frequency)QS 季度初频率
(quater start frequency)BQS 营业季度初频率
(business quater start frequency)A / Y 年末频率
(year end frequency)BA / BY 营业年末频率
(business year end frequency)AS / YS 年初频率
(year start frequency)BAS / BYS 营业年初频率
(business year start frequency)BH 营业小时频率
(business hour frequency)H 小时频率
(hourly frequency)T / min 分钟频率
(minutely frequency)S 秒频率
(secendly frequency)L / ms 毫秒频率
(milliseconds)U / us 微秒频率
(microseconds)N 纳秒频率
(nanoseconds)axis=0
:需要采样的轴向(0为行方向)closed
:在降采样时,各时间段哪一侧(left/right)是闭合的。label
:在降采样时,index用区间的左界值还是右界值。
空值相关
import pandas as pd
import numpy as np
--snip--
# 赋空值
# NaN是C语言类型的空值,比直接赋值None更合适
position.loc[position.city=='深圳', 'city'] = np.NaN
# 统计数据框中每列缺失值的数量(每列缺失的行数)
position.isnull().sum()
# 统计数据框中每列有多少非空值(非空行)
# 方式一:
position.notnull().sum
# 方式二:
position.shape[0] - position.isnull().sum()
# 对空值进行填充
position.fillna(1, inplace=True) # 统一填充
# 删除空值所在的行(默认)或列
position.dropna()
去重
# 查找重复值(默认:第一次遇到不会当作重复值,将第二次开始遇到的值作为重复值看待)
position[position.duplicated()]
s = pd.Series([1, 1, 2, 4, 5])
# 判断是否有重复
s.is_unique
s[s.duplicated()] # 会筛选出索引为1处对应的值1,索引为0处的1不会被筛选出来
s[s.duplicated(keep='last')] # 保留最后一个重复的值,即索引为0处的1会被筛选出来
# 去重
s.drop_duplicates() # 默认保留第一个重复值(keep=’first')
# 获取每月的消费人数 (需要根据user_id去重)
# 方式一
plt.plot(df['user_id'].apply(lambda x:len(x.drop_duplicates())))
# 方式二
d = df.groupby(['month', 'user_id']).count().reset_index()
plt.plot(d.groupby('month')['user_id'].count())
# 返回去重后的列
s.unique()
# 返回去重后的元素个数
s.nunique()
移动数据:shift()
参数:
periods
:移动的幅度(可正可负),默认为1。只移动数据,不移动索引,移动之后没有对应值的,赋值为``NaN`
freq
:用于时间序列索引,默认为None
DateOffset
timedelta
time rule string
按照参数值移动时间索引,不移动数据值。
axis
:指定移动方向0
:上下移动(默认)1
:左右移动
apply()
apply()
可以将函数应用到所有的行/列上进行处理。
apply()
中的匿名函数``lambda中的
x`到底指代什么:对某一列操作时:
每一个x依次对应各行的具体值
对多个列(axis**=0**,默认)操作时:
每一个x依次对应每一列
对多个列(axis**=1**)操作时:
每一个x依次对应每一行(依次对每个单元格进行操作)
# Series级别
# 使用匿名函数,向 position表格中的avg列的每一行 都在末尾添加一个字符k
position.avg.apply(lambda x:str(x)+'k')
# 显式创建函数
def func(x):
if x > 20:
return '20+k'
else:
return str(x)+'k'
position.avg.apply(func)
# -----------------------------------------------
# DataFrame级别
# 方式一
def func(x):
if x.avg > 20:
return '20+k'
else:
return '0~20k'
position.apply(func, axis=1) # axis=1应用于每行;axis=0应用于每列
# 方式二
def func(x):
if x > 20:
return '20+k'
else:
return '0~20k'
position.apply(lambda x:func(x.avg), axis=1) # axis=1应用于每行;axis=0应用于每列
匿名函数lambda中条件语句的使用
# 条件语句为 A if 条件 else B # 即 满足条件则输出A,否则输出B x.apply(lambda x:'1' if x>= 0 else '0')
将apply()
应用到聚合函数上
获取不同城市中工资水平前五的信息:
def func(x, n):
r = x.sort_values('avg', ascending=False) # 按工资降序排序
return r[:n] # 返回前n
position.groupby('city').apply(func, n=3)
Pandas中map()
, apply()
和applymap()
的区别
参考:Pandas中的map(), apply()和applymap()的应用
它们的区别在于应用的对象不同:
map()
是一个Series的函数,将一个自定义的函数应用于Series结构中的每个元素
apply()
将函数作用于DataFrame中的每个行或者列
applymap()
将函数作用于DataFrame中的每个元素
数据透视
pivot_table()
position.pivot_table(index=['city', 'education'],
columns='workYear',
values=['avg', 'top'],
margins=True)
# index 指定索引(可多重)
# columns 可选,用于细分数据(相当于Excel透视表里的列标签)
# values 筛选列(相当于Excel透视表里的数值)
# margins=True 对各列进行汇总计算,默认为False
# aggfunc 应用于参数values
position.pivot_table(index=['city', 'education'],
columns='workYear',
values=['avg', 'top'],
aggfunc={'avg':'mean', 'top':np.sum}).reset_index().to_csv('test.csv') # avg求平均值,对top求和
注:数据透视不适合用来去重
连接数据库
- 安装相关工具包
pip install pymysql
注意:如果是安装了Anaconda,要在Jupyter Notebook中载入pymysql包,则必须在Jupyter Notebook中打开Python3或Ternimal的页面输入上述代码。另外单独安装的Python是独立于Anaconda的,两者下载的工具包可能无法通用。
连接
conn = pymysql.connect( host = 'localhost', # 本地数据库,也可输入 '127.0.0.1' user = 'root', password = '123456', db = 'test', port = 3306, charset = 'utf8' )
查询数据库
cur = conn.cursor() # 获取游标 cur.execute('SELECT * FROM courses') # 执行SQL语句 data = cur.fetchall() # 调取SQL语句执行后的结果(元组的形式) for d in data: print(d[0], d[1], d[2])
提交对数据库的修改
conn.commit()
关闭数据库的连接
cur.close() conn.close()
使用Pandas对数据库进行操作
import pymysql
import pandas as pd
from sqlalchemy import create_engine
# 读取
def reader(query, db='data'):
sql = query
engine = create_engine('mysql+pymysql://root:123456@localhost:3306/{0}?charset=utf8'.format(db)) # 允许使用pymysql方法连接mysql数据库
df = pd.read_sql(sql, engine) # 将数据库中数据作为DataFrame读取
return df
reader("SHOW TABLES")
df_company = reader("SELECT * FROM company")
df_dataanalyst = reader("SELECT * FROM dataanalyst")
merged = pd.merge(df_dataanalyst, df_company, on='companyId')
result = merged.groupby(['city', 'companyFullName']).count()['positionId'].reset_index()
# 写入
result.to_sql(name='newtable',
con='mysql+pymysql://root:123456@localhost:3306/data?charset=utf8',
if_exists='append',# 表不存在时,创建新表(if_exists默认为fail,表不存在时无法写入),但为了符合数据库中的数据格式要求,最好按默认的fail
index = False) # 不将索引作为字段写入数据库
Matplotlib
Matplotlib是一个Python的2D绘图库。它以各种硬拷贝格式和跨平台的交互式环境生成出版质量级别的图形。我们通常使用该库将数据可视化,更形象直观地暴露问题所在。
Scikit-learn
Scikit-learn(Sklearn)是机器学习中常用的第三方模块,对常用的机器学习方法进行了封装,是建立在Numpy、Scipy、Matplotlib之上,简单高效的数据挖掘和数据分析工具。
Jupyter
Jupyter是一个Web应用程序,可以用来编写Python代码、图表展示、数值处理和转换、数值模拟和统计建模等各种任务。
Jupyter Notebook
快捷键 | 功能 |
---|---|
Tab | 自动补全 |
Shift + Tab | 显示函数示例 |
Shift + Enter | 执行当前,并跳转至下一Cell |
Ctrl + Enter | 执行当前,并留在当前Cell |
问题
利用apply(),使用自定义函数更新DataFrame每行单元格的内容,但是得到的不是DataFrame对象
# # 定性 用户18个月的消费
def active_status(data):
status = []
for i in range(18): # 一共18个月的记录
# 如果本月没有消费
if data[i] == 0:
if len(status) > 0: # 记录过状态
if status[i-1] == 'unreg': # 上个月是未注册
status.append('unreg') # 仍然是未注册
else: # 之前已注册(有过消费)
status.append('unactive') # 为不活跃
else: # 没记录过状态
status.append('unreg') # 为未注册
# 本月有消费
else:
if len(status) == 0: # 没记录过状态
status.append('new') # 新用户(第一次消费)
else: # 之前记录过
if status[i-1] == 'unactive': # 上个月为不活跃
status.append('return') # 更新为 回流
elif status[i-1] == 'unreg': # 上个月为未注册
status.append('new') # 新用户(第一次消费)
else:
status.append('active') # 更新为活跃
return status
cdnow_purchase.apply(active_status, axis=1) # 对每一行进行操作
原因:
自定义函数的返回值是列表,而不是Series,导致apply()之后的结果不是一个DataFrame对象。
解决方法:
将自定义函数的返回值转换为Series对象
--snip-- return pd.Series(status, index=cdnow_purchase.columns) # index参数是原DataFrame对象的各列列名