Day17

前回の続き。

def download_document(doc_id, save_path):
    params = {'type': 1}
    doc = get_document(doc_id, params)
    if doc.status_code == 200:
        with open(save_path + doc_id + '.zip', 'wb') as f:
            for chunk in doc.iter_content(chunk_size=1024):
                f.write(chunk)


・文書をダウンロードする関数。paramsでtype:1を指定して、この上に書かれていたget_document関数にdoc_idとparamsを渡して、返ってきたstatus_codeが200つまりOKだったら、save_path+doc_id+.zipで構成されているファイルを開いて、doc.iter_content()でバイナリデータを1024バイトずつ数回に分けてファイルに書き込んでいる。chunk とは一塊の意味で、このwith からの文は何かにおいての定番の書き方らしい。

今日は以上。備忘録。

Day17

PythonをAtomエディタのatom-runnerで実行する | RemoNote
ImportError: no module named requestsでハマった時は上記を見る。

MacのAtomでPython3.x.xを適用する方法 - Qiita
importError が出るのはAtom Runnerが原因ではないかと思い、これも参考にした。でも違ったが、前進。

結局PATHが原因でImportErrorが出ていた。

import sys
print(sys.path)
['', '--------------', '-----------------']

sys.path.append('requestsやモジュールが置いてあるところのPATH')

import requests

これでできる。

以上。ここや、以前に記したものはすべて備忘録。以上。

Day16 わからないが頑張るで!

今日からはデータ分析の基本コードを書いていく。

import pandas as pd 
data = [
    [60,90,89],
    [89,79,89],
    [99,88,77],
]
df = pd.DataFrame(data)
df

pandasのデータフレームにデータを入れると表が出力される

df.columns = ["english", "math", "art"]
df.index = ["a-ta","b-ko","c-suke"]

とすると縦の列にeng math art が出力される
横の行にはa-ta、b-ko、c-sukeが表示される


次にデータ量が多くなると大変なのでそういう時はCSVファイルを読み込むようにする。以下はUTF-8形式。

import pandas as pd 
df = pd.read_csv("test.csv")
df 


ファイル形式がShift-JIS形式もあるのでUTF-8形式かどちらか確認したら、以下のように使用する。

import pandas as pd 
df = pd.read_csv("test.csv", encording="Shift_JIS")
df
df.columns
df.index

で列の項目と、行の項目を取ってこれる。

列名をリストに変換することもできる。

list1 = [i for i in df.columns]
print(list1)

出力結果:["www", "eee", "rrrrrrr", "tttt"]

list2 = [i for in df.index]
print(list2)

出力結果:["赤か", "ええええ", "かかか", "djdjdjd"]

データの数を取ることもできる.

len(df)
出力結果:6


欠損値の処理もできる。以下など。

# 欠損値を前の値で埋める
df = df.fillna(method="ffill")

# 欠損値を平均値で埋める
df = df.fillna(df.mean())

以上!今日は以上!

edinet xbrl python 写経

# データをまとめて取得する関数
SUMMARY_TYPE = 2
def download_all_documents(date, save_path, doc_type_codes=['120', '130', '140', '150','160','170']): 
    params = {'date':date, 'type':SUMMARY_TYPE}
    doc_summary = get_submitted_summary(params)
    df_doc_summary = pd.DataFrame(doc_summary['results'])
    df_meta = pd.DataFrame(doc_summary['metadata'])

    # 対象となる報告書のみ抽出
    if len(df_doc_summary) >= 1:
        df_doc_summary = df_doc_summary.loc[df_doc_summary['docTypeCode'].isin(doc_type_codes)]

        # 一覧を保存
        if not os.path.exists(save_path + date):
            os.makedirs(save_path + date)
        df_doc_summary.to_csv(save_path + date + '/doc_summary.csv')

        # 書類を保存
        for _, doc in df_doc_summary.iterrows():
            download_document(doc['docID'], save_path + date + '/')
            open_zip_file(doc['docID'], save_path + date + '/')
        return df_doc_summary

    return df_doc_summary


・SUMMARY_TYPEを2に設定しておく。
・download_all_documents関数には引数をdate, save_path, doc_type_codeを指定する
・get_submitted_summaryにparamsを渡すとjsonが取得できるため、paramsを用意する
・doc_summaryに取ってきたjsonを入れる。
・df_doc_summaryに取ってきたjsonからresultsデータを取り出して代入
・df_metaにも同様に取ってきたjsonからmetaデータを取り出して代入

~~~~if文~~~~~~~
・len(df_doc_summary).......df_doc_summaryの長さが1以上だったら
 ・df_doc_summaryに、locで位置を指定して、ここではdf_doc_summary['docTypeCode'] で列を指定、.isinでdoc_type_codesが含まれているものを返す。それをdf_doc_summaryに代入
  
  locの記述について。ありがとうございます。
  Pandasで要素を抽出する方法(loc,iloc,iat,atの使い方) - DeepAge

   #一覧を保存
  ・os.path(ファイルやディレクトリが指定したパスに存在するかを確認したり、パスからファイル名や拡張子を取得するなどを行う際に利用される)にsave_pathとdateが存在していなかったら、
   ・os.makedirsでディレクトリを作成
  ・df_doc_dummaryを、save_pathとdateとdoc_summary.csvを渡してcsvファイルに書き出す。

  #書類を保存
  ・繰り返し処理を用いてdf_doc_summaryのデータフレームに入っているデータをiterrows()メソッドを用いて、1行ずつ、インデックス名(行名)とその行のデータ(pandas.Series型)のタプル(index, Series)を取得する。
    ・前回作成したdownload_document関数にdoc['docID']とパス名を渡す
    ・open_zip_fileで解凍。


# zipファイルを解凍する関数
def open_zip_file(doc_id, save_path):
    if not os.path.exists(save_path):
        os.makedirs(save_path + doc_id)

    with zipfile.ZipFile(save_path + doc_id + '.zip') as zip_f:
        zip_f.extractall(save_path + doc_id)

・この関数はdoc_idとsave_pathを渡して使用する関数
・もしos.pathにsave_pathが存在していなかったら
 ・save_pathにdoc_idを付け加えたディレクトリを作成

 ・with zipfile.ZipFile........ZipFileオブジェクトはclose()で閉じる必要があるが、with文を使うと中身が終わったときに自動で閉じてくれる。 .extractall().........ZipFileオブジェクトのextractall()メソッドで、ZIPファイルの中身がすべて解凍(展開)される。第一引数pathに展開先のディレクトリのパスを指定する。省略するとカレントディレクトリに解凍される。アリ10。
PythonでZIPファイルを圧縮・解凍するzipfile | note.nkmk.me
つまりzip_f.extractall()で中身を解凍して解凍先に展開。


# 日付でループするので、そのための設定をし、とりあえず2020年3月1日から2020年6月30日までに提出された報告書を取得する。
from datetime import datetime, timedelta

def date_range(start_date: datetime, end_date: datetime):
    diff = (end_date - start_date).days + 1
    return (start_date + timedelta(i) for i in range(diff))

start_date = datetime(2020, 3, 1)
end_date = datetime(2020, 6, 30)
# 取得する。今回は有価証券報告書のみに限定する。指定したフォルダに4か月間に提出された有価証券報告書が保存される。
doc_type_code = ['120']
save_path = 'original_data/'

for i, date in enumerate(date_range(start_date, end_date)):
    date_str = str(date)[:10]
    df_doc_summary = download_all_documents(date_str, save_path)
    if i == 0:
        df_doc_summary_all = df_doc_summary.copy()
    else:
        df_doc_summary_all = pd.concat([df_doc_summary_all, df_doc_summary])
# 上記で取得したデータを使用して、取得したいデータを抽出する方法
from edinet_xbrl.edinet_xbrl_parser import EdinetXbrlParser

# 以下の関数でkeyとcontext_refを指定し、pathで指定したファイルからデータを取得する。
def get_one_xbrl_data(path, key, context_ref):
    parser = EdinetXbrlParser()

    # 指定したxbrlファイルをパースする
    xbrl_file_path = path
    edinet_xbrl_object = parser.parse_file(xbrl_file_path)

    # データの取得
    data = edinet_xbrl_object.get_data_by_context_ref(key, context_ref)
    if data is not None:
        return data.get_value()
# 例えば今期の売上を取りたい場合は、
# keyがjpcrp_cor:NetSalesSummaryOfBusinessResultsで、contextRefがCurrentYearDurationなので、以下のように指定する。
import glob

seccode = 7816
# 文書IDと提出日を取得
doc_id = df_doc_summary_all.query(f'secCode=="{seccode}0"').docID.values[0]
date = df_doc_summary_all.query(f'secCode=="{seccode}0"').submitDateTime.values[0]

# XBRLを取得
file_path = save_path + date[:10] + '/' + doc_id + '/XBRL/PublicDoc/*.xbrl'
file_path = glob.glob(file_path)[0]

# データを取得
get_one_xbrl_data(file_path, key='jpcrp_cor:NetSalesSummaryOfBusinessResults', context_ref='CurrentYearDuration')

# テキスト情報も取得可能
get_one_xbrl_data(file_path, key='jpcrp_cor:BusinessRisksTextBlock', context_ref='FilingDateInstant')


# 欲しいデータをまとめて取得
account_df = pd.read_csv('account_list.csv').fillna('')

def get_xbrl_data(path, account_df):
    parser = EdinetXbrlParser()

    # parse xbrl file and get data container
    xbrl_file_path = path
    edinet_xbrl_object = parser.parse_file(xbrl_file_path)
    ## データの取得
    account_df['value'] = ''
    for idx, account in account_df.iterrows():
        key = account['key']
        context_ref = account['context_ref']
        data = edinet_xbrl_object.get_data_by_context_ref(key, context_ref)

        if data is not None:
            account_df.loc[idx, 'value'] = data.get_value()
            #print(key, context_ref, account['value'])
    return account_df


また新しい発見したら更新する。

ありがとうございます!
https://data-analytics.fun/2020/12/22/ufo-analysis-data-collection/

Day15

import requests
import os
import zipfile

import numpy as np
import pandas as pd
from pandas import DataFrame


import matplotlib.pyplot as plt
import seaborn as sns; sns.set();

・seaborn.....pythonでグラフを作るためのライブラリ
sns.set()でグラフが作れちゃうらしい。

# 一覧の取得関数を作成
EDINET_API_URL = "https://disclosure.edinet-fsa.go.jp/api/v1"
def get_submitted_summary(params):
    url = EDINET_API_URL + '/documents.json'
    response = requests.get(url, params=params)

    assert response.status_code==200

    return response.json()

・edinet_api_urlを作成して、

・そこにparamsを渡すget_submitted_summary関数を作る。
・urlに最初に作成したUR Lにdocuments.jsonを付け加える。
・responseにrequestした情報をurlとparamsを取得して、
・status_codeが200じゃなかったらエラーを表示する。assertは条件文がTrue以外の時に例外を投げる。
・response.json().......jsonを読み取る。

# 作成した関数を呼び出して一覧を取得し、pandasのDataFrameに変換することで利用できる
date = '2020-03-30'
SUMMARY_TYPE = 2
params = {'date':date, 'type':SUMMARY_TYPE}
doc_summary = get_submitted_summary(params)
df_doc_summary = pd.DataFrame(doc_summary['results']) # 1
df_doc_summary


# 書類を取得する関数
def get_document(doc_id, params):
    url = EDINET_API_URL + '/documents/' + doc_id
    response = requests.get(url, params)

    return response

# zip形式でダウンロードする関数
def download_document(doc_id, save_path):
    params = {'type': 1}
    doc = get_document(doc_id, params)
    if doc.status_code == 200:
        with open(save_path + doc_id + '.zip', 'wb') as f:
            for chunk in doc.iter_content(chunk_size=1024):
                f.write(chunk)


df_doc_summary.query('edinetCode=="E31070"')

download_document(doc_id='S100IC1B', save_path='./')

・df_doc_summary.query('edinetCode=="E31070"').........queryメソッドを使用することで、行を選択する事ができる。


次回からはコードを読むことに労力をかけよう。そしてそれがたまったら再度それを書いてみよう。そうしよ。だから次回からは、説明を多くとることにしよ。

ありがとうございます!
https://data-analytics.fun/2020/12/22/ufo-analysis-data-collection/

Day14 Django session json 取り出し

# zip_code
{
    "message": null,
    "results": [
        {
            "address1":"北海道",
            "address2":"札幌市",
            "address3":"札幌",
            "kana1":"ホッカイドウ",
            "kana2":"サッポロシ",
            "kana3":"サッポロ",
            "prefcode":"1",
            "zipcode":"1000001"
        }
    ],
    "status":200
}
{
    "message":null,
    "results":null,
    "status":200
}

def fetch_address(zip_code):
    REQUEST_URL = f'http://zipcloud.ibsnet.co.jp/api/search?zipcode={zip_code}'
    address = ' '
    response = request.get(REQUEST_URL)
    response = json.loads(response.text)
    result, api_status = response['results'], response['status']
    if api_status == 200;
        result = result[0]
        address = result['address1'] + result['address2'] + result['address3']
        return address

~~~~~get_address関数について~~~~~~~
・この関数はzip_codeで入力し、ちゃんと情報が返って来たら、その情報を合わせて返す。
・response = request.get(REQUEST_URL).............responseに、返ってきたURLを取得して、代入
json.loads(response.text) .......... おそらくjson.loads()でresponse.textを読み込んでいる。ただresponse.textがなんでできるのかがわからない。
・result, api_status = response['results'], response['status'] .............. これでresultにresponse['results']を、api_statusにresponse['status']を入れる。
  
 ~~~~if文の中身~~~~~~~~
 api_statusが200だったら、
  resultに最初の辞書を入れる。
  次にaddressに辞書の中身を追加する
  addressを返す。



#Buy Form
classBuyForm(forms.Form):
    zip_code = forms.CharField(
        label = '郵便番号',
        max_length = 7,
        required = False,
        widget=forms.TextInput(attrs={'placeholder': '数字7桁(ハイフンなし)'})
    )

    address = forms.CharField(
        label = '住所',
        max_length = 100,
        required = False
    )

・formで住所検索とアドレスを入力するフォームを作成
・label ......ただのラベルでは.....なかった。これはhtmlで参照する事ができるlabelらしい。例えばhtmlで{{ buy_form.zip_code.label_tag }}のように使う事ができるらしい。ありがとうございます!
Django - フォームの改造 - Qiita

@login_required
def bag(request):
    user = request.user
    bag = request.session.get('bag', {})
    bag_products = dict()
    total_price = 0
    for product_id, num in bag.items():
        product = Product.objects.get(id=product_id)
        bag_products[product] = num
        total_price = product.price * num

    buy_form = BuyForm(request.POST or None)
    if buy_form.is_valid():
        if 'search_address' in request.POST:
            zip_code = request.POST['zip_code']
            address = get_address(zip_code)
            if not address:
                messages.warning(request, "住所を取得できませんでした。")
                return redirect('app:bag')
            buy_form = BuyForm(initial={'zip_code':zip_code, 'address':address})

        if 'buy_product' in request.POST:
            if not buy_form.cleaned_data['address']:
                messages.warning(request, "住所の入力は必須だ")
                return redirect('app:bag')

            if not bool(bag):
                messages.warning(request, "バッグは空やで")
                return redirect('app:bag')

            if total_price > user.point:
                messages.warning(request, "ポイントがねえぜ")
                return redirect('app:bag')

            for product_id, num in bag.items():
                if not Product.objects.filter(pk=product_id).exists():
                    del bag[product_id]
                product = Product.objects.get(pk=product_id)
                sale = Sale(product=product, user=request.user, amount=num, price=product.price)
                sale.save()

            user.point -= total_price
            user.save()
            del request.session['bag']
            messages.success(request, "商品の購入が完了や。")
            return redirect('app:bag')

        context = {
            'buy_form': buy_form,
            'bag_products': bag_products,
            'total_price': total_price, 
        }

・buy_form = BuyForm(request.POST or None) ............ buy_formにBuyFormでrequest.POSTを入れる

~~~~~~if文の中身~~~~~~~~~~
・buy_form.is_valid():......buy_formが有効だったら
 ・送られてきた情報が''search_addressだったら
  ・送られてきた情報からzip_codeを取り出す
  ・addressにget_address関数のzip_codeを入れて
  ・もしアドレスがなかったら...
  ・buy_formにBuyFormにinitialで初期値を設定。

 ・送られてきた情報が'buy_product'だったら
  ・もしbuy_formにaddressが入力されていなかったら
  ・bagが空か否か
  ・ポイント不足か否か

   ~~for文の中身~~~
  ・product_idとnumにbag辞書から取り出したキーとバリューを入れて
   ・もしproduct_idが存在していなかったら、
    ・bag辞書のproduct_idを削除
   ・productにProductテーブルからproduct_idをとってきて代入
   ・saleにSaleテーブルに入れる値を渡して代入
   ・保存

  ・total_priceからuserpointを減らす
  ・userを保存。
  ・終わったsessionを削除
  ・bag関数に返す。

 ・contextにbuy_formとbag_productsとtotal_priceをセット。

Day13-3 sessionについてだぜい

# urls.py
urlpatterns = [
    ...
    ...
    path('change_item_amount', views.change_amount, name='change_amount'),
    ...
]

"""
何かボタンをhtmlにおきたい時はそれ専用のurlを作ったりもする。
でもリダイレクト先はviews.pyのどこかの関数に送ったりする場合もある。
今回で言えば、change_amount.htmlというのは存在しない。
っていうか作らない。なんせそんなの必要ないから。当然か。。。。
"""


# views.py

@login_required
@require_POST
def change_amount(request):
    product_id = request.POST["product_id"]
    bag_session = request.session['bag']
    if product_id in bag_session:
        if 'remove_amount' in request.POST:
            bag_session[product_id] -= 1
        if 'addon_amount' in request.POST:
            bag_session[product_id] += 1
        if bag_session[product_id] <= 0:
            del bag_session[product_id]
    return redirect('app:bag')


# xxx.html

{% block content %}

<form action={%  url 'app:change_amount' %} method='post'>{% csrf_token %}
    <input type="hidden" name="product_id" value="{product.id}">
    <input type="submit" name="remove_amount" value="一つ減らす">
    <input type="submit" name="addon_amount" value="一つ増やす">
</form>

{% endblock %}

・product_id = request.POST["product_id"].......product_idに送られて来たproduct_idを代入
・bag_session = request.session['bag'] ........'bag'というsessionを取り出して、bag_sessionに代入
・if product_id in bag_sessionの文から.......送られて来たproduct_idがbag_sessionの中に存在したら、
   ・'remove_amount'(htmlのinputのnameでつけたところ。formかinputはnameとvalueをペアにして情報を送る。)の情報が送られて来たら、bag_session[product_id]を一つ減らす。その下は逆のパターン
   ・で0個以下だったら、bag_session[product_id]を削除する。



っちゅう感じやな。よし。おけーーーーーい。次いいいいいいいいい!