Loading [MathJax]/extensions/tex2jax.js

Rainbow Engine

IT技術を分かりやすく簡潔にまとめることによる学習の効率化、また日常の気付きを記録に残すことを目指します。

Python

PythonのSQLAlchemyで「Identifier ‘XXX’ exceeds maximum length of 30 characters」エラーや「DtypeWarning: Columns (X) have mixed types.」警告が出た時の対処について

投稿日:2020年3月16日 更新日:

(0)目次&概説

(1) エラー1:sqlalchemy.exc.ArgumentError
 (1-1) 発生状況・エラーメッセージ
  (1-1-1) エラーメッセージ
  (1-1-2) エラーとなったソース
 (1-2) 原因
 (1-3) 対処方法
  (1-3-1) OracleDBのバージョンが12.2以上の場合
  (1-3-2) OracleDBのバージョンが12.1以下の場合
(2) 警告1:DtypeWarning: Columns (4) have mixed types.
 (2-1) 発生状況・エラーメッセージ
  (2-1-1) エラーメッセージ
  (2-1-2) エラーとなったソース
 (2-2) 原因
 (2-3) 対処方法

(1) エラー1:sqlalchemy.exc.IdentifierError

(1-1) 発生状況・エラーメッセージ

Web上にあるcsvデータを”datapackage”パッケージを用いて変数にロードし、それをPandasの”read_csv”関数でDataframeに取り込み、最後にPandasの”to_sql”関数でDBにINSERTしようとした際に発生しました(EngineはSQLAlchemyの”create_engine”で作成)。

(1-1-1) エラーメッセージ(抜粋)
  1. sqlalchemy.exc.IdentifierError: Identifier 'tournaments_1877-2017_unindexed_csv' exceeds maximum length of 30 characters

 

(1-1-2) エラーとなったソース
  1. import datapackage
  2. import cx_Oracle
  3. import sqlalchemy
  4. import pandas as pd
  5. import requests
  6. from io import StringIO
  7.  
  8. def main():
  9. headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36'}
  10. url = 'https://[your URL].json'
  11. dp = datapackage.Package(url)
  12. res = dp.resources
  13. from sqlalchemy import create_engine
  14. engine = create_engine('oracle://[your schema]:[your password]@[hostname]:[port]/[sid]')
  15. for res in res:
  16. if res.tabular:
  17. if res.descriptor['datahub']['type'] == 'derived/csv':
  18. s=requests.get(res.descriptor['path'], headers= headers).text
  19. df = pd.read_csv(StringIO(s))
  20. tab_name = res.descriptor['name']
  21. df.to_sql(con=engine,name=tab_name.lower(),schema='[your schema]',index=False,if_exists='replace')
  22.  
  23. if __name__ == '__main__':
  24. main()
  25.  

(図101)
エラー画面のキャプチャ

目次にもどる

(1-2) 原因

Oracleが許容するテーブルの最大文字数(30バイト)を超えたテーブル名を登録しようとしたためエラーとなっています(※正確にはOracleの12.2=12c Release2以降ではこの上限が128バイトに緩和されているため、12.1以前のバージョンを使用している場合のみ30バイトの上限制約があります)。今回の私の環境は11gなので、この30バイト制限に引っかかってしまいました・・。

目次にもどる

(1-3) 対処方法

(1-3-1) OracleDBのバージョンが12.2以上の場合

上限が128バイトのため、SQLAlchemy側でもその上限値を利用できるように”create_engine”ファンクションの引数に「max_identifier_length=128」を指定します。

  1. engine = create_engine('oracle://[your schema]:[your password]@[hostname]:[port]/[sid]',max_identifier_length=128)

 

このオプションですが、12.1以下のDBで実行しても結局は以下のエラーになり駄目でした(当然ですが・・)

  1. sqlalchemy.exc.DatabaseError: (cx_Oracle.DatabaseError) ORA-00972: identifier is too long
 
(1-3-2) OracleDBのバージョンが12.1以下の場合

残念ながら30バイト以下になるように調整を行うしか無さそうです・・(他に回避策があれば是非教えてください!)。私の場合は「str型」オブジェクトのreplaceメソッドを用いて文字列を短くしていきました・・。

(例)
テーブル名を”tmp_name”に格納して、strオブジェクトの”replace”メソッドを使って”_csv”といったような余分な文言を空白に置換して30バイト以下になるように調整をかけています。また下記の「res」はdatapackageの「Resouce」型オブジェクトで、「res.descriptor[‘name’]」でcsvファイルの名前を取得しています。
(※datapackageパッケージ⇒packageモジュール⇒Packageクラス⇒resourcesメソッド(Resoucesオブジェクトを返却))

  1. tmp_name = res.descriptor['name']
  2. tmp_name = tmp_name.replace('_unindexed','')
  3. tmp_name = tmp_name.replace('_csv','')
  4. tmp_name = tmp_name.replace('-','_')

 

(図102)
テーブル名を30バイト以下にするとエラーが消える

(2) 警告1:DtypeWarning: Columns (4) have mixed types.

(2-1) 発生状況・エラーメッセージ

(2-1-1) 警告メッセージ

“Columns”の後ろの(4)はカラムの番号を意味しており、(4)だとテーブルの左から4つ目のカラムになります。

  1. C:\Program Files\PyScripter\Lib\rpyc.zip\rpyc\core\protocol.py:590: DtypeWarning: Columns (4) have mixed types. Specify dtype option on import or set low_memory=False.
(2-1-2) 警告が出たソース
  1. import datapackage
  2. import cx_Oracle
  3. import sqlalchemy
  4. import pandas as pd
  5. import requests
  6. from io import StringIO
  7.  
  8. def main():
  9. headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36'}
  10. url = 'https://[your URL].json'
  11. dp = datapackage.Package(url)
  12. res = dp.resources
  13. from sqlalchemy import create_engine
  14. engine = create_engine('oracle://[your schema]:[your password]@[hostname]:[port]/[sid]')
  15. for res in res:
  16. if res.tabular:
  17. if res.descriptor['datahub']['type'] == 'derived/csv':
  18. s=requests.get(res.descriptor['path'], headers= headers).text
  19. df = pd.read_csv(StringIO(s))
  20. tmp_name = res.descriptor['name']
  21. tmp_name = tmp_name.replace('_unindexed','')
  22. tmp_name = tmp_name.replace('_csv','')
  23. tmp_name = tmp_name.replace('-','_')
  24. print(tmp_name)
  25. #df.to_sql(con=engine,name=tmp_name.lower(),schema='TENNISDBUSR2',index=False,if_exists='replace')
  26.  
  27. if __name__ == '__main__':
  28. main()

 

(図201)
コンソールに「DtypeWarning」が表示されている

目次にもどる

(2-2) 原因

一つのカラムに複数のデータ型の値が入り混じっている事が原因です。Pandasの”read_csv”関数はデータを読み込む際、csvの全てのデータを読み込んだ上で各カラム毎に何のデータ型かを推測しています。この処理はメモリを多く消費するのですが、low_memory=Trueのオプションで実行するとメモリの消費を抑えるためにデータ型の推測をしないため、そのような警告文が出ています。
しかし、この対処自体では問題が解決するわけではないため、後述の「dtype指定」を行っていくのが現実的となります。

目次にもどる

(2-3) 対処方法

もし警告が出ているカラムのデータ型が予め特定できる場合は、「dtype指定」を行うのが良いとされています。
「dtype指定」は例えば「dtype={‘week_year’:’int16′,’week_month’:’int16′}」のようにカラム毎に読み込む際のデータ型を事前に指定して、それを”read_csv”関数の引数として与える事で、データ型を特定するためのメモリ消費を削減できます。
今回は対象の列が「datetype」型のため、dtypeにて指定する事はできません(csv自体がinteger型、string型、float型のみを保持できるため)。そのためWorkaroundとして”read_csv”関数の引数にて「parse_dates=[列名list]」のオプションを指定して明示的にdatetype型としてデータを抽出させる事で対応します。

■修正前

  1. df = pd.read_csv(StringIO(s))

 

■修正後

  1. df = pd.read_csv(StringIO(s),parse_dates=['match_time'])

 

(図202)
parse_datesで”match_date”カラムを明示的にdatetime型で読み込み

目次にもどる

Adsense審査用広告コード


Adsense審査用広告コード


-Python

執筆者:


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

関連記事

Pythonのdatapackage学習中に遭遇したエラー「StopIteration」と「AttributeError」の対応

(0)目次&概説 (1) 記事の目的 (2) エラー1:AttributeError: ‘generator’ object has no attribute ‘n …

no image

PythonでValueError: If using all scalar values, you must pass an indexエラーが出た時の対処方法

  <目次> (1) PythonでValueError: If using all scalar values, you must pass an indexエラーが出た時の対処方法  ( …

Pythonでcsvを読み込んでデータベース化する方法~pandasのto_sqlを用いたテーブルへのINSERT~

<目次> (1) Pythonでcsvを読み込んでデータベース化する方法  (1-1) 構文(to_sql)  (1-2) 構文(to_sql)の引数  (1-3) サンプルプログラム (1) Pyt …

pyenvのインストール手順(Windows10の例)

  <目次> (1) pyenvのインストール手順(Windows10の例)  (1-1) pyenvとは?  (1-2) pyenvのインストール手順 (1) pyenvのインストール手順 …

PythonのFlaskフレームワークを用いたRest APIのサンプル

  <目次> (1) PythonのFlaskフレームワークを用いたRest APIのサンプル  (1-1) Flaskとは  (1-2) STEP1:Flaskフレームワークの導入  (1 …

  • English (United States)
  • 日本語
S