Anacanda

Python 如何优雅地使用 pandas

2019-10-24  本文已影响0人  发哥的档案室

如何优雅地使用 pandas 对数据进行整理-1 (数据类型介绍:变量,列表,字典,数据框)

为什么要学会使用 pandas ?
我觉得答案是:将数据结构化,以便更好地管理

而 pandas 你可以理解为 利用代码 实现你手动对 Excel 表格处理的功能
实际上,pandas 能实现的功能,基本在 Excel 上都能完成
两个之间只是一个 “时间效率” 和 “一劳永逸” 的事情
习惯用哪种是个人的一种选择罢了

数据类型

在处理数据时,首先我们得明白数据长什么样子,我们期望输入的数据长什么样子。

这一块的理解我其实并不太到位,日后随学习的深入再更。
在这里我们先说明一些无关紧要的概念

Python 的基本数据类型 (我不知道专业的人是不是这么讲)

变量:

a = 2         ## 整数型数字变量
b = 3.56      ## 浮点型数字变量
one = 'John'  ## 字符变量
r9A = True    ## 布尔型变量

##### 需要注意的是
##  1. 变量名可以包括字母,数字和下划线 (_)
##  2. 变量名不能以数字开头

列表 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], 这个也叫 列表的切片

例一:找出100内的所有质数

### 比如说,我想取出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 文件,打开并逐行打印

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

  1. 构建字典的方法:
########## 第一种(推荐)
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'}  

  1. 如何取字典里面的东西
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'

  1. 在更多的时候,我们并不会动手完完整整的码一个字典,太累了
    所以,我们需要一些优雅的技巧

例一,如何合并并将两个等长的列表(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() 构建较复杂字典


### 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 (终于到重点了)

  1. 如何构建 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 的数据处理操作 (筛选,过滤,合并,计算,重塑等)
再下一篇,我想将统计学的内容整理进来
至此,才能优雅的玩转数据


最后说一下,我写的方法其实不是最优雅的,一定有其他计算机大佬写得比我漂亮 (比较我是生物背景的)。所以如果本篇出现错漏,或者有更好地思路,欢迎留言讨论。

后记,原本不想写那么多的,因为写太多可读性差,看一会就不想看了。
但是我个人的使用经验又是各部分都有关联,如果单独拆开来讲就不太实用,”知道是什么“和“知道如何灵活使用”还是两码事。
所以我想综合写一下比较实用的思路,至于每个数据类型的细节处理,以后会独立成章,有空再分享吧。

作者:发哥
链接:发哥的档案室 - 简书
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

上一篇 下一篇

猜你喜欢

热点阅读