本期知识点
1、 文本格式资料的读取、分列;
2、 用正则表达式筛选出目标数据;
3、 列表与DataFrame的追加与合并;
4、 字符串的分割
案例分析
以中国气象局提供的“中国ADTD雷电系统定位数据要素数据”为例,数据以文本格式(.txt)按时间存储。数据的表达形式比较复杂,日期以‘-’分隔,时间以‘:’分隔,位置和要素信息以‘中文=’标识,行政区划以‘中文:’标识,且分隔符为多个空格。利用pandas(以下称pd)的read_table() 读取数据时,分隔参数sep=‘ ’选择要慎重,若引号间为一个空格,运行会报错,若引号间用’\t’则不能有效识别分隔,返回一维字符串(仅一列)。
需求
读取文件中信息,年月日时分秒单独立存储,要素与行政区划信息去掉前面的中文和符号描述。
文件打开显示如下:
(点击放大图片)
给出两个处理方案
方案一:分列读取,再通过循环列中的值处理冗余的中文
1、 在利用pd.read_table() 读取文件的时候(代码行1)
分隔符参数‘sep=’用正则表达式’\s+’表示分隔符为一个或多个连续的空格,其意义是:’\s’表示空格,’+’表示有一位或多位。
names=命名列索引,这个参数需要传入一个列表,记得用’[ ]‘括上哦。
header=None声明原数据中没有表头。若原数据中有表头,则在header后面填上表头的行号,记得是从0开始的。
encoding表示读取文件的编码方式,一般在Python的UI界面新建脚本,首行会有‘# -*- coding: utf-8 -*- ‘声明,意思是一下代码默认用UTF-8编码方式。GB2312是常用的中文编码方式,这里就不展开说了。2、 根据数据特征,逐列进行筛选处理
两层嵌套循环,第一层循环的是每一列,第二层循环每列的每一个要素。
我声明了一个新的pd.DataFrame【命名为data_prc】作为完成处理的数据(代码行2),并将原数据的第一列直接赋值给它。其他列完成处理后用pd.concat()实现合并(实际上这里是一个新建的逻辑)。
通过字符串的split()方法,完成每个要素的分割和筛选。split返回一个列表,可用list.append()将处理结果在加载到中间变量temp中,最后与已处理好的其他结果加载到一起。
这里用到的两个拼接有几点需要注意:
list.append(),将括号里的数据加载到list的末尾,更新list,理论上不限制加载的数据类型,list也不用重新赋值(不需要list=list.append());
pd.DataFrame.concat([df1,df2]),将df1和df2进行连接,用其他数据类型会报错,axis=1表示按行拼接(变长),axis=0表示按列进行拼接(变宽)。
pd.concat()不改变原有的数据(需要df_new=pd.concat()才能得到拼接后数据)。
01
具体执行代码如下:
data=pd.read_table(fdir+fl,encoding="GB2312",names=["ID","Date","Time","lat","lon","Intensity","Gradient","err","LocMe","Prov","City","District"],header=None,sep="\s+")data_prc=pd.DataFrame() #创建新DF用于存储处理后的数据data_prc["ID"]=data["ID"]for ite in names[1:]:temp=[] #临时存储单元if ite == "Date":for i in data[ite]:temp.append(i.split("-"))data_prc=pd.concat([data_prc,pd.DataFrame(temp,columns=["Year","Mon","Day"])],axis=1) elif ite == "Time":for i in data[ite]:temp.append(i.split(":"))data_prc=pd.concat([data_prc,pd.DataFrame(temp,columns=["HH","MM","SS"])],axis=1)elif ite in ["lat","lon","Intensity","Gradient","err"]:for i in data[ite]:temp.append(i.split("=")[-1])data_prc=pd.concat([data_prc,pd.DataFrame(temp,columns=[ite])],axis=1)else:foriindata[ite]:temp.append(i.split(":")[-1])data_prc=pd.concat([data_prc,pd.DataFrame(temp,columns=[ite])],axis=1)
这样读出来的数据如下:
!
方案二:用正则表达式逐行处理
这个方案用到了正则表达式库re,需要在代码开头import re,在定义匹配模板pattern的时候,把序号ID跳过了,所以最后的数据处理结果没有序号列。
1、 定义正则表达式模板pattern
模板用字符串表示(就是引号括起来),引号前的r表示原生字符(正则表达式里使用"\"作为转义字符,也就是‘\\s‘在计算机看来才是‘\s‘,原来的’\s‘是一个空格),
为了我等粗心人再也不用担心漏写反斜杠’,推荐大家用r的。
将需要提取的内容用小括号括起来,具体正则表达式规则大家可以去看看官方文档,这就介绍本案例用到的几个:
i.\d表示任意数字字符(和[0-9]一个意思),{number}表示前面这个会出现number次,所以
’\d{4}’就表示‘
这里会有4个数字’
ii.?表示前面这个会出现0或任意次,
‘(-?\d+\.?\d?’表示一个可负可正的浮点数,由一个可能出现的负号
’-?’,一个任意长度的数字
‘\d+‘一个可能存在的点
’\.?‘和另一个任意长度的数字
‘\d? ’组成。
iii.\w(小写哦)表示非特殊字符,包括字母、汉字、数字、下划线
iv.其他要完全一致的信息,直接原样写到字符串中就可以了
v.最后一个技术细节,用一个小括号吧pattern引号里的内容都括起来,否则会报错
2、 读取数据行数,逐行处理数据
在数据读取时,我没有给data赋值列名,系统会自动分配列名为0,所以代码第6行我表示某一列时,列索引用[0]。DA[0][i]表示第0列的第i行
用re.findall(pattern,dat)将找到dat中所有能匹配pattern规则的东西,返回值rep从第二个要素开始(rep[0][1:])取就是我们需要的数据啦。第一个要素是所有值,对应V.中的括号。
3、将处理结果转为pd.DataFrame结构(代码行9),并赋值列名
01
具体执行代码如下:DA=pd.read_table(fdir,encoding="GB2312",header=None)pattern=r".*((\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d+\.?\d+)\s+纬度=(\d+\.?\d+)\s+经度=(\d+\.?\d+)\s+强度=(-?\d+\.?\d+)\s+陡度=(-?\d+\.?\d+)\s+误差=(-?\d+\.?\d*)\s+定位方式:(\d*)\s+省:(\w*)\s+市:(\w*)\s+县:(\w*))"(n,m)=DA.shapeline=[]for i in np.arange(m):dat=DA[0][i]rep=re.findall(pattern,dat) line.append(rep[0][1:])df=pd.DataFrame(line,columns=["Year","Mon","Day","HH","MM","SS","lat","lon","Intensity","Gradient","err","LocMe","Prov","City","Distric"])
如果只想取某个地区的数据,在循环处理前加个字符串包含(if a in b:)的判断就可以跳过不想要的行了,这样处理效率会更高。
这样获取的数据结果如下:
*
TIPS:
在处理文件I/O时有python和C两个引擎,可以用关键字’engine=’进行定义使用的分析引擎。可以选择C或者是python,C引擎快但是Python引擎功能更多些。上述功能中,C引擎支持的正侧表达式不能超过一位("c" engine does not support regex separators > 1 char)
END
文字
||
罗鼓手说挺好的
排版
||星星伴月