2018-12-24

PandasでUnicodeDecodeError: 'shift_jis' codec can't decode byte 0xff in position 0: illegal multibyte sequenceが起こった時の対処法

状況


- 読み込もうとしているCSVファイルが、unicode形式になっている
- 文字コードの確認方法はこちら。

CSVファイルが文字化けしてExcelで見れない時の対処法について|Excel実務のノウハウのお勉強


- pandasのread_csvメソッドで。以下のようにCSVを読み込もうとする

stop_words = pd.read_csv(stop_words_filename, encoding="shift-jis")

問題


- 以下のエラーが発生した

UnicodeDecodeError: 'shift_jis' codec can't decode byte 0xff in position 0: illegal multibyte sequence

原因


- Pandasは、基本的にurf-8で文字コードを読み込もうとしている。utf-8以外の場合、encoding指定をしてあげる必要がある。
- 上のコードでは、shift-jisで読み込もうとしている
- しかし、元のファイルがunicodeなので読み込めていない。

対策


- 元のファイルをutf-8でencoding指定なし、もしくはshift-jis形式に変換をおこなう必要がある。
- どちらにせよ、ファイルの形式をunicodeから何等かの値に変更する必要がある

Excelの場合はこちらをutf-8形式に保存する
ExcelファイルをUTF-8のCSV形式で保存する方法(Excel 2016最新機能)40,710

libreofficeで修整する場合はこう。

https://help.libreoffice.org/Common/Export_text_files/ja




2018-12-20

Flaskを利用した並列処理をThreadを利用する際の忘備録

# スレッド(thread)とは

> プログラムの処理の実行単位
引用:スレッド (thread)とは

# スレッドを複数利用するタイミング


・並列処理を行う必要がある場合

# Pythonで複数Threadを利用する場合



  • Celery
    • ジョブキューを使う
    • redisなど使うため準備が少し必要
  • Thread

import time
import threading

def func1():
    while True:
        print("func1")
        time.sleep(1)


def func2():
    while True:
        print("func2")
        time.sleep(1)


if __name__ == "__main__":
    thread_1 = threading.Thread(target=func1)
    thread_2 = threading.Thread(target=func2)

    thread_1.start()
    thread_2.start()

引用:Pythonの並列・並行処理サンプルコードまとめ


# Flaskで利用する場合



from datetime import datetime
from flask import Flask, make_response
from time import sleep
import threading

app = Flask(__name__)

class MyThread(threading.Thread):
    def __init__(self):
        super(MyThread, self).__init__()
        self.stop_event = threading.Event()

    def stop(self):
        self.stop_event.set()

    def run(self):
        try:
            for _ in range(1000):
                print(f'{datetime.now():%H:%M:%S}')
                sleep(1)

                # 定期的にフラグを確認して停止させる
                if self.stop_event.is_set():
                    break
        finally:
            print('時間のかかる処理が終わりました\n')

jobs = {}

@app.route('/start/<id>/')
def root(id):
    t = MyThread()
    t.start()
    jobs[id] = t
    return make_response(f'{id}の処理を受け付けました\n'), 202

@app.route('/stop/<id>/')
def stop(id):
    jobs[id].stop()
    del jobs[id]
    return make_response(f'{id}の中止処理を受け付けました\n'), 202

@app.route('/status/<id>/')
def status(id):
    if id in jobs:
        return make_response(f'{id}は実行中です\n'), 200
    else:
        return make_response(f'{id}は実行していません\n'), 200


引用:PythonでThreadを使うflaskサンプルを作ってみた

# 並列処理の注意点



  • Flaskのデフォルトでは複数のリクエストを同時に処理することができません。
  • 並列処理を有効にして同時アクセスを可能にするにはthreaded=Trueパラメータを設定します。


引用) Flaskのデフォルトでは同時アクセスを処理できない





2018-12-07

Flaskで多対多のデータベース構成にするときにやるあれ

Flaskで簡単なWebアプリを作るときに、DBの持ち方をどうするか問題にぶち当たったので、忘備録。

* Flask-SQLAlchemy
* Flask-Migrate

を使って、DBを管理している。

モデルは

* User
* Followers

というテーブルを持っている。User間のフォロー機能を実装したい。


app/init.py
from flask import Flask
from config import Config, CONSUMER_KEY, CONSUMER_SECRET, CALLBACK_URL
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager


app = Flask(__name__)
db = SQLAlchemy(app)
migrate = Migrate(app, db)

app.config.from_object(Config)
login = LoginManager(app)

from app import routes, models
from app.models import User

でモデルが

models.py

from datetime import datetime
from app import db
from flask_login import UserMixin
from app import login

followers = db.Table('followers',
    db.Column('follower_id', db.Integer, db.ForeignKey('user.id')),
    db.Column('followed_id', db.Integer, db.ForeignKey('user.id'))
)

likers = db.Table('likers',
    db.Column('liker_id', db.Integer, db.ForeignKey('user.id')),
    db.Column('liked_id', db.Integer, db.ForeignKey('user.id'))
)

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    registered_at = db.Column(db.DateTime, index=True, default=datetime.utcnow)
    last_updated_at = db.Column(db.DateTime, index=True, default=datetime.utcnow)
    last_logined_at = db.Column(db.DateTime, index=True, default=datetime.utcnow)
    twitter_id = db.Column(db.String(64), index=True, unique=True) 
    screen_name = db.Column(db.String(64), index=True, unique=True)
    username = db.Column(db.String(64), index=True, unique=True)
    image_url = db.Column(db.String(128), index=True, unique=True)
    description = db.Column(db.String(300), index=True, unique=True)

    followed = db.relationship(
    'User', secondary=followers,
    primaryjoin=(followers.c.follower_id == twitter_id),
    secondaryjoin=(followers.c.followed_id == twitter_id),
    backref=db.backref('followers', lazy='dynamic'), lazy='dynamic')

    liked = db.relationship(
    'User', secondary=likers,
    primaryjoin=(likers.c.liker_id == twitter_id),
    secondaryjoin=(likers.c.liked_id == twitter_id),
    backref=db.backref('likers', lazy='dynamic'), lazy='dynamic')

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

    def mutual_ids_list(self):
        print ("twitter_id".format(self.twitter_id))
        followed_ids = self.followed.filter(
            followers.c.followed_id == self.twitter_id)
        following_ids = self.followed.filter(
            followers.c.follower_id == self.twitter_id)
        print ("followed_ids:".format(followed_ids))
        print ("follower_ids:".format(following_ids))
        print (list(set(following_ids & set(followed_ids))))

    def like(self, user):
        if not self.is_liking(user):
            self.liked.append(user)

    def unlike(self, user):
        if self.is_liking(user):
            self.liked.remove(user)

    def is_likeing(self, user):
        return self.likeed.filter(
            likers.c.liked_id == user.id).count() > 0

    def __repr__(self):
        return '<User {}>'.format(self.twitter_id)

class Follower(db.Model):
    id = db.Column(db.Integer, primary_key=True)

class Liker(db.Model):
    id = db.Column(db.Integer, primary_key=True)

的な感じ。これで、pythonから多対多のモデルが作れるのか確認。

python3

>>> from app.models import User, Follower
>>> from app import db

>>> u1 = User(twitter_id=3)
>>> u2 = User(twitter_id=4)
>>> db.session.add(u1)
>>> db.session.add(u2)
>>> db.session.commit()
>>> u1.follow(u2)
>>> u2.follow(u1)
>>> db.session.commit()
>>> u1.followed.all()
[<User 4>]


できてる。sqlite3にアクセスして、followerのテーブルにデータがあるのか確認。

bash

$ sqlite3 app.db

SQLite version 3.11.0 2016-02-15 17:29:24
Enter ".help" for usage hints.
sqlite> select * from followers
   ...> ;
|
|
1|2
2|1
1|2
1|2
2|1
3|4
4|3

できているはず。
あとはこのfollowersテーブルにsqlalchemyからアクセスしたいんだけど、そこがようわからんので調査が必要。

FlaskでWebアプリを作るときの忘備録

FlaskでWebアプリを作っているので、今後使いそうなところの忘備録

## flask-login


主な機能

  • login/logoutが login_user(user)/logout_user()で簡単に管理できる
  • ログインが必要なページを @login_requiredデコレーターで簡単に管理できる
  • グローバル変数current_userがスクリプトからもテンプレートからも使える
  • ログインしてない時はcurrent_userにAnonymousUserMixinオブジェクトがセットされ、いい感じに使える
    • is_active, is_authenticated == False
    • is_anonymous == True
    • get_id() returns None

## SQLAlchemy


  • Railsでいうrake db:migrateみたいな感じで、マイグレーション管理ができるパッケージ
  • flask db init -> DBの初期化
  • flask db migrate -> マイグレーションファイルの作成
  • flask db upgrade -> マイグレーションファイルの実行
  • flask db downgrade -> ロールバック

## One to One, One to Manyのリレーション


  • One to Manyのリレーションは下記コードの通り


class Person(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)
    addresses = db.relationship('Address', backref='person', lazy=True)

class Address(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(120), nullable=False)
    person_id = db.Column(db.Integer, db.ForeignKey('person.id'),
        nullable=False)

  • One to Oneのリレーションは下記コードの通り
    • modelsの外部にForeignKey指定でユーザーIDを指定してあげる
    • 引用元はflask mega tutorial


app/models.py: Followers association table
followers = db.Table('followers',
    db.Column('follower_id', db.Integer, db.ForeignKey('user.id')),
    db.Column('followed_id', db.Integer, db.ForeignKey('user.id'))
)

  • ユーザーモデルの中に、以下のように書いてあげる。


class User(UserMixin, db.Model):
    # ...
    followed = db.relationship(
        'User', secondary=followers,
        primaryjoin=(followers.c.follower_id == id),
        secondaryjoin=(followers.c.followed_id == id),
        backref=db.backref('followers', lazy='dynamic'), lazy='dynamic')

  • フォローするなら、こんな感じ。


class User(UserMixin, db.Model):
    #...

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

routingしてあげるときはこんな感じ。

app/routes.py: Follow and unfollow routes.
@app.route('/follow/<username>')
@login_required
def follow(username):
    user = User.query.filter_by(username=username).first()
    if user is None:
        flash('User {} not found.'.format(username))
        return redirect(url_for('index'))
    if user == current_user:
        flash('You cannot follow yourself!')
        return redirect(url_for('user', username=username))
    current_user.follow(user)
    db.session.commit()
    flash('You are following {}!'.format(username))
    return redirect(url_for('user', username=username))

@app.route('/unfollow/<username>')
@login_required
def unfollow(username):
    user = User.query.filter_by(username=username).first()
    if user is None:
        flash('User {} not found.'.format(username))
        return redirect(url_for('index'))
    if user == current_user:
        flash('You cannot unfollow yourself!')
        return redirect(url_for('user', username=username))
    current_user.unfollow(user)
    db.session.commit()
    flash('You are not following {}.'.format(username))
    return redirect(url_for('user', username=username))




参考

注目の投稿

 PythonのTweepyを利用して、Twitter APIを利用している。 その中で、ハマったポイントをメモしておく。 まず、Searchに関して。 Twitter検索は、クライアントアプリ側では、全期間の検索が可能になっている。 一方で、APIを利用する際は、過去1週間しか...