• Articles posted by Conan Zhang

Author Archives: Conan Zhang

About Conan Zhang

Programmer(Java,R,PHP,Javascript)

用R语言实现余弦相似度Cosine Similarity

R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大。

R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。

要成为有理想的极客,我们不能停留在语法上,要掌握牢固的数学,概率,统计知识,同时还要有创新精神,把R语言发挥到各个领域。让我们一起动起来吧,开始R的极客理想。

关于作者:

  • 张丹,分析师/程序员/Quant: R,Java,Nodejs
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/r-cosine-similarity/

前言

在文字处理时,我们经常需要判断两段文字是否相似,如果这两段文字的用词越相似,它们的内容就应该越相似。这个场景下,我们就可以考虑先把文字转换成词频向量,然后用余弦相似度来判断是否相似。

目录

  1. 余弦相似度介绍
  2. R语言实现余弦相似度
  3. 计算2个文本的相似度

1. 余弦相似度介绍

余弦相似度,通过测量两个向量的夹角的余弦值来度量它们之间的相似性。例如,将两篇文章向量化,余弦距离可以避免因为文章的长度不同而导致距离偏大,余弦距离只考虑两篇文章生成的向量的夹角。

余弦相似度被大量用于对比:如人脸对比、声音对比,来快速判断两个图片或者两段声音的相似度,进而判断是不是来自同一个人。当一个图像或者声音样本具有n维的特征,我们就可以把他认为是n维向量,两个样本使用余弦相似度比对时,就是对两个n维向量的夹角余弦值,其大小进行衡量。

余弦相似度的取值范围是[-1,1],相同两个向量的之间的相似度为1。余弦距离的取值范围是[0,2]。

  • 当夹角为0,两个向量同向,相当于相似度最高,余弦值为1,表示完全正相关。
  • 当夹角90°,两个向量垂直,余弦为0,表示不相关。
  • 当夹角180°,两个向量反向,余弦为-1,表示完全负相关。

计算公式:

相比其他的距离度量,余弦相似度更加注重两个向量在方向上的差异,而非距离或长度上。

欧式距离与余弦距离的对比:

  1. 欧式距离的数值受到维度的影响,余弦相似度在高维的情况下也依然保持低维完全相同时相似度为1等性质。
  2. 欧式距离体现的是距离上的绝对差异,余弦距离体现的是方向上的相对差异。

不同情况不同选择:

  1. 两个人分别取了蓝球(1,0)与红球(0,1),这两个向量的欧式距离较小,可是事实是这两个球是不同的,而余弦距离为2表示的是完全不同的意思。所以在这种情况下选择余弦距离更具合理性。
  2. 两个人对APP的使用次数与使用时长分别表示为(1,10),(10,100),可知余弦相似度较小,说明这两个人的行为时相同的,可是,事实是不同的,两个人的活跃度有着极大的差异,第二个人的活跃度更高。

2. R语言实现余弦相似度

我们可以安装lsa包,使用cosine()函数,计算2个向量的余弦相似度。

lsa(Latent Semantic Analysis 潜在语义分析)包基本思想是,文本确实具有高阶(=潜在语义)结构,但这种结构会被词语用法(如通过使用同义词或多义词)所掩盖。通过对给定的文档-术语矩阵进行截断奇异值分解(双模式因子分析),以统计方式得出概念指数,从而克服了这一可变性问题。

lsa包的安装过程比较简单。


> install.packages(lsa)
> library(lsa>

计算2个向量的余弦相似度。


> vec1 = c( 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 )
> vec2 = c( 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0 )
> cosine(vec1,vec2) 
          [,1]
[1,] 0.2357023

3. 计算2个文本的相似度

有了计算余弦相似度的工具后,那么接下来,我们就可以尝试计算文本的相似度了。

使用余弦相似度,计算文本相似的具体步骤:

  1. 定义两个文本内容
  2. 对两个文本内容进行分词
  3. 列出所有的词,并计算词频
  4. 整理词频为词向量
  5. 用余弦相似度计算词向量

第一步,我们定义两段文本内容,分别是

  • 句子1:”我买了一个手机,又配了一个手机壳”
  • 句子2:”我有3年没有换新手机了,还是原来的手机壳”

> txt1<-"我买了一个手机,又配了一个手机壳"
> txt2<-"我有3年没有换新手机了,还是原来的手机壳"

第二步,对上面2个句子进行分词,这里我们使用jiebaR包进行分词,关于jiebaR包的具体用法,请参考文章R语言中文分词包jiebaR


# 加载jiabaR包
> library(jiebaR)

# 定义分词引擎
> wk<-worker()

# 分别对两个文本进行分词
> w1<-wk[txt1]
> w2<-wk[txt2]

第三步,进出所有的词,并查看词频


> w1
 [1] "我"   "买"   "了"   "一个" "手机" "又"   "配"   "了"   "一个" "手机" "壳"  
> w2
 [1] "我"     "有"     "3"      "年"     "没有"   "换"     "新手机" "了"     "还是"   "原来"   "的"    
[12] "手机"   "壳" 

# 查看词频
> table(w1)
w1
  壳   了   买   配 手机   我 一个   又 
   1    2    1    1    2    1    2    1 

> table(w2)
w2
     3     的   还是     换     壳     了   没有     年   手机     我 新手机     有   原来 
     1      1      1      1      1      1      1      1      1      1      1      1      1 

第四步,整理词频为词向量,这里我把生成词向量过程,封装成一个函数叫dfm(),形成单词矩阵。


# 加载工具包
> library(plyr)
> library(magrittr)

# 封装词向量函数
> dfm<-function(w1,w2){
+     t1<-table(w1) %>% ldply
+     t2<-table(w2) %>% ldply
+     names(t1)<-c("seg","cnt1")
+     names(t2)<-c("seg","cnt2")
+     mm<-merge(t1,t2,by="seg",all=TRUE)
+     mm$cnt1[which(is.na(mm$cnt1))]<-0
+     mm$cnt2[which(is.na(mm$cnt2))]<-0
+     list(dat=t(mm[,-1]),seg=mm$seg)
+ }

# 查看词向量的输出结果
> m<-dfm(w1,w2);m
$dat
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16] [,17]
cnt1    0    0    0    0    1    2    1    0    0     1     2     1     0     2     0     1     0
cnt2    1    1    1    1    1    1    0    1    1     0     1     1     1     0     1     0     1

$seg
 [1] "3"      "的"     "还是"   "换"     "壳"     "了"     "买"     "没有"   "年"     "配"     "手机"  
[12] "我"     "新手机" "一个"   "有"     "又"     "原来"  

第五步,用余弦相似度计算词向量的相似度,即两段文本的余弦相似度。结果是0.4,表示不是太相似。


> library(lsa)
> cosine(m$dat[1,],m$dat[2,]) 
          [,1]
[1,] 0.4036037

那我们换一组句子再试试,比如

  • 句子1:如果这两句话的用词越相似,它们的内容就应该越相似。
  • 句子2:如果这两句话的内容越相似,它们的用词也应该越相似。

用R语言计算上面2个句子的余弦相似度,得0.95,表示非常相似了。


> txt1<-"如果这两句话的用词越相似,它们的内容就应该越相似"
> txt2<-"如果这两句话的内容越相似,它们的用词也应该越相似"
> w1<-wk[txt1]
> w2<-wk[txt2]
> m<-dfm(w1,w2);m
$dat
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
cnt1    2    1    1    1    1    1    2    0    1     1     2     1
cnt2    2    0    1    1    1    1    2    1    1     1     2     1

$seg
 [1] "的"     "就"     "两句话" "内容"   "如果"   "它们"   "相似"   "也"     "应该"   "用词"   "越"    
[12] "这"    

> cosine(m$dat[1,],m$dat[2,]) 
     [,1]
[1,] 0.95

本文我们了解余弦相似度的原理和实现,并且应用了余弦相似度计算,从而可以判断两个文本之间的相似程度,让我们进行文本匹配时又多了一种方法。本文代码:https://github.com/bsspirit/r-string-match/blob/main/cosine.r

转载请注明出处:
http://blog.fens.me/r-cosine-similarity/

打赏作者

R语言中的位运算

R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大。

R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。

要成为有理想的极客,我们不能停留在语法上,要掌握牢固的数学,概率,统计知识,同时还要有创新精神,把R语言发挥到各个领域。让我们一起动起来吧,开始R的极客理想。

关于作者:

  • 张丹,分析师/程序员/Quant: R,Java,Nodejs
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/r-bit-operation/

前言

本来是要研究字符串的匹配的问题,然后看着看着就到了文本距离的计算,然后就又到了位运算。要不然也不会想到,用R语言搞二进制的位运算研究。每种算法一旦刨根问底,都是会到计算机的底层计算逻辑。

赶上了问题,就认真面对问题,把二进制和位运算一些看看。关于进制转换的文章,请参考用R语言进制转换-二进制八进制十六进制

目录

  1. 什么是位运算
  2. 位运算的计算过程
  3. R语言中的位运算

1. 什么是位运算

计算机中的所有数值,都是在内存中都是以二进制的形式储存的,即 0、1 两种状态。位运算就是直接对内存中的二进制位数据进行的操作。比如,“&”与运算是一个逻辑运算符,6的二进制是110,11的二进制是1011,那么6 & 11的结果就是2,它是二进制对应位进行逻辑运算的结果(0表示False,1表示True,空位都当0处理)。

位运算概览:

符号描述运算规则
&两个位都为1时,结果才为1
|两个位都为0时,结果才为0
xor异或两个位相同为0,相异为1
~取反0变1,1变0
<<左移各二进位全部左移若干位,高位丢弃,低位补0
>>右移各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)

2. 位运算的计算过程

2.1 按位与(&)

定义:参加运算的两个数据,按二进制位进行”与”运算。

运算规则:两位同时为1,结果才为1,否则结果为0。

0&0=0 , 0&1=0, 1&0=0, 1&1=1

举例说明:

(3)₁₀ & (5)₁₀ =    (00000011)₂ & (00000101)₂ = (00000001)₂ = (1)₁₀

操作十进制12345678
300000011
&500000101
=100000001

(23)₁₀ & (15)₁₀ =    (00010111)₂ & (00001111)₂ = (00000111)₂ = (7)₁₀

操作十进制12345678
2300010111
&1500001111
=700000111

与运算的用途:

1)清零

如果想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为零的数值相与,结果为零。

2)取一个数的指定位

比如取数 X=1010 1110 的低4位,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位与运算(X&Y=0000 1110)即可得到X的指定位。

3)判断奇偶

只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数。

 

2.2 按位或(|)

定义:参加运算的两个对象,按二进制位进行”或”运算。

运算规则:参加运算的两个对象只要有一个为1,其值为1。

0|0=0, 0|1=1, 1|0=1,  1|1=1

举例说明:

(3)₁₀ | (5)₁₀ =    (00000011)₂ | (00000101)₂ = (00000111)₂ = (7)₁₀

操作十进制12345678
300000011
|500000101
=700000111

(23)₁₀ | (15)₁₀ =    (00010111)₂ | (00001111)₂ = (00011111)₂ = (31)₁₀

操作十进制12345678
2300010111
|1500001111
=3100011111

注意:负数按补码形式参加按位或运算。

或运算的用途:

1)常用来对一个数据的某些位设置为1

比如将数 X=1010 1110 的低4位设置为1,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位或运算(X|Y=1010 1111)即可得到。

2.3 按位异或(xor)

定义:参加运算的两个数据,按二进制位进行”异或”运算。

运算规则:参加运算的两个对象,如果两个相应位相同为0,相异为1。

0 xor 0=0,  0 xor 1=1,  1 xor 0=1,  1 xor 1=0

举例说明:

(3)₁₀ xor (5)₁₀ =    (00000011)₂ xor (00000101)₂ = (00000111)₂ = (7)₁₀

操作十进制12345678
300000011
xor500000101
=600000110

(23)₁₀ xor (15)₁₀ =    (00010111)₂ xor (00001111)₂ = (00011000)₂ = (24)₁₀

操作十进制12345678
2300010111
xor1500001111
=2400011000

异或的几条性质:

  1. 交换律 (a xor b)  == (b xor a)
  2. 结合律 (a xor b) xor  c == a xor  (b xor  c)
  3. 对于任何数x,都有 x xor  x=0,x xor  0=x
  4. 自反性: a xor  b xor  b = a xor  0=a

异或运算的用途:

1)翻转指定位

比如将数 X=1010 1110 的低4位进行翻转,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行异或运算(X  xor  Y=1010 0001)即可得到。

2)与0相异或值不变

例如:(10101110)₂  xor  (00000000)₂ = (10101110)₂

2.4 按位非(~)

定义:参加运算的一个数据,按二进制进行”取反”运算。

运算规则: 对一个二进制数按位取反,即将0变1,1变0。

 ~1=0, ~0=1

举例说明:

~ (3)₁₀ =    ~(00000011)₂  = (11111100)₂ = (-4)₁₀

操作十进制12345678
~300000011
=-411111100

~(23)₁₀  =    ~(00010111)₂ = (11101000)₂ = (-24)₁₀

操作十进制12345678
~2300010111
=-2411101000

按位非运算的用途:

1)使一个数的最低位为零

使a的最低位为0,可以表示为:a & ~1。~1的值为 1111 1111 1111 1110,再按”与”运算,最低位一定为0。因为” ~”运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他运算符都高。

2.5 按位左移(<<)

定义:将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。

设 a=1010 1110,a = a<< 2 将a的二进制位左移2位、右补0,即得a=1011 1000。

若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2。

举例说明:

(3)₁₀<< 1=     (00000011)₂ << (1)₁₀= (00000110)₂ = (6)₁₀

操作十进制12345678
300000011
<<1
=600000110

(3)₁₀<< 2=     (00000011)₂ << (2)₁₀ = (00001100)₂ = (12)₁₀

操作十进制12345678
300000011
<<2
=1200001100

2.6 按位右移(>>)

定义:将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。

例如:a=a>>2 将a的二进制位右移2位,左补0 或者 左补1得看被移数是正还是负。

操作数每右移一位,相当于该数除以2。

举例说明:

(30)₁₀>> 1=     (00011110)₂ << (1)₁₀= (00001111)₂ = (15)₁₀

操作十进制12345678
3000011110
>>1
=1500001111

(30)₁₀>> 4=     (00011110)₂ << (4)₁₀ = (00000001)₂ = (1)₁₀

操作十进制12345678
300011110
>>4
=100000001

3. R语言中的位运算

R语言位运算操作概览:

操作函数描述语法
bitwAnd按位与(&)bitwAnd(value1,value2)
bitwOr按位或 (|)bitwOr(value1,value2)
bitwXor按位异或(XOR)bitwXor(value1,value2)
bitWnot按位非(~)bitwNot(value)
bitwShiftL左移bitwShiftL(value,n)
bitwShiftR右移bitShiftR(value,n)

位运算的6个操作,对应上文中的位运算的计算过程。

为了方便查看二进制的结果,我写了intToBitString()函数,方便对十进制转二进制阅读。


# 十进制转二进制,默认截取8位
> intToBitString<-function(num,size=8){
+   a<-as.integer(intToBits(num))
+   pos<-max(which(a==1),8)
+   a<-a[1:pos]
+   paste(rev(a), collapse = "")
+ }

把十进制转二进制。


# 把30转二进制
> intToBitString(30)
[1] "00011110"

# 使用默认函数
> intToBits(30)
 [1] 00 01 01 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[20] 00 00 00 00 00 00 00 00 00 00 00 00 00

按位与操作,计算 3 & 5, 23 & 15


> bitwAnd(3,5)
[1] 1

> bitwAnd(23,15)
[1] 7

# 查看二进制数
> intToBitString(3)
[1] "00000011"
> intToBitString(5)
[1] "00000101"
 intToBitString(23)
[1] "00010111"
> intToBitString(15)
[1] "00001111"

按位或操作,计算 3 | 5, 23 | 15


> bitwOr(3,5)
[1] 7

> bitwOr(23,15)
[1] 31

按位异或,计算 3 xor 5, 23 xor 15


> bitwXor(3,5)
[1] 6

> bitwXor(23,15)
[1] 24

按位非,计算 ~3 , ~23


> bitwNot(3)
[1] -4
> bitwNot(23)
[1] -24

# 查看-4 和 -24的二进制
> intToBitString(-4)
[1] "11111111111111111111111111111100"
> intToBitString(-24)
[1] "11111111111111111111111111101000"

左移,计算 3<<1 , 3<<2


> bitwShiftL(3,1)
[1] 6
> bitwShiftL(3,2)
[1] 12

# 查看3, 6, 12的二进制
> intToBitString(3)
[1] "00000011"
> intToBitString(6)
[1] "00000110"
> intToBitString(12)
[1] "00001100"

右移,计算 30>>1 , 30>>4


> bitwShiftR(30,1)
[1] 15
> bitwShiftR(30,4)
[1] 1

# 查看30,15,1的二进制
> intToBitString(30)
[1] "00011110"
> intToBitString(15)
[1] "00001111"
> intToBitString(1)
[1] "00000001"

用R实现二进制计算,比原本想象的容易不少,代码不仅简单,而且计算函数功能明确,又get到了新的知识。

转载请注明出处:
http://blog.fens.me/r-bit-operation/

打赏作者

R语言中的ASCII码

R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大。

R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。

要成为有理想的极客,我们不能停留在语法上,要掌握牢固的数学,概率,统计知识,同时还要有创新精神,把R语言发挥到各个领域。让我们一起动起来吧,开始R的极客理想。

关于作者:

  • 张丹,分析师/程序员/Quant: R,Java,Nodejs
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/r-ascii/

前言

ASCII是计算机中最重要的字符集,我们键盘就是ASCII的子集,由26个字母、数字、符号、控制键组成,用127个基础代码就能展示出绚烂多彩的计算机世界,基础中的基础,核心中的核心。

目录

  1. ASCII介绍
  2. ASCII码转换
  3. ASCII码比大小

1. ASCII介绍

ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)是一套基于拉丁字母的字符编码,共收录了 128 个字符,用一个字节就可以存储,它等同于国际标准 ISO/IEC 646。

ASCII 编码于 1967 年第一次发布,最后一次更新是在 1986 年,迄今为止共收录了 128 个字符,包含了基本的拉丁字母(英文字母)、阿拉伯数字(也就是 1234567890)、标点符号(,.!等)、特殊符号(@#$%^&等)以及一些具有控制功能的字符。ASCII 编码是美国人给自己设计的,他们并没有考虑欧洲那些扩展的拉丁字母,也没有考虑韩语、日语、中文。ASCII 码使用指定的 7 位或 8 位二进制数组合来表示 128 或 256 种可能的字符。

ASCII码大致可以分作三部分组成。

  • 第一部分:ASCII非打印控制字符0~31及127(共33个),是控制字符或通信专用字符(其余为可显示字符),如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BS(退格)、BEL(振铃)等;通信专用字符:SOH(文头)、EOT(文尾)、ACK(确认)等;ASCII值为 8、9、10 和 13 分别转换为退格、制表、换行和回车字符。它们并没有特定的图形显示,但会依不同的应用程序,而对文本显示有不同的影响。
  • 第二部分:ASCII打印字符32~126(共95个)是字符(32sp是空格),其中48~57为0到9十个阿拉伯数字;65~90为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等
  • 第三部分:扩展ASCII打印字符,后128个称为扩展ASCII码,目前许多基于x86的系统都支持使用扩展(或“高”)ASCII。扩展 ASCII 码允许将每个字符的第 8 位用于确定附加的 128 个特殊符号字符、外来语字母和图形符号。

同时还要注意,在标准ASCII中,其最高位(b7)用作奇偶校验位。所谓奇偶校验,是指在代码传送过程中用来检验是否出现错误的一种方法,一般分奇校验和偶校验两种。奇校验规定:正确的代码一个字节中1的个数必须是奇数,若非奇数,则在最高位b7添1;偶校验规定:正确的代码一个字节中1的个数必须是偶数,若非偶数,则在最高位b7添1。

2. R语言ASCII码转换

在R语言中,可以使用 charToRaw() 函数用于将给定的字符转换为其相应的ASCII值或原始对象。


# 转换字符到ASCII码
> charToRaw("A")
[1] 41

> charToRaw("ABCD")
[1] 41 42 43 44

这里要注意,A的输出结果为41,与ASCII码中A的值65不一致,是因为charToRaw()函数是以十六进制输出的,再把十六进制转十进制,结果为65。关于R语言进制转换,请参考文章用R语言进制转换-二进制八进制十六进制


# 转十进制显示
> strtoi(charToRaw("A"), 16L)
[1] 65

> strtoi(charToRaw("ABCD"), 16L)
[1] 65 66 67 68

ASCII的双向转换,可以使用rawToChar()函数,将ASCII转成字符串。


# 定义一个字符串
> x <- "A test string";x
[1] "A test string"

# 判断是ASCII
> is.raw(x)
[1] FALSE

# 转ASCII
> y <- charToRaw(x);y
 [1] 41 20 74 65 73 74 20 73 74 72 69 6e 67

# 判断是否向量
> is.vector(y)
[1] TRUE

# 把ASCII转字符串
> rawToChar(y)
[1] "A test string"

# 判断是ASCII
> is.raw(y)
[1] TRUE

建立ASCII编码检查函数isASCII(),用于判断是否都是ASCII码字符。


> # ASCII检查
> isASCII <-  function(txt) {
+   all(charToRaw(txt) <= as.raw(127))
+ }

进行ASCII编码检查


> x <- "A test string";x
[1] "A test string"

# 检查x
> isASCII(x)  # true
[1] TRUE

# 随意字符
> isASCII("78fdaj djfakkje AKI&(*&(*&(*&(*,\\")
[1] TRUE

# 使用Latin-1字符集,检查失败
> isASCII("\xa325.63")
[1] FALSE

# 使用中文字符集,检查失败
> isASCII("你好")
[1] FALSE

3. 字符比大小

通常编程语言都支持字符比较大小,字符串与字符串比较原理其实都是比较单个字符的ASCII码值。比如,A和B比较大小,A对应65,B对应66,A应该是小于B的。

在比较字符串时,设置一个计数器,从零开始,一直循环到最短的那个字符结束,一位一位进行比较。

  • 如果字符串1的第n位的ASCII码值 等于 字符串2的第n位的ASCII码值,则继续比较下一位。
  • 如果字符串1的第n位的ASCII码值 大于 字符串2的第n位的ASCII码值,则字符串1 > 字符串2;
  • 如果字符串1的第n位的ASCII码值 小于 字符串2的第n位的ASCII码值,则字符串1 < 字符串2;
  • 如果每一位的ASCII码值都相等,而且长度相同, 则字符串1 == 字符串2

用R语言实现比较A和B。


> strtoi(charToRaw("AB"), 16L)
[1] 65 66
> x<-"A"
> y<-"B"
> x>y
[1] FALSE

比较2个字符串,ABCDE和ABCD,当比较到E时,字符串2第5位计0,字符串1第5位E为69,则x>y。


> strtoi(charToRaw("ABCDEE"), 16L)
[1] 65 66 67 68 69 69
> x<-"ABCDE"
> y<-"ABCD"
> x>y
[1] TRUE

比较2个字符,A和a,A的ASCII码为65,a的ASCII码为97,应该A小于a,但结果正好相反。


> strtoi(charToRaw("Aa"), 16L)
[1] 65 97
> x<-"A"
> y<-"a"
> x>y
[1] TRUE

# 比较A和a
> "A">"a"
[1] TRUE

> "a">"A"
[1] FALSE

出意外了!!!!目前还没有找到原因,希望有了解的朋友,给我留言。

本文我们了解ASCII的定义,和在R语言中的转换方法。在做文字处理的过程中,会有大量的操作离不开ASCII编码的使用。

转载请注明出处:
http://blog.fens.me/r-ascii/

打赏作者

用R语言进制转换-二进制八进制十六进制

R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大。

R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。

要成为有理想的极客,我们不能停留在语法上,要掌握牢固的数学,概率,统计知识,同时还要有创新精神,把R语言发挥到各个领域。让我们一起动起来吧,开始R的极客理想。

关于作者:

  • 张丹,分析师/程序员/Quant: R,Java,Nodejs
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/r-binary-octal-hexadecimal/


前言

在用计算机进行数据计算时,避免不少会涉及到进制的转换操作,从十进制转换到二进制,从十进制转换到十六进制等,转换的过程,其实就是数学计算的过程。本文我们就利用R语言进行进制转换的实现。

目录

  1. 二进制八进制十六进制
  2. 转换十进制
  3. 十进制转换非十进制

1. 二进制八进制十六进制

数制,是一种计数的方法,是用一组固定的符号和统一的规则来表示数值的方法。在计数过程中采用进位的方法称为进位计数制(进制),包括数位、基数和位权三个要素。

  • 数位:指数字符号在一个数中所处的位置。
  • 基数:指在某种进位计数制中数位上所能使用的数字符号的个数。例如十进制的基数为10
  • 位权:数制中某一位上的1所表示数值的大小(所处位置的价值)。例如十进制的230,1的位权是100,2的位权是10,3的位权是1。

在计算机编程中,常用到的进制有二进制,八进制,十进制,十六进制。

进制数下标表示举例组成说明

二进制2、B(10)₂=10B0,1Binary:二进制
八进制8、O、Q(10)₈=10O=10Q 0,1,2,3,4,5,6,7Octal:八进制,字母O与数学0容易混淆,常用Q替代
十进制10、D(10)₁₀=10D0,1,2,3,4,5,6,7,8,9Decimal:十进制
二进制16、H(10)₁₆=10H0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,FHexadeciaml:十六进制

进制转换快速查表,反应了不同数制之间的对应及转换关系。

2.  转十进制计算方法

2.1 二进制转十进制

二进制转为十进制要从右到左用二进制的每个数去乘以2的相应次方,小数点后则是从左往右。

计算举例:(0010)₂ 转十进制 为(2)₁₀

二进制0010
2的幂2⁰

(101)₂ = 1 * 2² + 0 * 2¹ + 1 * 2 ⁰ = 4 + 0 + 1 = (5)₁₀

(1110)₂ = 1 * 2³ + 1 * 2² + 1 * 2¹ + 0 * 2⁰ = 8 + 4 + 2 + 0 = (14)₁₀

(0010)₂ = 0 * 2³ + 0 * 2² + 1 * 2¹ + 0 * 2⁰ = 0 + 0 + 2 + 0 = (2)₁₀

R语言实现,使用strtoi()函数,可以把二进制的字符串,转为十进制的数值。


> strtoi(c("101", "1110", "0010"), 2L)
[1]  5 14  2

2.2 八进制转十进制

八进制转为十进制要从右到左用八进制的每个数去乘以8的相应次方,小数点后则是从左往右。

计算举例:

(375)₈ = 3 * 8² + 7 * 8¹ + 5 * 8 ⁰ = 192 + 56 + 5 = (253)₁₀

(1110)₈ = 1 * 8³ + 1 * 8² + 1 * 8¹ + 0 * 8⁰ = 512 + 64 + 8 + 0 = (584)₁₀

R语言实现


> strtoi(c("375", "1110"), 8L)
[1] 253 584

2.3 十六进制转十进制

十六进制转为十进制要从右到左用八进制的每个数去乘以16的相应次方,小数点后则是从左往右。

计算举例:

(10A)₁₆ = 1 * 16² + 0 * 16¹ + 10 * 16 ⁰ = 256 + 0 + 10 = (266)₁₀

(111F)₁₆ = 1 * 16³ + 1 * 16² + 1 * 16¹ + 15 * 16⁰ = 4096 + 256 + 16 + 15 = (4383)₁₀

R语言实现


> strtoi(c("10A","111F"), 16L)
[1]  266 4383

3.  十进制转换非十进制计算方法

使用辗转相除法:

  • 整数部分:整数除以进制数(2,8,10,16),倒取余数,直到整数为0。
  • 小数部分:小数乘以进制数(2,8,10,16),正取整数,直接小数为0。

以 十进制转二进制为例,分为整数转二进制,和小数转二进制。

3.1 整数转二进制:

采用”除2取余,逆序排列”法:

  1. 用2整除一个十进制整数,得到一个商和余数
  2. 然后再用2去除得到的商,又会得到一个商和余数
  3. 重复操作,一直到商为小于1时为止
  4. 然后将得到的所有余数全部排列起来,再将它反过来逆序排列。

计算举例:

计算(5)₁₀的二进制。

计算位置
5/2211
2/2102
1/2013

(5)₁₀ = 反转(5%2, 2%2, 1%2) = 反转(1,0,1) = (101)₂

计算(42)₁₀的二进制。

计算位置
42/22101
21/21012
10/2503
5/2214
2/2105
1/2016

(42)₁₀ = 反转(42%2 ,  21%2  , 10%2 , 5%2, 2%2, 1%2) = 反转(0,1,0,1,0,1 ) = (101010)₂

R语言实现,我们可以使用intToBits()函数,输入十进制整数,会得到二进制的结果。


> intToBits(5)
 [1] 01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[20] 00 00 00 00 00 00 00 00 00 00 00 00 00

> intToBits(42)
 [1] 00 01 00 01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00
[20] 00 00 00 00 00 00 00 00 00 00 00 00 00

因为计算机内部表示数的字节单位是定长的,如8位、16位、32位。所以位数不够时,高位补零。R语言intToBits(),int是32位的,因此出现了大量的补的位数。

intToBits()函数输出结果非常长,不太利于阅读,我们可以改造一下显示。创建一个intToBitString()函数,把二进制的intToBits()结果转为字符串进行输出,同时去掉多余的0。


> intToBitString<-function(num){
+   a<-as.integer(intToBits(num))
+   a<-a[1:max(which(a==1))] + paste(rev(a), collapse = "") + } # 输出二进制结果 > intToBitString(5)
[1] "101"
> intToBitString(42)
[1] "101010"

3.2 小数转二进制:

采用”乘2取整,顺序排列”法:

  1. 用2乘十进制小数,可以得到积,将积的整数部分取出
  2. 再用2乘余下的小数部分,又得到一个积,再将积的整数部分取出
  3. 重复操作,直到积中的小数部分为零,此时0或1为二进制的最后一位,或者达到所要求的精度为止

计算举例:

(0.25)₁₀ = (0.25*2, 0.5*2) = (0,1) = (0.01)₂

(0.111)₁₀ = (0.111*2 ,  0.222*2  , 0.444*2 , 0.888*2, 0.776*2, 0.552*2, 0.104*2 …) = (0,0,0,1,1,1,0 … ) = (0.000110)₂ 取前7位

(5.25)₁₀ = 反转(5%2, 2%2, 1%2) . (0.25*2, 0.5*2) = (101)₂ .(0.01)₂ = (101.01)₂

用R语言实现,使用numToBits()函数,可以把带小数的数值,转换为64位的二进制数,然后可以用packBits()函数,进行数值还原来十进制数。


> a1<-numToBits(0.25);a1
 [1] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[20] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[39] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 01 01
[58] 01 01 01 01 01 00 00
> packBits(a1,type="double")
[1] 0.25

> a2<-numToBits(0.111);a2
 [1] 01 00 00 00 01 00 01 01 00 01 00 00 00 01 00 00 01 01 00
[20] 01 01 00 01 01 01 00 00 01 01 01 01 01 00 01 01 01 01 01
[39] 01 00 00 01 00 01 00 01 01 00 00 00 01 01 01 01 00 01 01
[58] 01 01 01 01 01 00 00
 
> a3<-numToBits(5.25);a3
 [1] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[20] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[39] 00 00 00 00 00 00 00 00 00 00 01 00 01 00 01 00 00 00 00
[58] 00 00 00 00 00 01 00
> packBits(a3,type="double")
[1] 5.25

本文我们进制转换的数学计算原理和R语言的函数使用,可以很方便的利用不同进制特点,构建自己的计算模型。

转载请注明出处:
http://blog.fens.me/r-binary-octal-hexadecimal/

打赏作者

用R语言实现汉明距离算法Hamming Distance

R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大。

R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。

要成为有理想的极客,我们不能停留在语法上,要掌握牢固的数学,概率,统计知识,同时还要有创新精神,把R语言发挥到各个领域。让我们一起动起来吧,开始R的极客理想。

关于作者:

  • 张丹,分析师/程序员/Quant: R,Java,Nodejs
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/r-hamming-distance/

前言

在文字处理时,经常会用到编码方式,把文字和数字进行编码转换,从而计算文本的相似性。比如英文,差一个字母,字母的顺序写反了。要实现对文字的模糊匹配,就可以用到汉明距离的思路,计算将一个字符串变换成另外一个字符串所需要替换的字符个数。

目录

  1. 汉明距离介绍
  2. 算法原理
  3. R语言代码实现

1. 汉明距离介绍 Hamming Distance

Hamming Distance汉明距离,是以理查德·卫斯里·汉明的名字命名的。在信息论中,两个等长字符串之间的汉明距离是两个字符串对应位置的不同字符的个数。换句话说,它就是将一个字符串变换成另外一个字符串所需要替换的字符个数。汉明距离的计算思路与莱文斯坦距离算法有类似之处,参考文章R语言莱文斯坦距离算法Levenshtein Distance

汉明距离计算,例如:

  • 二进制 1011101 与 1001001 之间的汉明距离是 2。
  • 字符串 2143896 与 2233796 之间的汉明距离是 3。
  • 字符串 toned 与 roses 之间的汉明距离是 3。

汉明重量,是字符串相对于同样长度的零字符串的汉明距离,也就是说,它是字符串中非零的元素个数:对于二进制字符串来说,就是 1 的个数,所以 1110001 的汉明重量是 4。

2. 算法原理

我们以d(x,y)表示两个数字x,y之间的汉明距离。

  • 对于两个等长字符串,可直接进行两个字符串对应位置的不同字符的个数。
  • 对于两个数字,可先转为二进制,然后进行异或运算,并统计结果为1的个数。

两个二进制组(00)与(01)的距离是1,(110)和(101)的距离是2。在一个码组集合中,任意两个编码之间汉明距离的最小值称为这个码组的最小汉明距离。最小汉明距离越大,码组越具有抗干扰能力。

例如,2个数字93和73,93的二进制为10001001,73的二进制为10110001,然后进行xor异或运算,结果为10100,对结果统计1的个数,即得到汉明距离为2。

3. R语言实现

3.1 字符串类型

针对字符串的建立汉明距离算法,计算汉明距离。


> haming_string<-function(txt1,txt2){
+   len1<-nchar(txt1)
+   len2<-nchar(txt2)
+   
+   if(len1!=len2){
+     stop(paste("length is not equal"))
+   }
+   
+   n<-0
+   for(i in 1:len1){
+     t1<-substring(txt1,i,i)
+     t2<-substring(txt2,i,i)
+     if(t1!=t2){
+       n<-n+1
+     }
+   }
+   return(n)
+ }

汉明距离测试。


> haming_string("1011101","1101001")
[1] 3
> haming_string("2143896","2233796")
[1] 3
> haming_string("toned","roses")
[1] 3

3.2 数字类型
针对数字的建立汉明距离算法,先转换为二进制,再进行异或计算,计算汉明距离。


> haming_int<-function(num1,num2){
+   b1<-intToBits(num1)
+   b2<-intToBits(num2)
+   x<-xor(b1,b2);x
+   length(which(x==1))
+ }

汉明距离测试。


> haming_int(93,73)
[1] 2
> haming_int(137,177)
[1] 3

本文我们了解汉明距离的原理和实现,后面我们就可以利用汉明距离的特性,对字符串和数字进行相似度的距离计算,从而实现文本的模糊匹配。本文代码:https://github.com/bsspirit/r-string-match/blob/main/hanmming.r

转载请注明出处:
http://blog.fens.me/r-hamming-distance/

打赏作者