用 Python 在 Excel 中画画

十字绣大家都知道吧,今天咱们来玩个电子版的十字绣。

用 Python 读取图片的像素值,然后输出到 Excel 表格中,最终形成一幅像素画,也就是电子版的十字绣了。

准备

既然要读取图片,那就需要用到 Pillow 库,操作 Excel 需要用到 openpyxl 库,先把这两个库安装好。


nbsp;pip3 install openpyxl

nbsp;pip3 install Pillow

色值转换

从图片读取的像素块色值是 RGB 值,而 openpyxl 向 Excel cell 内填充颜色是十六进制色值,因此咱们先写一个 RGB 和十六进制色值转换的一个函数。

def rgb_to_hex(rgb):
    rgb = rgb.split(',')
    color = ''
    for i in RGB:
        num = int(i)
        color += str(hex(num))[-2:].replace('x''0').upper()
    return color

图片转换

有了色值转换函数,接下来要做的操作就是逐行读取图片的 RGB 色值,之后将 RGB 色值转换为十六进制色值填充到 Excel 的 cell 中即可。

def img2excel(img_path, excel_path):
    img_src = Image.open(img_path)
    # 图片宽高
    img_width = img_src.size[0]
    img_height = img_src.size[1]

    str_strlist = img_src.load()
    wb = openpyxl.Workbook()
    wb.save(excel_path)
    wb = openpyxl.load_workbook(excel_path)
    cell_width, cell_height = 1.01.0

    sheet = wb["Sheet"]
    for w in range(img_width):
        for h in range(img_height):
            data = str_strlist[w, h]
            color = str(data).replace("(""").replace(")""")
            color = rgb_to_hex(color)
            # 设置填充颜色为 color
            fille = PatternFill("solid", fgColor=color)
            sheet.cell(h + 1, w + 1).fill = fille
    for i in range(1, sheet.max_row + 1):
        sheet.row_dimensions[i].height = cell_height
    for i in range(1, sheet.max_column + 1):
        sheet.column_dimensions[get_column_letter(i)].width = cell_width
    wb.save(excel_path)
    img_src.close()

最后再来个入口函数,就大功告成啦~

if __name__ == '__main__':
    img_path = '/Users/xyz/Documents/tmp/03.png'
    excel_path = '/Users/xyz/Documents/tmp/3.xlsx'
    img2excel(img_path, excel_path)

惊艳时刻

激动的心,颤抖的手,来看下最终效果咋样。

神操作!居然有人用 Python 在 Excel 中画画

是不是觉得有那么一丝丝韵味呢…

总结

今天派森酱带大家一起实现了 Excel 像素画,小伙伴们可以发挥自己的想象,比如把女神的头像藏进 Excel 中然后发她,你猜女神会不会被惊艳到呢。

对此你还有什么好玩的想法,可以在评论区和其他小伙伴一起交流哦~

— EOF —
转自:https://mp.weixin.qq.com/s/Ct-mWlX5W3RwZJf8uCXQbw

Pycharm安装后无法启动(屏幕一闪就没了)

电脑上原来的pycharm版本有点低2019版,而且最近出了一点小问题,打开设置打不开。

算了算了,找一个新版本的来试一下

上官网去下载一个最新版 https://www.jetbrains.com/pycharm/download/#section=windows,我下载的是2021版,现在2022版还没有正式发布

安装很方便,一路下一步就可以

刚刚开始时会检测到有旧的版本,会问是否要删除旧版本的pycharm,由于有些项目还需要使用,懒得换环境,所以旧版本就没有卸载

安装完成后,双击桌面上图标,打开了开屏页,然后一闪,就没了。

看任务管理器里也没有进程,重启电脑一切照旧。

度娘一下,有说需要用管理员权限运行的,有说是安装了破解软件的,还有说是winsock问题,有说没有从官网下载软件……各种答案,各种尝试,还是没有用

也看到有说要修改配置文件

怀疑是因为多个版本的pycharm共存,导致高版本用不了低版本的配置的原因

找到配置文件

C:\Users\你的用户名\AppData\Roaming\JetBrains\PyCharm2021.3

打开 pycharm64.exe.vmoptions

最后一行修改为对应的 -javaagent: 参数就可以

然后重新打开pycharm 就可以了

顺手把汉化也装了

File ==> settings ==> plugins ==> 搜索 chinese ,下载最多的那个就是

学习Flask主站源码,构建自己的Web站点

大家好,我是肖恩,源码解析每周见

flask—website,是flask曾经的主站源码,使用flask制作,包含模版渲染,数据库操作,openID认证, 全文检索等功能。对于学习如何使用flask制作一个完备的web站点,很有参考价值,我们一起来学习它。

项目结构

flask-website已经归档封存,我们使用最后的版本8b08,包括如下几个模块:

模块 描述
run.py 启动脚本
websiteconfig.py 设置脚本
update-doc-searchindex.py 更新索引脚本
database.py 数据库模块
docs.py 索引文档模块
openid_auth.py oauth认证
search.py 搜素模块
utils.py 工具类
listings 一些展示栏
views 蓝图模块,包括社区,扩展,邮件列表,代码片段等
static 网站的静态资源
templates 网站的模版资源

flask-website的项目结构,可以作为flask的脚手架,按照这个目录规划构建自己的站点:

.
├── LICENSE
├── Makefile
├── README
├── flask_website
│   ├── __init__.py
│   ├── database.py
│   ├── docs.py
│   ├── flaskystyle.py
│   ├── listings
│   ├── openid_auth.py
│   ├── search.py
│   ├── static
│   ├── templates
│   ├── utils.py
│   └── views
├── requirements.txt
├── run.py
├── update-doc-searchindex.py
└── websiteconfig.py
  • run.py作为项目的启动入口
  • requirements.txt描述项目的依赖包
  • flask_website是项目的主模块,里面包括:存放静态资源的static目录; 存放模版文件的templates目录;存放一些蓝图模块的views模块,使用这些蓝图构建网站的不同页面。

网站入口

网站的入口run.py代码很简单,导入app并运行:

from flask_website import app
app.run(debug=True)

app是基于flask,使用websiteconfig中的配置进行初始化

app = Flask(__name__)
app.config.from_object('websiteconfig')

app中设置了一些全局实现,比如404页面定义,全局用户,关闭db连接,和模版时间:

@app.errorhandler(404)
def not_found(error):
    return render_template('404.html'), 404

@app.before_request
def load_current_user():
    g.user = User.query.filter_by(openid=session['openid']).first() 
        if 'openid' in session else None

@app.teardown_request
def remove_db_session(exception):
    db_session.remove()

@app.context_processor
def current_year():
    return {'current_year': datetime.utcnow().year}

加载view部分使用了两种方式,第一种是使用flask的add_url_rule函数,设置了文档的搜索实现,这些url执行docs模块:

app.add_url_rule('/docs/', endpoint='docs.index', build_only=True)
app.add_url_rule('/docs/<path:page>/', endpoint='docs.show',
                 build_only=True)
app.add_url_rule('/docs/<version>/.latex/Flask.pdf', endpoint='docs.pdf',
                 build_only=True)

第二种是使用flask的蓝图功能:

from flask_website.views import general
from flask_website.views import community
from flask_website.views import mailinglist
from flask_website.views import snippets
from flask_website.views import extensions
app.register_blueprint(general.mod)
app.register_blueprint(community.mod)
app.register_blueprint(mailinglist.mod)
app.register_blueprint(snippets.mod)
app.register_blueprint(extensions.mod)

最后app还定义了一些jinja模版的工具函数:

app.jinja_env.filters['datetimeformat'] = utils.format_datetime
app.jinja_env.filters['dateformat'] = utils.format_date
app.jinja_env.filters['timedeltaformat'] = utils.format_timedelta
app.jinja_env.filters['displayopenid'] = utils.display_openid

模版渲染

现在主流的站点都是采用前后端分离的结构,后端提供纯粹的API,前端使用vue等构建。这种结构对于构建小型站点,会比较复杂,有牛刀杀鸡的感觉。对个人开发者,还需要学习更多的前端知识。而使用后端的模版渲染方式构建页面,是比较传统的方式,对小型站点比较实用。

本项目就是使用模版构建,在general蓝图中:

mod = Blueprint('general', __name__)

@mod.route('/')
def index():
    if request_wants_json():
        return jsonify(releases=[r.to_json() for r in releases])

    return render_template(
        'general/index.html',
        latest_release=releases[-1],
        # pdf link does not redirect, needs version
        # docs version only includes major.minor
        docs_pdf_version='.'.join(releases[-1].version.split('.', 2)[:2])
    )

可以看到首页有2种输出方式,一种是json化的输出,另一种是html方式输出,我们重点看看第二种方式。函数render_template传递了模版路径,latest_release和docs_pdf_version两个变量值。

模版也是模块化的,一般是根据页面布局而来。比如分成左右两栏的结构,或者上下结构,布局定义的模版一般叫做layout。比如本项目的模版就从上至下定义成下面5块:

  • head 一般定义html页面标题(浏览器栏),css样式/js-script的按需加载等
  • body_title 定义页面的标题
  • message 定义一些统一的通知,提示类的展示空间
  • body 页面的正文部分
  • footer 统一的页脚

使用layout模版定义,将网站的展示风格统一下来,各个页面可以继承和扩展。下面是head块和message块的定义细节:

<!doctype html>
{% block head %}
<title>{% block title %}Welcome{% endblock %} | Flask (A Python Microframework)</title>
<meta charset=utf-8>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
<script type=text/javascript
  src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
{% endblock %}
<div class=box>
  ...
  <p class=nav>
    <a href="{{ url_for('general.index') }}">overview</a> //
    <a href="{{ url_for('docs.index') }}">docs</a> //
    <a href="{{ url_for('community.index') }}">community</a> //
    <a href="{{ url_for('extensions.index') }}">extensions</a> //
    <a href="https://psfmember.org/civicrm/contribute/transact?reset=1&id=20">donate</a>
  {% for message in get_flashed_messages() %}
    <p class=message>{{ message }}
  {% endfor %}
  ...

本项目首页的general/index继承自全局的layout,并对其中的body部分进行覆盖,使用自己的配置:

{% extends "layout.html" %}
    ....
{% block body %}
  <ul>
    <li><a href="{{ latest_release.detail_url }}">Download latest release</a> ({{ latest_release.version }})
    <li><a href="{{ url_for('docs.index') }}">Read the documentation</a>
    <li><a href="{{ url_for('mailinglist.index') }}">Join the mailinglist</a>
    <li><a href=https://github.com/pallets/flask>Fork it on github</a>
    <li><a href=https://github.com/pallets/flask/issues>Add issues and feature requests</a>
  </ul>
  ...
  • 这个列表主要使用了蓝图中传入的latest_release变量,展示最新文档(pdf)的url

数据库操作

网站有交互,必定要持久化数据。本项目使用的sqlite的数据库,比较轻量级。数据库使用sqlalchemy封装的ORM实现。下面的代码展示了如何创建一个评论:

@mod.route('/comments/<int:id>/', methods=['GET''POST'])
@requires_admin
def edit_comment(id):
    comment = Comment.query.get(id)
    snippet = comment.snippet
    form = dict(title=comment.title, text=comment.text)
    if request.method == 'POST':
        ...
        form['title'] = request.form['title']
        form['text'] = request.form['text']
        ..
        comment.title = form['title']
        comment.text = form['text']
        db_session.commit()
        flash(u'Comment was updated.')
        return redirect(snippet.url)
    ...
  • 创建comment对象
  • 从html的form表单中获取用户提交的title和text
  • 对comment对象进行赋值和提交
  • 刷新页面的提示信息(在模版的message部分展示)
  • 返回到新的url

借助sqlalchemy,数据模型的操作API简单易懂。要使用数据库,需要先创建数据库连接,构建模型等, 主要在database模块:

DATABASE_URI = 'sqlite:///' + os.path.join(_basedir, 'flask-website.db')
# 创建引擎
engine = create_engine(app.config['DATABASE_URI'],
                       convert_unicode=True,
                       **app.config['DATABASE_CONNECT_OPTIONS'])
# 创建session(连接)                
db_session = scoped_session(sessionmaker(autocommit=False,
                                         autoflush=False,
                                         bind=engine))
# 初始化
def init_db():
    Model.metadata.create_all(bind=engine)

# 定义基础模型
Model = declarative_base(name='Model')
Model.query = db_session.query_property()

Comment数据模型定义:

class Comment(Model):
    __tablename__ = 'comments'
    id = Column('comment_id', Integer, primary_key=True)
    snippet_id = Column(Integer, ForeignKey('snippets.snippet_id'))
    author_id = Column(Integer, ForeignKey('users.user_id'))
    title = Column(String(200))
    text = Column(String)
    pub_date = Column(DateTime)

    snippet = relation(Snippet, backref=backref('comments', lazy=True))
    author = relation(User, backref=backref('comments', lazy='dynamic'))

    def __init__(self, snippet, author, title, text):
        self.snippet = snippet
        self.author = author
        self.title = title
        self.text = text
        self.pub_date = datetime.utcnow()

    def to_json(self):
        return dict(author=self.author.to_json(),
                    title=self.title,
                    pub_date=http_date(self.pub_date),
                    text=unicode(self.rendered_text))

    @property
    def rendered_text(self):
        from flask_website.utils import format_creole
        return format_creole(self.text)

Comment模型按照结构化的方式定义了表名,6个字段,2个关联关系和json化和文本化的展示方法。

sqlalchemy的使用,在之前的文章中有过介绍,本文就不再赘述。

openID认证

一个小众的网站,构建自己的账号即麻烦也不安全,使用第三方的用户体系会比较合适。本项目使用的是Flask-OpenID这个库提供的optnID登录认证。

用户登录的时候,会根据用户选择的三方登录站点,跳转到对应的网站进行认证:

@mod.route('/login/', methods=['GET''POST'])
@oid.loginhandler
def login():
    ..
    openid = request.values.get('openid')
    if not openid:
        openid = COMMON_PROVIDERS.get(request.args.get('provider'))
    if openid:
        return oid.try_login(openid, ask_for=['fullname''nickname'])
    ..

从对应的模版上更容易理解这个过程, 可以看到默认支持AOL/Google/Yahoo三个账号体系认证:

{% block body %}
  <form action="" method=post>
    <p>
      For some of the features on this site (such as creating snippets
      or adding comments) you have to be signed in.  You don't need to
      create an account on this website, just sign in with an existing
      <a href=http://openid.net/>OpenID</a> account.
    <p>
      OpenID URL:
      <input type=text name=openid class=openid size=30>
      <input type=hidden name=next value="{{ next }}">
      <input type=submit value=Login>
    <p>
      Alternatively you can directly sign in by clicking on one of
      the providers here in case you don't know the identity URL:
    <ul>
      <li><a href=?provider=aol>AOL</a>
      <li><a href=?provider=google>Google</a>
      <li><a href=?provider=yahoo>Yahoo</a>
    </ul>
  </form>
{% endblock %}

在三方站点认证完成后,会建立本站点的用户和openid的绑定关系:

@mod.route('/first-login/', methods=['GET''POST'])
def first_login():
    ...
        db_session.add(User(request.form['name'], session['openid']))
        db_session.commit()
        flash(u'Successfully created profile and logged in')
    ...
  • session中的openid是第三方登录成功后写入session

三方登录的逻辑过程大概就如上所示,先去三方平台登录,然后和本地站点的账号进行关联。其具体的实现,主要依赖Flask-OpenID这个模块, 我们大概了解即可。

全文检索

全文检索对于一个站点非常重要,可以帮助用户在网站上快速找到适合的内容。本项目展示了使用whoosh这个纯python实现的全文检索工具,构建网站内容检索,和使用ElasticSearch这样大型的检索库不一样。总之,本项目使用的都是小型工具,纯python实现。

全文检索从/search/入口进入:

@mod.route('/search/')
def search():
    q = request.args.get('q') or ''
    page = request.args.get('page'type=int) or 1
    results = None
    if q:
        results = perform_search(q, page=page)
        if results is None:
            abort(404)
    return render_template('general/search.html', results=results, q=q)
  • q是搜素的关键字,page是翻页的页数
  • 使用perform_search方法对索引进行查询
  • 如果找不到内容展示404;如果找到内容,展示结果

在search模块中提供了search方法,前面调用的perform_search函数是其别名:

def search(query, page=1, per_page=20):
    with index.searcher() as s:
        qp = qparser.MultifieldParser(['title''content'], index.schema)
        q = qp.parse(unicode(query))
        try:
            result_page = s.search_page(q, page, pagelen=per_page)
        except ValueError:
            if page == 1:
                return SearchResultPage(None, page)
            return None
        results = result_page.results
        results.highlighter.fragmenter.maxchars = 512
        results.highlighter.fragmenter.surround = 40
        results.highlighter.formatter = highlight.HtmlFormatter('em',
            classname='search-match', termclass='search-term',
            between=u'<span class=ellipsis> … </span>')
        return SearchResultPage(result_page, page)
  • 从ttile和content中搜素关键字q
  • 设置使用unicode编码
  • 将检索结果封装成SearchResultPage

重点在index.searcher()这个索引, 它使用下面方法构建:

from whoosh import highlight, analysis, qparser
from whoosh.support.charset import accent_map
...
def open_index():
    from whoosh import index, fields as f
    if os.path.isdir(app.config['WHOOSH_INDEX']):
        return index.open_dir(app.config['WHOOSH_INDEX'])
    os.mkdir(app.config['WHOOSH_INDEX'])
    analyzer = analysis.StemmingAnalyzer() | analysis.CharsetFilter(accent_map)
    schema = f.Schema(
        url=f.ID(stored=True, unique=True),
        id=f.ID(stored=True),
        title=f.TEXT(stored=True, field_boost=2.0, analyzer=analyzer),
        type=f.ID(stored=True),
        keywords=f.KEYWORD(commas=True),
        content=f.TEXT(analyzer=analyzer)
    )
    return index.create_in(app.config['WHOOSH_INDEX'], schema)

index = open_index()
  • whoosh创建本地的索引文件
  • whoosh构建搜素的数据结构,包括url,title,,关键字和内容
  • 关键字和内容参与检索

索引需要构建和刷新:

def update_documentation_index():
    from flask_website.docs import DocumentationPage
    writer = index.writer()
    for page in DocumentationPage.iter_pages():
        page.remove_from_search_index(writer)
        page.add_to_search_index(writer)
    writer.commit()

文档索引构建在docs模块中:

DOCUMENTATION_PATH = os.path.join(_basedir, '../flask/docs/_build/dirhtml')
WHOOSH_INDEX = os.path.join(_basedir, 'flask-website.whoosh')

class DocumentationPage(Indexable):
    search_document_kind = 'documentation'

    def __init__(self, slug):
        self.slug = slug
        fn = os.path.join(app.config['DOCUMENTATION_PATH'],
                          slug, 'index.html')
        with open(fn) as f:
            contents = f.read().decode('utf-8')
            title, text = _doc_body_re.search(contents).groups()
        self.title = Markup(title).striptags().split(u'—')[0].strip()
        self.text = Markup(text).striptags().strip().replace(u'¶', u'')
    
    @classmethod
    def iter_pages(cls):
        base_folder = os.path.abspath(app.config['DOCUMENTATION_PATH'])
        for dirpath, dirnames, filenames in os.walk(base_folder):
            if 'index.html' in filenames:
                slug = dirpath[len(base_folder) + 1:]
                # skip the index page.  useless
                if slug:
                    yield DocumentationPage(slug)

  • 文档读取DOCUMENTATION_PATH目录下的源文件(项目文档)
  • 读取文件的标题和文本,构建索引文件

小结

本文我们走马观花的查看了flask-view这个flask曾经的主站。虽然没有深入太多细节,但是我们知道了模版渲染,数据库操作,OpenID认证和全文检索四个功能的实现方式,建立了相关技术的索引。如果我们需要构建自己的小型web项目,比如博客,完全可以以这个项目为基础,修改实现。

经过数周的调整,接下我们开始进入python影响力巨大的项目之一: Django。敬请期待。

小技巧

本项目提供了2个非常实用的小技巧。第1个是json化和html化输出,这样用户可以自由选择输出方式,同时站点也可以构建纯API的接口。这个功能是使用下面的request_wants_json函数提供:

def request_wants_json():
    # we only accept json if the quality of json is greater than the
    # quality of text/html because text/html is preferred to support
    # browsers that accept on */*
    best = request.accept_mimetypes 
        .best_match(['application/json''text/html'])
    return best == 'application/json' and 
       request.accept_mimetypes[best] > request.accept_mimetypes['text/html']

request_wants_json函数中判断头部的mime类型,进行根据是application/json还是text/html决定展示方式。

第2个小技巧是认证装饰器, 前面一个是登录验证,后一个是超级管理认证:

def requires_login(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if g.user is None:
            flash(u'You need to be signed in for this page.')
            return redirect(url_for('general.login', next=request.path))
        return f(*args, **kwargs)
    return decorated_function

def requires_admin(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not g.user.is_admin:
            abort(401)
        return f(*args, **kwargs)
    return requires_login(decorated_function)

这两个装饰器,在view的API上使用, 比如编辑snippet需要登录,评论需要管理员权限:

@mod.route('/edit/<int:id>/', methods=['GET''POST'])
@requires_login
def edit(id):
    ...

@mod.route('/comments/<int:id>/', methods=['GET''POST'])
@requires_admin
def edit_comment(id):
    ...

参考链接

  • https://github.com/pallets/flask-website
学习Flask主站源码,构建自己的Web站点
转自:

Apriori 关联规则算法(Python代码)

Apriori 关联规则算法(Python代码)

一、关联规则概述
1993年,Agrawal等人在首先提出关联规则概念,迄今已经差不多30年了,在各种算法层出不穷的今天,这算得上是老古董了,比很多人的年纪还大,往往是数据挖掘的入门算法,但深入研究的不多,尤其在风控领域,有着极其重要的应用潜力,是一个被低估的算法,很少见到公开的文章提及,我尝试一一剖析,希望给你带来一定的启示。
我倒是进行了比较深刻、全面的思考,并进行了大量的实验,这个话题感觉可以聊三天三夜。世界风云变幻,但本质没变化,各种关联一直存在,有意或无意的!
比如你女朋友,低头玩手指+沉默,那大概率生气了,那这就是你总结出来的规则。啤酒与尿布的例子相信很多人都听说过吧,故事是这样的:在一家超市中,人们发现了一个特别有趣的现象,尿布与啤酒这两种风马牛不相及的商品居然摆在一起,但这一奇怪的举措居然使尿布和啤酒的销量大幅增加了。为什么有这么奇怪现象呢?是因为美国妇女在丈夫回家前买尿布,然后丈夫顺手买了自己喜欢的啤酒,所以发生了这么有趣的事情。
很多人只记住了啤酒尿不湿,很少深入思考,我们稍微转换下,日常的事情,也存在非常多的关联规则?

二、应用场景举例

1、股票涨跌预测

放量+高换手率 -> 大概率上涨,历史数据挖掘,假如发现放量+高换手率的股票大概率上涨,则挖掘当天满足条件的个股,然后第二天买入,躺赚。

2、视频、音乐、图书等推荐

根据历史数据,如果大规模的存在某些用户看剧列表为:小时代 -> 上海堡垒,那么一个新的用户看了小时代,马上就给推荐上海堡垒,那大概率也会被观看,呼兰的账号,就是这么脏的。

3、打车路线预测(考虑时空)

根据大量的数据挖掘出以下规则
早上:起点家->目的地公司,
晚上:起点家->目的高铁站
周末:起点家->目的地购物中心
那当你每天早上打开软件的时候,打车软件就会推荐你的公司作为目的地,大大的减少用户的打车时间。如下图,我输入小区名称,马上给我推荐了三个地方,杭州东站第一位,因为平时的打车这个组合的支持度最高。
Apriori 关联规则算法(Python代码)

4、风控策略自动化挖掘

根据历史标题,总结出规律发现商品标题包含 老司机+百度网盘 -> 色情风险高,那后面遇到这标题包含这两个词语的,就直接拒绝了。
根据历史行为数据,发现了沉默用户+非常用地登录+修改密码->大概率都被盗号了,那一个新的账户满足这个三个条件,那马上就进行账户冻结或者实人认证,就能避免盗号风险的发生。
根据历史数据,发现用户A +B 每天都相隔10s登录 ,则可以认为A、B存在关联关系,可能是机器控制的同一批薅羊毛账户。
风控策略的自动化挖掘,这个也是我们后续要重点关注和讲解的地方。

三、3个最重要的概念

关联规则有三个核心概念需要理解:支持度、置信度、提升度,下面用最经典的啤酒-尿不湿案例给大家举例说明这三个概念,假如以下是几名客户购买订单的商品列表:
Apriori 关联规则算法(Python代码)

1、支持度

支持度 (Support):指某个商品组合出现的次数总订单数之间的比例。
在这个例子中,我们可以看到“牛奶”出现了 4 次,那么这 5 笔订单中“牛奶”的支持度就是 4/5=0.8。
Apriori 关联规则算法(Python代码)
同样“牛奶 + 面包”出现了 3 次,那么这 5 笔订单中“牛奶 + 面包”的支持度就是 3/5=0.6
Apriori 关联规则算法(Python代码)
这样理解起来是不是非常简单了呢,大家可以动动手计算下 ‘尿不湿+啤酒’的支持度是多少?

2、置信度

置信度 (Confidence):指的就是当你购买了商品 A,会有多大的概率购买商品 B,在包含A的子集中,B的支持度,也就是包含B的订单的比例。
置信度(牛奶→啤酒)= 3/4=0.75,代表购买了牛奶的订单中,还有多少订单购买了啤酒,如下面的表格所示。
Apriori 关联规则算法(Python代码)
置信度(啤酒→牛奶)= 3/4=0.75,代表如果你购买了啤酒,有多大的概率会购买牛奶?
Apriori 关联规则算法(Python代码)
置信度(啤酒→尿不湿)= 4/4=1.0,代表如果你购买了啤酒,有多大的概率会买尿不湿,下面的表格看出来是100%。
Apriori 关联规则算法(Python代码)
由上面的例子可以看出,置信度其实就是个条件概念,就是说在 A 发生的情况下,B 发生的概率是多大。如果仅仅知道这两个概念,很多情况下还是不够用,需要用到提升度的概念。比如A出现的情况下B出现的概率为80%,那到底AB是不是有关系呢,不一定,人家B本来在大盘中的比例95%。你的A出现,反而减少了B出现的概率。

3、提升度

提升度 (Lift):我们在做商品推荐或者风控策略的时候,重点考虑的是提升度,因为提升度代表的是A 的出现,对B的出现概率提升的程度。
提升度 (A→B) = 置信度 (A→B)/ 支持度 (B)
所以提升度有三种可能:
  • 提升度 (A→B)>1:代表有提升;

  • 提升度 (A→B)=1:代表有没有提升,也没有下降;

  • 提升度 (A→B)<1:代表有下降。

提升度 (啤酒→尿不湿) =置信度 (啤酒→尿不湿) /支持度 (尿不湿) = 1.0/0.8 = 1.25,可见啤酒对尿不湿是有提升的,提升度为1.25,大于1。
可以简单理解为:在全集的情况下,尿不湿的概率为80%,而在包含啤酒这个子集中,尿不湿的概率为100%,因此,子集的限定,提高了尿不湿的概率,啤酒的出现,提高了尿不湿的概率。

4、频繁项集

频繁项集(frequent itemset) :就是支持度大于等于最小支持度 (Min Support) 阈值的项集,所以小于最小值支持度的项目就是非频繁项集,而大于等于最小支持度的的项集就是频繁项集,项集可以是单个商品,也可以是组合。
频繁集挖掘面临的最大难题就是项集的组合爆炸,如下图:
Apriori 关联规则算法(Python代码)
随着商品数量增多,这个网络的规模将变得特别庞大,我们不可能根据传统方法进行统计和计算,为了解决这个问题,Apriori算法提出了两个核心思想:
某个项集是频繁的,那么它的所有子集也是频繁的
{Milk, Bread, Coke} 是频繁的 → {Milk, Coke} 是频繁的
如果一个项集是 非频繁项集,那么它的所有超集也是非频繁项集
{Battery} 是非频繁的 → {Milk, Battery} 也非平凡
如下图,如果我们已知B不频繁,那么可以说图中所有绿色的项集都不频繁,搜索时就要这些项避开,减少计算开销。
Apriori 关联规则算法(Python代码)
同理,如果下图所示,{A,B}这个项集是非频繁的,那虚线框后面的都不用计算了,运用Apriori算法的思想,我们就能去掉很多非频繁的项集,大大简化计算量,当然,面对大规模数据的时候,这种排除还是解决不了问题,于是还有FP-Growth(Frequent pattern Growth,频繁模式增长树)这种更高效的方法,后面有机会慢慢讲。
Apriori 关联规则算法(Python代码)
需要注意的是:
1)如果支持度和置信度阈值过高,虽然可以在一定程度上减少数据挖掘的时间,但是一些隐含在数据中的非频繁特征项容易被忽略掉,难以发现足够有用的规则;
2)如果支持度和置信度阈值过低,可能会导致大量冗余和无效的规则产生,导致较大计算量负荷。

四、Python算法介绍

这里用的是Python举例,用的包是apriori,当然R语言等其他语言,也有对应的算法包,原理都是一样的,大家自行进行试验。
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
#包安装 我们使用efficient-apriori,python中也可以利用apyori库和mlxtend库pip install efficient-apriori
#加载包from efficient_apriori import apriori
‘’‘apriori(transactions: typing.Iterable[typing.Union[set, tuple, list]], min_support: float=0.5, min_confidence: float=0.5, max_length: int=8, verbosity: int=0, output_transaction_ids: bool=False)上面就是这个函数的参数min_support:最小支持度min_confidence:最小置信度max_length:项集长度‘’‘
# 构造数据集data = [('牛奶','面包','尿不湿','啤酒','榴莲'), ('可乐','面包','尿不湿','啤酒','牛仔裤'), ('牛奶','尿不湿','啤酒','鸡蛋','咖啡'), ('面包','牛奶','尿不湿','啤酒','睡衣'), ('面包','牛奶','尿不湿','可乐','鸡翅')]#挖掘频繁项集和频繁规则itemsets, rules = apriori(data, min_support=0.6, min_confidence=1)#频繁项集print(itemsets){1: {('啤酒',): 4, ('尿不湿',): 5, ('牛奶',): 4, ('面包',): 4}, 2: {('啤酒''尿不湿'): 4, ('啤酒''牛奶'): 3, ('啤酒''面包'): 3, ('尿不湿''牛奶'): 4, ('尿不湿''面包'): 4, ('牛奶''面包'): 3}, 3: {('啤酒''尿不湿''牛奶'): 3, ('啤酒''尿不湿''面包'): 3, ('尿不湿''牛奶''面包'): 3}}itemsets[1] #满足条件的一元组合{('啤酒',): 4, ('尿不湿',): 5, ('牛奶',): 4, ('面包',): 4}itemsets[2]#满足条件的二元组合{('啤酒', '尿不湿'): 4,('啤酒', '牛奶'): 3,('啤酒', '面包'): 3,('尿不湿', '牛奶'): 4,('尿不湿', '面包'): 4,('牛奶', '面包'): 3}itemsets[3]#满足条件的三元组合{('啤酒', '尿不湿', '牛奶'): 3, ('啤酒', '尿不湿', '面包'): 3, ('尿不湿', '牛奶', '面包'): 3}#频繁规则print(rules)[{啤酒} -> {尿不湿}, {牛奶} -> {尿不湿}, {面包} -> {尿不湿}, {啤酒, 牛奶} -> {尿不湿}, {啤酒, 面包} -> {尿不湿}, {牛奶, 面包} -> {尿不湿}]
#我们把max_length=2这个参数加进去看看itemsets, rules = apriori(data, min_support=0.6,min_confidence=0.5,max_length=2)#频繁项集print(itemsets){1: {('牛奶',): 4, ('面包',): 4, ('尿不湿',): 5, ('啤酒',): 4, ('R',): 4}, 2: {('R', '啤酒'): 4, ('R', '尿不湿'): 4, ('R', '牛奶'): 3, ('R', '面包'): 3, ('啤酒', '尿不湿'): 4, ('啤酒', '牛奶'): 3, ('啤酒', '面包'): 3, ('尿不湿', '牛奶'): 4, ('尿不湿', '面包'): 4, ('牛奶', '面包'): 3}}#通过这个数据我们可以看到,项集的长度只包含有两个项了
五、挖掘实例
每个导演都有自己的偏好、比如周星驰有星女郎,张艺谋有谋女郎,且巩俐经常在张艺谋的电影里面出现,因此,每个导演对演员的选择都有一定的偏爱,我们以宁浩导演为例,分析下选择演员的一些偏好,没有找到公开的数据集,自己手动扒了一部分,大概如下,有些实在有点多,于是简化下进行分析。
Apriori 关联规则算法(Python代码)
可以看到,我们一共扒了9部电影,计算的时候,支持度的时候,总数就是9.
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
#把电影数据转换成列表 data = [['葛优','黄渤','范伟','邓超','沈腾','张占义','王宝强','徐峥','闫妮','马丽'], ['黄渤','张译','韩昊霖','杜江','葛优','刘昊然','宋佳','王千源','任素汐','吴京'], ['郭涛','刘桦','连晋','黄渤','徐峥','优恵','罗兰','王迅'], ['黄渤','舒淇','王宝强','张艺兴','于和伟','王迅','李勤勤','李又麟','宁浩','管虎','梁静','徐峥','陈德森','张磊'], ['黄渤','沈腾','汤姆·派福瑞','马修·莫里森','徐峥','于和伟','雷佳音','刘桦','邓飞','蔡明凯','王戈','凯特·纳尔逊','王砚伟','呲路'], ['徐峥','黄渤','余男','多布杰','王双宝','巴多','杨新鸣','郭虹','陶虹','黄精一','赵虎','王辉'], ['黄渤','戎祥','九孔','徐峥','王双宝','巴多','董立范','高捷','马少骅','王迅','刘刚','WorapojThuantanon','赵奔','李麒麟','姜志刚','王鹭','宁浩'], ['黄渤','徐峥','袁泉','周冬雨','陶慧','岳小军','沈腾','张俪','马苏','刘美含','王砚辉','焦俊艳','郭涛'], ['雷佳音','陶虹','程媛媛','山崎敬一','郭涛','范伟','孙淳','刘桦','黄渤','岳小军','傅亨','王文','杨新鸣']] #算法应用 itemsets, rules = apriori(data, min_support=0.5, min_confidence=1) print(itemsets){1: {('徐峥',): 7, ('黄渤',): 9}, 2: {('徐峥', '黄渤'): 7}} print(rules) [{徐峥} -> {黄渤}]
通过上述分析可以看出:
在宁浩的电影中,用的最多的是黄渤和徐峥,黄渤9次,支持度100%,徐峥7次,支持度78%,(‘徐峥’, ‘黄渤’) 同时出现7次,置信度为100%,看来有徐峥,必有黄渤,真是宁浩必请的黄金搭档,且是一对好基友。
 
当然,这个数据量比较小,我们基本上肉眼也能看出来,这里只是提供一个分析案例和基础方法,巩固下基础知识,算是开胃菜,大规模的数据,人眼无法直接感知的时候,算法的挖掘与发现,就显得特别有意义了,后续会陆续推出相应的文章。

 

转自:https://mp.weixin.qq.com/s/2iNO3som2MUnhJroMn9y5Q

读书:赵志强《Python量化投资:技术、模型与策略》

对于量化投资,Pythoner 学习流程一般可以分为如下六个部分。1)了解基础语法和数据结构。2)掌握Pandas的使用基础并进阶。3)掌握统计理论及金融学术理论。4)掌握金融量化实践、策略研究理论。5)学习回测平台开发。6)学习平台开发。

chp1 简介

广义,凡是借助于数学模型和计算机实现的投资方法都可以称为量化投资。

国内比较常见的量化投资方法包括股票多因子策略(阿尔法)、期货CTA策略、套利策略和高频交易策略等

量化投资策略的最大特点是其具有一套基于数据的完整交易规则。

量化投资的优势可以总结为三个词:客观性、大数据、响应快。量化投资一般通过回测来证实或者证伪策略的历史有效性;在研究或者决策中,通常会引入大量的数据来进行分析。;用计算机进行自动分析,所以分析和响应速度都十分迅速,一般能达到秒级,高频交易甚至是以微秒为单位的。

夏普比率是一种衡量策略表现是否优秀的常用指标,夏普值越高表示策略越优秀。

投资不是“优化”问题,投资是“预测”问题,是要预测市场的下一步应该怎么走。

AI在金融投资领域最大的问题是,可用的样本数据极其有限,也无法大量生成。股市有多少历史数据,就有多少样本数据,但也只有这么多。极其有限的样本数据,加上极其庞大的特征维度,是AI在金融预测建模上举步维艰的根本原因。众所周知的是,训练数据是AI的基本养料,数据有限,就会导致模型很难得到大幅度的提升。就那么多有效的因子,大家反复挖掘,失效的速度也越来越快。

在数据分析领域(包括量化投资),编程语言具有两大作用,一个是科学计算、统计等算法层面,主要用于业务的相关研究;另一个是系统应用开发,主要用来搭建基础IT设施,比如数据库、交易平台等。

python是坠吼滴

chp2 平台与工具

python 3.x ;Anaconda;IDE()

chp3 Python金融分析常用库介绍

NumPy(Numerical Python的简称)是高性能科学计算和数据分析的基础包

SciPy 是基于NumPy的,提供了更多的科学计算功能,比如线性代数、优化、积分、插值、信号处理等。

Pandas具有NumPy的ndarray所不具有的很多功能,比如集成时间序列、按轴对齐数据、处理缺失数据等常用功能

StatsModels是Python的统计建模和计量经济学工具包,包括一些描述统计、统计模型估计和推断。一般来讲,StatsModels能够很好地满足各类研究人员的统计计算需求。

DataFrame是一个表格型的数据结构

对于DataFrame数据类型,可以使用[]运算符来进行选取,这也是最符合习惯的。但是,对于工业代码,推荐使用loc、iloc等方法。因为这些方法是经过优化的,拥有更好的性能。

chp4 可视化分析

Python的可视化分析最底层的库是Matplotlib

Pandas 简单的可视化

seaborn 统计可视化库

本质都是调用 Matplotlib

散点图是最常用的探索两个数据之间相关关系的可视化图形

直方图主要用于研究数据的频率分布

Matplotlib和seaborn的中文乱码问题的解决方案。第一种是在画图的时候指定字体,第二种是修改配置文件

seaborn已经封装好了很多功能,在进行可视化分析时,优先考虑使用seaborn

javascript 中的 highcharts可以绘制很多动态的可视化图形,进行数据可视化研究非常方便。https://www.highcharts.com/stock/demo/intraday-candlestick

python 调用highcharts :https://github.com/kyper-data/python-highcharts/blob/master/examples/highstock/candlestick-and-volume.py

chp5 统计基础

以下都是numpy库内函数

rand和random_sample都是均匀分布(uniform distribution)的随机数生成函数。rand_sample传入一个n维的元组元素作为参数

randn 和 standard_normal是正态分布的随机数生成函数

randint和random_integers是均匀分布的整数生成函数

shuffle可以随机打乱一个数组,并且改变此数组本身的排列。

Permutation用于返回一个打乱后的数组值,但是并不会改变传入的参数数组本身。

N次伯努利试验的结果分布即为二项分布。使用binomial(1,p)即为一次二项分布。使用binomial(1,p,n)即表示生成n维的二项分布数组,也就是伯努利分布。

统计量,就是利用数据的函数变化,从某种维度来反映全体数据集的特征的一种函数。

mean函数可用于计算数组的平均值

median函数可用于计算数组的中位数

std函数用于计算数组的标准差

var函数用于计算数组的方差

频率分布直方图其实是对一个变量的分布密度(分布)函数进行近似估计的一个手段。

np.histogram可以用来计算一位数组的直方图数据

SciPy包含了大量的处理连续随机变量的函数,每种函数都位于与其对应的分布类中。每个类都有对应的方法来生成随机数,从而计算PDF、CDF,使用MLE进行参数估计,以及矩估计等。

回归分析是通过建立模型来研究变量之间的相互关系并进行模型预测的一种有效工具。

案例:以平安银行的月度收益率为例来了解某只股票的风险回报率是否与大盘指数基金风险回报率有关,且相关度有多高?

思考的问题是,如果大盘涨1个点,那么平安银行股票统计概率上会上涨多少点?

假定如下模型:银行月度收益率=α+β*深证成指月度收益率+ε

即平安银行月度收益率与深证成指月度收益率呈线性相关,并且受随机扰动项影响

这又带来了新的问题。这个结论可靠度有多高?这个系数会不会浮动?从历史数据来看,这个浮动区间应该是多少?

能否利用数据来拒绝这一假设。这一类问题,在统计学上统称为假设检验(Hypothesis Testing),为了解决这一问题,需要使用的手段被称为统计推断(Statistical Inference)。

当我们拒绝这个正确的假设时,我们犯的错误,称为第一类错误(Type I Error)

即使假设是错误的,我们也会接受这个假设,这类错误称为第二类错误(Type II Error)

拒绝假设的概率函数称为功效函数(Power Function)。而由于我们无法同时满足最小化两类错误,因此必须要寻找到一个平衡点,以控制更严重的错误,并适当牺牲犯不严重错误的概率。哪一类错误更为严重是根据实际问题来确定的。

常用的设计检测框架的模式是,控制第一类错误的发生概率,并且调节假设,使得发生第一类错误更加严重,这个严重度会得到统计学者的控制。

chp6 数据预处理和初步探索

收集到初步的样本数据之后,接下来需要考虑的几个问题是:样本数据的质量是否有问题,如果有问题,应该怎么处理?样本数据是否出现了意外的情况?样本数据包含哪些基本的统计特征,有没有明显的规律?为了便于后续的深入分析和建模,我们需要对数据进行哪些处理?

数据清理:原始数据的可能问题,数据缺失、噪声或者离群点、数据不一致

中心趋势度量主要包括均值、中位数、众数

数据散布或者发散的度量。这些度量包括方差、极差、分位数等。

方差和标准差是最常见的数据散布度量,它们可用于指出数据分布的散布程序。低标准差意味着数据趋向于非常靠近均值,而高标准差则表示数据散布在一个大的范围中

极差是数据中最大值和最小值的差值,数值X的N个样本x1,x2,…,xN的极差可以记作max(X)-min(X)。

挑选某些数据点,这些数据点刚好可以将数据划分成大小相等的集合,这些数据点称为分位数

直方图是以一种图形方法来概括给定数值X的分布情况的图示。

散点图(scatter plot)是确定两个数值变量X、Y之间看上去是否存在联系,以及具有怎样的相关模式的最有效的图形方法之一

五数概括由中位数、两个四分数、最小、最大值组成

chp7 Pandas进阶与实战

可以将多重索引对象看成是一个由元组(tuple)元素组成的数组,其中,每一个元组对象都是唯一的。MultiIndex既可以由嵌套数组创建(使用MultiIndex.from_arrays),也可以由元组组成的数组创建(使用MultiIndex.from_tuples),或者指定每个维度的索引值,自动循环生成索引(使用MultiIndex.from_product)

chp8 金融基础概念