两年前咬咬牙跳了Matlab的坑,入手了Python,从此一发不可收的成了PY的重度依赖者。本人研究工作皆涉及大量的数据处理工作,PY和R作为数据分析的两驾马车,得其一者得天下。另外,我接触的许多软件皆比较小众,每次在涉及二次开发时,很多都是Matlab之流不支持的,而PY又往往是官方指定接口。因此,PY作为程序界的黏合剂,实在是方便至极。
如今机器学习和深度学习之热,再次炒热了PY。当然,涉及到统计模型,R的功力还是更深的。很多前沿或者有一定深度的统计模型,在R中都能快速实现,但在PY中则没有现成的package。因此,现在不得不承认,PY和R,各有千秋,要都熟稔才行。
写此文,是为记录一些灵感,供广大PY爱好者,也供自己,学习与查阅。List形式的for in if else
爬到一组房价数据,但经纬度皆以'121.43247'的string形式存储于DataFrame的一列中,且对于空缺值,以int形式的0或者float行驶的0.00填充。也就是说,该列存在多种数据格式,必须写条件判断才能循环。现需要将其进行修正提取,将'121.43247'提取为121.43247,而对于空值,统一以int形式的0填充。
于是,最低级的写法出现了:
for jj in range(0, len(all_fangjia)):
if all_fangjia.loc[jj, 'len'] > 3:
all_fangjia.loc[jj, 'new_lat'] = all_fangjia.loc[jj, 'lat'].split('\'')[1]
all_fangjia.loc[jj, 'new_lon'] = all_fangjia.loc[jj, 'lon'].split('\'')[1]
该法思路清晰,但速度奇慢。对该列数据进行遍历,先判断该数据长度,如果大于3,说明是string形式的,然后再按照'''进行拆分(需要用\来转义),选取第二个值进行提取。
思路是对的,但速度实在太慢了。于是,就要请出循环的list风格化了:
all_fangjia['new_lon'] = [var.split('\'')[1] if len(var) > 3 else 0 for var in all_fangjia['lon']]
all_fangjia['new_lat'] = [var.split('\'')[1] if len(var) > 3 else 0 for var in all_fangjia['lat']]
将代码压缩至了两行,速度更是提升了几十上百倍(具体提升量级没算,但反正速度是飞快的了)。此法非常关键,掌握了对之后的数据处理效率大有提升。佛系空格分隔符的处理
在拿到某些奇葩的原始数据文件时,其不同列间的分隔不是传统的',',而是奇葩的不规整的空格符,也就是说,某两列用了三个空格符来分隔,某两列则用了四个,甚至在一列中,某两行用了2个空格分隔,某两行则用了3个。。
对于这种佛系空格分隔符,一种处理方法就是用正则(re)表达式,而另一种非常简单的方法,则是:
import pandas as pd
tem=pd.read_csv('583211-2017.out', delim_whitespace=True, engine='python')
即在熊猫包里面的read_csv中,设置delim_whitespace=True即可。字符串数据转化为数字编号
比如有N个样本,且存在一列专门对其类别进行标记,但标记用的全是字符串,如“大”、“中”、“小”。为了之后处理方便,需要将其变成0、1、2这种数字形式。这时就需要请出category类型来操作了。相关操作皆针对DataFrame格式实现。
obj_df["body_style"] = obj_df[“body_style"].astype('category')
obj_df["body_style_cat"] = obj_df["body_style"].cat.codes绘图时批量改变所有字体大小
在利用matplotlib绘图时,题目、坐标轴标签、坐标轴名称等等的字体大小都需要分别设置,非常麻烦,而下面的方法则可以批量一次性设置,修改起来也就随之方便了。
应注意,如果有多个ax,则还需要再嵌套一层循环,先指向某一个ax.
import matplotlib.pyplot as plt
fig,ax=plt.subplots()
for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] +
ax.get_xticklabels() + ax.get_yticklabels()):
item.set_fontsize(20)批量快速导入Oracle
做数据工作的,拿Python去接数据库是非常常见的事情,而Oracle又是数据库里面的老大哥。在此不介绍如何安装接口包cx_Oracle,只介绍如何快速将大量数据一次性导入到Oracle中。
在没Get到此技能之前,我都是一条条的往里面插入数据的,数据量小还好,一旦大起来,速度就奇慢无比了。
于是,便有了下面的思路:先打包,再导入:
#导入连接包
import cx_Oracle as oracle
db = oracle.connect('scott/redhat@192.168.223.138:1521/oracle.test')
#对待导入的数据进行处理
DFV = DFV.fillna('None')
DFV = DFV.values.tolist()
rows = []
for jj in range(len(DFV)):
# 转list
row = (DFV[jj][0], DFV[jj][1], DFV[jj][2], DFV[jj][3], DFV[jj][4], DFV[jj][5], DFV[jj][6])
rows.append(row)
# 写入数据
cr = db.cursor()
cr.prepare(
'insert into OTJ_WATERLINK_WK2 (linkid,fromnode,LONGITUDE,LATITUDE,GRIDID,ROADNAME,SECT) values (:1, :2, :3, :4, :5, :6, :7)')
cr.executemany(None, rows)
db.commit()
cr.close()
试过的都知道,速度杠杠的。再也不用担心大型数据文件要花上好几天才能撸进Oracle了。Groupby不支持的函数如何使用
数据处理里面的groupby简直就是小白第一课也得学会的技能了。但groupby方便虽方便,很多时候却不支持一些函数。比如,我要对某一列进行groupby,并对groupby后的数据块内的另一列求分位数。这时:
train_day=data.groupby([‘TIMEID']).percentile()['GOSPEED']
却显示报错,原因是groupby之后的数据块不支持percentile()这个函数。
这时你想到的可能就是只能写循环一步步进去了,不慌,groupby还给我们留了后路:
dg=data.groupby('TIMEID')
for a,b in dg:
z = np.percentile(b['GOSPEED'],5)
不只是percentile(),其他什么函数,都是可以这么玩的。速度虽然比groupby慢了一些,但比直接写循环进去要快不少。指定区间,计算频率
做频率分布直方图大家都会做,非常简单,对离散型变量做频数统计也很简单,value_counts()函数就行,但如何对连续型变量按照指定的区间就行频率统计呢?这里就需要用到cut和value_counts()的结合了。
cut函数可以将一个区间进行切割,返回切割后的小块,再将其作为参数传递给value_counts()函数,就可以得出结果了。
xse = range(1, 5000, 1)
fanwei = list(range(0, 4500, 500))
fenzu = pd.cut(xse, fanwei, right=False)
print(fenzu.categories) # 分组区间,长度8
pinshu = fenzu.value_counts() # series,区间-个数读入输出文件的中文乱码问题
这个问题大家几乎都会遇到,解决方法也非常简单,只要指定对了编码,自然就不会乱码了:
输出CSV乱码的话:
import codecs
FGIS.to_csv(‘FINALPOINT.csv',encoding="utf_8_sig",index=False)
导入CSV乱码的话:
test=pd.read_csv(‘busgps_0309_0310_71.txt',encoding='gbk') #gbk不行就改成‘gb18030’
不论读入导出啥文件,记住encoding不要乱,编码就不会乱。数据结构化输出及读取
某个变量需要先保存好,下次再来直接读取,而不是重新计算?MATLAB里面可以直接保存WORKPLACE里面的变量,PY怎么做呢?用pickle
import pickle
#导出
output = open('FWRON.pkl', 'wb')
pickle.dump(FWRON, output, -1)
output.close()
#读取
pkl_file = open('FWRON.pkl', 'rb')
FWRON = pickle.load(pkl_file)
pkl_file.close()多版本PY的管理
由于不同的包可能在不同版本下才能生存,所以一台电脑有好几个PY很正常,而解决他们的共生问题也是十分的头疼。比如我的电脑里就有三个版本的PY(我也不知道怎么这么多)。。其中,conda管理的两个:2.7和3.4;还有在系统下的一个3.6。
对于用conda来管理的各种版本,则可以使用conda来进行切换,相对要简单很多。切换完毕后,就可以在该版本下进行包的安装管理。强烈建议用conda而非pip来安装package。
conda info --envs
source activate python34 # conda activate geo_env
conda install -c conda-forge osmnx
sudo pip install [package_name] --upgrade
而我之前没用conda之前,一直都在用系统的3.6。所以,很多时候我还是要对3.6系统下的环境做配置。下面记录了更新pip以及利用pip指定版本安装包的过程。注意全程加上python3来指代PY3的版本(我默认是用的2.7),以及,记得加上--user,否则会一直报错[Errno 13] Permission denied。
curl https://bootstrap.pypa.io/get-pip.py | python3 - --user
python3 -m pip install --user osmnx
PY版本是非常头疼的事情。最好的办法是完全基于conda来配置自己的环境。不要和我一样,多个PY版本分散在各个地址,配置起来非常麻烦。一行代码解决两个字符串组的匹配
近期在做特征的时候,需要对异常站点进行清洗。其间遇到一个问题,记录如下:
有一个list A,里面存储着系列表征站点错误的关键词,如“关闭”、“不开放”、“非运营”。
另外有一个list B,里面存储着所有站点的名称,如“虹桥站”、“上海南站”、“五角场站”。
在list B中,有部分站点是出错的,这些站点会在名称中进行标记,如虹桥站出错了,该站的名字会改成“(关闭)虹桥站”,当然,也可能改成“(不开放)虹桥站”。
现在需要把list B中所有的出错站点找出来。
问题复述一遍就是:以list A中的每一个元素为关键词,对list B中每一个元素进行匹配,如果list B中某个元素包含list A中的任意一个元素,则将list B中的该元素标记为FALSE。
当然写循环,用 A in B,是肯定可以做的。但是,有没有更简洁的写法呢?
尝试了一下,是有的:
Wrong_list=['关闭','不开放','非运营']
Test_list=['虹桥站','(不开放)虹桥站']
Bool_result=[any(list(wrongs in var for wrongs in Wrong_list)) for var in Test_list]
最后返回:Bool_result=[FALSE, TRUE]
需要注意:
1)两个for的顺序:先写for wrongs in Wrong_list,再写for var in Test_list,最终得出的Bool_result才是针对Test_list的。
2)list在此的作用:将generator object 转化为bool格式。
3)any在此的作用:表示“只要有一即可”。
4)括号在此的作用:为any提供计算优先级。applymap与匿名函数
常常会遇到需要对矩阵中的所有数值执行某个函数的情况,但又懒得写def,这时候就可以祭出applymap大杀器了:
DF.applymap(lambda x: -(x * math.log(x)) if x > 0 else np.nan)
这句话的功能是,对DF这个矩阵里面的每一个大于0的值,执行-(x * math.log(x))的运算,如果该值小于0,则置为nan。
要注意applymap和apply的区别。后者是对行或列进行处理:
DF.apply(lambda x: sum(x != 0), axis=1)
如上面的代码,则是返回每一行(axis=1)中不等于0的个数。去除DF中含有重复名字的列
有时候MERGE多了,难免会出现一个DF里面有好一些列完全一致——内容一致,列名也一致。这在某些时候,列名一致是容易出错的,最好需要及时清理他们。清理方法是:
DF=DF.ix[:,~DF.columns.duplicated()]
一句话就可以去重啦,非常的利索有没有。选取groupby后某列最小值对应的行
做数据处理的时候常常会遇到这样的问题:对于一个DF,我们按照A、B两列进行groupby后,选取每个group内C列最小值所对应的行并返回。
DF1=DF.loc[DF.groupby(['A','B'])['C'].idxmin()]
原理其实很简单,用到了一个idxmin(),可以返回最小值对应的行索引。根据列类型选取列
很多时候如果列很多,而且我们需要选取特定类型的列进行变化。比如,在做线性回归时,把所有BOOL类型的列改为0,1类型:
Exposure_DATA_NEW.loc[:, Exposure_DATA_NEW.dtypes == np.bool] = Exposure_DATA_NEW.loc[:,Exposure_DATA_NEW.dtypes == np.bool].astype(int)
这里用到了DF.dtypes == np.bool,来对列进行圈取。对每一个group进行NA均值填充
很多时候我们在做缺失值填充时,会需要先groupby,然后再对每一个group,计算该group的均值,并填充至该group的缺失值处:
Exposure_DATA["surfacewid"] = Exposure_DATA.groupby("rank_artclass")["surfacewid"].transform(
lambda x: x.fillna(x.mean()))CX_ORACLE的中文乱码问题
在利用CX_ORACLE读入数据时,不做处理,中文就会直接跳问号。需要在程序前加上:
import os
import sys
reload(sys)
sys.setdefaultencoding("gbk")
os.environ['NLS_LANG'] = 'SIMPLIFIED CHINESE_CHINA.UTF8'去除列名,竖向叠加
npCombined_Net = np.concatenate([metro_route.as_matrix(), tem_null.as_matrix()], axis=0)
metro_route = pd.DataFrame(npCombined_Net, columns=metro_route.columns)Brew的安装与运用
如何安装:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
安装完还找不到怎么办:
export PATH=/usr/local/bin:$PATH
利用brew来安装一个mysql:
brew install mysql矩阵的去方向groupby
在做出一个网络边矩阵时,常常会出现这样的情况,我们需要的是无向的,但边矩阵却是有向的。即,假如矩阵一共三类,‘FROM’,‘TO’,‘VALUE’,我们认为FROM 3 TO 1和FROM 1 TO 3是一类的,因此,我们需要把FROM 3 TO 1和FROM 1 TO 3的VALUE 求均值。怎么做:
edge_centrality_nodir = pd.DataFrame(np.sort(edge_centrality[['from', 'to']], axis=1), edge_centrality.index, edge_centrality[['from', 'to']].columns)
edge_centrality_nodir['bc'] = edge_centrality['bc']
edge_centrality_nodir_f = edge_centrality_nodir.groupby(['from', 'to']).mean()['bc'].reset_index()
其中edge_centrality为有向矩阵,edge_centrality_nodir为矩阵无向化,edge_centrality_nodir_f为最终groupby后的结果。矩阵无向化的过程,实际是对每一行进行重新排序的过程。注意把from 和 to两列单独拎出来。绘图label只显示两位小数
from matplotlib.ticker import FormatStrFormatter
fig, ax = plt.subplots()
ax.yaxis.set_major_formatter(FormatStrFormatter('%.2f'))range()不能产生float??
不要慌,用arange:
import numpy as np
np.arange(1,10,0.1)DF某一列中是否包含某个字符
比如要判断DF的某一列中是否含有“A”这个字符:
DF['Names'].str.contains('A')
那如果要把“A”这个字符替换成“-”怎么办呢:
DF['Names'].str.replace('A','-')
真心的方便呀。比如你在处理时间字段时,有些直接就成“XX年XX月XX日”这种格式了,这时你为了转化为datetime,首先就是把“年”、“月”、“日”都替换成“-”。多个DF的merge
譬如你有N个DF,这些DF具有相同的KEY列,你需要把他们按照这个KEY列一并MERGE起来。怎么做?
首先把需要MERGE的放在dfs这个list里面,然后用reduce来解决:
from functools import reduce
dfs = [ord_count, ord_real_mn, order_coup_mn, order_final_mn]
df_final = reduce(lambda left, right: pd.merge(left, right, on='authid_s'), dfs)
我们的宗旨是:代码这东西,多写一行都是在浪费生命。list以及numpy的repeat
在构造全序列时,常常需要对一个list进行重复,重复又分为两种:[1,2,3]--->[1,1,2,2,3,3]
[1,2,3]--->[1,2,3,1,2,3]
需要注意,在python中这两种写法是截然不同的。假设我们需要构建三列,第一列为站点ID,第二列为每一天,第三列为每一个小时:
timerange = pd.DataFrame(pd.date_range(start='1/1/2017', end='12/31/2017', freq='D'))
timerange[0] = timerange[0].astype(str)
full_time_range = pd.DataFrame({'SHOPSEQ': np.repeat(list(shop_need_seq), 365 * 24),
'date': list(np.repeat(list(timerange[0]), 24)) * len(shop_need_seq),
'HOUR0': list(range(1, 25, 1)) * len(shop_need_seq) * 365})DF中的mean和count是怎么对待NAN的?
The internalcount()function will ignoreNaNvalues, and so willmean(). The only point where we getNaN, is when the only value isNaN. Then, we take the mean value of an empty set, which turns out to beNaN
即:默认情况下,DF的 count()和mean()函数都是自动忽视NAN的,在计算均值时,除非你的所有数都是NAN,才会出现NAN的结果。reshape(-1)?-1是什么size?
这是非常能提现python之懒的一个点,懒得什么境界呢?就是你只知道变形后的列数,懒得算变形后的行数,你就拿-1代替好了。。:
假如是这么一个array:
z = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
直接-1之后,变成12行1列的矩阵:
z.reshape(-1)
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
我想把他变成2列,但我懒得算有几行:在行数那里写-1,系统自动帮你补全为6行:
z.reshape(-1, 2)
array([[ 1, 2],
[ 3, 4],
[ 5, 6],
[ 7, 8],
[ 9, 10],
[11, 12]])Datetime格式的LIST相减并返回秒
DT格式直接相减,得到的格式是非秒的,因此需要再做一个匿名函数转化:
train['check_trip_duration'] = (train['dropoff_datetime'] - train['pickup_datetime']).map(lambda x: x.total_seconds())多索引排序后的重索引
在做多索引排序后,常常遇到的一个问题是,我们想按照排序后的结果,对每个组内的数据按照排序后的结果进行索引重定义:
DF=DF.sort_values(['A','B'],ascending=True)
DF=DF.groupby('A').apply(lambda x: x.reset_index(drop=True)).drop('A',axis=1).reset_index()
第一句是多索引排序,排序完成后,我们先对GROUP后的结果做一个匿名函数进行reset_index,这时DF的索引变成A+range(0,len(A))的格式。注意,在进行再重索引时,务必先将A删去,否则会出现两列A而无法进行。绘图时解决中文乱码、批量设置字体大小以及扩大图像可容纳的点位
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rcParams['agg.path.chunksize'] = 10000
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
plt.rcParams.update({'font.size': 22})
这几句在做中文图、整体修改图的字体大小的时候可以加上。APPLY函数
当需要以DF格式的两列为参数,做一些函数关系与条件判断的处理的时候,通过先构建函数再APPLY的方式,可以避免逐行循环,提高效率。
比如我想以某DF的某一列为参数,对另一列中的string进行切片,并需要满足一些规则:
def eachrowsplit(index0, strr):
if index0 - 3 >= 0:
return strr[index0 - 3:index0]
elif index0 == 2:
return strr[index0 - 2:index0]
elif index0 <= 1:
return np.nan
else:
return np.nan
在定义好这个函数后,我们再调用APPLY,将函数APPLY到对应的两列上:
Baishitong_station1['price_final'] = Baishitong_station1.apply(lambda row: eachrowsplit(row['Index_yuan'], row['price']), axis=1)对比两串字符串的相似性
目前有许多算法来界定字符串的相似性。在这里介绍一种,可以无序判断的,即“广东深圳”与“深圳广东”他认为相似度是100%:
def similar(a, b):
# return SequenceMatcher(None, a, b).ratio()
return fuzz.token_sort_ratio(a, b) / 100
然后对DF的两列进行对比:
Baishitong_station1['simi_name'] = Baishitong_station1.apply(lambda row: similar(row['name'], row['parkName']), axis=1)Groupby+values_count+stacked bar plot
两行代码搞定,感谢unstack大法,还可以做筛选
tem = ALL_DATA_RAW.groupby(['app', 'time_diff_int']).size()
tem[(tem > 100) & (tem < 100000)].unstack(fill_value=0).plot(kind='bar', stacked=True)Groupby后返回第一行和最后一行且不skip nan
依旧是一行代码搞定
ALL_DATA_RAW.groupby('parkSeq').nth([0, -1])
这里不采用.first()的原因是.nth()是不会忽视NAN的:
“The difference between them is how they handle NaNs, so.nth(0)will return the first row of group no matter what are the values in this row, while.first()will eventually return the firstnotNaNvalue in each column.”distplot+groupby怎么实现
g = sns.FacetGrid(ALL_DATA_RAW_1, hue="app", palette="Set1", legend_out=False)
g = (g.map(sns.distplot, "ratio", hist=True)).add_legend()
distplot是不带hue,但可以借助FacetGrid的hue来实现groupby的绘制:旋转矩阵
pd.pivot_table(tem, values='PFloor_Area', index=['ParkSeq'], columns='Land_Category').reset_index()PYCHARM的全局搜索快捷键失效
CTRL+SHIFT+F是全局搜索,不要用双shift,搜不全的。
但是CTRL+SHIFT+F同时还是微软自带的简繁切换快捷键,需要先把这个关了:多维转一维
df2=FSEMF.groupby([‘day','m5']).agg({'volume':'sum'}).unstack().fillna(-1).stack().reset_index()计算连续时序长度
# Build dataframw
tem = pd.DataFrame({'ID': np.repeat(range(1, 10), 31),
'Date': list(pd.date_range('2020-03-01', '2020-03-31', freq='D')) * 9})
tem1 = tem.sample(200)
# Sort
tem1 = tem1.sort_values(by=['ID', 'Date']).reset_index(drop=True)
# Diff
tem1['Diff'] = tem1.groupby('ID').diff()['Date'].dt.days
tem1.loc[tem1['Diff'] != 1, 'Diff'] = np.nan
tem1['CUMSUM'] = tem1.Diff.groupby(tem1.Diff.isna().cumsum()).cumsum()
# Max
final = tem1.groupby(['ID']).max()['CUMSUM'].reset_index()
final['CUMSUM'] = final['CUMSUM'] + 1