python数据分分析

数据处理的步骤

graph LR 处理数据-->清洗数据

目的:如何用python操作数据
对象:需要的是结构化的数据
工具:用到的库

库名 用途
Numpy
Pandas
matplotlib
JupyterNotebook
Scipy
Scikit-learn
Statsmodels

python的内建数据结构;元组、列表、字典

元组

>tuple = (4,5,6)

元组的拆包特性:

>tuple = (4,5,6)
>a,b,c = tup
>b
5

嵌套元组的拆包特性

>tuple = 4,5,(6,7)
>a,b,(c,d) = tuple
>d
7

利用元组的拆包特性,实现交换

>a,b =1,2
>b,a = a,b
>a
2
>b
1

python自带的更高级的拆包特性:*rest(余下的部分),帮助你从某个起始部分采集数据

>values = 1,2,3,4,5,6
>a,b,*rest = values
>rest
[4,5,6]

一般为了方便,开发者会用 *_代替*rest来表示非采集部分的变量

元组的统计计数:count,统计某个元素出现的次数

>a = (1,2,2,2,3,4,4,2)
>a.count(2)
4

元组和列表的区别:可变与不可变。

列表

增加与移除元素的方法

  1. 增加到尾巴:append
  2. 插入到任意位置:insert
  3. 移除元素:pop、
  4. 移除第一个符合的元素:remove

不考虑性能,可以使用append和remove,将python列表作为一个完全合适的数据结构

查找元素

  1. 查找元素是否在列表中:in
  2. 查找元素是否不在列别中: not in

列表的查找性能很差

列表与“字典和集合”相比,检查列表中是否有一个值,是非常缓慢的,这是因为python在列表中是线性逐个扫描的机制,而在集合和字典中是全局同时。

python数据结构 检查方式
列表 逐个扫描
字典、集合 全局扫描

性能更好的添加列表的方式

用“+”号可以添加列表内容,但因为这样链接过程创建了新的列表,是一种相对高代价的操作(内存占用),所以使用extend将元素加到已存在的列表是更好的方式。

排序:sort

  1. 正常引用sort用大小排序
  2. 给排序附加条件:(key=)
b.sort(ley = len) #按字符长度排序

使用二分搜索并将值插到合适的位置
bisect.bisect:找到元素应被插入的位置,保持排序
bisect.insort:将元素插入到对应位置

import bisect
> c = [1,2,2,2,3,4,7]
> bisect.bisect(c,2) # 排序2,显示应该排队位置在4。位置从0开始,此时并未插入元素
> 4
> bisect.bisect(c,5) # 5对应排序,显示应该排队位置在6,位置从0开始,此时并未插入元素
> 6
> bisect.insort(c,6) # 插入6,此时插入元素
> c
> [1,2,2,2,3,4,6,7]

需要注意:
bisect并不会检测列表是否自己排序,如果对未排序的列表使用bisect,他能用但结果不一定正确。

切片

切片基本上使用[start:stop]这种形式.。
负索引可以反着搜索。
步进值:[::2]

如果需要对元组或者列表进行翻转,可以将步进值传入-1:[::-1]

构建索引:enumerate(翻译成中文:列举、枚举)

for i,v in enumerate(list): # i是索引,v是值

比如我们要构建索引:

> some_list = ["foo","bar","baz"]
> mapping = {}
> for i,v in enumerate(some_list):
>	mapping[v] = i
> mapping
{"foo":1,"bar:2","baz":3}

按照顺序排列,然后出一个新序列:sorted

> sorted([7,1,2,2,6,0,3,2])
[0, 1, 2, 2, 3, 6, 7]
> sorted("horse race")
[' ', 'a', 'c', 'e', 'e', 'h', 'o', 'r', 'r', 's']

配对zip

> seq1 = ["foo","bar","baz"]
> seq2 = ["one","two","three"]
> zipped = (seq1,seq2)
> list(zipped)
[['foo', 'bar', 'baz'], ['one', 'two', 'three']]

zip处理的长度由最短的序列决定

> seq3 =[False,True]
> list(zip(seq1,seq2,seq3))

[('foo', 'one', False), ('bar', 'two', True)]

zip的使用场景:同时遍历多个序列

> for i,(a,b) in enumerate(zip(seq1,seq2)):
>    print("{0}:,{1},{2}".format(i,a,b))

0:,foo,one
1:,bar,two
2:,baz,three

zip的使用场景:行列置换

> people = [("ZQ","She"),("Kate","Liu"),("Sopu","Wu")]
> first_names,last_names = zip(*people)
> first_names

('ZQ', 'Kate', 'Sopu')

> last_names

('She', 'Liu', 'Wu')

倒序排序:reversed

reversed是一个生成器(生成器:没有实例的时候,是不会产生一个倒序的列表)

> list(reversed(range(10)))

[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

字典dict

{key:value} 键值对,添加

empty_dict = {}
d1 = {"a":"some value","b":[1,2,3.4]}
print(d1)
d1[7] = "an integer" #添加键值对
print(d1)

> {'a': 'some value', 'b': [1, 2, 3.4]}
> {'a': 'some value', 'b': [1, 2, 3.4], 7: 'an integer'}

只能搜索键,不能搜索值:in

"b" in d1

> True

删除的两种方式:del,pop

graph LR A("dict的删除")---B("del:直接删除") A---C("pop:pop方法删除的同时返回被删的值,并删除键")
d1[5] = "some value"
d1["dummy"]="another value"
print(d1)

> {'a': 'some value', 'b': [1, 2, 3.4], 7: 'an integer', 5: 'some value', 'dummy': 'another value'}

del d1[5] # 直接删除模式
d1
> {'a': 'some value','b': [1, 2, 3.4],7: 'an integer','dummy': 'another value'}

remo = d1.pop("dummy") #先存储返回值
d1 #然后才能删除
> {'a': 'some value', 'b': [1, 2, 3.4], 7: 'an integer'}

迭代器:返回的keys值,values值

list(d1.keys())
> ['a', 'b', 7]

list(d1.values())
> ['some value', [1, 2, 3.4], 'an integer']

d1.update({"b":"foo","c":12}) # update可以用来合并两个字典
d1
> {'a': 'some value', 'b': 'foo', 7: 'an integer', 'c': 12}
  • 排序会乱:update方法改变了字典中元素位置,
  • 会覆盖相同的:因此对于任何原字典中已经存在的键,如果传给update方法的数据也含有相同的键,则它的值将会被覆盖。

两个序列按元素配对

mapping ={}
for key,value in zip(key_list,value_list):
	mapping[key]=value  #构建字典

字典的本质是:含有两个元素的元组,基于这个原理,我们可以用“元组”的数据结构来构建“字典”

\[含有2个元素的元组=字典 \]

mapping={}
dict(zip(range(5),reversed(5))) #用两个元组构建字典
mapping
> {0:4,1:3,2:2,3:1,4:0} # 构建字典成功

还有一种字典推导式

设置默认值
第一种解题方法:通常情况下的代码逻辑:

if key in some_dict:
	value = some_dict[key]
else:
	value = default_value

第二种解题方法:get方法 和 pop方法

value = some_dict.get(key,default_value)

会在不是字典的时候发生的情况:

  1. get方法,返回none
  2. pop方法,抛出异常

你能想象一个场景,用一些字词组组成数据结构“列表”

words = ["apple","bat","bar","atom","book"]

接下来实现一个要求:1.分类

by_letter = {}
for word in words:
	letter = word[0]
	if letter not in by_letter:
	by_letter[letter] = [word] # 以首字母形成字典
	else:
	by_letter[letter].append(word) # 在字典内部添加

by_letter
> {"a":["apple","atom"],"b":["bat","bar","book"]}

实现分类更简单的方法

字典的setdefault方法,实现分类

for word in words:
	letter = word[0]
	by_letter.setdefault(letter,[]).append(word)

内建得集合模块类:defaultdict,实现分类功能

from collections import defaultdict
by_letter = defaultdict(list)    # 传入类型
for word in words:
	by_letter[word[0]].append(word)

字典中得“键"是不可变得对象:所以我引用了哈希值得概念,我们可以使用哈希函数让值变得唯一。

hash('string') # 字符串是唯一得
>  
hash((1,2,(2,3))) # 元组是唯一得
> 
hash((1,2,[2,3])) # 列表是可变得,所以会哈希失败
>

为了将列表作为键、一种方式就是将其转换成元组。元组内部可以哈希化,它就可以哈希化。

集合:set

集合得特性:无序、唯一、支持数学意义上得集合操作(交集、差集、并集、对成差集等)

创建方式:set函数、或者大括号{}。

set([2,2,2,1,3,3])
> {1,2,3}
{2,2,2,1,3,3}
> {1,2,3}

并集(集合得合并) :union 和符号 "|"

a = set([1,2,3,4,5])
b = set([3,4,5,6,7,8])
a.union(b)

> {1,2,3,4,5,6,7,8}

a | b
> {1,2,3,4,5,6,7,8}

交集:intersection 和符号"&"

a.intersection(b)
> {3,4,5}
a & b
> {a & b}

python集合得操作

函数 等效 作用
a.add(x) N/A 将元素x加入集合a
a.clear N/A 重置集合为空,清空所有元素
a.remove(x) N/A 从集合a中移除x元素
a.pop() N/A 移除任意元素,如果为空,则抛出keyError
a.union(b) a | b 并集
a.upadate(b) a | =b 将a所有得元素设置成b得并集
a.intersection(b) a & b 交集
a.inersection_update(b) a & =b 将a设置成b得交集
a.difference(b) a-b 在a不在b得元素
a.symmetric_difference(b) a ^ b 所有在a或b中,但不是同时在a、b中元素
a.symmetric_difference_update(b) a ^ =b 将a的内容设为所有在a或b中,但不是同时在a、b中的元素
a.issubset N/A 如果a包含于b返回True

代码效率的问题:效率更高的代码形式

c =a.copy()
c |=b
c

> {1,2,3,4,5,6,7,8}

d = a.copy()
d &= b
d
{3,4,5}

如何想包含列表型元素,必须转换成元组。

my_data= [1,2,3,4]
my_set = {tuple(my_data)}
my_set
> {(1,2,3,4)}

你可以检查一个集合是否是另一个集合的子集or超集

a_set = [1,2,3,4]
{1,2,3}.issubset(a_set)
> True

a_set.issuperset({1,2,3})
> True

当且仅当两个集合的内容一摸一样时,两个集合才相等

{1,2,3} == {3,2,1}
> True

python的语言特性,推导式:列表、集合、字典的推导式

  1. 允许你过滤一个容器的元素
  2. 用一种简明的表达式转换传递给过滤器的元素
  3. 从而生成一个新的列表
[expr for var in collection if conditon]

等价于

result = []
for var in collection:
	if condition:
		result.append(expr)

列表的推导式操作:[在这括号里面敲代码]

strings = ["a","as","bat","car","dove","python"]
[x.upper() for x in strings if len(x)>2]

> ["BAT","CAR","DOVE","PYTHON"]

集合与字典的推导式:是列表推导式的自然扩展
字典的推导式:{key:value的代码}

dict_dump = {key-expr : value-expr for value in collection if condition}

集合的推导式:{集合用大括号}

set_comp = {expr for value in collection if condition}

使用map函数获得类似的表达:

set(map(len,strings)) # 创建一个由strings变量的长度组成的集合

> {1,2,3,4,6}

map函数的作用:为指定序列做映射。

创建一个字符串与其位置相匹配的字典:

loc_mapping = {val:index for index,val in enumerate(strings)}
loc_mapping

> {"a":0,"as":1,"bat":2,"car":3,"dove":4,"python":5}

多层嵌套推导式

some_tuples = [(1,2,3),(4,5,6),(7,8,9)]
flattened = [x for tup in some_tuples for x in tup]

函数def

def my_function(x,y,z=1.5)

return
  • 特性:
    如果没有return,则自动返回None
    x和y是位置函数,z是关键字函数
    函数的主要限制是关键词参数,必须在位置参数后

全局变量 global,通常全局变量用来存储系统中的某些状态,如果你发现你大量使用了全局变量,可能你需要面向对象编程(使用类)

返回多个值:数据分析中经常需要返回多个值,用来干啥不知道

函数是对象:用来去除空格、移除标点符号、调整大小写等

利用正则表达式,这章节有个非常华丽的操作:
我们正常使用使用正则表达式来去除空格、移除标点符号、调整大小写的操作步骤

  1. 去除空格: strip()
  2. 移除标点符号:re.sub("[! #? ]","",value)
  3. 调整大小写:title()
import re
def clean_strings(strings):
	result=[]
	for value in strings:
		value = value.strip()  # 1
		value = re.sub("[! #? ]","",value) # 2
		value = value.title() # 3
		result.append(value)
	return result

一个非常华丽的操作:用一个列表来代替上面的1,2,3步骤

def remove_punctuation(value):
	return re.sub("[! #? ]","",value) # 设置一个去除标点的函数,方便下文引用

clean_ops = [str.strip,remove_punctuation,str.title] # 列表代替操作。

def clean_strings(strings,ops):
	result = []
	for value in strings:
		for function in ops:
			value = function(value)
		result.append(value)
	return result

以上模式我们撑之为 函数化,在更高层次上,使clean_strings函数更具有 复用性、通用性,接下来我们可以更进阶一点的。

for x in map (remove_punctuation,states):
	print(x)

将函数传给map函数作为参数

for x in map(remove_punctuation,states):
	print(x)

匿名函数lamda

匿名函数:

  1. 一种通过单个语句生成函数的方式,其结果是返回值。
  2. 使用lamda关键字定义
  3. 仅拜倒“我们声明一个匿名函数”的意思
def short_function(x):
	return x*2
equiv_anon  = lamda x: x*2
def apply_to_list(some_list,f):
	return [f(x) for x in some_list]
ints = [4,0,1,5,6]

apply_to_list (ints,lambda x:x * 2)

# 也能写成这样的
[X*2 for x in inits]

根据字串中不同字母的数量对一个字符串集合进行排序

strings = ["foo","card","bar","aaaa","abab"]
strings.sort(key = lambda x:len(set(list(x)))) #将一个匿名函数传给列表的sort方法
strings
> ["aaaa","foo","abab","bar","card"]

和def关键字声明的函数不同,匿名函数对象自身并没有一个显示_name_属性,这是lambda函数被称为匿名函数的一个原因

柯里化:部分参数应用

以数学家(Haskell Curry)命名,通过部分参数应用的方式从已有的函数中衍生出新的函数。

def add_number(x,y):
	return x+y

使用上述函数,我们可以衍生出一个只有一个变量的新函数add_five

add_five = lambda y : add_numbers(5,y)

第二个参数对于函数add_numbers就是柯里化了,这里什么神奇的地方,只是定义了一个新函数,而且这个新函数调用了已经存在的函数。

from functools import partial
add_five = partial (add_numbers,5)

生成器

在认识生成器之前,我们需要了解迭代器

迭代器的作用:

  1. 用于在上下文中,向python解释器生成对象的对象。
  2. 在python语言中,一切都是对象,迭代器本身也是对象

一个 for循环的逻辑,迭代器、生成器相继出场。

graph LR A("for循环") B("python解释器") C("生成一个迭代器") D("生成对象") A-->B-->C--->D

生成器是啥?
生成器:是构造新的可遍历对象的一种非常简洁的方式。

普通函数 生成器
执行并一次返回单个结果 返回一个多结果的序列

生成器函数的特点:

1.暂停:在每一个元素产生之后暂停
2.下一个请求:暂停直到下一个请求
3. yield:如需创建一个生成器,只需要将函数中的return 替换成 yield

def squares(n=10):
	print("Generating squares from 1 to {0}".format(n**2))
	for  i in range(1,n+1):
		yield i**2

当你实际调用生成器时,代码并不会立即执行

gen = squares()
gen

> 结果会报错  <generator object squares at 内存地址>

直到请求生成器中的元素,它才会执行它的代码。

graph LR A("for的一次请求") B("接收到for的请求") C("调用生成器") D("执行代码") A-->B-->C-->D
for x in gen:
	print(x,end=" ")

> Generating squares from 1 to 100 1 4 9 16 25 36 49 64 81 100

创建生成器表达式

生成器表达式,只需要将列表推导式的中括号"[]"替代为小括号"()"即可

gen = (x ** 2 for x in range(100))
gen
> <generator object <genexpr> at 内存地址>

等价:

def _make_gen():
	for x in range(100):
		yield x ** 2
gen = _make_gen()

很多情况下,生成器表达式可以用来替换列表推导式:

sum (X ***2 for  x in range(100))
>
dict((i,i **2) for i in range(5))
>

标准库中的itertools模块

itertools模块适用于大多数数据算法的生成器集合。
groupby可以根据任意的序列和一个函数,通过函数的返回值对序列中连续的元素进行分组。

import itertools
first_letter = lambda x:x[0]
names = {"Alan","Adam","Wes","Will","Albert","Steven"}
for letter,names in itertools.groupby(names,first_letter):
	print(letter,list(names)) # names is a generator

> A ["Alan","Adam"]
> W ["Wes","Will"]
> A ["Albert"]
> S ["Steven"]
函数 描述
combinations(iterable,k) 根据iterable参数中的所有元素,生成一个可能的序列,忽略顺序
permutations(iterable,k) 根据iterable参数中所有元素按顺序生成包含所有可能K元组的序列
groupby(iterable[,keyfunc]) 根据每一个独一的key生成(key,subiterator)元组
product(*iterable,repeat=1) 以元组的形式,根据输入的可遍历对象胜场笛卡尔积、与嵌套的for循环类似

错误处理和异常处理

def attempt_float(x):
	try:
		return float(x)
	except:
		return x

比如我只想处理ValueError:

def attempt_float(x):
	try:
		return float(x)
	except ValueError:
		return x

比如我想处理多个错误

def attempt_float(x):
	try:
		return float(x)
	except (TypeError,ValueError):
		return x

比如我想让一部分代码无论try是否报错都要执行

f = open(path,"w")
try:
write_to_file(f)
finally:
f.close()

这样我们可以让程序结束后总是关闭

f = open(path,"w")
try:
	write_to——file(f)
except:
	print("Failed")
else:
	print("successeded")
finally:
	f.close()

Ipython中的异常

graph LR A("%run 执行一个脚本")--报错-->B("Ipython会默认打出完整的调用堆栈跟踪(报错追溯)")

Ipython解释器 比 普通python解释器 高级的地方

  1. Ipython解释器提供更多的额外的上下文(报错信息),可用%mode命令来控制上下文数量呢!
  2. 可以从plain(普通)模式切换到Verbose(复杂)模式
  3. 在错误发生后,进入异常堆栈(使用debug或者pdb命令)进行交互式事后调试

python中处理数据文件,不用pandas的情况

打开文件进行读取、写入

graph LR A("两种路径") B("相对路径") C("绝对路径") J("三种文件打开方式") D("只读模式:r") E("写入模式:w") I("x模式:x") F("关闭文件") G("显式关闭:close") H("执行后关闭:with:</br>使用with代码块的会在结束后自动关闭</br>") L("将文本写入文件") M("write") N("writelines") A---B A---C J---D J---E J---I F---G F---H L---M L---N
python的文件模式 描述
r 只读模式
w 只写模式,重名文件则被删除
x 只写模式,存在重名则创建失败
a 添加到已存在的文件,文件不存在就创建
r+ 读写模式
b 二进制文件模式,一般需要添加到别的模式中:“rb”,“wb”
t 文件文本模式,默认解码编码“Unicode”,可指定其他编码,可添加其他模式中:“rt”,“wt”

对于可读文件,常用的方法:read、seek、tell

原理 概述
read 通过读取字节数来推进文件句柄的位置
tell 给出句柄当前的位置
# 文件里面的内容是:01234567890
f = open(path)
f.read(10)
> "01234567890"

f2 = open(path,"rb") # binary mode
f2.read(10)
> 

有时候会出现编码格式引起的变化,尽管读取了10个字符,但当前句柄位置为11。

f.tell()
>11

f2.tell()
>10

这是因为默认编码下需要那么多字节(11)对10个字母进行解码。可以用sys模块来检查文件的默认编码

# 检查默认编码
import sys
sys.getdefaultenoding()
>"utf-8"

seek可以将句柄位置改变到文件中特定的字节

f.seek(3)
> 2

f.read(1)
> 
"2"

#最后去,请牢记,关闭文件
f.close()
f2.close()

Numpy

Numpy的效率比python块100倍,且占用内存更小

多维数组:Numpy ndarray

读作:n-darray

import numpy as np
# 生成随机数组
data = np.random.randn(2,3)
data
>

# 给data加上一个数学操作
data * 10
data + data
#方阵数学

一个ndarray是一个通用的多维同类数据容器,也就是说,它包含的每一个元素均为相同类型。每一个数组都有一个shape属性,用来表征数组每一维度的数量;每一个数组都有一个dtype属性,用来描述数组的数据类型。

属性 描述
shape 数组每一维度的数量(多少行,多少列)
dtype 描述数组的数据类型

生成ndarry

生成数组最简单的方式就是使用array函数

data1 = [6,7.5,8,0,1]
arr1 = np.array(data1)
arr1

> array([6.,7.5,8.,0.,1.])

同等长度的列表会自动转换多维数组

data2 = [[1,2,3,4],[5,6,7,8]]
arr2 = np.array(data2)
arr2
> array([[1,2,3,4],[5,6,7,8]])
# 利用ndim,shape来确认这一点特性。
arr2.ndim
> 2
arr2.shape
> (2,4)

除非显式指定,不然np.array会自动推断生成数组的数据类型。

arr1.dtype
> dtype("float64")

arr2.dtype
> dtype("int64")

给定长度和形状后,

  1. zeros可以一次性创造全0数组。
  2. ones可以一次性创造全1数组。
  3. empty则可以创建一个没有初始化数值的数组。
  4. 想要创建高维数组,则需要为shanpe传递一个元组。
np.zeros(10)
> array([0.,0.,0.,0.,0.,0.,0.,0.,0,.0])

np.zeros((3,6))
> ([0.,0.,0.,0.,0.,0.],[0.,0.,0.,0.,0.,0.],[0.,0.,0.,0.,0.,0.])

np.empty((2,3,2))
array([[0.,0.,0.],[0.,0.,0.],[0.,0.,0.]],[[0.,0.,0.],[0.,0.,0.],[0.,0.,0.]])

用empty来生成一个全0数组,并不安全,有些时候他可能会返回未初始化的垃圾数值。

arange是python内建函数range的数组版,是内建函数不是ndarry函数。

np.arange(15)
array([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14])

不公平并不意味着你有绝对正确

函数名 描述
array
asarray 将输入转换成ndarry,如果已经是ndarry则不再复制
arange python内建函数

ndarray的数据类型

  1. dtype:
graph LR A("一个特殊对象,包含") B("内存块信息") C("元数据") A---B A---C
  1. dtype是Numpy能够与其他系统数据灵活交互的原因。比如C和Fortran
  2. 当你需要在内存或者硬盘上做更深入的存取操作时,尤其是大数据集时,你才真正需要了解存储的数据类型
类型 类型代码 描述
int8,uint8 i1,u1 有符号和无符号的8位整数
int16,uint16 i2,u2 有符号和无符号的16位整数
int32,uint32 i4,u4 有符号和无符号的32位整数
int64,uint64 i8,u8 有符号和无符号的64位整数
float16 f2
float32 f4或f 标准单精度浮点数,兼容C语言float
float64 f8或d 保准双精度浮点数,兼容C语言double和python float
float128 f16或g 拓展精度浮点数
complex64 c8 基于32位浮点数的复数
complex128 c16 基于64位浮点数的复数
complex256 c32 基于128位浮点数的复数
bool ? 布尔值,存储True或False
object O python object类型
string_ S 修正ASC Ⅱ字符串类型,例如:生成一个长度为10的字符串类型,使用“S10”
unicode_ U 修正Unicode类型,例如:生成一个长度为10的Unicode类型,使用“U10”

转换数据类型:astype
astype会生成一个新的数组。

arr = np.array([1,2,3,4,5])
arr.dtype
> dtype("int64")  # 原本的数据类型是整数

float_arr = arr.astype(np.float64) # 将整数转换成浮点数
float_arr.dtype
> dtype("float64") 

“常规” 转换成 ”数字“

numeric_strings = np.array(["1.25","-9.6","42"],dtype = np.string_)
numeric_strings.astype(float)

> array([ 1.25,-9.6,42. ])

某些原因会导致转换失败
比如:用python内建的float代替np.float64,因为字符串转换成float64位,将会抛出ValueError

格式刷:使用另一组数的dtype

int_array = np.arange(10)
calibers = np.array([.22,.270,.357,.380,.44,.50],dtype = np.float64)
int_array.astype(calibers.dtype)
> array([0.,1.,2.,3.,4.,5.,6.,7.,8.,9.])

向量化:Numpy允许批量操作,无需任何for循环。

  1. 标量计算得问题:
    能不能对单独某个列或者行进行标量计算

  1. 标量布尔运算,例如大于小于等于,返回True or False。

  2. 广播特性:不同尺寸的数组间的操作需要用到广播特性。

Numpy基础索引和切片

数据来源 概念区别
python内建列表 理论上它产生的切片是一个复制
Numpy数组索引 它产生的切片是一个视图,对视图的任何修改都会体现在原数组上
arr = np.arange(10)
arr[5:8] = 12
arr

> array([0,1,2,3,4,12,12,12,8,9])

arr_slice = arr[5:8]
arr_sliece
> array([12,12,12])

# 不写切片值得[:]将会引用数组得所有值。
arr[:] = 0
arr

> array([0,0,0,0,0,0,0,0,0,0])

在一个二维数组中,每一个索引值对应得元素不再是一个值,而是一个一维数组:

arr2d = np.array([1,2,3],[4,5,6],[7,8,9])
arr2d[2]

> array([7,8,9])

因此,单个元素可以通过递归得方式获得。

一维:只有一行

参数 数据1 数据2 数据3...
0 1 2 3

二维:

0 1 2 3
0 0,1,2 2,0,3 4,5,7
1 1,0,2 3,5,7 7,7,7
2 9,7,6 5,5,4 3,0,0

三维:

arr3d = np.array([[1,2,3],[4,5,6]] , [[7,8,9],[10,11,12]])

arr3d[1,0] #对索引值的探索。
 
> array([7,8,9])

数组的切片索引

arr2d[:2] #数组沿着0轴进行切片,含义是选择arr2d的前两“行”
arr2d[1,:2] #选择第二行但是只选择前两列
arr2d[:2,2] #选择第三列,但是只选择前两行

索引切片:下索引-->上索引,少一个位置。

布尔索引

  1. 有一组人名,我们要选择所有叫做“Bob”的行。
  2. 首先生成一个含盖人名的数组names,然后生成随机数data
names = np.array(["Bob","Joe","Will","Bob","Will","Joe","Joe"])
data = np.random.randn(7,4)  # randn生成随机正态分布数据,数据7行4列
  1. 假设data和names一一对应
  2. 选中所偶“Bob”的行,这个操作并没有关联data的数据,所以要看第6步
names = "Bob"
  1. 这个时候Numpy会产生一个新数组,这个数组就是布尔数组。
# 用于比较names数组和字符串“Bob”
array([True,False,False,True,False,False,False],dtype = bool)
  1. 然后往data数组中传入布尔值进行索引
data [names == "Bob"]

> array ([[xxx,xxx,xxx,xxx],[xxx,xxx,xxx,xxx]])

1. 布尔数组的长度必须和数组轴索引长度一致。虽然不一致并不会报错,但会出问题
2. 可以用切片或者整数值对布尔值进行混合和匹配

索引Bob行,并切片想要的列

data [names == "Bob",2:] #Bob行,从2号索引列后面所有的列
data [names == "Bob",3] #Bob行,筛选3号列

选择除Bob以外的其他数据

  1. 对条件取反
  2. "~"表示对通用条件取反
names  != "Bob"
data = [~(names == "Bob")]

选择“多个”时,对多个布尔值进行联合,“& (and)”、“| (or)”

mask = (namse == "Bob") | (names == "Will")
mask
array([True,False,True,True.True,Fasle,False],dtype = bool)

data[mask]

1. 使用布尔值索引选择数据,总是生成数据的拷贝
2. Python的关键字and和or对布尔值数组并没有用,需要用“&”和“|”来替换

将data中所有的负值设置成“0”

data[data < 0 ] = 0

Numpy的专业术语:神奇的索引,利用数组对Numpy数组进行索引

  1. 用于描述使用整数数组进行数据索引
    arr = np.empty([8,4])
  2. 符合特定顺序的子集
# 假设有一个8*4的数组
arr = np.empty((8,4))
for i in range(8):
	arr[i] = i

# 这里面都是索引值
arr[[4,3,0,6]]

# 最后索引出数列
> array([[4,4,4,4],
[3,3,3,3],
[0,0,0,0],
[6,6,6,6]])

多个索引数组时的情况。
生成一个8*4的矩阵

arr = np.arange(32).reshape((8,4))

arr[[1,5,7,2],[0,3,1,2]] # 一维
arr[[1,5,7,2]] [:,[0,3,1,2]] # 二维
索引值 0 1 2 3
0 0 1 2 3
1 4 5 6 7
2 8 9 10 11
3 12 13 14 15
4 16 17 18 19
5 20 21 22 23
6 24 25 26 27
7 28 29 30 31

对应举证切片的对应单元格,索引值,0开始。

代表值
1 0 4
5 3
7 1
2 2

不考虑数组的维数(例子中是二维),神奇索引的结果总是一维的。在这样的背景下,我们要切出一个二维数组的操作。
首先arr[[1,5,7,2]]索引了二维数组中的1752四行,而arr[:,[0,3,1,2]]则表示,冒号“:”表示索引出全部行,而[0,3,1,2]对应索引出来的列。

切片解释的插图

数组转置和换轴

  1. 转置是一种特殊的数据重组形式

通用函数:ufunc,在ndarray中进行逐元素操作的函数

使用数组表达式来实现多种数据操作任务,来代替显示循环的方法。

线性代数:方阵数学问题

伪随机数数生成

随机漫步,随机能否可控?

编制模块

``
posted @ 2023-08-09 17:48  SheZQ  阅读(24)  评论(0编辑  收藏  举报