首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >零基础5天入门Python数据分析:第四课

零基础5天入门Python数据分析:第四课

作者头像
明月AI
发布2021-10-28 10:21:39
发布2021-10-28 10:21:39
4050
举报
文章被收录于专栏:野生AI架构师野生AI架构师

在第一第二天已经讲了notebook的基础使用,python的基础语法及常用的数据结构及其运算,包括:

  • 整型: int
  • 浮点型: float
  • 布尔型: bool
  • 字符串: str
  • 元组: tuple
  • 列表: list
  • 集合: set
  • 字典: dict

其中,前五种类型是不可变类型,后三种是可变类型,而不可变类型才能作为集合的元素或者字典的键。

在第三天还讲了:

  • 格式化输出
  • 错误信息
  • 条件语句
  • 循环语句

今天还会继续讲语法部分:

  • 推导式
    • 元组推导式
    • 列表推导式
    • 集合推导式
    • 字典推导式
  • 函数
    • 内置函数
    • 对象方法
    • 自定义函数
    • 函数参数:必选参数,默认参数,不定参数
    • 函数变量作用域
    • 匿名函数

涉及的语法比较多,像匿名函数,类,包等,这些我们知道怎么用即可。

1. 推导式

推导式是python比较特殊的语法,其他编程语言比较少见,使用起来很就方便,能提升代码的可读性。

主要包含:

  • 元组推导式
  • 列表推导式
  • 集合推到式
  • 字典推导式

1.1 元组, 列表和集合推导式

这三种推导式的语法几乎是一样的,放到一起去讲。其语法结构:

例如,我们如果要计算列表中每个元素的平方值,组成新的列表:

代码语言:javascript
复制
data = [1, 2, 3]

# 使用循环的写法
new_data = []
for val in data:
    new_data.append(val**2)

print(new_data)
代码语言:javascript
复制
data = [1, 2, 3]

# 使用推导式的写法
# 如果是元组,则将下面语句的中括号改为小括号
new_data = [val**2 for val in data]
print(new_data)

上面可以看到,对于某些循环语句,改用推导式,将是非常简洁的。事实上,大多数循环语句都能改成使用推导式的形式来实现。不过,注意不要走极端,并不是所有循环语句改成推导式都会提升可读性。

总结一下,列表推导式的语法如下:

代码语言:javascript
复制
[func(item) for item in data]

其中,func(item)是指对item做某种运算,如上面例子的平方。

下面看一个复杂一点的推导式。

代码语言:javascript
复制
a = [1, 3, 6]
b = [2, 1, 3]

# 如果我们需要计算:
# a的每个元素和b的每个元素的乘积
c = [ia*ib for ia, ib in zip(a, b)]
print(c)
代码语言:javascript
复制
data = [4, 1, 2, 3, 5, 7, 8]

# 如果我们需要计算:
# data的偶数位上的数的平方
new_data = [val**2 for key, val in enumerate(data) if key % 2 == 1]
print(new_data)

这是把zip或者enumerate函数和推导式结合在一起使用。

上面说的例子,对于元组和集合完全适用。如:

代码语言:javascript
复制
# 只需要把中括号改成大括号即可
new_data = {val**2 for key, val in enumerate(data) if key % 2 == 1}
print(new_data)

1.2 字典推导式

字典也可以使用推导式的形式定义:

代码语言:javascript
复制
{key: val for key, val in zip(keys, values)}
代码语言:javascript
复制
# 姓名和成绩
names = ['张三', '李四', '王五']
scores = [66, 89, 59]

# 使用字典推导式定义一个字典
# 将姓名和成绩关联起来
data = {name: score for name, score in zip(names, scores)}
print(data)
代码语言:javascript
复制
# 将一个字典复制到另一个字典也可以这样写:
data1 = {'张三': 66, '李四': 89, '王五': 59}
data2 = {k: v for k, v in data1.items()}
print(data2)

2. 函数

函数的调用方式:value = function_name(param),通过函数名去调用,前面我们已经用过不少了,如print, len等,都是函数。

2.1 内置函数

常用的内置函数(前面有些已经涉及到了):

  • print
  • round: 四舍五入
  • abs:绝对值
  • len:长度
  • min:最小值
  • max
  • sum
  • sorted:排序
  • range
  • zip
  • enumerate

还有几个可以用于类型转换的函数:

  • int
  • float
  • bool
  • tuple
  • list
  • set

这几个暂时没涉及到,后面可能需要用到的。

像for, in, and, or, not,is等,这些是语法的关键字。

python的常用的内置函数基本就在这里的,我们需要大概掌握它们的用法。如果一时不记得这些函数有哪些参数,我们也需要知道怎么找到说明,一种方式是直接通过搜索引擎查找,第二种当然是找人问,而在notebook中,还有一种常用的方式,如下:

代码语言:javascript
复制
# 函数名的后面,加一个问号,然后运行单元格
# 以range函数为例:
range?

其输出结果:

代码语言:javascript
复制
Init signature: range(self, /, *args, **kwargs)
Docstring:
range(stop) -> range object
range(start, stop[, step]) -> range object

Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step.  range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted!  range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).
Type:           type
Subclasses:

我们需要学会看这样的帮助文档。关于该函数的调用,关键的说明信息是:

代码语言:javascript
复制
range(stop) -> range object
range(start, stop[, step]) -> range object

该函数可以接受的参数个数是3个:

  • 如果只是传一个参数:range(stop)
  • 如果传入两个参数:range(start, stop)
  • 如果传入三个参数:range(start, stop, step)

前面两种调用形式前面已经涉及过,我们分别来看看:

代码语言:javascript
复制
data1 = range(10)
data2 = range(5, 10)
data3 = range(5, 10, 2)

# 我们将这3个结果转成列表来观察结果
print(list(data1))
print(list(data2))
print(list(data3))

显然,step这个参数的作用是步长的意思。

2.2 数据变量的常用函数

对于字符串,元组,列表,集合和字典等,python都提供了很多内置的函数,而每个函数又有不同的参数,我们很难去记住它们。最重要的是,我们需要大概知道每种类型大概能实现什么功能,然后去哪里可以查到这些方法,及其使用的方式。这是我们学习编程最重要的技能之一。

例如,如果我们需要知道字符串有哪些内置函数则可以在cell中输入:str.之后,然后按Tab键,就会出来该类型所有的方法列表:

str包含的函数很多很丰富,我们往下拉滚动条,就能看到我们之前用过的replace,接着我们可以继续查看该函数的用法:

输入:str.replace?,然后运行单元格,结果如下:

代码语言:javascript
复制
Signature: str.replace(self, old, new, count=-1, /)
Docstring:
Return a copy with all occurrences of substring old replaced by new.

  count
    Maximum number of occurrences to replace.
    -1 (the default value) means replace all occurrences.

If the optional argument count is given, only the first count occurrences are
replaced.
Type:      method_descriptor

该函数的使用方式是str.replace(self, old, new, count=-1, /),作用是讲字符串中的old字符串替换成new字符串,说明中还有特别说明count参数的作用,还有返回值的说明。这里的self是类本身,这里暂时不用管,在类的部分会讲到。

我们也可以输入"变量名.",然后按Tab键,这也会出现函数列表。

在前面我们使用如下:

代码语言:javascript
复制
s = 'This is a string string.'
# 调用的方式跟上面说到的内置函数有点点不同
new_s = s.replace('string', '字符串')
print(new_s)
代码语言:javascript
复制
# 这种方式也是完全一样的
s = 'This is a string.'
s.replace?

对于list, set, dict等,查看帮助文档的方式都是类似的。

我们总结一下,这几种类型的常用方法:

  • 字符串:index, replace, find, lower, upper, split, strip, startswith, endswith, join (字符串的方法比较多)
  • 元组:index
  • 列表: index, copy, append, insert, remove, sort
  • 集合: copy, add, remove, intersetion, union
  • 字典: copy, keys, values, items

这些常见的函数,大家可以都查一查其帮助文档,大致了解其用法。

这只是一部分,还有其他的函数,需要用的时候可以去了解其用法。

代码语言:javascript
复制
s = 'This is a string'

# 按空格分拆字符串
ls = s.split(' ')
print(ls)

# 使用一个等号将列表拼接起来,组成一个新的字符串
'='.join(ls)

2.3 自定义函数

函数就是一种封装,把相关的功能组合在一起。例如,前面的打印九九乘法表,我们把它封装成一个函数:

代码语言:javascript
复制
# 定义一个函数:nine_nine_multi
def nine_nine_multi():
    for i in range(1, 10):           # 注意缩进
        for j in range(1, i+1):
            print("%d * %d = %d" % (i, j, i*j))
    # return None

# 函数调用
nine_nine_multi()

我们已经知道sum函数可以对列表进行求和,现在我们重新造一个轮子:

代码语言:javascript
复制
# 定义列表的求和函数
# list_sum是函数名
# ls是函数的参数
# 注意:理解缩进的层级
def list_sum(ls):
    ls_sum = 0
    for val in ls:
        ls_sum += val

    # 返回语句
    return ls_sum      # 返回计算结果

a = [1,2,3,4,5]
a_sum = list_sum(a)
print(a_sum)

我们重点来理解一下这个函数执行机制,在执行到函数调用的语句时,执行状态如下图:

我们定义的函数名有点类似变量名,它指向了一个函数。

下一步就是执行函数调用:

这里我们需要注意: 外部定义的列表a和函数参数ls其实指向的是同一个列表。如果在函数里面,改变了ls参数的值,会导致外部的列表变量a的值也跟着改变。因为这里的参数是复杂类型,对于其他的类型,如集合,字典,类等都会产生这样的效果。但是对于简单类型则不会产生这样的副作用。

我们继续往前执行,就跟前面的循环类似了:

而执行到return语句时:

这个Return Value在返回之后,会赋值给a_sum变量。

注意:函数是不一定是需要又return语句的,默认会返回一个None。

作为一个练习,大家可以实现一个阶乘的函数,并观察其执行过程。

2.4 函数参数

在python里,函数参数有几种类型:必选参数,默认参数,不定参数。

必选参数就是前面自定义函数时用到的,调用时,必须对应传递,否则就会报错。

2.4.1 默认参数

我们知道,在调用函数时如果不指定某个参数,Python 解释器会抛出异常。为了解决这个问题,Python 允许为参数设置默认值,即在定义函数时,直接给形式参数指定一个默认值。这样的话,即便调用函数时没有给拥有默认值的形参传递参数,该参数可以直接使用定义函数时设置的默认值。

代码语言:javascript
复制
# a是必选参数
# b就是带默认值的参数
def test(a, b=2):
    print(a, b)

# 不传b参数,则b参数使用默认值
test(1)
# 给b参数参数,通常这样:b=4
test(1, b=4)

带默认值的参数需要放在必选参数的后面,否则会报语法错误,如下:

2.4.2 不定参数

我们查看一些函数的帮助文档,例如range,参数里会有*args, **kwargs这样的参数,通常我们自己实现函数的时候,尽量应该避免这种情况,不过,我们还是得理解这种语法,以便我们更好的理解某些函数的用法。

需要说明的是,args和kwargs都可以理解为参数名,使用其他的名字也是一样的,只是args和kwargs是习惯的命名。

对这两个参数大概可以这样理解:

  • 一个星号:可变参数在传入函数后,被封装成一个 tuple 来进行使用。
  • 两个型号:关键字参数,不定长参数转换为字典传入函数。
代码语言:javascript
复制
range?

不定参数和必选参数或者是默认参数也是可以一起使用的,例如:

2.5 变量作用域的问题

在上面的图,我们定义了一个求和函数,其实有两个变量的作用域:

  • 全局作用域(Global frame):在函数外部定义的变量和函数都是定义在全局作用域上,在函数内部也可以使用这些变量。
  • list_sum函数作用域:函数作用域是一种子作用域,而子作用域的变量不能在全局上使用,即不能在函数外部直接使用函数内部的变量。
代码语言:javascript
复制
def test(a):
    local_a = 20    # 定义一个函数内部的变量
    print(a)
    print(global_a)       # 这里会打印10

# 定义一个全局变量
global_a = 10
param = 15
test(param)

# 在函数外部使用函数内部的变量会报错
print(local_a)

如果我们定义了多个函数,则每个函数都会有自己的内部作用域。

但是需要主要,如果我们在函数内部改变全局变量,会发生什么呢?请看:

代码语言:javascript
复制
def test(a):
    # 在函数内部改变重新定义之后,global_a这个变量已经变成了该函数的内部变量
    # 在这里改变重新赋值并不会影响全局的global_a
    global_a = 5    
    print('In test: ', global_a)    # output: 5


# 定义一个全局变量
global_a = 10
print('Global: ', global_a)    # output: 10
test(20)

# 外部的变量并不会改变
print('Global: ', global_a)    # output: 10

在函数内部赋值之后,其实这两个变量已经完全是两个独立的变量了,虽然它们的名字是一样的。就像在这里有一个叫“张三”的人,而在另一个地方也有一个叫“张三”的人,它们两个同名,但却完全是独立的两个人。

但是如果我们在函数内部尝试操作全局的变量,例如四则运算,则会报错:

如果我们需要作为一个全局变量来使用,则需要使用global语句:

代码语言:javascript
复制
def test(a):
    global global_a      # 声明该变量是全局变量
    global_a += 5
    print('In test: ', global_a)


# 定义一个全局变量
global_a = 10
print('Global: ', global_a)
test(20)
print('Global: ', global_a)

这会输出:

代码语言:javascript
复制
Global:  10
In test:  15
Global:  15

这时,全局的变量就会跟着改变了。

注意:尽量不要使用全局的变量,尽量避免在函数内部的操作对外部造成影响,这会让程序的可读性变得很差。如果需要,通常可以作为函数的参数传入。

2.6 匿名函数

现在我们来看一个常用的函数:sorted,这个函数可以用于元组和列表等的排序。先查看它的用法:

代码语言:javascript
复制
Signature: sorted(iterable, /, *, key=None, reverse=False)
Docstring:
Return a new list containing all items from the iterable in ascending order.

A custom key function can be supplied to customize the sort order, and the
reverse flag can be set to request the result in descending order.
Type:      builtin_function_or_method

先看一下其简单用法:

代码语言:javascript
复制
a = [1, 3, 2]
print(sorted(a))    # output: 1,2,3
print(sorted(a, reverse=False))    # output: 1,2,3

# 逆序排序
print(sorted(a, reverse=True))    # output: 3,2,1
代码语言:javascript
复制
[1, 2, 3]
[1, 2, 3]
[3, 2, 1]

对于一些复杂的列表或者元组,我们可能就需要用到sorted函数的key参数,该参数可以指定排序的值:

代码语言:javascript
复制
# 定义一个列表: 一个班级学生的考试成绩如下
scores = [
    ['张三', 80],
    ['李四', 70],
    ['王五', 90]
]

# 定义一个key参数的值
def score_sort(x):
    return x[1]     # 指定按照下标1的值,即成绩进行排序

# 如果我们按成绩进行排名
# 这告诉我们,函数也是一个对象,可以作为其他函数的参数,也可以赋值给一个变量
res = sorted(scores, key=score_sort, reverse=True)
print(res)
代码语言:javascript
复制
[['王五', 90], ['张三', 80], ['李四', 70]]

现在这样是可以实现功能的,但是这样很哆嗦,有没有更简洁优雅的方式呢?

显然是有的,就是匿名函数:

代码语言:javascript
复制
res = sorted(scores, key=lambda x: x[1], reverse=True)
print(res)

lambda x: x[1]: 这就是一个匿名函数:

  • 匿名函数以lambda开始
  • 冒号前面的x是匿名函数的输入参数,当然输入参数可以有多个
  • 冒号后面的x[1]是匿名函数的返回值

这个匿名函数的效果和前面的score_sort函数的效果是一样的,可以对比一下。

红色椭圆圈住的地方就是匿名函数,它有自己的作用域,有自己的参数,有自己的返回值。

从图示我们可以看到,在实际运行的时候,其实是有点类似循环的,每次取列表的一个元素传入匿名函数的参数x,返回值就是x[1]。

3. 类与对象

对于做数据分析来说,类并不是那么重要,不过我们得掌握其基础的用法,因为很多场景下,我们会接触到类的对象。

在python中,一切皆对象,我们前面接触到的变量,函数等,都是对象,而所有对象都是某个类的实例化。对于数据分析师来说,我们对“类”秉持工具主义就好了,知道怎么去调用类和类的方法即可。

类方法的调用是需要使用点号进行调用的,在前面已经涉及到,例如:

代码语言:javascript
复制
# 定义一个字符串变量
# s就是一个字符串对象
s = 'string!'
# 调用s对象的replace方法
s.replace('!', '.')

跟类相关的主要概念有几个,我们以房子来类比,以辅助理解:

  • 类:就是概念上的房子
  • 实例对象:具体的一个房子
  • 方法:房子的功能,例如居住
  • 属性:房子的大小,面积等,就是属性

我们不准备深入去讲解这些概念,但是我们需要有所了解。

4. 包:package

前面讲到的东西只是python最核心的部分,但是python还有内容众多的包,几乎无所不包,这些包又分为python内置的包,还有浩如烟海的第三方包。

4.1 常用数学函数包:math

现在我们讲一个数学包:

代码语言:javascript
复制
# 在使用包的函数,需要先引入该包
import math       # 这时包引用的语句

# PI的值
# 包的变量:pi是在math这个包中定义好的常量
print(math.pi)

# 求平方根
# sqrt是math包中定义的函数
print(math.sqrt(2))

# 查看包函数的帮助文档
# 如果帮助信息不清晰,则可以使用搜索引擎进行搜索
math.sqrt?

包的引用语法还有以下的形式:

代码语言:javascript
复制
# 将numpy引入进来,并命名为np,这时在代码中就可以使用np进行调用
# numpy是一个数据分析常用的第三方包
import numpy as np

# 从math包中单独引入sqrt这个函数
from math import sqrt

包引用语句通常放在代码行的最前面。

math这个包的内容很丰富,几乎囊括了中学数学里用到的很多函数,不可能也没必要一一讲解。

4.2 变量的深度复制

前面已经讲过列表字典等变量的复制问题,但是那都不是太彻底的。我们可以看一下下面这个例子:

如果我们观察b变量,发现其值也已经被修改了,因为这个copy只是浅层的copy,如果元素中包含有组合类型的话,就会出现这样的问题。

如果我们需要彻底的复制的话,这时需要用到一个“copy”的包:

代码语言:javascript
复制
from copy import deepcopy

a = [1, 2, ["a", "b", "c"]]
# deep copy a to b
b = deepcopy(a)

# change 
a[2][1] = 'abc'
print(b)
代码语言:javascript
复制
[1, 2, ['a', 'b', 'c']]

显然,使用deepcopy之后,改变a的值并不会导致b的值也关联被改变,这时a和b已经是完全两个独立的变量了。

至此,python数据分析入门的语法部分算是学完了。

5. 练习

  1. 实现一个函数:统计一个字符串中,不同字符的个数。
  2. 实现斐波那契数列的通项计算:F[n]=F[n-1]+F[n-2](n>=3,F[1]=1,F[2]=1)
  3. 实现一个函数:计算100以内的素数。

未完待续。。。

ps: 所有代码都在notebook运行。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 野生AI架构师 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 在第一第二天已经讲了notebook的基础使用,python的基础语法及常用的数据结构及其运算,包括:
    • 1. 推导式
      • 1.1 元组, 列表和集合推导式
      • 1.2 字典推导式
    • 2. 函数
      • 2.1 内置函数
      • 2.2 数据变量的常用函数
      • 2.3 自定义函数
      • 2.4 函数参数
      • 2.5 变量作用域的问题
      • 2.6 匿名函数
    • 3. 类与对象
    • 4. 包:package
      • 4.1 常用数学函数包:math
      • 4.2 变量的深度复制
    • 5. 练习
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档