Python处理MP3的歌词和图片

Thursday, January 21st, 2010

一些MP3播放器(包括iphone、ipod、itouch、blackberry等)可以在播放mp3的时候显示专辑图片、歌词等信息而不需要额外的图片文件和歌词文件,仅仅一个mp3文件就搞定,比较有意思。除了用专门的软件(比如itunes)来制作这样的mp3,我们还可以用程序来批量生成。 查阅mp3头信息ID3V2的技术文档,发现可以往ID3信息里面加入歌词和图片信息(可以在页面上查找Lyrics、Attached picture就能发现相应的内容)。有了官方格式上的支持,我们要做的就是把歌词和图片加入到MP3文件中去。 测试一些开源的软件包,发现一个比较可靠的:eyeD3,由python语言编写,直接上代码: #coding=utf-8 import eyeD3 import re # mp3文件 mp3_file = '/path/to/foobar.mp3' # lrc歌词文件 lrc_file = '/path/to/foobar.lrc' # 专辑图片 pic_file = '/path/to/foobar.jpg' # 实例化eyeD3 tag = eyeD3.Tag() # 绑定到mp3文件 tag.link(mp3_file) # 去掉原文件中可能存在的图片 tag.removeImage() # 去掉原文件中可能存在的歌词 tag.removeLyrics() # 设定编码,非常重要,否则不支持中文 tag.encoding = '\x01' # 添加图片 tag.addImage(3, pic_file, u'') # 添加歌词,注意要utf-8编码,去掉lrc中时间信息 tag.addLyrics(re.sub('(\[.*?\][\n]*)+', '', unicode(open(lrc_file, 'r')).read(), 'utf8'))) # 更新到文件 tag.update() 代码非常简单,需要注意的是设定编码,不然歌词就乱码了。有了eyeD3之后,可以写个爬虫,从网上抓下歌词和图片直接灌进MP3文件里面,剩下的就是享受了。

从140秒到2秒的优化

Thursday, November 12th, 2009

从2亿个0~2,000,000,000之间的数字样本中找出不重复的记录总数,首先想到的是bloom filter,转念一想既然全都是数字,bloom filter有点太重,bitarray也许更有效,于是第一个版本出来,部分代码如下: ba = bitarray(212**4) cnt = 0 for i in data: if (not ba[i]): cnt += 1 ba[i] = True print cnt 大概需要140s左右,觉得if (not ba[i]):这个比较费,改了第二版: for i in data: ba[i] = True print ba.count() 速度有所提升,到了120s左右,开始打起多核运算的主意了,山寨了一个map-reduce,首先通过maper把数据按照除4得余分成4份: def maper(data): ...

Python转换office word文件为HTML

Thursday, March 12th, 2009

这里测试的环境是:windows xp,office 2007,python 2.5.2,pywin32 build 213,原理是利用win32com接口直接调用office API,好处是简单、兼容性好,只要office能处理的,python都可以处理,处理出来的结果和office word里面“另存为”一致。 #!/usr/bin/env python #coding=utf-8 from win32com import client as wc word = wc.Dispatch('Word.Application') doc = word.Documents.Open('d:/labs/math.doc') doc.SaveAs('d:/labs/math.html', 8) doc.Close() word.Quit() 关键的就是doc.SaveAs('d:/labs/math.html', 8)这一行,网上很多文章写成:doc.SaveAs('d:/labs/math.html', win32com.client.constants.wdFormatHTML),直接报错: AttributeError: class Constants has no attribute 'wdFormatHTML' 当然你也可以用上面的代码将word文件转换成任意格式文件(只要office 2007支持,比如将word文件转换成PDF文件,把8改成17即可),下面是office 2007支持的全部文件格式对应表: wdFormatDocument = ...

slope one算法在Netflix Prize上的表现

Tuesday, January 6th, 2009

本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享,转载请保留链接http://chaoqun.17348.com/2009/01/slope-one-on-netflix-prize/ 前段时间做了个slope one算法在Netflix Pirze上的测试,关于slope one算法可以参考我之前写的文章:Slope one:简单高效的推荐算法,Netflix Prize是DVD在线租赁商Netflix于2006年10月2日发起一项竞赛:任何组织或个人只要能够提交比它现有电影推荐系统Cinematch效果好10%的新方法,就可以获得100万美元的奖金。竞赛最多持续到2011年10月2日。同时,NetflixPrize还提供每年5万美元的年度进步奖。为什么是五万美元的年度进步奖呢?因为100万美元大奖存银行,每年的利息是5万美元,看来老外是挺有意思的。 Netflix Pirze采用RMSE作为评测的标准,RMSE中文的意思是均方根误差,计算的公式:  其中Ot是原始数据,Ft是预测数据,n是样本数。 目前最好的成绩是有BellKor in BigChaos创造的0.8598(2009.01.05数据),比官方数据提高9.63%,这样可以推算出官方的数据应该是0.9492左右,BellKor in BigChaos也是2008年度进步奖的获得者,他们更新数据的频率很高,如不出意外,最后的大奖应该是他们的。 目前的方向大多是SVD奇异值分解,可以参见BellKor in BigChaos他们的文章http://www.netflixprize.com/community/viewtopic.php?id=1193,这里有一个开源的项目nprize,也是一个SVD处理Netflix Prize的模型,大部分代码是Python的(国外很多用Python做数据挖掘和自然语言处理的案例),成绩到了0.9046,折算比官方数据要高4.70%,如果你想拿SVD热热手,可以看看这个开源项目,过些日子我会写一些SVD应用在推荐系统方面的文章,着急的可以阅读吴军的文章数学之美 系列十八 - 矩阵运算和文本处理中的分类问题。 由于对slope one算法比较熟悉,决定看看slope one算法在netflix pirze上的表现如何,netflix prize训练数据太大,大概有1亿多条的打分记录,OpenSlopeOne在运行效率方面不是很好,OpenSlopeOne处理几百万上千万的打分数据还是非常不错的,更多的话需要更多的机器水平扩展,如果有好的Mysql DBA可以尝试优化一下mysql server的参数,估计效果会很好,当然也需要有好的机器配合,我这里是用Python写的一个程序,多进程+多线程+手工Map-Reduce,需要源代码的话可以找我拿。 跑出来的结果不是很好,我的得分是0.9829,比官方数据差0.0337,大概差3.55%,分析原因可能是用户打分的数据普遍偏高,人们大多给自己喜欢的电影打分,对于不喜欢的数据打分就少很多了,下面是打分数据的分布情况: mysql> select rating,count(*) as times from nf_log group by rating; +--------+----------+ | rating | times    | +--------+----------+ |      1 |  4617904 | |      2 | 10131945 | |      3 | 28810978 | |      4 | ...

用Python创建线程池

Tuesday, December 23rd, 2008

本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享,转载请保留链接http://chaoqun.17348.com/2008/12/python-thread-pool/ 最近在做一些数据挖掘方面的工作,需要对海量的数据进行处理,在目前的硬件环境下,多进程+多线程的方式对运算时间的减少大有裨益,我用的是Python语言,开发效率高,运算效率也不低。 Python里面成型的线程池可以看以下http://www.chrisarndt.de/projects/threadpool/,看了一下API介绍,应该写的比较完备了,我这里想介绍的是Python线程池实现的原理以及一个简明的线程池代码实例。 我这里是用Queue这个包来实现的,Queue翻译成中文就是队列,全局的,我们把任务放进队列中去,然后开N个线程,每个线程都去队列中取一个任务,执行完了之后告诉系统说我执行完了,然后接着去队列中取下一个任务,直至队列中所有任务取空,退出线程。 这就是一般的线程池实现的原理,下面看一个实际的代码: import time import threading import Queue class Worker(threading.Thread):     def __init__(self, name, queue):         threading.Thread.__init__(self)         self.queue = queue         self.start()     def run(self): # 著名的死循环,保证接着跑下一个任务         while True:             # 队列为空则退出线程             if self.queue.empty():                 break             # 获取一个项目             foo = self.queue.get()             # 延时1S模拟你要做的事情             time.sleep(1)             # 打印             print self.getName(),':', foo             # 告诉系统说任务完成             self.queue.task_done() # 队列 queue = ...

利用orange进行关联规则挖掘

Tuesday, August 26th, 2008

本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享,转载请保留链接http://chaoqun.17348.com/2008/08/data-mining-with-python-orange-association_rule/ 最近,趁着项目的间隙,折腾了一阵数据挖掘,在同事的帮助下,对新浪音乐用户的听歌记录进行了一个简易挖掘,希望能根据用户以往的听歌记录,推荐出用户可能感兴趣的其他歌曲。 Orange: 一个模块化的C++数据挖掘包,提供python接口(好像也只提供了python接口),网址是http://www.ailab.si/orange/ 关联分析: 我这里用的是类似购物篮分析,每个用户的听歌id是一个事务,不熟悉关联分析的同学可以去搜一些相关方面的资料。 数据准备: 简单清洗掉一些“脏”数据(逻辑上有问题的数据,比如某个用户在5s听了200首歌),得到类似下面的数据 15615,355029,750367,762147,803787,805014,999712,999712,999712,1013641,1024215,1028429 871029,952779,962769 1023040,1024077,1024215,1025600 757946,873801,873801,873801 862257,873479 286056,286056,286056,286056,286056,286056,286056,286056,286056,286056 873801,873801,873801,873801,873801,947750,947750 473221,473537,504206,504206,504206,504206,504206,504206 947750,1005430,1005430 974748,1024215 873479,873479,873801,873801,947750,965748,999721,1024215,1024215,1024215,1024215,1024215 873801,873801,873801 每一行是一个用户的听歌记录,没有做去重处理(orange示例中也没有,是不是可能会增加歌曲的权重?不清楚,没有去阅读orange代码),注意文件名一定要以.basket为扩展名,程序中文件地址是d:/datamining/sample.basket。 程序: # 导入orange包 import orange # 导入数据,注意不需要后缀 data = orange.ExampleTable("d:/datamining/sample") # 挖掘关联规则,输入最低支持度、最低置信度、最大项集数 rules = orange.AssociationRulesSparseInducer(data, support = 0.5, confidence = 0.6, maxItemSets = 1000000) # 打印出规则来 for r in rules: print "%5.3f %5.3f %s" % (r.support, r.confidence, r) 是不是非常的简单?Orange实现的是Apriori算法,由于Apriori算法的问题,一旦数据量非常大,你就等着你的内存消耗光吧,反正我这边要是把所有数据都导入进去的话,笔记本1.5G的的内存根本不够用,可以试试FP-tree算法,我这边参考文章利用sql改良构建fp-tree之技术,已经把fp-tree的前缀路径都找出来了,需要的朋友可以私下找我要,由fp前缀路径挖频繁集需要用到递归,用sql去处理就非常费劲了,所以后面的算法还需要自己去探索。 居于关联规则的挖掘就告一段落,因为算法的计算复杂度非常高,效果倒不是太好(因为对于音乐,用户可能听多遍,这样打分就不一样,可能用关联规则去挖电影类的数据比较好,因为电影一般最多就看一遍),现在研究的是协同过滤,如果不出意外的话,一个改良版的PHP+Mysql实现slope one算法过几天就要出来了,到时候我会开源出来的。