王大龙的小站

要战胜的是过去的自己。


  • 首页

  • 归档

  • 关于

  • 标签

  • 分类

kaggle的提高得分技巧

发表于 2020-05-07 | 分类于 面试准备 , 比赛

交流才是提高的真正trick。

这个trick简单来说就是融合不同模型的得分。这个trick在深度学习中其实也有体现,那就是双流网络。将不同模型的输出(得分)融合,最终的结果考虑了多种输出。这里主要记录一下实现(融合得分)的过程。

1.读取两个要融合的提交文件。这里v3是课程中提供的baseline,v1是M5的notebook中得分较高的kernel,两者的得分都在0.47左右。通过融合,得分可以提高到0.465。

1
2
3
4
import pandas as pd
import numpy as np
v3 = pd.read_csv('submissionV3.csv')
v1 = pd.read_csv('submission_V1.csv')

2.由于两个文件的id索引的顺序不同,要排序其中一个文件。这里cat.reorder_categories是临时设置的排序规则,相当于自定义排序。

1
2
3
4
5
6
7
8
# 设置成“category”数据类型
v3['id'] = v3['id'].astype('category')
list_custom = list(v1['id'])
# inplace = True,使 recorder_categories生效
v3['id'].cat.reorder_categories(list_custom, inplace=True)
# inplace = True,使 df生效
v3.sort_values('id', inplace=True)
v3.index = list(range(0,60980))

pandas 中 inplace 参数在很多函数中都会有,它的作用是:是否在原对象基础上进行修改

inplace = True:不创建新的对象,直接对原始对象进行修改;

inplace = False:对数据进行修改,创建并返回新的对象承载其修改结果。

默认是False,即创建新的对象进行修改,原对象不变,和深复制和浅复制有些类似。

注意这里index要重新修改,否则后面进行加法操作的时候,它按照index进行相加,如果没改的话就直接乱套了

3.新建一个新的DataFrame,并执行均值输出。

1
2
3
4
v2 = pd.DataFrame(np.random.randn(60980,29),columns=v1.columns)
v2['id'] = v1['id']
for i in range(1,29):
v2[f"F{i}"] = (v1[f"F{i}"]+v3[f"F{i}"])/2

4.最后将得到的结果保存

1
v2.to_csv('submissionv2.csv')

kaggle课程记录二-自回归建模

发表于 2020-05-07 | 分类于 面试准备 , 比赛

1.时间序列基础

分成点预测和区间预测。

点预测是准确(定量)预测,区间预测是有置信度的预测。

什么时间序列可预测?

1.我们知道哪些因素会影响时间序列。

2.有大量的数据是可用的。

3.预测不会反向影响我们试图预测的事物。(能够预测股票的万能机器)

什么时间序列可定量建模?

1.关于过去的数据是可用的。

2.有理由假设过去的一些模式会在未来延续下去。

建模方式

1.回归模型

当下时刻的预测变量由当下时刻的其他变量所决定。

这里的误差包含两部分,一部分是随机波动,另一部分是没有被其他变量所解释的信息。

2.自回归模型

对未来的预测是基于变量的过去值,而不是基于可能影响系统的外部变量。“误差”项允许

随机波动和不包含在模型中的相关变量的影响。

3.动态回归模型

加入了日期以及价格等信息。

名词解释

1.趋势性(trand)

当一个时间序列数据⻓期增⻓或者⻓期下降时,表示该序列有 趋势 。在某些场合,趋势 代表着“转换方向”。例如从增⻓的趋势转换为下降趋势。

2.季节性(season)

当时间序列中的数据受到季节性因素(例如一年的时间或者一周的时间)的影响时,表示 该序列具有 季节性 。季节性总是一个已知并且固定的频率。注意,季节性可能是复合 的。

3.周期性

当时间序列数据存在不固定频率的上升和下降时,表示该序列有 周期性 。这些波动经常 由经济活动引起,并且与“商业周期”有关。

4.滞后

在时间序列中,我们经常会去研究当下时刻的 y**t 和以前的某个数的关系。

y*t−*k 称为
y**t 的 k 阶滞后,记为

lag。

5.自相关

$r_k$ 表示的是 $y_t$ 和他的 k 阶滞后的相关程度。 $r_k$ 称为 ACF (Autocorrelation Coefficient)

通过分析可以看到t和t-7,以及t-14相关性较高。随着时间的远离,相关性会降低。

6.偏相关性

我们有时候需要排除掉这种间接的关系。想去研究 y**t 和
y*t−*k 是否有直接的关系。这个就是偏自相关性。

7.平稳性

是一个前提的假设条件。

这个定理就是时间序列遍历性定理。所以如果一条时间序列满足遍历性,我们就可以通过 对一条轨道在时间上有限样本来估计一些性质。那如何保证时间序列具有遍历性呢? 那 就是这条时间序列是平稳的,且任意两点的相关性随着间隔的⻓度会逐渐降低。

简单点说,平稳的时间序列的性质不随观测时间的变化而变化,比如方差和均值。 所以平稳的时间序列,一定是没有趋势和季节性的。

判断时间序列平稳的方法有两种,一种是通过看图大概判断。一种是严格的通过统计方法 来判别。

1.看原始时序图,看acf和pacf的图,相关性会快速下降到0附近。

2.单位根检验

p 值。p 越小代表代表我们拒绝原假设,那么该时间序列就越该平稳(一般 是threshold 取0.05 或者0.01)

8.白噪声

对所有时间其自相关系数为零的随机过程

2.简单规则模型

###均值法预测

###朴素预测法

季节性朴素预测法

飘移法

我们考虑到时间序列具有趋势性。

数据调整

考虑到其他的因素影响,通货膨胀、人口增长

3.时间序列分解

当我们想要把时间序列分解为多个成分时,我们通常将趋势和周期组合为“趋势-周期”项 (有时也简单称其为趋势项)。因此,我们认为时间序列包括三个成分:趋势-周期项, 季节项和残差项(残差项包含时间序列中其它所有信息)。

后面我们假设处理的都是加法模型。

首先估计出趋势周期项

将原始时间序列减去趋势周期项

估计季节项

如何估计出趋势周期项?

移动平均估计趋势
$$
\hat{y}{t}=\frac{1}{m} \sum{i=-k}^{k} y_{t+k}
$$
m 为窗口的大小,是为一个奇数 m = 2k + 1。

STL时间序列分解

就是分解成趋势、周期(季节)和残差。

用得分来度量趋势和季节程度。(启发:将时间序列划分成两个部分,并分别来进行训练)

4.指数平均模型

这就是一种加权的移动平均模型。就是离当下时刻越远,则他的权重就越小。

一阶指数平滑模型

预测

二阶指数平滑模型

趋势

三阶指数平滑模型

季节

三阶指数模型就是出名的 holt-winter 模型。这个模型可以考虑时间序列的趋势,季节 性。是一个很常用的指数模型。

5.自回归建模

最出名的自回归模型就是 ARIMA 模型。前提条件是时间序列是弱平稳的。

AR(auto correlation)模型

$$
y_{t}=c+\phi_{1} y_{t-1}+\phi_{2} y_{t-2}+\cdots \phi_{p} y_{t-p}+\epsilon_{t}
$$

基于目标变量历史数据的组合对目标变量进行预测。

MA(moving average)模型

$$
y_{t}=c+\epsilon_{t}+\theta_{1} \epsilon_{t-1}+\theta_{2} \epsilon_{t-2}+\theta_{3} \epsilon_{t-3} \cdots \theta_{q} \epsilon_{t-q}
$$

通过历史的误差来建立一个类似回归的模型。

ARIMA模型

ARIMA 模型就是把 AR 模型和 MA 模型合起来,并通过差分的方式,使得不平稳的时间

序列变平稳。( I 表示 Integrated 在这里指差分的逆过程)。
$$
y_{t}=c+\phi_{1} y_{t-1}+\phi_{2} y_{t-2}+\cdots \phi_{p} y_{t-p}+\epsilon_{t}+\epsilon_{t}+\theta_{1} \epsilon_{t-1}+\theta_{2} \epsilon_{t-2}+\theta_{3} \epsilon_{t-3} \cdots \theta_{q} \epsilon_{t-q}
$$
以上模型称为 ARIMA 模型,简记为 ARIMA(p, d, q), p 表示 AR 部分的阶数。 q 表示

MA 部分的阶数。 d 表示差分的阶数。 构建 ARIMA 模型的三个步骤:

做差分,使得时间序列平稳 估计阶数 p 和 q 训练模型,学习参数

通过 acf 和 pacf 可以快速的确定 p 和 q。

缺点:无法做季节性检测

SARIMA模型

引入季节性。

6.层次时间序列

所谓层次时间序列,是指我们有很多的底层的时间序列。这些底层的时间序列可以通过聚 合,形成一个层次结构。

这次的M5就是一个层次时间序列

下面是一种快速聚合的方法。

三种预测方案:

1.自下而上的预测

核心在于预测准每一条底层的时间序列。

2.自上而下的预测

我们得到聚合的时间序列(全部聚合得到一条),并对该时间序列进行预测 建模。将预测的值通过某种方式分配给底层的时间序列。根据分配方式的不同,有不同的 实现方案。

如何分配?

使用历史平均比例。

通过遍历历史值,得到每一条底层时间序列占这条总的时间序列的比值。

对分配比例进行预测

通过模型,去预测底层时间序列占这条总的时间序列比值的变化情况。

3.自中向上下的预测

我们可以选择一个中间聚合的时间序列进行预测。比如按店 铺,甚至是按商品品类。因为过多的聚合,会导致信息的丢失,所以我们可以选择一个中 间层次的聚合时间序列进行预测,然后自上自下进行展开预测。

enlighten

将样本分别预测,也就是用不同的模型去预测不同的样本。这其实有点投机取巧了,但是为了提高分数,这没什么不可以的。那我觉得可以针对每一个样本计算它的趋势。也就是趋势是和每一个样本相关的。有30490个样本,那么就有30490个趋势。这个参数是与样本有关的。就和2s-AGCN中的动态图一样。(用样本数据计算得到的)。

链表问题

发表于 2020-05-04 | 分类于 面试准备 , 程序员面试指南

冲冲冲。

链表问题

链表和队列的区别在于指针,而指针问题又会涉及到遍历,多指针等特别的解法,同时也可以结合其他的数据结构来解链表问题,例如回文链表等。

1.约瑟夫环问题

题目:这道题目的变体很多,但是剥去词藻,其本质就是约瑟夫环问题。有m个人,围成一个环,编号为 0、1、2、3、、、m-1,从第一个人开始循环报数,假设数到n的那个人出列,然后从下一个人继续数数,数到n出列,以此循环,最后那个人为胜利者,求胜利者的编号。

分析:这道题目可以暴力求解,就是构造一个带环的链表,然后模拟整个过程,最后剩下一个的时候就是得到了结果。但是我们这里采用另一种思路,其时间复杂度是O(n),(好像有O(logn)的解法)。其思想基于递归,有点像动态规划的状态转移。参考圆圈中最后剩下的数字。关键找到前一个过程和后一个过程的映射关系。以及关系等式的化简。首先定一个关于最后剩下的数字的函数f(n,m),在这n个数字中,第一个被删除的数字是k=(m-1)%n,在剩下的序列中,K+1排在前面,剩下的数字也是有一个关于最后剩下的数字的函数f‘(n-1,m),f(n,m)== f‘(n-1,m),接下来就是把剩下的数字做一个映射,使得最后剩下的数字逆映射到原来的序列中(映射定义为P,逆映射定义为P’=(x+k+1)%n。,带入x、k之后,我们可以得到一个递归公式,看代码吧。

1
2
3
4
5
6
7
8
9
10
11
import sys
sys.setrecursionlimit(1000000)
class Solution:
def LastRemaining_Solution(self, n, m):
# write code here
if n == 0:
return -1
elif n == 1:
return 0
else:
return (self.LastRemaining_Solution(n-1,m)+m)%n

2.判断回文问题

题目:判断一个链表是否是回文结构

分析:回文问题可以借助额外的数据结构来求解,改进算法也是基于此。将链表按顺序输入到一个栈中,然后再一一弹出,并对照原来的链表上的值。

1
2
3
4
5
6
7
8
9
10
11
def is_palindrome_1(head):
stack = []
cur = head
while cur is not None:
stack.append(cur)
cur = cur.next
while stack:
if stack.pop().val != head.val:
return False
head = head.next
return True

3.逆序链表

题目:将一个链表逆序

分析:需要靠指针来进行逆序操作,一共有三个指针,pre、next以及head(这是指向当前位置的指针,因为不用head了,就直接使用head,一般是用另一个别名来替代)。算是模板,就是组件,其他算法可以靠这个逆序来进一步完成。

1
2
3
4
5
6
7
8
9
10
if not head or head.next == None:
return head
pre = None
next = None
while head != None:
head.next = pre # 改变next的方向
next = head.next
pre = head
head = next # 调整指针的位置
return pre

4.链表环问题

题目:给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

分析:也是一个模板算法。在下一题中就有用到。这个算法分成三个部分,分别是找环中的某个点,计算环的长度,以及最后的找到环的入口节点。1.找环中的某点的思路就是用两个指针,一个慢指针,一个快指针。(慢指针走一步,快指针走两步)。当两个指针相遇时,一定是在环中的某个节点。2.计算环的长度就是直接标记一下第一步的那个节点,然后开始遍历,当再次遇到标记的节点时,走的步数就是环的长度。3.第三步则考虑从链表头节点开始走,绕过环一次后到达环入口的步数为m+n,m指的是从链表开头走到环入口一共走过的步数,n则是环的长度。我们让一个指针先走n步,然后再同步让另一个指针和这个指针一起走m步,当两个指针相遇时就是指向环入口的节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Solution:
def EntryNodeOfLoop(self, pHead):
# write code here
# 找到环,环如果不存在那就返回None
def FindLoop(pHead):
if not pHead:
return None
pHead1 = pHead
pHead2 = pHead.next
if pHead2 == None:
return None
while pHead1 and pHead2 and pHead1 != pHead2:
pHead1 = pHead1.next
pHead2 = pHead2.next
if pHead2 == None:
return None
else:
pHead2 = pHead2.next
return pHead1
# 计算环的长度
def CountLoop(head):
loopHead = head.next
res = 1
while loopHead != head:
loopHead = loopHead.next
res += 1
return res
loopNode = FindLoop(pHead)
if not loopNode:
return None
# 找到环的入口
loop_num = CountLoop(loopNode)
pTmp = pHead
for i in range(loop_num):
pTmp = pTmp.next
while pTmp != pHead:
pTmp = pTmp.next
pHead = pHead.next
return pTmp

5.链表相交的问题

题目:请实现一个函数没如果两个链表相交,请返回相交的第一个节点;如果不相交,返回None。

分析:在剑指里面也有这一道题,但是在分析中没有提到链表是否存在环的问题,这里需要做一个补充。首先需要明确的一点是,两个相交的链表要嘛都是有环的,要嘛就都没有环。所以问题就变成了三个:

1.如何判断一个链表是否有环,如果有,则返回第一个进入环的节点。这里就是参考问题4;

2.如果链表无环,那如何判断链表是否相交?这里参考剑指,简单来说就是用两个链表的长度差来同步两个链表的遍历进度,然后看两个指针是否会相遇。

3.如果链表有环,如何判断链表是否相交?这个是新的问题。

​ (1)如果loop1 == loop2,那么两个链表的拓扑结构如下所示:

在这种情况下,有点类似问题二,只不过,这里把loop当做是两个链表的终点。

​ (2)如果loop1 != loop2,那么有两种情况:

让链表从loop1出发,如果在回到loop1之前遇到loop2,那么说明两个链表相交,返回loop1和loop2其中一个就行;如果没有遇到,那就说明两个链表不相交。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 这里仅写第三种问题
def bothLoop(head1, node1, head2, node2):
if head1 == None or head2 == None:
return None
if node1 == node2:
cur1 = head1
cur2 = head2
n = 0
# 这里将cur1作为比较长的链表的头,cur2作为较短的链表的头
while cur1 != node1:
n += 1
cur1 = cur1.next
while cur2 != node1:
n -= 1
cur2 = cur2.next
cur1 = head1 if n >= 0 else head2
cur2 = head1 if cur1 == head2 else head2
n = abs(n)
while n != 0:
n -= 1
cur1 = cur1.next
while cur1 != cur2:
cur1 = cur1.next
cur2 = cur2.next
return cur1
else:
cur1 = node1.next
while cur1 != node1:
if cur1 == node2:
return node1
cur1 = cur1.next
return None

6.将搜索二叉树转换成双向链表

题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

分析:以根为中心的迭代算法,将根左边转换完成之后,连接到根(注意是左部分的最右边的节点连接到根),将根右边转换完成后也连接到根。若存在左边的部分,则返回指向左部分的第一个节点,否则返回根。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution:
def Convert(self, pRootOfTree):
# write code here
if not pRootOfTree:
return None
left = self.Convert(pRootOfTree.left)
p = left
while p and p.right:
p = p.right
if p:
pRootOfTree.left = p
p.right = pRootOfTree
right = self.Convert(pRootOfTree.right)
if right:
pRootOfTree.right = right
right.left =pRootOfTree
return left if left else pRootOfTree

kaggle的特征工程

发表于 2020-05-03 | 分类于 面试准备 , 比赛

进度略慢,打算边看代码,边看课程。不然跟不上进度了。现在感觉大多是用模型融合来做,就是baseline一个,然后那个0.4706的一个,权重自己取,注意id匹配的问题。

Pandas

发表于 2020-05-02 | 分类于 面试准备 , 比赛

今天更新一下Pandas的教程,同样还是莫烦老师的视频。Pandas是在numpy基础上发展来的包,可以简单理解为,numpy是数组,而Pandas是字典,为什么这么说呢,Pandas更像是数据库,行和列有对应的名称,所以你可以通过名称来索引某个数据,数据是有名字的,而不仅仅是坐标。一些操作,我感觉也和数据库超像,很惭愧,刚学数据库,我差不多都忘光了,这就是不常温习的后果(赶紧做完这个先刷几道题目)。Pandas分成:创建、索引、导入导出、操作(concat、merge)和绘图

1.创建

1.Series

就是一个列(由于没有制定index(就是行索引),所以默认是012··

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pandas as pd
import numpy as np
s = pd.Series([1,3,6,np.nan,44,1])

print(s)
"""
0 1.0
1 3.0
2 6.0
3 NaN
4 44.0
5 1.0
dtype: float64
"""

2.DataFrame

是一种表格形式的数据结构,每列可以是不同的值类型(注意单列的值类型尽量要一致),DataFrame方法中包含三个参数,第一个是一个array类型的矩阵,index是行索引,column是列索引。没有的话就默认。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
dates = pd.date_range('20160101',periods=6)
df = pd.DataFrame(np.random.randn(6,4),index=dates,columns=['a','b','c','d'])

print(df)
"""
a b c d
2016-01-01 -0.253065 -2.071051 -0.640515 0.613663
2016-01-02 -1.147178 1.532470 0.989255 -0.499761
2016-01-03 1.221656 -2.390171 1.862914 0.778070
2016-01-04 1.473877 -0.046419 0.610046 0.204672
2016-01-05 -1.584752 -0.700592 1.487264 -1.778293
2016-01-06 0.633675 -1.414157 -0.277066 -0.442545
"""


#另一种形式
df2 = pd.DataFrame({'A' : 1.,
'B' : pd.Timestamp('20130102'),
'C' : pd.Series(1,index=list(range(4)),dtype='float32'),
'D' : np.array([3] * 4,dtype='int32'),
'E' : pd.Categorical(["test","train","test","train"]),
'F' : 'foo'})

print(df2)

"""
A B C D E F
0 1.0 2013-01-02 1.0 3 test foo
1 1.0 2013-01-02 1.0 3 train foo
2 1.0 2013-01-02 1.0 3 test foo
3 1.0 2013-01-02 1.0 3 train foo
"""
可见,Pandas是以列索引为主的。

3.属性

1.查看类型:

1
2
3
4
5
6
7
8
9
10
11
12
print(df2.dtypes)

"""
df2.dtypes
A float64
B datetime64[ns]
C float32
D int32
E category
F object
dtype: object
"""

2.查看对列的序号:

1
2
3
print(df2.index)

# Int64Index([0, 1, 2, 3], dtype='int64')

3.查看列索引:

1
2
3
print(df2.columns)

# Index(['A', 'B', 'C', 'D', 'E', 'F'], dtype='object')

4.查看DataFrame的所有值(这个就是脱掉索引)这个很常见

1
2
3
4
5
6
7
8
print(df2.values)

"""
array([[1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'test', 'foo'],
[1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'train', 'foo'],
[1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'test', 'foo'],
[1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'train', 'foo']], dtype=object)
"""

4.查看数据的总结

1
2
3
4
5
6
7
8
9
10
11
12
13
df2.describe()

"""
A C D
count 4.0 4.0 4.0
mean 1.0 1.0 3.0
std 0.0 0.0 0.0
min 1.0 1.0 3.0
25% 1.0 1.0 3.0
50% 1.0 1.0 3.0
75% 1.0 1.0 3.0
max 1.0 1.0 3.0
"""

5.翻转数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
print(df2.T)

"""
0 1 2 \
A 1 1 1
B 2013-01-02 00:00:00 2013-01-02 00:00:00 2013-01-02 00:00:00
C 1 1 1
D 3 3 3
E test train test
F foo foo foo

3
A 1
B 2013-01-02 00:00:00
C 1
D 3
E train
F foo

"""

6.排序

排序分成两种,按index(索引)排序,和按值排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
print(df2.sort_index(axis=1, ascending=False))

"""
F E D C B A
0 foo test 3 1.0 2013-01-02 1.0
1 foo train 3 1.0 2013-01-02 1.0
2 foo test 3 1.0 2013-01-02 1.0
3 foo train 3 1.0 2013-01-02 1.0
"""

print(df2.sort_values(by='B'))

"""
A B C D E F
0 1.0 2013-01-02 1.0 3 test foo
1 1.0 2013-01-02 1.0 3 train foo
2 1.0 2013-01-02 1.0 3 test foo
3 1.0 2013-01-02 1.0 3 train foo
"""

2.索引

DataFrame的性质有点像Defaultdict,就是你给一个不存在的索引,他就会自动添加。当然会有点不一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
dates = pd.date_range('20130101', periods=6)
df = pd.DataFrame(np.arange(24).reshape((6,4)),index=dates, columns=['A','B','C','D'])

"""
A B C D
2013-01-01 0 1 2 3
2013-01-02 4 5 6 7
2013-01-03 8 9 10 11
2013-01-04 12 13 14 15
2013-01-05 16 17 18 19
2013-01-06 20 21 22 23
"""

# 选取数据
print(df['A'])
print(df.A)

"""
2013-01-01 0
2013-01-02 4
2013-01-03 8
2013-01-04 12
2013-01-05 16
2013-01-06 20
Freq: D, Name: A, dtype: int64
"""

#跨多行或多列
print(df[0:3])

"""
A B C D
2013-01-01 0 1 2 3
2013-01-02 4 5 6 7
2013-01-03 8 9 10 11
"""

print(df['20130102':'20130104'])

"""
A B C D
2013-01-02 4 5 6 7
2013-01-03 8 9 10 11
2013-01-04 12 13 14 15
"""

需要指出的一点是,不能直接给行和列索引来选定某一个值,需要用loc。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
print(df.loc['20130102'])
"""
A 4
B 5
C 6
D 7
Name: 2013-01-02 00:00:00, dtype: int64
"""

print(df.loc[:,['A','B']])
"""
A B
2013-01-01 0 1
2013-01-02 4 5
2013-01-03 8 9
2013-01-04 12 13
2013-01-05 16 17
2013-01-06 20 21
"""

print(df.loc['20130102',['A','B']])
"""
A 4
B 5
Name: 2013-01-02 00:00:00, dtype: int64
"""

如果忘记了名称怎么办?用iloc,这是根据序列来着。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
print(df.iloc[3,1])
# 13

print(df.iloc[3:5,1:3])
"""
B C
2013-01-04 13 14
2013-01-05 17 18
"""

print(df.iloc[[1,3,5],1:3])
"""
B C
2013-01-02 5 6
2013-01-04 13 14
2013-01-06 21 22

"""

还有一种和numpy很像的,就是通过判断来选取

1
2
3
4
5
6
7
print(df[df.A>8])
"""
A B C D
2013-01-04 12 13 14 15
2013-01-05 16 17 18 19
2013-01-06 20 21 22 23
"""

如果是单行或者是单列的情况的话,则可以不用loc,注意对行选取的话,要给定一个范围,有个小细节,如果给的是名称索引的话,是左闭右闭的;给的是序列索引的话,是左闭右开的。

1
2
3
4
5
6
7
8
9
10
11
12
df['F'] = np.nan
"""
A B C D F
2013-01-01 0 2222 2 3 NaN
2013-01-02 4 5 6 7 NaN
2013-01-03 8 0 1111 11 NaN
2013-01-04 12 0 14 15 NaN
2013-01-05 16 0 18 19 NaN
2013-01-06 20 0 22 23 NaN
"""
# 给定一个范围
print(df['2016-01-01':])

###处理丢失数据

1.如果想直接去掉右Nan的行或列,可以使用dropna

1
2
3
4
5
6
7
8
9
10
11
df.dropna(
axis=0, # 0: 对行进行操作; 1: 对列进行操作
how='any' # 'any': 只要存在 NaN 就 drop 掉; 'all': 必须全部是 NaN 才 drop
)
"""
A B C D
2013-01-03 8 9.0 10.0 11
2013-01-04 12 13.0 14.0 15
2013-01-05 16 17.0 18.0 19
2013-01-06 20 21.0 22.0 23
"""

2.将Nan的值用其他值替换,比如替换成0,使用fillna:

1
2
3
4
5
6
7
8
9
10
df.fillna(value=0)
"""
A B C D
2013-01-01 0 0.0 2.0 3
2013-01-02 4 5.0 0.0 7
2013-01-03 8 9.0 10.0 11
2013-01-04 12 13.0 14.0 15
2013-01-05 16 17.0 18.0 19
2013-01-06 20 21.0 22.0 23
"""

3.判断是否有缺失数据

1
2
np.any(df.isnull()) == True  
# True

3.导入导出

就两个操作嘛,导入和导出。导入就是pd.read_???,什么格式的文件就用什么方法。导出就是data.to_???,要输出什么格式的文件就用什么方法。

1
2
3
4
5
6
7
import pandas as pd #加载模块

#读取csv
data = pd.read_csv('student.csv')

#打印出data
print(data)

4.操作

我个人感觉是最难的一部分。

###1.concat(基本的合并方法)

参数:1.axis(合并方向):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pandas as pd
import numpy as np

#定义资料集
df1 = pd.DataFrame(np.ones((3,4))*0, columns=['a','b','c','d'])
df2 = pd.DataFrame(np.ones((3,4))*1, columns=['a','b','c','d'])
df3 = pd.DataFrame(np.ones((3,4))*2, columns=['a','b','c','d'])

#concat纵向合并
res = pd.concat([df1, df2, df3], axis=0)

#打印结果
print(res)
# a b c d
# 0 0.0 0.0 0.0 0.0
# 1 0.0 0.0 0.0 0.0
# 2 0.0 0.0 0.0 0.0
# 0 1.0 1.0 1.0 1.0
# 1 1.0 1.0 1.0 1.0
# 2 1.0 1.0 1.0 1.0
# 0 2.0 2.0 2.0 2.0
# 1 2.0 2.0 2.0 2.0
# 2 2.0 2.0 2.0 2.0

2.ignore_index(重置index):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#承上一个例子,并将index_ignore设定为True
res = pd.concat([df1, df2, df3], axis=0, ignore_index=True)

#打印结果
print(res)
# a b c d
# 0 0.0 0.0 0.0 0.0
# 1 0.0 0.0 0.0 0.0
# 2 0.0 0.0 0.0 0.0
# 3 1.0 1.0 1.0 1.0
# 4 1.0 1.0 1.0 1.0
# 5 1.0 1.0 1.0 1.0
# 6 2.0 2.0 2.0 2.0
# 7 2.0 2.0 2.0 2.0
# 8 2.0 2.0 2.0 2.0

3.join(合并方式),就两种方式outer就是取并集,inner就是取交集。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import pandas as pd
import numpy as np

#定义资料集
df1 = pd.DataFrame(np.ones((3,4))*0, columns=['a','b','c','d'], index=[1,2,3])
df2 = pd.DataFrame(np.ones((3,4))*1, columns=['b','c','d','e'], index=[2,3,4])

#纵向"外"合并df1与df2
res = pd.concat([df1, df2], axis=0, join='outer')

print(res)
# a b c d e
# 1 0.0 0.0 0.0 0.0 NaN
# 2 0.0 0.0 0.0 0.0 NaN
# 3 0.0 0.0 0.0 0.0 NaN
# 2 NaN 1.0 1.0 1.0 1.0
# 3 NaN 1.0 1.0 1.0 1.0
# 4 NaN 1.0 1.0 1.0 1.0

#重置index并打印结果
res = pd.concat([df1, df2], axis=0, join='inner', ignore_index=True)
print(res)
# b c d
# 0 0.0 0.0 0.0
# 1 0.0 0.0 0.0
# 2 0.0 0.0 0.0
# 3 1.0 1.0 1.0
# 4 1.0 1.0 1.0
# 5 1.0 1.0 1.0

2.merge

主要用于两组有key column的数据注意哦,这里是两组。

1.on(就是按什么key来合并,依据索引合并)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import pandas as pd

#定义资料集并打印出
left = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']})

print(left)
# A B key
# 0 A0 B0 K0
# 1 A1 B1 K1
# 2 A2 B2 K2
# 3 A3 B3 K3

print(right)
# C D key
# 0 C0 D0 K0
# 1 C1 D1 K1
# 2 C2 D2 K2
# 3 C3 D3 K3

#依据key column合并,并打印出
res = pd.merge(left, right, on='key')

print(res)
A B key C D
# 0 A0 B0 K0 C0 D0
# 1 A1 B1 K1 C1 D1
# 2 A2 B2 K2 C2 D2
# 3 A3 B3 K3 C3 D3

2.依据index合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import pandas as pd

#定义资料集并打印出
left = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
'B': ['B0', 'B1', 'B2']},
index=['K0', 'K1', 'K2'])
right = pd.DataFrame({'C': ['C0', 'C2', 'C3'],
'D': ['D0', 'D2', 'D3']},
index=['K0', 'K2', 'K3'])

print(left)
# A B
# K0 A0 B0
# K1 A1 B1
# K2 A2 B2

print(right)
# C D
# K0 C0 D0
# K2 C2 D2
# K3 C3 D3

#依据左右资料集的index进行合并,how='outer',并打印出
res = pd.merge(left, right, left_index=True, right_index=True, how='outer')
print(res)
# A B C D
# K0 A0 B0 C0 D0
# K1 A1 B1 NaN NaN
# K2 A2 B2 C2 D2
# K3 NaN NaN C3 D3

#依据左右资料集的index进行合并,how='inner',并打印出
res = pd.merge(left, right, left_index=True, right_index=True, how='inner')
print(res)
# A B C D
# K0 A0 B0 C0 D0
# K2 A2 B2 C2 D2

3.how 就是connect的join。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import pandas as pd

#定义资料集并打印出
left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'],
'key2': ['K0', 'K1', 'K0', 'K1'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
'key2': ['K0', 'K0', 'K0', 'K0'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']})

print(left)
# A B key1 key2
# 0 A0 B0 K0 K0
# 1 A1 B1 K0 K1
# 2 A2 B2 K1 K0
# 3 A3 B3 K2 K1

print(right)
# C D key1 key2
# 0 C0 D0 K0 K0
# 1 C1 D1 K1 K0
# 2 C2 D2 K1 K0
# 3 C3 D3 K2 K0

#依据key1与key2 columns进行合并,并打印出四种结果['left', 'right', 'outer', 'inner']
res = pd.merge(left, right, on=['key1', 'key2'], how='inner')
print(res)
# A B key1 key2 C D
# 0 A0 B0 K0 K0 C0 D0
# 1 A2 B2 K1 K0 C1 D1
# 2 A2 B2 K1 K0 C2 D2

res = pd.merge(left, right, on=['key1', 'key2'], how='outer')
print(res)
# A B key1 key2 C D
# 0 A0 B0 K0 K0 C0 D0
# 1 A1 B1 K0 K1 NaN NaN
# 2 A2 B2 K1 K0 C1 D1
# 3 A2 B2 K1 K0 C2 D2
# 4 A3 B3 K2 K1 NaN NaN
# 5 NaN NaN K2 K0 C3 D3

res = pd.merge(left, right, on=['key1', 'key2'], how='left')
print(res)
# A B key1 key2 C D
# 0 A0 B0 K0 K0 C0 D0
# 1 A1 B1 K0 K1 NaN NaN
# 2 A2 B2 K1 K0 C1 D1
# 3 A2 B2 K1 K0 C2 D2
# 4 A3 B3 K2 K1 NaN NaN

res = pd.merge(left, right, on=['key1', 'key2'], how='right')
print(res)
# A B key1 key2 C D
# 0 A0 B0 K0 K0 C0 D0
# 1 A2 B2 K1 K0 C1 D1
# 2 A2 B2 K1 K0 C2 D2
# 3 NaN NaN K2 K0 C3 D3

4.indicator,记录how的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import pandas as pd

#定义资料集并打印出
df1 = pd.DataFrame({'col1':[0,1], 'col_left':['a','b']})
df2 = pd.DataFrame({'col1':[1,2,2],'col_right':[2,2,2]})

print(df1)
# col1 col_left
# 0 0 a
# 1 1 b

print(df2)
# col1 col_right
# 0 1 2
# 1 2 2
# 2 2 2

# 依据col1进行合并,并启用indicator=True,最后打印出
res = pd.merge(df1, df2, on='col1', how='outer', indicator=True)
print(res)
# col1 col_left col_right _merge
# 0 0.0 a NaN left_only
# 1 1.0 b 2.0 both
# 2 2.0 NaN 2.0 right_only
# 3 2.0 NaN 2.0 right_only

# 自定indicator column的名称,并打印出
res = pd.merge(df1, df2, on='col1', how='outer', indicator='indicator_column')
print(res)
# col1 col_left col_right indicator_column
# 0 0.0 a NaN left_only
# 1 1.0 b 2.0 both
# 2 2.0 NaN 2.0 right_only
# 3 2.0 NaN 2.0 right_only

5.解决overlapping问题

就是解决索引名称冲突问题

1
2
3
4
5
6
7
8
9
10
11
12
import pandas as pd

#定义资料集
boys = pd.DataFrame({'k': ['K0', 'K1', 'K2'], 'age': [1, 2, 3]})
girls = pd.DataFrame({'k': ['K0', 'K0', 'K3'], 'age': [4, 5, 6]})

#使用suffixes解决overlapping的问题
res = pd.merge(boys, girls, on='k', suffixes=['_boy', '_girl'], how='inner')
print(res)
# age_boy k age_girl
# 0 1 K0 4
# 1 1 K0 5

5.Plot(只是其中一种形式)

这里matplotlib只是用来show图片的。

1
2
3
4
5
6
7
8
data = pd.DataFrame(
np.random.randn(1000,4),
index=np.arange(1000),
columns=list("ABCD")
)
data.cumsum()
data.plot()
plt.show()

numpy

发表于 2020-04-30 | 分类于 面试准备 , 比赛

数据科学的比赛,不了解numpy和pandas的话,简直是寸步难行。之前虽然有用过numpy和pandas的一些方法,但都没有系统学习过,所以还是决定要系统的过一遍。所以简单看了一下莫烦的教程。numpy库主要可以分成几个部分:属性、创建、运算、索引以及操作(合并与分割)。

1.属性

我们都知道,在数据科学中,我们主要的对象就是数据,那为了方便数据的管理与运输,我们又通常将他们转换为矩阵的形式,numpy操作的对象大部分就是矩阵。那作为矩阵一般都有什么属性,就很容易理解了。

1.ndim:维度,维度就是描述数据特征的特征数量,例如一系列的目标在二维空间中,怎么描述这些目标呢?当然需要行和列来描述了。这里维度就是2(行和列)。

2.shape:行数和列数

3.size:元素的个数

4.dtype:元素的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
array = np.array([[1,2,3],[2,3,4]])  #列表转化为矩阵
print(array)
"""
array([[1, 2, 3],
[2, 3, 4]])
"""
print('number of dim:',array.ndim) # 维度
# number of dim: 2

print('shape :',array.shape) # 行数和列数
# shape : (2, 3)

print('size:',array.size) # 元素个数
# size: 6

a = np.array([2,23,4],dtype=np.int)
print(a.dtype)
# int 64

2.创建

前面说到numpy主要操作的对象是矩阵,那我们在敲代码的时候肯定不能直接写:

1
a = np.矩阵((1,1))

哈哈哈,这里有个关键字嘛,就是array。在

1
2
3
4
5
6
7
8
a = np.array([2,23,4])  # list 1d
print(a)
# [2 23 4]

# 这里可以指定矩阵中元素的类型
a = np.array([2,23,4],dtype=np.int)
print(a.dtype)
# int 64

当然这是最普通的,还有一些简便的生成特殊矩阵的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 全零数组
a = np.zeros((3,4)) # 数据全为0,3行4列
"""
array([[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.]])
"""
# 全一数组
a = np.ones((3,4),dtype = np.int) # 数据为1,3行4列
"""
array([[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]])
"""
# 全空数组(每个值实际上都是接近零的数
a = np.empty((3,4)) # 数据为empty,3行4列
"""
array([[ 0.00000000e+000, 4.94065646e-324, 9.88131292e-324,
1.48219694e-323],
[ 1.97626258e-323, 2.47032823e-323, 2.96439388e-323,
3.45845952e-323],
[ 3.95252517e-323, 4.44659081e-323, 4.94065646e-323,
5.43472210e-323]])
"""

# 连续数组
a = np.arange(10,20,2) # 10-19 的数据,2步长
"""
array([10, 12, 14, 16, 18])
"""

#线段型数据(没错就是lin space??嗯?)
a = np.linspace(1,10,20) # 开始端1,结束端10,且分割成20个数据,生成线段
"""
array([ 1. , 1.47368421, 1.94736842, 2.42105263,
2.89473684, 3.36842105, 3.84210526, 4.31578947,
4.78947368, 5.26315789, 5.73684211, 6.21052632,
6.68421053, 7.15789474, 7.63157895, 8.10526316,
8.57894737, 9.05263158, 9.52631579, 10. ])
"""

3.运算

运算有分成对矩阵单个元素进行运算的,也有对某一个维度进行运算的,也有对整个矩阵进行运算的。

1
2
3
import numpy as np
a=np.array([10,20,30,40]) # array([10, 20, 30, 40])
b=np.arange(4) # array([0, 1, 2, 3])

1.单个元素:

1
2
3
4
5
6
7
c=a-b  # array([10, 19, 28, 37])
c=a+b # array([10, 21, 32, 43])
c=a*b # array([ 0, 20, 60, 120])
c=b**2 # array([0, 1, 4, 9])
# 逻辑判断,这个也挺经常用到
print(b<3)
# array([ True, True, True, False], dtype=bool)

2.某一个维度:(axis指定维度)

1
2
3
4
5
6
7
8
9
10
11
12
print("a =",a)
# a = [[ 0.23651224 0.41900661 0.84869417 0.46456022]
# [ 0.60771087 0.9043845 0.36603285 0.55746074]]

print("sum =",np.sum(a,axis=1))
# sum = [ 1.96877324 2.43558896]

print("min =",np.min(a,axis=0))
# min = [ 0.23651224 0.41900661 0.36603285 0.46456022]

print("max =",np.max(a,axis=1))
# max = [ 0.84869417 0.9043845 ]

3.整个矩阵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 矩阵乘积(一般的运算方法都有两种形式,不一一列举了)
c_dot = np.dot(a,b)
# array([[2, 4],
# [2, 3]])
c_dot_2 = a.dot(b)
# array([[2, 4],
# [2, 3]])


# 所有元素相加,取最值
np.sum(a) # 4.4043622002745959
np.min(a) # 0.23651223533671784
np.max(a) # 0.90438450240606416
np.argmin(a) # 返回最小值的索引
np.argmax(a) # 返回最大值的索引


# 取平均
print(np.mean(A)) # 7.5
print(np.average(A)) # 7.5


# 累差运算,计算的是每一行中后一项和前一项的差,故一个3行4列矩阵通过函数计算得到的矩阵便是3行3列的矩阵。
print(np.diff(A))


# nonzero是将所有非零元素的行与列坐标分割开,重构成两个分别关于行和列的矩阵。
print(np.nonzero(A))
# (array([0,0,0,0,1,1,1,1,2,2,2,2]),array([0,1,2,3,0,1,2,3,0,1,2,3]))


# 排序
A = np.arange(14,2, -1).reshape((3,4))

# array([[14, 13, 12, 11],
# [10, 9, 8, 7],
# [ 6, 5, 4, 3]])

print(np.sort(A))

# array([[11,12,13,14]
# [ 7, 8, 9,10]
# [ 3, 4, 5, 6]])


# 矩阵的转置
print(np.transpose(A))
print(A.T)

# array([[14,10, 6]
# [13, 9, 5]
# [12, 8, 4]
# [11, 7, 3]])
# array([[14,10, 6]
# [13, 9, 5]
# [12, 8, 4]
# [11, 7, 3]])


# 修建函数,将要被执行用的矩阵,而后面的最小值最大值则用于让函数判断矩阵中元素是否有比最小值小的或者比最大值大的元素,并将这些指定的元素转换为最小值或者最大值。
print(A)
# array([[14,13,12,11]
# [10, 9, 8, 7]
# [ 6, 5, 4, 3]])

print(np.clip(A,5,9))
# array([[ 9, 9, 9, 9]
# [ 9, 9, 8, 7]
# [ 6, 5, 5, 5]])

4.索引

和python的list索引比较像,就不展开了,说一下迭代输出吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import numpy as np
A = np.arange(3,15).reshape((3,4))

print(A.flatten())
# array([3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])

for item in A.flat:
print(item)

# 3
# 4
……
# 14
flat 返回的是迭代器,而flatten返回的是一个数列

5.操作

合并

1.np.vstack()

1
2
3
4
5
6
7
8
9
import numpy as np
A = np.array([1,1,1])
B = np.array([2,2,2])

print(np.vstack((A,B))) # vertical stack
"""
[[1,1,1]
[2,2,2]]
"""

2.np.hstack()

1
2
3
4
5
6
7
D = np.hstack((A,B))       # horizontal stack

print(D)
# [1,1,1,2,2,2]

print(A.shape,D.shape)
# (3,) (6,)

3.np.newaxis(),添加一个新的维度,可以用于维度对齐或者是将序列转换成矩阵,这里也可以用reshape来实现序列转换到矩阵。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
print(A[np.newaxis,:])
# [[1 1 1]]

print(A[np.newaxis,:].shape)
or print(A.reshape((1,-1)).shape)
# (1,3)

print(A[:,np.newaxis])
"""
[[1]
[1]
[1]]
"""

print(A[:,np.newaxis].shape)
or print(A.reshape((-1,1)).shape)
# (3,1)

4.np.concatenate(),是前面两个函数的复合版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
C = np.concatenate((A,B,B,A),axis=0)

print(C)
"""
array([[1],
[1],
[1],
[2],
[2],
[2],
[2],
[2],
[2],
[1],
[1],
[1]])
"""

D = np.concatenate((A,B,B,A),axis=1)

print(D)
"""
array([[1, 2, 2, 1],
[1, 2, 2, 1],
[1, 2, 2, 1]])
"""

分割

1.等量分割

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
A = np.arange(12).reshape((3, 4))
print(A)
"""
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
"""

print(np.split(A, 2, axis=1))
"""
[array([[0, 1],
[4, 5],
[8, 9]]), array([[ 2, 3],
[ 6, 7],
[10, 11]])]
"""

print(np.split(A, 3, axis=0))

# [array([[0, 1, 2, 3]]), array([[4, 5, 6, 7]]), array([[ 8, 9, 10, 11]])]

2.不等量分割

1
2
3
4
5
6
7
8
9
10
print(np.array_split(A, 3, axis=1))
"""
[array([[0, 1],
[4, 5],
[8, 9]]), array([[ 2],
[ 6],
[10]]), array([[ 3],
[ 7],
[11]])]
"""

3.其他分割方法

1
2
3
4
5
6
7
8
9
10
11
12
13
print(np.vsplit(A, 3)) #等于 print(np.split(A, 3, axis=0))

# [array([[0, 1, 2, 3]]), array([[4, 5, 6, 7]]), array([[ 8, 9, 10, 11]])]


print(np.hsplit(A, 2)) #等于 print(np.split(A, 2, axis=1))
"""
[array([[0, 1],
[4, 5],
[8, 9]]), array([[ 2, 3],
[ 6, 7],
[10, 11]])]
"""

kaggle课程记录一

发表于 2020-04-29 | 分类于 面试准备 , 比赛

开新坑,主要记录在直播课中一些比较重要的信息,顺便梳理一下主要的思路。

这一篇记录的是开营仪式,主要分两个部分:介绍Kaggle,时序建模通用流程,介绍赛题以及baseline思路

1.Kaggle

kaggle就是一个平台,里面有很多比赛,和数据科学比较相关,是google公司的。M5属于featured类的比赛。

Kaggle比赛有金、银和铜的奖牌。称号的话有GM、M、EX、Con、Nov和User。

在一个比赛界面中,比较重要的是Notebooks和Discussion以及Leaderboard。在打比赛的时候应该经常Discussion里面看看。

A/B榜:就是验证集和测试集。

2.时序建模的通用流程

1.EDA(数据分析)

2.特征工程

3.模型训练(多数使用机器学习的方法)

4.线下验证

时间建模方案:

规则建模、自回归建模、趋势拟合、机器学习建模、深度学习建模

(高分的方案一定是复合的方案!!!!)

3.M5赛题背景介绍

M5就是一个预测销售量(未来28天)的问题。

一共有3张表(销量和店铺、所在州的信息的主要表、节假日信息表、销售价格表)。

A榜:1913天数据预测后28天数据(2016-04-25 to 2016-05-22)

B榜:1941天数据预测后28天数据 (2016-05-23 to 2016-06-19)

ps:B榜6月1日公布,然后6月30日截止。

样本的组成:(42840)

其中30490条是某商品在某家商店中的销量,其他则是由这30490条的信息聚合得到的时间序列。

评估指标WRMSSE

Weighted Root Mean Squared Scaled Error(WRMSSE)

左边理解一下:下面的分母是训练集的标签(销售量)差值,n是训练集的大小,h是要预测的天数(28天)。分子就是真实值和预测的差值。

右边的理解一下:因为每个时间序列都有一个预测,总共有42840个时间序列,所以去一个期望,这里的w的计算方式是看每个时间序列后28天的销售额的占比与level(看前面那张图)的倒数相乘(分母是30490个个样本的后28天的销售额的总和)。w的累加为1。
目标函数和损失函数是不一样的,在比赛当中找一个好的损失函数非常的重要。

4.baseline思路介绍

1.数据分析EDA

总的销量有年周期性,趋势向上。

数据的分布不是高斯分布,是柏松分布。

特征构造用抽取窗口特征:

1.前7天

2.前28天

3.前7天均值

4.前28天均值

或者关联其他维度的信息:日期、价格等

###baseline的代码分析

传统的时间序列预测模型无法拟合趋势。这是因为基模型是CART决策树,输出的范围限定在了训练集的范围内。所以我们最后的预测值的出来后还要去乘以一个系数。(这个系数可以是超参数,也可以重新另外训练一个趋势模型来得到,后面的方法会好一点。但是在baseline中是用前面一种方法。)

栈与队列

发表于 2020-04-28 | 分类于 面试准备 , 程序员面试指南

前几次在复盘笔试的时候,有遇到几题出现在面试指南中的原题,或者是基于面试指南修改的题目(就是改成带有自己公司色彩的题目),所以意识到这本书应该也得刷一刷,所以最近会更新刷书过程中遇到的之前没遇到过的题目的总结和思考。

栈与队列

栈和队列对于python来说是差不多的数据结构,因为他们都可以用python中的list来实现,只不过在出栈、出队列中略有差别。出栈是pop(),出队列是pop(0)。因为栈是先进后出的,而队列是先进后出的。在众多的相关类型的题目中,有一类题型很经常出现,而且变种的题型很多,那就是单调栈。在前几天我曾经有复盘过一题就是用单调栈解的最大矩形。今天我总共要梳理5题,其中三题就和单调栈相关,可见其重要程度。(另外两题是递归)而且我觉得单调栈说难也不是很难,其定义很容易理解,主要是要想如何利用单调栈的性质去解相关的题目,这点是比较不容易做到的。

1.递归函数逆序一个栈

题目:逆序一个栈:实现两个递归函数
1->每次从栈中取出最底层的元素
2->实现一个递归将取出最底层的最底层元素按后来先执行的顺序插入到栈中

思路:递归思想就是不断调用本身去重复执行类似的操作,说实话,我递归感觉也不是很熟悉。常见的递归比如说二叉树的遍历,以及斐波那契数列问题。这题将实现分成两步,就是上面提到的两点,首先将栈的最底层元素取出(其他元素不变)。然后对栈进行逆序。(通过递归实现)第一步递归:递归出口是将栈中最后一个元素返回,递归主体是往下取最后一个元素,将非最后一个元素再装回栈中。第二步递归则是先填后面的元素(栈顶靠后),再填最后的元素(栈底)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def get_last_and_remove(stack):
"""
从栈中取出并删除栈底元素
"""
item = stack.pop()
if not stack:
return item
else:
last = get_last_and_remove(stack)
stack.append(item)
return last

def reverse(stack):
"""
对栈进行逆序
"""
if not stack:
return
else:
last = get_last_and_remove(stack)
reverse(stack)
stack.append(last)

2.用递归来求解汉诺塔问题

题目:用两种方法解决汉诺塔问题:
1,递归 2,非递归:栈
要求:
移动汉诺塔的过程中,不能直接从左移动到右,要先移动到mid,从右到左同理
打印出每一步移动的过程以及返回总步数
如 2层汉诺塔的移动过程:
move 1 from left to mid
move 1 from mid to right
move 2 from left to mid
move 1 from right to mid
move 1 from mid to left
move 2 from mid to right
move 1 from left to mid
move 1 from mid to right
It will take 8 steps

思路:这里限制了移动的方法,但实际上,思路还是差不多的,只不过需要考虑的情况变多了。原来的汉诺塔问题用递归方法求解是很好理解的。递归方法要知道在这一步递归中完成了那些事情,最重要的是要知道参数的具体含义,感觉有点和dp还有回溯类似。在原始的问题中,主要是要理解三个位置的性质转换,汉诺塔一共有三个柱子,分别是left位,mid位,以及right位,其中两个位置是from和to,剩下的一个则是tmp位,在子问题中,他们的性质会发生改变。在限制问题中,主要是要理解一共有几种情况,要对不同情况分别讨论。移动的可能性可以分成两种,一种是只要一步,也就是from和to是mid的时候;另一种是from和to不是mid的时候。

1
2
3
4
5
6
7
8
def move(n,a,b,c):   #n为圆盘数,a代表初始位圆柱,b代表过渡位圆柱,c代表目标位圆柱
if n==1:
print(a,'-->',c)
else:
move(n-1,a,c,b) #将初始位的n-1个圆盘移动到过渡位,此时初始位为a,上一级函数的过渡位b即为本级的目标位,上级的目标位c为本级的过渡位
print(a,'-->',c)

move(n-1,b,a,c) #将过渡位的n-1个圆盘移动到目标位,此时初始位为b,上一级函数的目标位c即为本级的目标位,上级的初始位a为本级的过渡位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def hanoiProblem(num:int, left:str ,mid:str ,right:str ,From:str ,to:str ):
if num < 1:
return 0
else:
return process(num,left,mid,right,From,to)


def process(num:int,left:str,mid:str,right:str,From:str,to:str):
if num == 1:
if From == 'mid' or to == 'mid':
print('move 1 from ' + From + ' to ' + to)
return 1
else:
print('move 1 from ' + From + ' to ' + 'mid')
print('move 1 from '+'mid' + ' to ' + to)
return 2
if From == 'mid' or to == 'mid':
another = 'right' if From == 'left' or to == 'left' else 'left'
part1 = process(num-1,'left','mid','right',From,another)
print('move ' + str(num) +' from ' + From + ' to ' + to)
part2 = 1
part3 = process(num-1,'left','mid','right',another,to)
return part1 + part2 + part3
else:
part1 = process(num-1,'left','mid','right',From,to)
print('move '+str(num) +' from '+From+' to ' + 'mid')
part2 = 1
part3 = process(num-1,'left','mid','right',to,From)
print('move '+str(num) +' from '+' mid '+' to ' +to)
part4 = 1
part5 = process(num-1,'left','mid','right',From,to)
return part1 + part2 + part3 + part4 + part5

3.滑动窗口的最值

题目:给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

思路:这题在之前的面试中也出现过,我记得,最早之前。然后超时了。我那个时候好像没有用到单调栈,所以错了?这题需要我们维护一个长度最长为窗口大小的单调递减栈。单调栈我们之前介绍过,主要用来解需要关于i的向左或向右的第一个比他大或者小的位置信息,但在这一题中,用的是单调栈最原始的性质,那就是在栈底,永远是这个栈的最值,而且往后是这个栈的次最大值。代码的思路是用queue来维护这个单调栈,在入栈时,先判断栈顶是否已经超过窗口的范围,然后用单调栈的性质继续操作。最后判断当前的范围是否是窗口,然后添加结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution:
def maxInWindows(self, num, size):
# write code here
# queue存入num的位置信息
queue,res,i = [],[],0
while i < len(num) and size>0:
# 如果queue【0】的位置信息过期就出队列
if len(queue) > 0 and i-size+1 > queue[0]:
queue.pop(0)
# 弹出queue中所有比num【i】小的数字
while len(queue) > 0 and num[queue[-1]] < num[i]:
queue.pop()
# 新的数是一定会入队列的
queue.append(i)
# 从第一个窗口开始添加最大值
if i >= size-1:
res.append(num[queue[0]])
i += 1
return res

4.构造数组的maxtree

题目:

给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:

二叉树的根是数组中的最大元素。
左子树是通过数组中最大值左边部分构造出的最大二叉树。
右子树是通过数组中最大值右边部分构造出的最大二叉树。
通过给定的数组构建最大二叉树,并且输出这个树的根节点。

输入:[3,2,1,6,0,5]
输出:返回下面这棵树的根节点:

​ 6

​ / \

3 5
\ /
2 0

1

思路:这一题用的还是单调栈,这里就用了单调栈的另一个重要性质了,位置。我们这里引入两个哨兵,放在数组开头和结尾,简化一下代码逻辑。然后,我们还需要一个字典,用于储存数组对应的树节点,不然后面要描述节点之间的关系,就很难操作了。相信大家应该都还记得单调栈的主要针对的目标是弹出栈的那个元素吧,这里也是一样的,所以我们的代码主要就围绕这个被弹出栈的元素,一个元素被弹出栈,说明什么?说明有一个比他大的大哥来了,同时在栈里面在他旁边的那个元素也是比他大的,那这两个大哥到底谁是他的父节点呢?比一下就知道啦,谁比较小,谁就是他的父节点。(这里需要证明,略)然后我们总结一下规律,如果,选了左边的,那我就是他的右节点;选了右边的,那我就是他的左节点。最后由于一定有一个超级大哥,原始数组的元素都会被弹出栈,我们也就得到了所有的结果,最后被弹出的那个就是根。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution:
def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode:
# 加入两个哨兵
nums = [float('INF')]+nums+[float('INF')]
tmp = []
# 存储数组对应节点信息
node_dict = {}
for num in nums:
node_dict[num] = TreeNode(num)
# 最后进来的是超级大哥
for i in range(len(nums)):
if not tmp:
tmp.append(i)
else:
# 针对被弹出栈的元素
while nums[tmp[-1]]<nums[i]:
# 一共有三种情况
k = tmp.pop()
if nums[tmp[-1]] < nums[i]:
node_dict[nums[tmp[-1]]].right = node_dict[nums[k]]
elif nums[tmp[-1]] > nums[i]:
node_dict[nums[i]].left = node_dict[nums[k]]
else:
return node_dict[nums[k]]
# 最后一定记得是要入栈的
tmp.append(i)

5.最大值减去最小值等于或者小于num的子数组数量

题目:给一个数组arr,求出所有arr的子数组的最大值减去最小值小于等于num的数量

思路:最优解:
1,维持最大值和最小值的双端队列(参考滑动窗口的最大值数组)
2,i<j 两个指针遍历arr找出以arr[i]为开头的数组个数j-i 先移动j的位置,再移动i的位置。这里需要证明的是当某个位置的j不满足条件时,后面的j一定是不满足条件的,所以可以略过,而在这个j之前的以i开头的数组都是满足条件的,所以是加上j-i。
3,将所有的j-i进行相加;每个元素分别进出一次队列并判断,总时间复杂度O(n)。

需要注意:是否入栈是需要判断的,因为在break的时候,虽然j位置的元素已经入栈了,但是j没有+1,(注意i往前走的时候,j不是重新来的。)因此,可能导致重复添加元素,所以需要判别j是否已经在栈中了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def get_max(arr: list, num : int):
if not arr or len(arr) == 0 or num < 0:
return 0
qmax, qmin = [], []
i = 0
j = 0
ret = 0
while i < len(arr):
while j < len(arr):
if not qmin or qmin[-1] != j:
while qmin and arr[qmin[-1]] >= arr[j]:
qmin.pop()
qmin.append(j)
while qmax and arr[qmax[-1]] <= arr[j]:
qmax.pop()
qmax.append(j)
if arr[qmax[0]] - arr[qmin[0]] > num:
break
j += 1
ret += j - i
# 如果超出范围的话就弹出
if qmin[0] == i:
qmin.pop(0)
if qmax[0] == i:
qmax.pop(0)
i += 1

return ret

EM算法

发表于 2020-04-27 | 分类于 面试准备 , 机器学习

这是一块难啃的骨头,我实在啃不动。整体的算法逻辑我还是大致了解了,主要是涉及到具体的案例还不是很清楚,还有就是收敛性证明不是很清楚,之后再看的话,会回来补充。

1.什么是EM算法?

EM算法是极大似然估计(MLE)的延伸版本。MLE就是求解使得出现可观测(无隐含变量)样本集的概率最大的模型参数。那假如样本集有隐含变量怎么办呢?也就是说这个样本属于哪个概率模型我们不知道,我们就无法给出准确概率表达式,这样MLE的计算也就无从谈起了。所以我们需要解决这个不确定性的问题,解决的方法就是EM算法。题外话,MLE往另一个方向拓展就是最大后验概率估计(MAP)和贝叶斯估计,我还没复习到,就不拓展了。

2.为什么可以用EM算法解决隐含变量问题?

这是因为EM算法通过构造一个关于隐含变量的函数,并利用了Jesen不等式的性质,得出当这个隐含变量为隐含变量关于样本的条件概率时,可以构造出一个原目标函数的下界,这样求解原目标函数的极大值就转换成求解下界函数的极大值的问题。而这个下界是可以通过MLE进行求解的。后面通过迭代来不断提高这个下界的极大值来间接提高原目标函数的极大值,当这个极大值不变时,我们就得到了原目标函数的局部最大值。(为什么是局部最大值?,有点像梯度下降算法,一旦落入到某个鞍点时候,没有梯度就跳不出这个鞍点,而这个鞍点可能就只是一个极值。看下图。)

3.怎么推导EM?

上面一个问题中,我们说到,EM算法实质上是一个迭代算法,使用Jesen不等式的定义,通过不断构造局部下界,求这个下界的极大值来求解目标函数的极大值。那整体推导过程是怎么样的?我在知乎上看到一篇推导过程非常详细的回答,我直接给链接了,我这里不赘述了。彭一洋的回答

4.EM算法具体有哪些应用?

待补充···

最大矩形

发表于 2020-04-25 | 分类于 面试准备 , 算法

单调栈遇到很多次了,我觉得这种类型的题目要是不会做的话就很吃亏,毕竟很经常出,而且理解之后也不是很难,关键是思路要捋清楚。

给一下题目:

给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。(leetcode85 困难)

1
2
3
4
5
6
7
8
输入:
[
["1","0","1","0","0"],
["1","0","1","1","1"],
["1","1","1","1","1"],
["1","0","0","1","0"]
]
输出: 6

##分析:

我一开始是按照动态规划的思路去尝试解答的,但是一直找不到状态转移的思路。确实是有类似的题型的,就是可以通过动态规划的思路去求解,稍微不太一样的点是它找的是矩阵中最大的子正方形(leetcode221 中等),也就是长和宽是一样的矩形,它的状态转移方程是:$dp(i, j)=min(dp(i−1, j), dp(i−1, j−1), dp(i, j−1))+1$

这里不太赘述了,之后如果遇到的话再深入理解吧,它这里二维dp还可以优化成一维dp。这是因为它的状态转移方程只用到了周围的信息(那之前很多不是很多都可以优化?)

正式分析啊,这一题可以看作是一题单调栈中的经典题型(leetcode84 困难)——柱状图中最大的矩形的延伸题,就是说我们可以直接使用找柱状图最大的矩形的解法来解我们这一题。怎么说呢,我们将原矩阵切片,从第一行往下切,按例题来说就是切四次。第二次就是切到第二行,然后将被切下来的矩阵转换成柱状图。怎么转换呢?从底往上数1的个数,注意是从底开始数哦,假设底为0,然后上面是1的话也不算的,那这一列的长度就为0了。这样我们可以得到一个柱状图,然后我们用leetcode84的解法可以得到这个柱状图的最大矩形,我们每切一次(新的柱状图)就得到一个最大矩形,我们留下最大的那个就是我们想要的结果了。所以我们首先要解决的问题就是在一个柱状图中找到一个最大的矩形。

在柱状图中找到最大的矩形

思路:要想找到在柱状图中的最大矩形,我们可以再往下想,以第i根柱子为最矮柱子所能延伸的最大面积是多少。这样通过比较所有柱子的最大面积,我们就可以得到柱状图中的最大矩形。稍微伪代码点的解释就是以i为中心,向左找第一个小于heights【i】的位置left_i;向右找第一个小于heights[i]的位置right_i,也就是最大面积heights[i]*(right_i-left_i-1)

所以我们的问题又变成如何找right_i和left_i。

#####如何找到right_i(右边第一个小于i的位置)和left_i

思路:暴力法肯定不行。我们这里使用单调栈来解决这个问题。

所谓单调栈就是单调递增或者递减的栈。两者有什么区别呢?
单调增:从栈底到栈顶是单调增加的,用于找到数组中某一位置的值的左边第一个小于它的位置以及右边第一个小于它的位置。

单调减:从栈底到栈顶是单调减少的,用于找到数组中某一位置的值的左边第一个大于它的位置以及右边第一个大于它的位置。

为什么单调栈有这种作用呢?那就要理单调栈的操作规则。

单调栈的操作规则:(以单调增为例)

1.如果新的元素比栈顶元素大,就入栈

2.如果新的元素较小,那就一直把栈内元素弹出来,直到栈顶比新元素小(注意,这里相等的话是不弹出来的,虽然这样部分柱子的最大面积计算出来是有误的,但不影响最后的结果,后面会解释。)

这样操作单调栈之后会发生什么呢?

1.栈内的元素是递增的

2.当元素出栈时,说明这个新元素(要入栈的)是出栈元素向右找第一个比其小的元素。

3.当元素出栈后,新栈顶元素是出栈元素向左找第一个比其小的元素。

(注意,这里都是针对出栈元素来比较的,所以对于最后的结果而言,我们原始的直方图的每个柱子进栈之后,最后都应该以出栈为结束,所以这里需要引入两个哨兵:在输入数组两个各放置1个0)

还需要注意的一点是栈中存储的是位置信息,不是值,在敲代码的时候一定要区分开,很容易搞乱掉···

放代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
stack = []
# 放置哨兵
heights = [0] + heights + [0]
# 保留最佳结果
res = 0
for i in range(len(heights)):
#print(stack)
while stack and heights[stack[-1]] > heights[i]:
tmp = stack.pop()
# 每弹出一个元素,就计算这个元素的最大面积
res = max(res, (i - stack[-1] - 1) * heights[tmp])
# 由于最后一个元素是0,所以原始元素都会被弹出
stack.append(i)
return res

作者:powcai
链接:https://leetcode-cn.com/problems/largest-rectangle-in-histogram/solution/zhao-liang-bian-di-yi-ge-xiao-yu-ta-de-zhi-by-powc/
来源:力扣(LeetCode)

之前说的数相等的情况,我们可以忽视计算错误的结果,因为最后弹出的相等的元素是不会计算错误的,而且它的面积也会是最大(在所有相等的元素的计算结果之中),就会覆盖掉之前计算错误的情况。

矩阵中找最大矩形

前面我们通过单调栈解决了在直方图中找最大矩形的问题,接下来我们就顺理成章地解决在矩阵找最大矩形的问题。直接上代码:(重点是后面那个函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Solution:

# Get the maximum area in a histogram given its heights
def leetcode84(self, heights):
stack = [-1]

maxarea = 0
for i in range(len(heights)):

while stack[-1] != -1 and heights[stack[-1]] >= heights[i]:
maxarea = max(maxarea, heights[stack.pop()] * (i - stack[-1] - 1))
stack.append(i)

while stack[-1] != -1:
maxarea = max(maxarea, heights[stack.pop()] * (len(heights) - stack[-1] - 1))
return maxarea


def maximalRectangle(self, matrix: List[List[str]]) -> int:

if not matrix: return 0

maxarea = 0
# 这里的dp存储的就是每一个直方图的高度(前面一个直方图计算完之后就会被后面的直方图覆盖掉)
dp = [0] * len(matrix[0])
for i in range(len(matrix)):
for j in range(len(matrix[0])):

# update the state of this row's histogram using the last row's histogram
# by keeping track of the number of consecutive ones

dp[j] = dp[j] + 1 if matrix[i][j] == '1' else 0

# update maxarea with the maximum area from this row's histogram
maxarea = max(maxarea, self.leetcode84(dp))
return maxarea

作者:LeetCode
链接:https://leetcode-cn.com/problems/maximal-rectangle/solution/zui-da-ju-xing-by-leetcode/
来源:力扣(LeetCode)

延伸

刚才的题目是递增栈的应用,递减栈的应用题目也是按这个思路来做:

题目描述:有n个人站队,所有的人全部向右看,个子高的可以看到个子低的发型,给出每个人的身高,问所有人能看到其他人发现总和是多少。
输入:4 3 7 1
输出:2
解释:个子为4的可以看到个子为3的发型,个子为7可以看到个子为1的身高,所以1+1=2。

总结

总结一些常见的题目,比每次都错要好。

12…4<i class="fa fa-angle-right"></i>
王大龙

王大龙

33 归档
12 分类
54 标签
GitHub 新浪微博 CSDN
© 2020 王大龙
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4