【DS with Python】 csv & numpy & LC表达式 & 匿名函数 & PIL 模块基础


前言

想着往Data Scientist方向发展,为了适应未来全英文授课,故在coursera上看点网课,平时在这里记录一点学习的内容与思考,主要为了供日后自己复习和查阅,如有问题,欢迎指出。


一、Function

合理利用default可以使得函数更加灵活

def add_numbers(x,y,z=None): #use default

Python中的面相对象编程时的class名称要用驼峰命名法,即每个单词第一个字母大写,其他小写,函数命名的时候用’_'连接。


二、python types

python的数据类型主要包含boolean, str, int, float, NoneType还有tuple, list, dict,set

  • tuple:可以有多种数据结构,是有序的,不可单独更改内容,不可单独新增内容
  • list:可以有多种数据结构,是有序的,可以随时增删和修改
  • dict:用key-value的方式存储,key必须是不可变对象,对应的value数据类型可以不一样(在本周学习中利用csv.DictReader()模块时,遇到了OrderedDict,而回根据放入元素的先后顺序进行排序,所以输出的值是排好序的
  • set:无序集合,key不重复,与dict类似,但不储存值,且key不重复,方便查看所有的类型
#常见获得key-value的方法
for key in x:	#获得key
for value in x.values:	#获得值

三、unpacking 解包

如果我们有一个序列(sequence)或者list, tuple, dict我们都可以用unpacking的方法快速赋值给我们想要的变量,例如:

x=('Christopher','Brooks','brooksch@umich.edu')
fname,lname,email=x
print('fname:{},lname:{},email:{}'.format(fname,lname,email))

--output:
fname:Christopher,lname:Brooks,email:brooksch@umich.edu

四、csv Module

  1. 全局变量:设定小数位数
import csv
%precision 2 #将小数位数确定为2
  1. csv.DictReader()与Slicing切片
    可以利用DictReader()工具将数据按行创建字典,并可以用list将他们组合起来,得到列表mpg,接下来的操作都将利用mpg上进行,代码如下:
import csv
%precision 2

with open ('mpg.csv') as csvfile:
    mpg=list(csv.DictReader(csvfile))
mpg[0:1]

--Output:
[OrderedDict([('', '1'),
              ('manufacturer', 'audi'),
              ('model', 'a4'),
              ('displ', '1.8'),
              ('year', '1999'),
              ('cyl', '4'),
              ('trans', 'auto(l5)'),
              ('drv', 'f'),
              ('cty', '18'),
              ('hwy', '29'),
              ('fl', 'p'),
              ('class', 'compact')])]

此处所产生的list是OrderedDict,使用方法与dict类似

  1. 查看字典的所有key,并计算城市(cty)和高速(hwy)的平均每加仑燃料行驶英里数
mpg[0].keys()

--Output:
odict_keys(['', 'manufacturer', 'model', 'displ', 'year', 'cyl', 'trans', 'drv', 'cty', 'hwy', 'fl', 'class'])
sum(float(d['cty']) for d in mpg)/len(mpg) #利用LC表达式

summpg=0 #常规的迭代
for d in mpg:
	summpg+=float(d['cty'])
summpg/len(mpg) 

--Output:
16.86

(注:这里运用到了List comprehension列表推导式的方法,LC表达式本质是带有if statement的压缩的迭代取数列表,只要理解压缩的过程,就可以理解LC表达式,这些都会在稍后有简单的介绍笔记)

  1. 查看cylinder数目种类,并根据cylinder分类计算cty平均值

首先利用set得到种类

cylinders=set(d['cyl'] for d in mpg)
cylinders

--Output:
{'4', '5', '6', '8'}

首先考虑用list来展示答案,对于各种cylinder对应的平均mpg,考虑用tuple或者dict来展示,方便起见,利用tuple,这样我们就要先创建一个CtyMpgByCyl的空list

接下来是代码的思路

对cylinder进行迭代:
  设置两个统计量:
 1、当前cylinder的数量记为cyltypecount
 2、当前cylinder的mpg总里程数,记作summpg
 再对列表mpg进行迭代,
  如果迭代的值等于当前的cylinder值,
   对应的cyltypecount++,
   对应的summpg加上列表mpg中当前字典下的‘cty’中的值(mpg是一个列表,列表中的每一个元素都是字典格式)(注意:此时我们需要转换数据类型,因为读取数据时默认用str格式,而此处要进行数学运算!
 计算平均值并以tuple的格式添加到CtyMpgByCyl的列表中
排序
代码如下:

CtyMpgByCyl=[]
for c in cylinders:
    cyltypecount=0 #当前cylinder的数量
    summpg=0 #当前cylinder的mpg总里程数
    for d in mpg:
        if d['cyl']==c:#d['cyl']是当前的cylinder数
            summpg+=float(d['cty']) #转化成float格式,进行数学运算
            cyltypecount+=1
    CtyMpgByCyl.append((c,summpg/cyltypecount)) #用tuple格式输出
CtyMpgByCyl.sort(key=lambda x:x[0])
CtyMpgByCyl
--Output:
[('4', 21.01), ('5', 20.50), ('6', 16.22), ('8', 12.57)]

(注:这里用到了lambda函数,lambda函数又称匿名函数,故名思义是对函数格式的变形与缩简,仍旧与函数具有相似的构建逻辑,本次笔记后段会简单介绍此函数格式,在此处,简单来说,list是一个有序的数据格式,在list.sort中有一个参数是key即对何值作为主键进行排序,lambda x: x[0]表示是list中的每一个元素的第一个值进行排序,此处list中的每一个元素是tuple,例如(‘4’, 21.01),即对’4’,'5’等值进行排序,便有了输出值,下文中key=lambda x:x[1]也是相同的原理,对tuple的第二个元素排序)

  1. 看class分类,并根据class分类计算hwy平均值
    本质上与4.是一样的,可以通过查看自己写一遍代码来对比,查漏补缺,代码如下:
classes = set(d['class']for d in mpg)
classes
--Output:
{'2seater', 'compact', 'midsize', 'minivan', 'pickup', 'subcompact', 'suv'}
HwyMpgByclass=[]
for c in classes:
    clstypecount=0
    summpg=0
    for d in mpg:
        if d['class']==c:
            summpg+=float(d['hwy'])
            clstypecount+=1
    HwyMpgByclass.append((c,summpg/clstypecount))
HwyMpgByclass.sort(key=lambda x:x[1])
HwyMpgByclass

--Output:
[('pickup', 16.88),
 ('suv', 18.13),
 ('minivan', 22.36),
 ('2seater', 24.80),
 ('midsize', 27.29),
 ('subcompact', 28.14),
 ('compact', 28.30)]

五、time & datetime Module 时间模块

python中的time模块和datetime模块可以得到当前时间、时间间隔、和计算相应间隔下的时间。
首先介绍time模块下的时间,python的最早初始的时间是1970年1月1日0点0分0秒,用time.gmtime(0)即可查到,而现在的时间可以用time.time()查到,注意,查到的结果是秒,意思是离初始时间过了多少秒,为了得到日期,可以用datetime模块中datetime.fromtimestamp(tm.time())来得到对应的数据,并可以得到年、月、日等数据代码如下:

import time as tm
import datetime as dt
tm.gmtime(0)  #查看初始时间
--Output:
time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=0)
tm.time() #当前时间,秒
dtnow=dt.datetime.fromtimestamp(tm.time()) #当前时间 ,年月日时分秒
dtnow.year #获得年
dtnow.month #获得月
dtnow.day #获得日
print(dtnow,dtnow.year,dtnow.month,dtnow.day)
--Output:
2021-12-10 22:10:36.146800 2021 12 10

另一种获得当天日期的办法是datetime.date.today(),同时datetime模块还可以得到时间间隔timedelta,通过加减时间间隔,我们可以得到对应的目标时间,代码如下:

today=dt.date.today() #今天日期
delta=dt.timedelta(100) #时间间隔100天
target_time=today-delta #100天前
print('今天是:{}时间间隔:{}对应的间隔日期前是:{}'.format(today,delta,target_time))
--Output:
今天是:2021-12-10时间间隔:100 days, 0:00:00对应的间隔日期前是:2021-09-01


六、lambda 匿名函数与map函数

  • 在Python中,不通过def来声明函数名字,而是通过lambda关键字来定义的函数称为匿名函数或者lambda函数。

lambda函数能接收任何数量(可以是0个)的参数,但只能返回一个表达式的值,lambda函数是一个函数对象,直接赋值给一个变量,这个变量就成了一个函数对象。

一般函数与lambda函数:

#一般函数
def add(a,b,c):
	return a+b+c
#lambda函数
add = lambda a,b,c:a+b+c

在实际应用中可以采用**(lambda 参数:表达式)(参数)**的方式来更简化形式
来看以下例子:

people=['Dr. Bob Martin','Dr. Linda James','Dr. Joe Kings']
for person in people:
    print((lambda x:x.split()[0]+' '+x.split()[-1])(person))
--Output:
Dr. Martin
Dr. James
Dr. Kings

在这里,我们将people中的每一个元素person按空格进行分割lambda函数的作用是将第一个和第三个值用空格连接起来,并直接对person进行利用,才得到了最终结果

其实我们也可以利用map函数和lambda函数结合的方法进一步缩减语句

先介绍下map函数:

  • map() 会根据提供的函数对指定序列做映射。
    第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。

注意:map函数是一个lazy evaluation,他返回的是一个map object,在你试图查看他的内部之前,他并不会运行,所以我们需要根据自己的需求,查看其内部的值,此处,我们用list的方法查看其内部结构,代码如下:

list(map(lambda x:x.split()[0]+' '+x.split()[-1],people))
--Output:
['Dr. Martin', 'Dr. James', 'Dr. Kings']

可以看到,得到的结果是和上面一样的,这是对people对所有的元素进行了lambda函数的操作,简化了迭代的书写时间和空间成本


七、List Comprehension 列表推导式

LC表达式本质上是将迭代的语句进行压缩和重组(condensed form),其遵循的思路如下:

out_list=[out_express for out_express in input_list if out_express_condition]

我们来对一一下普通的迭代语句和LC表达式,就可以明显的发现其压缩的原理

#(normal Iteration)
for out_express in input_list:
	if out_express_condition:
		output_expression

简化之后大致可以分成三层

  • Iteration (eg.for out_express in input_list)
  • If statement (eg.if out_express_condition)
  • execution (eg.output_expression)

那么在LC表达式中也是三层:
[execution Iteration If statement]
用空格分割,只要搞明白这点,就可以轻松理解LC表达式

在LC表达式中使用if else逻辑

[1 if i >= 0.5 else 0 for i in x]

类比lambda表达式中的if-else语句:

life_cate = df['lifeExp'].apply(lambda x: 'more than 80' if x>= 80 else '70-80' if x>=70 else
                    '60-70' if x>=60 else 'less than 60')

八、Numpy module

  1. np.array
    可以通过np.array()的方法来获得矩阵,注意,在做二维矩阵的时候要记得加上一个square bracket[],这是一个容易犯的错误。
np.array([[1,2],[2,3]])
--Output:
[[1 2]
 [2 3]]
  1. np.arange
    可以用np.arange(start,end,skip)来获得一个sequence,start是序列起点(inclusive,包含)end是序列终点(exclusive,不包含)skip是间隔距离
print(np.arange(1,10,2))  #inclusive-exclusive
--Output:
[1 3 5 7 9]
  1. np.linspace
    首先注意是np.linspace不是np.linespace,可以用np.linspace(start,end,number)来获得一个序列,这个序列是float类型的,与arange不同的是它的三个参数,start是序列起点(inclusive,包含)end是序列终点(exclusive,包含),number是序列的个数
print(np.linspace(1,10,5)) #inclusive-inclusive
--Output:
[ 1.    3.25  5.5   7.75 10.  ]
  1. Element product or Matrix product
    对于得到的矩阵,用Asterix(*)来进行矩阵元素与矩阵元素的相乘,用@或者np.dot(a,b)来进行矩阵与矩阵的代数相乘。
a=np.array([[1,2],[2,3]])
b=np.array([[1,2],[2,3]])
print('do element product:\n',a*b)
print('do matrix product:\n',a@b,'\n or\n',np.dot(a,b))

--Output:
do element product:
 [[1 4]
 [4 9]]
do matrix product:
 [[ 5  8]
 [ 8 13]] 
 or
 [[ 5  8]
 [ 8 13]]
  1. np的基本函数
    包含sum(), mean(), min(), max() 用法是一目了然的
print(a.sum(),a.mean(),a.min(),a.max())
--Output:
8 2.0 1 3
  1. np.genfromtxt
    numpy从txt或者csv种获得文本的办法有很多,这里我们介绍一下np.genfromtxt:
#genfrom常见参数
np.genfromtxt('file',delimiter=',',skip_header=1,names=(),dtype=None,usecols=(0,-1))

在这里介绍几个重点参数:

  1. dtype:最终数组的数据类型,可以是str, int, float等,甚至可以自定义,例如[(‘myint’,‘i8’),(‘myfloat’,‘f8’), (‘mystring’,‘S5’)],此处i8就是int2^8即int64,同理f8就是float64,s5就是str5,如果设置成None,将会返回一个一维数组
  2. delimiter:分隔符,例如csv多以,做分割,在读csv文件时多用‘,’
  3. skip_header/skip_footer:跳过前几行/后几行
  4. names:可以用True,代表自动获取表头,也可以自定义名称,注意匹配列数,否则会报错
  5. usecols:选择需要的列数

我们以著名的iris.csv为例看看genfromtxt()读取的效果

iris=np.genfromtxt('iris.csv',dtype=None,delimiter=',',skip_header=0,names=True)
iris

--Outputs:
array([(5.1, 3.5, 1.4, 0.2, b'setosa'), (4.9, 3. , 1.4, 0.2, b'setosa'),
       (4.7, 3.2, 1.3, 0.2, b'setosa'), (4.6, 3.1, 1.5, 0.2, b'setosa'),
       (5. , 3.6, 1.4, 0.2, b'setosa'), (5.4, 3.9, 1.7, 0.4, b'setosa'),
       (4.6, 3.4, 1.4, 0.3, b'setosa'), (5. , 3.4, 1.5, 0.2, b'setosa'),
       (4.4, 2.9, 1.4, 0.2, b'setosa'), (4.9, 3.1, 1.5, 0.1, b'setosa'),
       ...#省略部分
       (6.5, 3. , 5.2, 2. , b'virginica'),
       (6.2, 3.4, 5.4, 2.3, b'virginica'),
       (5.9, 3. , 5.1, 1.8, b'virginica')],
      dtype=[('sepal_length', '<f8'), ('sepal_width', '<f8'), ('petal_length', '<f8'), ('petal_width', '<f8'), ('species', 'S10')])

可以看到,用得到一维矩阵的最后还有一个dtype,在这里储存了每一列的名字和字符类型,如果需要获得每一列名字,我们还可以用.dtype.names来获得(注意这是attribution,不是function,不要加parentheses,即圆括号)

iris.dtype.names
--Outputs:
('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species')
  1. Boolean Indexing
    我们知道数据对比可以得到boolean值,实际上list对比可以得到boolean矩阵,如下:
iris['sepal_width']>3.2

--Outputs:
array([ True, False, False, False,  True,  True,  True,  True, False,
       False,  True,  True, False, False,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True, False,  True,
        True,  True, False, False,  True,  True,  True, False, False,
        True,  True, False,  True,  True, False, False,  True,  True,
       False,  True, False,  True,  True, False, False, False, False,
       False, False,  True, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False,  True, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False,  True, False, False, False, False, False, False, False,
       False,  True, False, False, False, False, False, False, False,
        True, False, False, False, False, False, False,  True, False,
       False, False, False, False, False,  True, False, False, False,
       False,  True, False, False, False, False, False, False, False,
        True, False, False, False,  True, False])

我们可以把这个矩阵视为boolean indexing,来告诉我们该值是否需要被展示,代码如下:

iris[iris['sepal_width']>3.2]

--Outputs:
array([(5.1, 3.5, 1.4, 0.2, b'setosa'), (5. , 3.6, 1.4, 0.2, b'setosa'),
       (5.4, 3.9, 1.7, 0.4, b'setosa'), (4.6, 3.4, 1.4, 0.3, b'setosa'),
       (5. , 3.4, 1.5, 0.2, b'setosa'), (5.4, 3.7, 1.5, 0.2, b'setosa'),
       (4.8, 3.4, 1.6, 0.2, b'setosa'), (5.8, 4. , 1.2, 0.2, b'setosa'),
       (5.7, 4.4, 1.5, 0.4, b'setosa'), (5.4, 3.9, 1.3, 0.4, b'setosa'),
       (5.1, 3.5, 1.4, 0.3, b'setosa'), (5.7, 3.8, 1.7, 0.3, b'setosa'),
       (5.1, 3.8, 1.5, 0.3, b'setosa'), (5.4, 3.4, 1.7, 0.2, b'setosa'),
       (5.1, 3.7, 1.5, 0.4, b'setosa'), (4.6, 3.6, 1. , 0.2, b'setosa'),
       (5.1, 3.3, 1.7, 0.5, b'setosa'), (4.8, 3.4, 1.9, 0.2, b'setosa'),
       (5. , 3.4, 1.6, 0.4, b'setosa'), (5.2, 3.5, 1.5, 0.2, b'setosa'),
       (5.2, 3.4, 1.4, 0.2, b'setosa'), (5.4, 3.4, 1.5, 0.4, b'setosa'),
       (5.2, 4.1, 1.5, 0.1, b'setosa'), (5.5, 4.2, 1.4, 0.2, b'setosa'),
       (5.5, 3.5, 1.3, 0.2, b'setosa'), (4.9, 3.6, 1.4, 0.1, b'setosa'),
       (5.1, 3.4, 1.5, 0.2, b'setosa'), (5. , 3.5, 1.3, 0.3, b'setosa'),
       (5. , 3.5, 1.6, 0.6, b'setosa'), (5.1, 3.8, 1.9, 0.4, b'setosa'),
       (5.1, 3.8, 1.6, 0.2, b'setosa'), (5.3, 3.7, 1.5, 0.2, b'setosa'),
       (5. , 3.3, 1.4, 0.2, b'setosa'),
       (6.3, 3.3, 4.7, 1.6, b'versicolor'),
       (6. , 3.4, 4.5, 1.6, b'versicolor'),
       (6.3, 3.3, 6. , 2.5, b'virginica'),
       (7.2, 3.6, 6.1, 2.5, b'virginica'),
       (7.7, 3.8, 6.7, 2.2, b'virginica'),
       (6.7, 3.3, 5.7, 2.1, b'virginica'),
       (7.9, 3.8, 6.4, 2. , b'virginica'),
       (6.3, 3.4, 5.6, 2.4, b'virginica'),
       (6.7, 3.3, 5.7, 2.5, b'virginica'),
       (6.2, 3.4, 5.4, 2.3, b'virginica')],
      dtype=[('sepal_length', '<f8'), ('sepal_width', '<f8'), ('petal_length', '<f8'), ('petal_width', '<f8'), ('species', 'S10')])

可以看到,不符合条件的值就被删除了,接下来我们来计算一下所有符合sepal_width>3.2的样本中sepal_length的平均值:

print(iris[iris['sepal_width']>3.2]['petal_length'].mean())

--Outputs:
2.467441860465116

九、PIL Module

Python Imaging Library 简称PIL,常用于python的图像计算与操作,在这里我们用到模块中的Image和IPython中的display来进行操作。

(注:如果python3.x的环境中没有PIL模块是正常的,因为PIL模块在 python2.7之后就没有更新了,现在的python3.x中用的PIL实际上是Pillow模块,Pillow是在PIL基础上建立的,且环境中PIL和Pillow不能兼容,只要有了Pillow就可以运行PIL的代码了,且Pillow的语法仍是PIL)

以princess Zelda为例,首先用Image.open()打开我们要的图片

from PIL import Image
from IPython.display import display
im=Image.open('Zelda.jpeg')
display(im)

princess Zelda
彩色的图片涉及到RGB色彩理论,在这里我们先不考虑彩色图像,先转化成黑白图像,用im.conver(),

  • conver(‘1’)是转化成二值图像,非黑即白,在计算机中多用uint8(uint8解释见下文)来储存,即0是黑,255是白,转化后的图像噪点多

我们来看下转换后的效果:

im=im.convert('1')
display(im)

在这里插入图片描述

  • conver(‘L’)是转化成灰色图像,也是储存值也是uint8,0表示黑,255表示白,以下是转化公示:
    L = R ∗ 299 / 1000 + G ∗ 587 / 1000 + B ∗ 114 / 1000 L = R * 299/1000 + G * 587/1000+ B * 114/1000 L=R299/1000+G587/1000+B114/1000

我们来看一下转化的效果:

im=im.convert('L')
display(im)

在这里插入图片描述
接下来我们用numpy将这里的im转化成矩阵,看看转化后的结果是怎么样的

array=np.array(im)
array
--Output:
array([[215, 214, 213, ..., 216, 210, 210],
       [215, 214, 214, ..., 215, 215, 215],
       [215, 215, 215, ..., 215, 216, 216],
       ...,
       [217, 217, 216, ..., 213, 214, 214],
       [215, 215, 215, ..., 215, 215, 215],
       [215, 215, 215, ..., 216, 216, 216]], dtype=uint8)

这里解释一下什么是uint8,uint意思是unsigned integers,8意味着8bits per type,意味着每一个值的取值范围在0-2^8-1即0-255之间取值,如果我们用阈值255减去当前的数字,理论上我们就可以得到黑白颠倒的图像,得到的图像就像我们看到的老式胶片那样。

首先我们先设置一个阈值矩阵mask,并计算im矩阵与阈值矩阵的差值矩阵再用Image.fromarray得到图像:

mask=np.full(array.shape,255)
modified_array=mask-array
modified_array=modified_array.astype(np.uint8())
modified_im=Image.fromarray(modified_array)
display(modified_im)

在这里插入图片描述
是不是很像胶底

如果将im矩阵reshape一下,将宽度加倍,高度减半,会发生什么呢?

display(Image.fromarray(modified_array.reshape(237,-1)))

在这里插入图片描述

十、str/tuple/list/dict的常用功能

  1. .join()
    有时候我们需要将一个tuple或者一个list中几个个字符串连在一起形成一个新的字符串,就可以使用.join()的方法,语法是:str.join(sequence)
    例如,我们需要对一个list用空格进行拼和,就可以用以下语句
' '.join(team_name[0])

在这里插入图片描述


三、下节预告

下节的内容主要是正则表达式,用到re模块,然后做Quiz和Assignment的个人解析。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值