如何优雅地使用 pandas 对数据进行整理-1 (数据类型介绍:变量,列表,字典,数据框)
为什么要学会使用 pandas ?
我觉得答案是:将数据结构化,以便更好地管理而 pandas 你可以理解为 利用代码 实现你手动对 Excel 表格处理的功能
实际上,pandas 能实现的功能,基本在 Excel 上都能完成
两个之间只是一个 “时间效率” 和 “一劳永逸” 的事情
习惯用哪种是个人的一种选择罢了
数据类型
在处理数据时,首先我们得明白数据长什么样子,我们期望输入的数据长什么样子。
这一块的理解我其实并不太到位,日后随学习的深入再更。
在这里我们先说明一些无关紧要的概念
Python 的基本数据类型 (我不知道专业的人是不是这么讲)
- 变量 variable,列表list,字典dict,数据框dataframe
- 这里只讲 “他们是什么” 和 “怎么构造”,其他细节的操作以后有空再写
变量:
这里的变量不是统计学的概念,你可以将其理解为存储某个数据的词
-
需要注意的是,变量可以随意命名,但是要按遵守以下规则:
- 变量名可以包括字母,数字和下划线 (_) ,(那就是说,其他奇怪的符号都不可以)
- 变量名不能以数字开头。如:day1, day_1 是对的,1day 就不可以
- Python 里固有的函数、特殊含义字符,不能作为变量名。如:if, True 等。python 常见内置函数
a = 2 ## 整数型数字变量
b = 3.56 ## 浮点型数字变量
one = 'John' ## 字符变量
r9A = True ## 布尔型变量
##### 需要注意的是
## 1. 变量名可以包括字母,数字和下划线 (_)
## 2. 变量名不能以数字开头
- 但是,我们必须优雅,所以,以上的变量名都是不雅的示范
- 做了一名优秀的程序员,应该做到:看到变量名,就知道这个变量指代的是什么,如果你一直以 a, b, c 等简单的命名,不用一个月,两三天后你就会忘记自己写的是什么了,这对于代码的维护十分不利。
- 所以下面推荐两个我感觉比较好用的优雅命名法
- 下划线续命,比如:
- A_num ,可以用来表示字符串中“A”的个数。
- diff_word_list,可以用来表示一个“装不同单词”的列表
- 实际上,用多少个下划线(),用不用缩写(num, diff),也是全凭个人习惯,在优雅的同时得自己用的舒服。建议不要超过两个下划线(),太长的话实在太难看了,自己能看懂就行,能短就短。
-
驼峰命名法 Camel-Case - (是不是感觉更高雅了)
- 实际上,有些人不喜欢下划线
- 驼峰命名发就是将 多个单词 拼接在一起形成一个整体作为变量名,而区分开不同的单词就是大小写,如:
- myFisrtName, differentWordList
- 一般来说,第一个单词小写,往后每接一个单词都要首字母大写。据说这样可以提高程序的可读性,至于是不是这样,那就见仁见智啦。
-
匈牙利命名法 - (一看就觉得很厉害)
- 基本原則是:变量名 =属性 型別 物件描述
- 这种一般是大佬命名法,需要写巨型程序时比较有优势,对于普通人来说,前两种任选一种就足够了
列表 list
列表的构造非常简单,[] 中括号,逗号分割
-
列表,实际上可以理解为一个大的快递柜
- 它非常的宽容,你扔什么进去,它就能存什么。
- 而且它不挑,无论是重复的元素,还是不同类型的变量,它都能接纳
-
列表是有序的
- 宽容,不代表没有底线
- 实际上,列表很有自己的规则,里面存放的元素都是乖乖排队站好,而且每个都有自己的标号。(想要取出某个元素就像你知道货架号去取快递一样)
num_list = [1,8,6,1,7] ### 这是一个存放数字的列表
mixList = [1,'panda','4',9] ### 这是一个混合元素的列表
list2_in_list1 = [[1,8,6,1,7],[1,'panda','4',9]] ### 列表里面也能放列表
dict_in_list = [{'a':30},] ### 当然存个字典也不是什么问题
### 取出元素,本来不想讲的,还是提一下吧
### 比如说上面的 num_list, 我想取第二位 ‘8’
### 则:
num_list = [1,8,6,1,7]
target = num_list[1] ## 用中括号去,Python 从0可以计数,所以是[1], 这个也叫 列表的切片
- 然而,大多数情况下,不会有一个列表傻傻的站在那里等你。
- 更多的情况是,把列表当做一个容器,装你需要的东西。
- 装完以后再根据自己的需求,取列表中的元素。(切片)
- 所以列表的构建常常和循环(for)、判断(if)搭配使用。
例一:找出100内的所有质数
- 我们可能需要用到额外的东西:
- for / while 循环
- if 判断
- .append() 将元素添加到列表
- 可能还需 range(), len() 帮助你执行循环的好东西
### 比如说,我想取出100内所有质数构成列表
### 首先分析问题,质数的定义是:大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
### 判断一个数是否为质数 只要依次除以 所有比自身小的整数,只要如果商是整数只有一个(除1得到它本身),那这个数就是质数。如果不只一个整数商,那就是素数。
all_num = range(1,101)
## range()函数,获取一个 1 - 100 的列表, (其实要 list(range(1,101)才是我们熟悉的列表的样子,range()只是一个函数)
## 为啥是 101,如果是100,因为 Python 从0开始取,取100个数字就到99
## 现在我们从1开始取,要取100个数,当然要到101
prime_num = [] ## 新建一个空列表
for i in all_num:
test_num = i ## 依次取出 1 - 100
num = 0 ## 用于记录出现整数商的个数,因为质数的整数商应该只有一个,那就是它本身(除以1)
for j in range(1,test_num): ## 还是从1开始,取到比这个数自身小1
quotient = test_num/j ## 求商,看商是否为整数,如果是,说明这个数不只一个因数,那他就不是因素
if quotient in list(all_num): ## 可能的整数都在 all_num 里
num += 1 ## 如果是整数,记录数 +1
if num == 1: ## 如果商只有一个整数,那说明这是个质数
prime_num.append(i) ## 将这个数添加到质数的列表中
print(prime_num) ## 打印出来看看结果对不对
#--------------------------------结果————————————————————————
#[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
### 思考一下,为什么 num 为什么要放在第一个循环里面?
### 以上只是我的思路,不一定是最优的方法
### 实际上,也不用除比自己小的数,只要比自己二分之一小的数就足够了,以为除比自己二分之一大的数肯定是小于2 (自己想想吧,代码我不写了)
好吧,其实我给的只是一个无聊的例子。在实际应用中,要根据自己数据的特点,提取需要的数据,这就是为什么一开始说的,你要对自己的“数据长什么样子,希望整理后的数据长什么样子”,有个把握。然后利用循环,判断,将元素添加( .append() )到列表中,构建符合目的的列表。
例二:找出子目录下所有的 .csv 文件,打开并逐行打印
- (为什么要逐行打印?) - 因为你能逐行取出数据,说你可以随心所欲地对数据进行加工处理,比如“加减乘除”。
- CSV 文件说明一下,这是一种用逗号分割的文本文件。可以直接用 Excel 打开,Excel 也可以直接保存工作表为 csv 格式文件,这意味着,我们可以配合着 Excel 使用,因为 Excel 在可视化数据时具有独特的优势。
- 我们可以在 Excel 中预处理数据,如 “手动数据数据”,再放到 Python 中用 pandas 做进一步的加工
- 也可以用 pandas 处理好数据后保存为 csv 文件,然后用 Excel 打开查看结果。
-
一图胜前言,请看
- 我们可能需要用到额外的东西:
- os 调用系统一个库
- if string.endswith(): 判断文件是否以 ‘’.csv‘’ 结尾。同理,判断是否以 “xxx” 开头,应用 string.strartswith()
- .append() 将元素添加到列表
- 可能还需 range(), len() 帮助你执行循环的好东西
- pandas 打开,处理及保存 csv 文件 (提前用到,后面会细讲)
import os
import pandas as pd ### 简写,之后就不用敲 "pandas" 那么多字符,直接敲 "pd" 就好
filePath = '/User/data' ### 假设我的数据杂乱的放在这个目录(文件夹)下
fileList = os.listdir(filePath) ### 返回一个列表,这个列表包括这个目录下所有的文件以及文件的名字,要注意,是名字,不是路径。
csvFileList = [] ### 新建一个列表用于存放csv文件路径
for i in fileList:
if i.endswith('.csv'): ### 找出.csv 后缀的文件
file = filePath + '/' + i ### os.listdir() 获取到只是名字,要自己构造成完整的路径
csvFileList.append(file)
for csv in csvFileList: ### 依次打开 csv
data = pd.read_csv(csv) ### 调用 pandas 里面的 read_csv() 读取 csv 文件内容,
###实际上,直接 a = open(csv,'r') 也能打开,不过那是一个文本,
dataframe = pd.DataFrame(data) ### 用 pandas 的.DataFrame() 将数据装为 **数据框**, 这个是这一篇文章像将的重要数据结构
print(frame.ix[0]) ### ix[var1,var2] 第一个是行,第二个是列,后面会细讲。 这里的意思是取第一个除了打印,记住 Python 从0开始
####### 后记说明
### 一般来说,我们用 Excel 整理数据时,每一列,都有一个列表,即第一行,是列名。如:“姓名”,“年龄”,“分数”。
### 所以在我们取数据时,其实取的是第二行的数值,但pd.read_csv()其实是默认第一行为 header(列名),并默认加上 index(行号)
### 如果第一行不是 header 可以手动关闭
# data = pd.read_csv(csv,header=None) ##注意None,不是 False
### 如果想保存为新的 csv
# dataframe.to_csv(save_path) ## save_path 是存在的文件路径
字典 dict
- 字典的形式是: a = {‘key’:’value’} ; ( {}大括号,key键 和 value值 一一对应)
- 在列表中,我们是根据元素所在的位置来取出元素 (知道货架号去拿快递)。但是在字典里,每个元素都有一个对应的“钥匙” (这时相当于你那取货码去快递柜取快递一样)
- 字典是无序的,但是 key 和 value 是一一对应的。正是因为这样,所以有无顺序并不重要。反正 key 是唯一的,随时能找到 value。
- 因为 key 和 value 是一一对应的,所以一个字典里,key 的取值是唯一的,value 不一定,可以重复。(就像我的取货码key只能取自己的快递,但是我们可以买一样的东西 value)
- 构建字典的方法:
########## 第一种(推荐)
a_dict = {'apple':5} ### apple 对应的值是5
a2_dict = {'apple':'five'}
b_dict = {'apple':[4,8,5]} ### apple 对应一个列表,比如说3个人,各自有4,8,5 个苹果
c_dict = {'apple':{'big':5,'small':6}} ### apple 对应一个字典(这种叫嵌套字典)
# 字典构造的格式很简单,大括号{},里面是{'key1':value1,'key2':'value2'}
# 以上三个例子展示了字典的可变性,value 可以是变量(数字、字符串)、列表和字典,其中列表、字典里面有可以继续嵌套,但是这里温馨提示一句,字典嵌套太多很容易乱,而且代码的效率低(嵌套多,循环多),所以最好还是想办法简化你的程序。
#------------------------------------------------------------#
############ 第二种 在已有字典的情况下添加 (实用)
a_dict = {'apple':5}
a_dict['peach'] = 8
## 这时候的 a_dict = {'apple':5,'peach',8}
#------------------------------------------------------------#
############ 第三种 使用 dict.setdefault() (推荐)
a_dict.setdefault('banana',67) ## 第一次参数是 key, 第二个参数是预设的默认值
## 实际上,如果直接 写a_dict.setdefault('banana'),返回的是 None, 因为原本的 a_dict 里面没有 'banana' 这个键
#------------------------------------------------------------#
############ 第四种 用 dict() 函数转换 双值子序列
# 什么是 双值子序列 ?
# 如:letter=[['a','b'],['c','d'],['e','f']]
# 像这样两两配对好的 列表、元组
# 如:[('a','b'),(c','d'),('e','f')] , ['ab','cd','ef'] 这些都是
letter=[['a','b'],['c','d'],['e','f']]
l_dict = dict(letter)
## 这个就相当于l_dict2, 第一个元素为 key, 第一个元素为 value
l_dict2 = {'a':'b','c':'d','e':'f'}
- 如何取字典里面的东西
a_dict = {'apple':5,'peach',8}
### 取键
key = a_dict.keys() ### 结果:dict_keys(['apple','peach']),返回的并不是列表,而是一个对象
key = list(key) ### 结果['apple','peach'],好了现在是列表的
# 一般情况下,取键和循环搭配使用
for k in a_dict.keys():
key = k
print(key) ### 结果程序依次打印,'apple','peach'
### 取值,同理,.values()
### 键 和 值 一起取 .items()
for k,v in a_dict.keys():
key = k
value = v
print(key)
print(value) ### 依次打印'apple','peach','5','8'
- 在更多的时候,我们并不会动手完完整整的码一个字典,太累了
所以,我们需要一些优雅的技巧
例一,如何合并并将两个等长的列表(list)转为字典(dict)
- 为什么要合并两个等长的字典?
- 很简单的一个例子,学生成绩,学生名单一个列表,成绩一个列表,但是两者一一对应。(其他情况自己类别使用)
### 第二种字典构造方法 dict() 排上用场啦
name = ['xiaoming','zhubajie','shadiao','xiaoming2'] ## 字典 key 是唯一的,如果重命了要想办法区分开来
score = [90,88,45,100]
studentScore = dict(zip(name,score))
## zip() 函数将对象中*对应的*元素打包成一个个元组,返回一个对象。
## dict() 对这个对象解析,构成一个新的字典
print(studentScore)
#----------------结果
# {'xiaoming': 90, 'zhubajie': 88, 'shadiao': 45, 'xiaoming2': 100}
例二,如何是 dict.setdefault() 构建较复杂字典
- 很多时候,我们不是提前知道 key 和 value。而是在循环的过程中,找到目的值,再代入 key 和 value。
- dict.setdefault(key,default=None) 接收两个参数,第一个参数是健的名称,第二个参数是默认值。如果字典中包含有给定键,则返回该键对应的值,否则返回为该键设置的值。
- (之前处理数据时真的遇到过一些经典的例子,但是在写的时候想不起来,想到再更吧,一般来说,直接用 dict[key] = value 没毛病,但是也存在只能用 .setdefault() 预设默认值的情况)
### 1. 常规操作
studentScore = {'xiaoming': 90, 'zhubajie': 88, 'shadiao': 45, 'xiaoming2': 100,'surperman':1000,'kate':56,'sanman':92,'keiven':73,'xijinping':99,'caixukun':44,'wangzhe':66,'king':89}
## 比如说,我们想把所有 k 开头的同学成绩取出来构成一个新的字典
KstudentScore = {} ### 先建一个空白字典备用
for k,v in studentScore.items(): ## 依次取出 键,值
name = k
score = v ## 其实直接用k,v也行,但我觉得这样更优雅,哪怕过一年你也知道自己在干嘛
if name.startswith('k'):
KstudentScore.setdefault(name,score)
print(KstudentScore.setdefault)
## 或者 KstudentScore[name] = score 也是行的
## ---------------- 结果 ----------------------
## {'kate': 56, 'keiven': 73, 'king': 89}
##############################################################
### 2.key 对应的值是一个列表 list (字典同理)
### 比如说,每个同学有三次成绩,放在三个字典里(具体的我不写了)
## 那么第一个粗暴的方法就是我提前把说有的键值 key都准备好,对应一个列表[],到时候找到每找到一个同学,.append()添加进去就行啦
allScore = {'xiaoming':[],'laoli':[],'zhubajie':[]} ## 假设全班就三个人
for k1,v1 in studentScore1.items():
allScore[k1].append(v1)
for k2,v2 in studentScore2.items():
allScore[k2].append(v2)
for k3,v3 in studentScore3.items():
allScore[k3].append(v3)
## 完全没毛病
## 但是我们要优雅,所以
allScore2 = {}
score = [studentScore1, studentScore2, studentScore3]
for scoreDitc in score:
for k,v in scoreDitc.items():
allScore2.setdefault(k,[]).append(v) ## 直接构建字典并添加值到对应的列表
#---------------幻想中的结果
# {'xiaoming':[97,49,79],'laoli':[87,56,38],'zhubajie':[100,87,97]}
#### 如果要嵌套字典,原理一样,格式为
dict.setdefault(key,{})['key2']=value
由于每个人需要处理的数据都不尽相同,我也不能举出十全十美的例子,大家根据实际情况选择合适的工具使用。我感觉自己也没有讲清楚,如果有什么疑惑或建议,欢迎联系。
数据框 dataframe (终于到重点了)
- 数据框最通俗的理解就是一张 Excel 的工作表
- 为什么要学这种数据类型,因为这种是我们看着舒服的数据类型。(上图吧)
-
比如说我有一批水果的数据:(随便在 Excel 上面码了一些数字不要在意细节),这个就是一个 dataframe,所有的信息都非常清晰
-
- 如何构建 dataframe
### 1. 最常见的其实是我们直接读取原本就已经构建好的 csv 文件
data = pd.read_csv('/User/shop/fruit.csv') #读 csv 文件
df = pd.DataFrame(data) #转为数据框
print(df) #很多人不可看看结果是不会心死的
#----------------------结果
# student score
# 0 xiaoming 84
# 1 xiaodong 93
# 2 wujingtao 100
# 3 superman 0
# 前面也说了,pd.DataFrame() 会默认把第一行作为 header,并且会默认添加 index (就是上面你看到左边的 0123),如果不想用,可以禁掉,我们看看效果
data2 = pd.read_csv('/User/shop/fruit.csv',header = None) # 注意是 None,不是 False
df2 = pd.DataFrame(data2)
print(df2)
#----------------------结果
# 0 1
# 0 student score
# 1 xiaoming 84
# 2 xiaodong 93
# 3 wujingtao 100
# 4 superman 0
#
# 结果就是 会用数字作为你的 header
# 为什么需要 header 和 index,就是为了方便你取值 (另一篇再讲吧)
#------------------------------------------------------------#
### 2. 将字典转为 DataFrame (这个也很常用,上面是读取,这个是输出)
### 还记得上面三次考试成绩吗
### {'xiaoming':[97,49,79],'laoli':[87,56,38],'zhubajie':[100,87,97]} 我还是拿过来吧
### 现在我们想 优雅地 将这些数据转成 datafame,怎么做呢?
studentScore1 = {'xiaoming':97,'laoli':87,'zhubajie':100}
studentScore2 = {'xiaoming':49,'laoli':56,'zhubajie':87}
studentScore3 = {'xiaoming':79,'laoli':38,'zhubajie':97}
### 还是得准备些数据才能讲得清
dempDict = {} ##先创建个临时的空白字典备用
score = [studentScore1, studentScore2, studentScore3] ## 我们还是假设有三次成绩
examNum = 0
for scoreDitc in score:
examNum += 1 ##记录考试场数
name_list = [] ## 创建一个文件存学生名字,思考一下为什么要这么做
for name,score in scoreDitc.items(): ##这时我们需要分门别类的将数据放好
if name not in name_list:
name_list.append(name)
else:
pass
dempDict.setdefault('Exam{}'.format(examNum),[]).append(v) ## .format 是一个字符串格式化方法非常棒,我放另一篇里面讲
dempDict['name'] = name_list ## 最后将学生信息也存进 dempDict里面
df = pd.DataFrame(dempDict)
print(dempDict)
print('\n')
print(df)
#----------------------结果
#{'Exam1': [97, 87, 100], 'name': ['xiaoming', 'laoli','zhubajie'], 'Exam2': [49, 56, 87], 'Exam3': [79, 38, 97]}
# Exam1 name Exam2 Exam3
# 0 97 xiaoming 49 79
# 1 87 laoli 56 38
# 2 100 zhubajie 87 97
##### 1. 首先思考两个事情:(1) 为什么 dempDict = {} 是放在最外面的,放在循环里面可以吗? (2) 为什么name 要额外处理
比较重要的说明我还是放在外面吧
1. 首先,这种方法构建的时候,列表必须是等长的,如上面的列表全是都是3个元素。如果不等长会怎么样?抱歉会报错。如果真的缺数据怎么办,(比如有人缺考了),那就想想办法补齐,比如说补 0 或 空格。上面这个例子不好改,因为这里例子里面即使缺考,也必须会有一个值,否则就构不成字典,但是实际中你可以会遇到其他情况,这时可以选择用 .append(0) 或 .append(‘’) 代替,使得列表(list)等长
2. 敏锐的你一定注意到,dempDict 里面的每一个 键 key,就是对应 df (dataframe) 里面的 header
3. 但是也应该观察到,dempDict 是无序的,但是 df (dataframe) 里面的 header 的顺序是和 dempDict 保持一致的。这种不符合我们的期待,因为我们更想名字放在第一列,那要怎么改呢?
### 自定义 dataframe 的 header
scoreDict = {'Exam1': [89, 89, 89], 'name': ['xiaoming', 'laoli','zhubajie'], 'Exam2': [89, 89, 89], 'Exam3': [89, 89, 89]} ## 偷懒直接 copy 了
df = pd.DataFrame(dempDict,columns = ['name','Exam1','Exam2','Exam3']) ## 好吧其实加个columns就好了
print(df)
#----------------------结果
# name Exam1 Exam2 Exam3
# 0 xiaoming 97 49 79
# 1 laoli 87 56 38
# 2 zhubajie 100 87 97
#
## 漂漂亮亮哒
##### 如何保存? 如何将 dataframe 保持为 csv 文件
df.to_csv(sava_path) ### 很简单的一句话,sava_path是你要放的路径,包含文件名,如'/User/output/examScore.csv'
### 然而直接保存时,会默认保持 index (左边的一列数字),由于后续用 Excel 打开时,本身就带序号,还是从1开始,符合人性,所以很多人不希望输出 index,那要怎么办呢?
df.to_csv(sava_path,index = False) ### 加个index = False就好了
好吧这篇先说到这里吧,写得够多的了。
最终的目的是将数据整理好,形成 dataframe 的样子
再次强调,这篇文章的目的是将数据整理成下面这个
#----------------------结果
# name Exam1 Exam2 Exam3
# 0 xiaoming 97 49 79
# 1 laoli 87 56 38
# 2 zhubajie 100 87 97
#
下一篇,我打算讲 dataframe 的数据处理操作 (筛选,过滤,合并,计算,重塑等)
再下一篇,我想将统计学的内容整理进来
至此,才能优雅的玩转数据
最后说一下,我写的方法其实不是最优雅的,一定有其他计算机大佬写得比我漂亮 (比较我是生物背景的)。所以如果本篇出现错漏,或者有更好地思路,欢迎留言讨论。
后记,原本不想写那么多的,因为写太多可读性差,看一会就不想看了。
但是我个人的使用经验又是各部分都有关联,如果单独拆开来讲就不太实用,”知道是什么“和“知道如何灵活使用”还是两码事。
所以我想综合写一下比较实用的思路,至于每个数据类型的细节处理,以后会独立成章,有空再分享吧。
作者:发哥
链接:发哥的档案室 - 简书
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。