Python を使用して JDBC (または ODBC) で IRIS データベースにアクセスしています。 データを pandas データフレームにフェッチして、データを操作し、そこからグラフを作成したいと考えています。 JDBC の使用中に文字列処理で問題が発生しました。この投稿は、他の誰かが同じ問題を抱えている場合に役立つものです。 または、これを解決するより簡単な方法がある場合は、コメントで知らせてください!
私は OSX を使用しているので、私の問題がどれほど特殊なものであるかわかりません。私は Jupyter Notebook を使用していますが、他の Python プログラムまたはフレームワークを使用した場合でも、コードは通常同じです。
データベースからデータをフェッチすると、列の説明および任意の文字列データがデータ型 java.lang.String として返されます。文字列データ data を出力すると、予想される "paintherear" ではなく "(p,a,i,n,i,n,t,h,e,r,e,a,r)" のようになります。
これはおそらく、JDBC を使用してフェッチされるときに、データ型 java.lang.String の文字列が反復可能または配列として渡されるためです。 これは、使用している Python-Java ブリッジ (JayDeBeApi、JDBC など) が単一ステップで java.lang.String を Python str に自動的に変換しない場合に発生する可能性があります。
Python の str 文字列表現は、対照的に、文字列全体を単一の単位として持ちます。 Python が通常の str を (ODBC 経由などで) 取得する場合、個々の文字に分割されません。
この問題を解決するには、java.lang.String が Python の str 型に正しく変換されていることを確認する必要があります。 フェッチされたデータを処理するときにこの変換を明示的に処理できるため、データは反復可能または文字のリストとして解釈されません。
この文字列操作を行う方法はたくさんあります。これは私がやったことです。
import pandas as pd import pyodbc import jaydebeapi import jpype def my_function(jdbc_used) # Some other code to create the connection goes here cursor.execute(query_string) if jdbc_used: # Fetch the results, convert java.lang.String in the data to Python str # (java.lang.String is returned "(p,a,i,n,i,n,t,h,e,r,e,a,r)" Convert to str type "painintherear" results = [] for row in cursor.fetchall(): converted_row = [str(item) if isinstance(item, jpype.java.lang.String) else item for item in row] results.append(converted_row) # Get the column names and ensure they are Python strings column_names = [str(col[0]) for col in cursor.description] # Create the dataframe df = pd.DataFrame.from_records(results, columns=column_names) # Check the results print(df.head().to_string()) else: # I was also testing ODBC # For very large result sets get results in chunks using cursor.fetchmany(). or fetchall() results = cursor.fetchall() # Get the column names column_names = [column[0] for column in cursor.description] # Create the dataframe df = pd.DataFrame.from_records(results, columns=column_names) # Do stuff with your dataframe
ODBC 接続を使用すると、文字列が返されないか、NA になります。
Unicode データ (さまざまな言語の名前など) を含むデータベースに接続している場合、またはアプリケーションが非 ASCII 文字を保存または取得する必要がある場合は、データがデータ間で受け渡されるときに正しくエンコードされたままであることを確認する必要があります。データベースと Python アプリケーション。
このコードは、データベースへのデータの送信および取得時に、文字列データが UTF-8 を使用してエンコードおよびデコードされることを保証します。 これは、非 ASCII 文字を扱う場合、または Unicode データとの互換性を確保する場合に特に重要です。
def create_connection(connection_string, password): connection = None try: # print(f"Connecting to {connection_string}") connection = pyodbc.connect(connection_string ";PWD=" password) # Ensure strings are read correctly connection.setdecoding(pyodbc.SQL_CHAR, encoding="utf8") connection.setdecoding(pyodbc.SQL_WCHAR, encoding="utf8") connection.setencoding(encoding="utf8") except pyodbc.Error as e: print(f"The error '{e}' occurred") return connection
connection.setdecoding(pyodbc.SQL_CHAR、エンコーディング = "utf8")
SQL_CHAR 型 (通常は固定長の文字フィールド) をフェッチするときに、データベースから文字データをデコードする方法を pyodbc に指示します。
connection.setdecoding(pyodbc.SQL_WCHAR、エンコーディング = "utf8")
SQL_WCHAR、ワイド文字型 (つまり、SQL Server の NVARCHAR や NCHAR などの Unicode 文字列) のデコードを設定します。
connection.setencoding(encoding="utf8")
Python からデータベースに送信される文字列または文字データが UTF-8 を使用してエンコードされるようにします。
つまり、Python はデータベースと通信するときに、その内部 str 型 (Unicode) を UTF-8 バイトに変換します。
JAVA をインストール - dmg を使用
https://www.oracle.com/middleeast/java/technologies/downloads/#jdk23-mac
シェルを更新してデフォルトのバージョンを設定
$ /usr/libexec/java_home -V Matching Java Virtual Machines (2): 23 (arm64) "Oracle Corporation" - "Java SE 23" /Library/Java/JavaVirtualMachines/jdk-23.jdk/Contents/Home 1.8.421.09 (arm64) "Oracle Corporation" - "Java" /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home /Library/Java/JavaVirtualMachines/jdk-23.jdk/Contents/Home $ echo $SHELL /opt/homebrew/bin/bash $ vi ~/.bash_profile
JAVA_HOME をパスに追加します
export JAVA_HOME=$(/usr/libexec/java_home -v 23) export PATH=$JAVA_HOME/bin:$PATH
JDBC ドライバーを入手します
https://intersystems-community.github.io/iris-driver-distribution/
jar ファイルをどこかに置きます...$HOME に置きました
$ ls $HOME/*.jar /Users/myname/intersystems-jdbc-3.8.4.jar
ODBC が設定されていることを前提としています (別の日の例として、犬が私のメモを食べてしまいました...)。
注: これは私の実際のコードのハックです。変数名に注意してください。
import os import datetime from datetime import date, time, datetime, timedelta import pandas as pd import pyodbc import jaydebeapi import jpype def jdbc_create_connection(jdbc_url, jdbc_username, jdbc_password): # Path to JDBC driver jdbc_driver_path = '/Users/yourname/intersystems-jdbc-3.8.4.jar' # Ensure JAVA_HOME is set os.environ['JAVA_HOME']='/Library/Java/JavaVirtualMachines/jdk-23.jdk/Contents/Home' os.environ['CLASSPATH'] = jdbc_driver_path # Start the JVM (if not already running) if not jpype.isJVMStarted(): jpype.startJVM(jpype.getDefaultJVMPath(), classpath=[jdbc_driver_path]) # Connect to the database connection = None try: connection = jaydebeapi.connect("com.intersystems.jdbc.IRISDriver", jdbc_url, [jdbc_username, jdbc_password], jdbc_driver_path) print("Connection successful") except Exception as e: print(f"An error occurred: {e}") return connection def odbc_create_connection(connection_string): connection = None try: # print(f"Connecting to {connection_string}") connection = pyodbc.connect(connection_string) # Ensure strings are read correctly connection.setdecoding(pyodbc.SQL_CHAR, encoding="utf8") connection.setdecoding(pyodbc.SQL_WCHAR, encoding="utf8") connection.setencoding(encoding="utf8") except pyodbc.Error as e: print(f"The error '{e}' occurred") return connection # Parameters odbc_driver = "InterSystems ODBC" odbc_host = "your_host" odbc_port = "51773" odbc_namespace = "your_namespace" odbc_username = "username" odbc_password = "password" jdbc_host = "your_host" jdbc_port = "51773" jdbc_namespace = "your_namespace" jdbc_username = "username" jdbc_password = "password" # Create connection and create charts jdbc_used = True if jdbc_used: print("Using JDBC") jdbc_url = f"jdbc:IRIS://{jdbc_host}:{jdbc_port}/{jdbc_namespace}?useUnicode=true&characterEncoding=UTF-8" connection = jdbc_create_connection(jdbc_url, jdbc_username, jdbc_password) else: print("Using ODBC") connection_string = f"Driver={odbc_driver};Host={odbc_host};Port={odbc_port};Database={odbc_namespace};UID={odbc_username};PWD={odbc_password}" connection = odbc_create_connection(connection_string) if connection is None: print("Unable to connect to IRIS") exit() cursor = connection.cursor() site = "SAMPLE" table_name = "your.TableNAME" desired_columns = [ "RunDate", "ActiveUsersCount", "EpisodeCountEmergency", "EpisodeCountInpatient", "EpisodeCountOutpatient", "EpisodeCountTotal", "AppointmentCount", "PrintCountTotal", "site", ] # Construct the column selection part of the query column_selection = ", ".join(desired_columns) query_string = f"SELECT {column_selection} FROM {table_name} WHERE Site = '{site}'" print(query_string) cursor.execute(query_string) if jdbc_used: # Fetch the results results = [] for row in cursor.fetchall(): converted_row = [str(item) if isinstance(item, jpype.java.lang.String) else item for item in row] results.append(converted_row) # Get the column names and ensure they are Python strings (java.lang.String is returned "(p,a,i,n,i,n,t,h,e,a,r,s,e)" column_names = [str(col[0]) for col in cursor.description] # Create the dataframe df = pd.DataFrame.from_records(results, columns=column_names) print(df.head().to_string()) else: # For very large result sets get results in chunks using cursor.fetchmany(). or fetchall() results = cursor.fetchall() # Get the column names column_names = [column[0] for column in cursor.description] # Create the dataframe df = pd.DataFrame.from_records(results, columns=column_names) print(df.head().to_string()) # # Build charts for a site # cf.build_7_day_rolling_average_chart(site, cursor, jdbc_used) cursor.close() connection.close() # Shutdown the JVM (if you started it) # jpype.shutdownJVM()
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3