取得したFitbitデータをグラフ化する

出力されたHRに関するデータ「HR_2023-06-26.csv」をPythonでグラフ化してみましょう。大まかな手順は下記の動画をご覧ください。

ChatGPTに、データのグラフ化を行うPythonスクリプト作成を依頼します。

00:00:00,74
00:01:00,64
00:02:00,63
00:03:00,65
00:04:00,65
00:05:00,62
00:06:00,65
00:07:00,63
00:08:00,66
00:09:00,65
00:10:00,65
00:11:00,67
00:12:00,66
00:13:00,66
00:14:00,69
00:15:00,72
00:16:00,73
00:17:00,69
00:18:00,67

上記のデータはHH:MM:SS形式で指定された各時刻の心拍数です。このデータの名前は、「HR_2023-06-26.csv」です。このデータを読み込んで、グラフ化するPythonスクリプトを書いてください。

データを扱うスクリプトを作成するときは、上記のように、サンプルを与えてやるとそれに合わせたスクリプトを作成してくれます。上記プロンプトをお黒と、ChatGPTが下記のようなスクリプトを作成してくれます。

import pandas as pd
import matplotlib.pyplot as plt

# データを読み込む
data = pd.read_csv('HR_2023-06-26.csv', header=None, names=['Time', 'HeartRate'])

# 時刻データを正しいフォーマットに変換
data['Time'] = pd.to_datetime(data['Time'], format='%H:%M:%S')

# グラフを描画
plt.plot(data['Time'], data['HeartRate'])
plt.xlabel('Time')
plt.ylabel('Heart Rate')
plt.title('Heart Rate Variation')
plt.xticks(rotation=45)
plt.show()


このスクリプトは、pandasとmatplotlibというライブラリを使っているようなので、ライブラリのインストール方法も聞きましょう。

上記のスクリプト実行に必要なライブラリのインストール方法を教えてください.

返答は下記のとおりです。

pip install pandas matplotlib

では、上記で作成されたPythonスクリプトを、plotHR.pyという名前で保存し、実行してみましょう。すると、下記のようなグラフが作成されるはずです。

このようにして、日本語で指示(プロンプト)を与えることで、各種の目的に応じたPythonスクリプトを作成可能です。ChatGPTが記述したスクリプトの意味がわからない場合は、それを「ここの処理どういう意味?」と質問することで、より深く学習可能です。

計測したデータをPythonで取得する

ここでは、Fitbitで計測されたデータをPythonで取得する方法を解説します。作業のおおまかな手順は下記の動画をご覧ください。

Anacondaのインストール
Python実行環境であるAnacondaのインストールを行う。とりあえずAnaconda3をダウンロードして道なりにインストール。Environmentから仮想環境「Fitbit」を作成し、「OpenTerminal」でターミナルを表示する。Pythonコマンドで動作を確認する。Ctrl+Zで対話モードを終了できる。インストールは、こちらのサイトが参考になる。

Fitbitの開発者向けサイトでアプリケーションを登録する
PythonからFitbitAPIを使うにはアプリの登録が必要となる。FitbitDevelopperサイトにログインして、Manage>RegisterAppの順で進み、アプリケーションを登録する。設定は以下の画像の通りにする。URL関係は全て「http://kodamalab.sakura.ne.jp/wordpress/?p=46853」、RedirectURLは、「http://127.0.0.1:8080/」とする。ページ内で作成された「OAuth 2.0 Client ID」と「Client Secret」をPythonスクリプト中で使用することになる。
Devサイトでのアプリ作成方法

Fitbit API Python Clientのインストール
GitHub – orcasgit/python-fitbit: Fitbit API Python Client Implementationからcodeボタンを押し、Zipファイルをダウンロードし、中身をCドライブ直下に配置する(こちらのサイトが参考になる)。さらに、下記コマンドで追加パッケージをインストールする。

pip install -r requirements/base.txt
pip install cherrypy 

Pythonスクリプトでクラウド上のファイルを取得
c:\python-fitbitフォルダに下記スクリプトをfitbit01.pyという名前で保存し、実行する。Anaconda Promptから下記コマンドを打ち込む

python fitbit01.py

#fitbit01.py
import sys
import fitbit
import gather_keys_oauth2 as Oauth2
  
print('Hello FitbitAPP3') 
  
USER_ID     = "ほげ"
CLIENT_SECRET = "ほげ"
  
DATE = "2021-06-03" # 取得したい日付
  
  
server = Oauth2.OAuth2Server(USER_ID, CLIENT_SECRET)
server.browser_authorize()
ACCESS_TOKEN = str(server.fitbit.client.session.token['access_token'])
REFRESH_TOKEN = str(server.fitbit.client.session.token['refresh_token'])
  
#print(ACCESS_TOKEN) print('\n') 
#print(REFRESH_TOKEN)print('\n') 
  
"""Authorization"""
auth2_client = fitbit.Fitbit(USER_ID, CLIENT_SECRET, oauth2=True, access_token=ACCESS_TOKEN, refresh_token=REFRESH_TOKEN)
  
"""Getting data"""
#print('Getting data\n') 
#fitbit_stats = auth2_client.intraday_time_series('activities/heart', DATE, detail_level='1min')
   
##################################################
fitbit_stats = auth2_client.intraday_time_series('activities/heart', DATE, detail_level='1min')
stats = fitbit_stats['activities-heart-intraday']['dataset']
  
OUTPUT_FILE = "HR_%s.csv" % DATE
csv_file = open(OUTPUT_FILE, 'w')
for var in range(0, len(stats)):
    csv_file.write(stats[var]['time'])
    csv_file.write(",")
    csv_file.write(str(stats[var]['value']))
    csv_file.write("\n")
csv_file.close()
  
##################################################
fitbit_stats = auth2_client.intraday_time_series('activities/steps', DATE, detail_level='1min')
stats = fitbit_stats['activities-steps-intraday']['dataset']
  
OUTPUT_FILE = "STEP%s.csv" % DATE
csv_file = open(OUTPUT_FILE, 'w')
for var in range(0, len(stats)):
    csv_file.write(stats[var]['time'])
    csv_file.write(",")
    csv_file.write(str(stats[var]['value']))
    csv_file.write("\n")
csv_file.close()
  
##################################################
fitbit_stats = auth2_client.intraday_time_series('activities/calories', DATE, detail_level='1min')
stats = fitbit_stats['activities-calories-intraday']['dataset']
  
OUTPUT_FILE = "CALO%s.csv" % DATE
csv_file = open(OUTPUT_FILE, 'w')
for var in range(0, len(stats)):
    csv_file.write(stats[var]['time'])
    csv_file.write(",")
    csv_file.write(str(stats[var]['value']))
    csv_file.write("\n")
csv_file.close()

トラブルシューティング

・RedirectURLが、http://127.0.0.1:8080/で正しく動作しない場合
ポート8080をなにかのプログラムが使っている可能性がある。その場合は、RedirectURLを、「http://127.0.0.1:8088/」とし、python-fitbitフォルダ中のgather_keys_oauth2.pyの該当部分を「8088」に変更してスクリプトを実施する。

・Pythonのスクリプトは何で作ればいいの?
プログラミング用のテキストエディタ、Notepad++がおすすめです。

リアクション
下記のページから,感想,苦労した点などをお知らせください.
心理サイエンス応用演習1リアクション

キャラクタの配置 2023

前回授業へのコメント
 前回は光源の使い方を応用して,ホラーゲームのような環境を作成してみました。無料のアセットでも、質感が高いものがあることに驚かれた方も多いと思います。PCを買うのであればどのようなものが良いのか?との質問も複数いただきました。下記の動画で簡単に解説をしていますので、参考にしてください。

キャラクタを読み込んで見る
 ホラーっぽい背景に「お化けを配置したらもっと怖いだろう」という意見もあろうと思います。そこで、今回はキャラクタの読み込み、表示方法を紹介します。ここでは,Mixamoからモーション付きのキャラクを読み込み,シーン上に配置する方法を学びます。キャラクタの取り扱いができるようになると,様々な心理学的な実験に利用できるようになるので,利用法の幅が広がると思います。

リアクションの記入
 課題が終わった方は、下記リンクより、授業配信日を含め3日以内にリアクションを登録してください。ファイル等を送っていただく必要はありません。リアクションは出欠記録を兼ねており、その内容は成績評価の対象となることに留意してください。
心理サイエンス基礎演習1リアクション

TPSを作ってみる(1)2023

TPSゲームを作ってみる
 キャラクタを扱えるようになると、やっぱり自由に動かしてみたくなりますよね。キャラクタの表現方法は、基本的に1人称視点(FPS)と3人称視点(TPS)しかないと思いますが、体を動かしている感があるのはTPSかなと思います。今回と次回では、TPSゲームの作成を通して、キャラクタの使い方を学びましょう。完成すると、こんな感じのものができる予定です。今日の解説動画では、下記のアセットを使用しています。予めダウンロードしておくとスムーズに進められると思います。
・Standard Asset
・AllskyFree
・PBR Materials – Wood & Metal
・Industrial Models
Low Poly Props Pack 2023年現在利用不可のようです
Road Props PackおよびSimple Street Propsで置き換えてください

リアクションの記入
 課題が終わった方は、下記リンクより、授業配信日を含め3日以内にリアクションを登録してください。ファイル等を送っていただく必要はありません。
心理サイエンス基礎演習2リアクション

2要因参加者内計画の分散分析

ここでは2要因参加者内計画の分散分析の方法を解説します。データは、動画を見る際に、事前にネタバレを行った場合と行わなかった場合を比較したものです。分析には、JSSTARを用いています。

Pythonで音声合成してみる

今日はPythonから合成音声を操作します。

Anacondaのインストール
Python実行環境であるAnacondaのインストールを行う。とりあえずAnaconda3をダウンロードして道なりにインストール。終了すると、Anaconda PromptからPythonが実行できるようになる。インストールは、こちらのサイトが参考になる。インストールできたら、startメニューからAnaconda Promptを起動。Pythonコマンドで動作確認。Ctrl+Zで対話モード終了。

Anacondaで環境「INK」を作成する
Anacondaがインストールできたら、EnvironmentからINKを作成し、Open terminalを実行してください。

作業フォルダを作成する
「Windows+E」を押して、ファイルエクスプローラを出したら、Cドライブに入って、Cドライブ直下に「INK」フォルダを作成しましょう。作成したら、ターミナルから「CD c:\INK」と入力して、c:\INKに移動しましょう。「python –version」と入力して、Pythonのバージョンが表示されればOKです。

Coeiroinkを呼ぶためにライブラリをインストールする
下記のコマンドを順に打ち込んで、requests、pydub、pyaudioの3つのライブラリをインストールします。ライブラリはネットワークインストールされるので、インターネット接続が必須です。

pip install requests
pip install pydub
pip install pyaudio

Pythonから合成音声を鳴らしてみる
いよいよ鳴らしてみましょう。下記のプログラムをc:\inkフォルダに「ink01.py」という名前で保存し、ターミナルから「python ink01.py」と打ち込み、実行してみましょう。合成音声を鳴らすには、Coeiroinkを起動しておく必要があります。スクリプトの編集はnotepad++がオススメです。

import json
 
import requests
from pydub import AudioSegment, playback
 
# パラメータ
text = "こんにちは、つくよみちゃんです。よろしくお願いします。"
speaker_id = 0  # スピーカーID (0:つくよみちゃん)
 
# 音声合成のクエリの作成
response = requests.post(
    "http://localhost:50031/audio_query",
    params={
        'text': text,
        'speaker': speaker_id,
        'core_version': '0.0.0'
    })
query = response.json()
 
# 音声合成のwavの生成
response = requests.post(
    'http://localhost:50031/synthesis',
    params={
        'speaker': speaker_id,
        'core_version': "0.0.0",
        'enable_interrogative_upspeak': 'true'
    },
    data=json.dumps(query))
 
# wavの音声を再生
playback.play(AudioSegment(response.content,
    sample_width=2, frame_rate=44100, channels=1))

話者を変えるには、上記ソースコード中の「speaker_id 」を話者に応じた値に変更しましょう。各話者のspeaker_id は、coeiroinkのインストールフォルダの、speakerinfoフォルダの、各話者のフォルダ中にあるmetas.jsonに記載されています。

OpenAI APIを使う
APIのページにアクセスし、GoogleIDでログインする。API keysのページから、「+create new secret key」を押します。キーの名前を入力し、エンターを押すと、シークレットキーが発行されます。シークレットキーをメモしてください。シークレットキーは、誰にも知らせないようにしてください。また、Organization>Settingのページから、「Organization ID」をコピーしておきましょう。

ChatGPTとPythonで会話する
ChatGPTを使うには、OpenAIのライブラリが必要です。コマンドラインから「pip install openai==0.27.4」と入力し、ライブラリをインストールしましょう。下記のプログラムをGPT1.pyという名前で、INKフォルダに保存し、「python GPT1.py」で実行してみましょう。apikeyは上記でメモしたキーを使いましょう。

import openai
openai.organization = ""
openai.api_key      = ""
 
# =============================================================
# チャットボット関数
# =============================================================
 
def Ask_ChatGPT(message):
     
    # 応答設定
    completion = openai.ChatCompletion.create(
                 model    = "gpt-3.5-turbo",     # モデルを選択
                 messages = [{
                            "role":"user",
                            "content":message,   # メッセージ 
                            }],
     
                 max_tokens  = 1024,             # 生成する文章の最大単語数
                 n           = 1,                # いくつの返答を生成するか
                 stop        = None,             # 指定した単語が出現した場合、文章生成を打ち切る
                 temperature = 0.5,              # 出力する単語のランダム性(0から2の範囲) 0であれば毎回返答内容固定
    )
     
    # 応答
    response = completion.choices[0].message.content
     
    # 応答内容出力
    return response
 
# =============================================================
# チャットボット実行
# =============================================================
 
# 質問内容
message = "日本人の若者の特徴を教えてください"
 
# ChatGPT起動
res = Ask_ChatGPT(message)
 
 
# 出力
print(res)

上記プログラムは、適切に実行されると、しばらく待った後に下記のような結果が表示されるはずです。質問を変えて、実行してみましょう。

チャットをする
下記のコードを「GPT2.py」という生で保存しましょう。このコードを実行することで、OpenAIのAPIとチャットができるようになります。

import openai
openai.organization = "org-ほげ"
openai.api_key      = "sk-ほげ"
 
 
def main():
    amount_tokens = 0
    chat = []
 
    setting = input("ChatGPTに設定を加えますか? y/n\n")
    if setting == "y" or setting == "Y":
        content = input("内容を入力してください。\n")
        chat.append({"role": "system", "content": content})
 
    print("チャットをはじめます。q または quit で終了します。")
    print("-"*50)
    while True:
        user = input("<あなた>\n")
        if user == "q" or user == "quit":
            print(f"トークン数は{amount_tokens}でした。")
            break
        else:
            chat.append({"role": "user", "content": user})
 
        print("<ChatGPT>")
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",messages=chat
        )
        msg = response["choices"][0]["message"]["content"].lstrip()
        amount_tokens += response["usage"]["total_tokens"]
        print(msg)
        chat.append({"role": "assistant", "content": msg})
 
 
if __name__ == "__main__":
    main()

プログラムが起動すると、起動するとAIのキャラ設定を聞かれるので、yを押して、「20歳の女性として話してください」などの設定を記入してください。正しく動作すると、会話できるようになります。APIページのUSAGEから、残り金額を確認しながら会話してください。

CoeiroInkで会話内容を音声化してもらう
下記コードは、ChatGPTからの回答を音声化するものです。Coeiroinkが起動した状態で実行してください。ちなみに、Windows11の場合は「Windowsキー+H」で音声認識入力が可能です。音声会話を楽しんでみてください。

import openai
import json
import requests
from pydub import AudioSegment, playback

openai.organization = "ほげ"
openai.api_key      = "ほげ"
speaker_id = 0  # スピーカーID (0:つくよみちゃん)  
  
def main():
    amount_tokens = 0
    chat = []
  
    setting = input("ChatGPTに設定を加えますか? y/n\n")
    if setting == "y" or setting == "Y":
        content = input("内容を入力してください。\n")
        chat.append({"role": "system", "content": content})
  
    print("チャットをはじめます。q または quit で終了します。")
    print("-"*50)
    while True:
        user = input("<あなた>\n")
        if user == "q" or user == "quit":
            print(f"トークン数は{amount_tokens}でした。")
            break
        else:
            chat.append({"role": "user", "content": user})
  
        print("<ChatGPT>")
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",messages=chat
        )
        msg = response["choices"][0]["message"]["content"].lstrip()
        amount_tokens += response["usage"]["total_tokens"]
        print(msg)
        playsound(msg)
        chat.append({"role": "assistant", "content": msg})
  
def playsound(text):
    # 音声合成のクエリの作成
    response = requests.post(
        "http://localhost:50031/audio_query",
        params={
            'text': text,
            'speaker': speaker_id,
            'core_version': '0.0.0'
        })
    query = response.json()
     
    # 音声合成のwavの生成
    response = requests.post(
        'http://localhost:50031/synthesis',
        params={
            'speaker': speaker_id,
            'core_version': "0.0.0",
            'enable_interrogative_upspeak': 'true'
        },
        data=json.dumps(query))
     
    # wavの音声を再生
    playback.play(AudioSegment(response.content,
        sample_width=2, frame_rate=44100, channels=1))


if __name__ == "__main__":
    main()

会話用修正プログラムその1

import openai
import json
import requests
from pydub import AudioSegment, playback

openai.organization = "org-ほげ"
openai.api_key      = "sk-ほげ"
speaker_id = 0  #つくよみちゃんれいせい
speaker_id = 90  #リリンちゃん
  
def main():
    amount_tokens = 0
    chat = []
          
    option="敬語は使わず、馬鹿っぽい話し方をしてください。回答は50文字くらいで行ってください。ネガティブで攻撃的な性格です。「えーそんなんあったかなぁ」、「だめじゃん、もうやめればいいのに」、「いいかげんにしろよ」といった口調で話してください。一人称は「アタシ」でお願いします。";    
  
    print("チャットをはじめます。q または quit で終了します。")
    print("-"*50)
    while True:
        user = input("<あなた>\n")
        user2=user+option
        if user == "q" or user == "quit":
            print(f"トークン数は{amount_tokens}でした。")
            break
        else:
            chat.append({"role": "user", "content": user2})
  
        print("<ChatGPT>")
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",messages=chat
        )
        msg = response["choices"][0]["message"]["content"].lstrip()
        amount_tokens += response["usage"]["total_tokens"]
        print(msg)
        playsound(msg)
        chat.append({"role": "assistant", "content": msg})
  
def playsound(text):
    # 音声合成のクエリの作成
    response = requests.post(
        "http://localhost:50031/audio_query",
        params={
            'text': text,
            'speaker': speaker_id,
            'core_version': '0.0.0'
        })
    query = response.json()
     
    # 音声合成のwavの生成
    response = requests.post(
        'http://localhost:50031/synthesis',
        params={
            'speaker': speaker_id,
            'core_version': "0.0.0",
            'enable_interrogative_upspeak': 'true'
        },
        data=json.dumps(query))
     
    # wavの音声を再生
    playback.play(AudioSegment(response.content,
        sample_width=2, frame_rate=44100, channels=1))


if __name__ == "__main__":
    main()

情報処理基礎演習 情報リテラシー講座

情報リテラシー講座
学内のコンピュータを利用する上で、最低限理解していただきたいことを学習するために、下記の講座を用意しております。今週は、これらの動画を視聴することを授業の課題とします。

【情報系授業用①】学内でPCを利用する
【情報系授業用②】 パスワード管理
【情報系授業用③】セキュリティ基礎
【情報系授業用④】 著作権の考え方
【情報系授業用⑤】メールの使い方
【情報系授業用⑥】 データの管理
また、動画視聴後、効果測定(テスト)も受けるようにしてください。動画視聴、効果測定、リアクションの回答は今週末までに行うようにしてください。
 
Officeの申請について
 本年度からOfficeのライセンスを、申請者だけではなく付与することになったようです。近日中に情報センターから案内が出る予定なので、お手数ですが、それまでは大学内のコンピューター室を利用して課題を行うようにしてください。
 
リアクションの回答
 リアクション(感想)は、出欠記録を兼ねていますので、こちらも今週末までに送信するようにしてください。
AクラスリアクションBクラスリアクション

↑クラスを間違えないようにご注意ください

StandardAssetの追加

StandardAssetの導入方法
Unity2021などの新しいバージョンでは、アセットの導入方法が変わっています。特に、StandardAssetは、アセットストアから削除されてしまい通常の方法では導入が難しくなっています。下記の動画では、StandardAssetの導入方法を紹介します。

DavinciResolve入門

DavinciResolveとは
 DaVinci Resolveは、編集、カラーグレーディング、オーディオポストプロダクション、先進的なAI処理などの機能を備えており、映像制作業界で広く使用されているプロフェッショナルな映像編集ソフトウェアです。DaVinci Resolveは、その高度な機能と洗練されたワークフローにより、映画、テレビ番組、CM制作など、幅広い映像制作に利用されています。主要な機能をほとんど使うことができるDaVinci Resolveを無料で使用することができます。これは製造元のBlackMagic社の「若い人ほどクリエイティブな作業に触れてほしい」という願いによるものです。

なぜDavinciResolveを学ぶ事が大事なのか?
 大学生がDaVinci Resolveの使い方を学ぶことが重要な理由は、映像制作やデジタルメディアが現代の教育やビジネス環境で重要な役割を果たしているためです。DaVinci Resolveを使いこなすことで、プロフェッショナルな映像作品を制作し、プレゼンテーションやプロジェクトで高い評価を得ることができます。また、映像編集スキルは、今後のキャリアや就職活動にも役立ちます。さらに、DaVinci Resolveを使って自分のコンテンツを制作することで、創造力やコミュニケーション力を向上させることができ、将来の仕事で活かすこともできるでしょう。

ここでは、動画編集ソフトであるDavinciResolveについて学びます。まずはインストールして簡単な動画を作ってみましょう。

下記は、DavinciResolveを用いてCM動画を作成する手順を解説しています。作成したCM動画は、心理学の実験用に用いる事を想定しています。

リンク集
 DavinciResolveは多くの人に使われているので、学習用のサイトやYouTube動画がすごくたくさんあります。自分で調べて必要な作業が短時間でできるように鍛えましょう。AI系などの処理を必要とする場合や、4Kや長い動画を編集する場合はGPUが使えるDavinciResolveStudio(有料版)が必要です。実験室で2ライセンス保有しているので、使いたい方は声をかけてください。

動画編集ソフトDavinciResolveの使い方 授業動画よりよほどためになる
速度変更の方法
モザイクをかける方法。顔をトラッキングもできるらしい。
AI関連の機能のみならずGPUを使うものは皆有料版が必要らしい。
FreeBGM素材
トランジションを適用できない場合はどうしたらよい!?}
解像度を8kに変更
OpticalFlowでフレーム補間(すげぇ!)
音声と映像のフェードアウト(簡単ダァ!)
Proxyの使い方(4k編集早くなるのかなぁ?ワクワクするね!)
カラーグレーディング動画(奥が深い!)
カラーグレーディングを複製したい(その3がおすすめ)

OBS Studio入門

OBS Studioとは
 OBS Studio(Open Broadcaster Software Studio)は、オープンソースの無料ビデオ録画およびストリーミングソフトウェアです。このソフトウェアは、ユーザーがデスクトップ画面の録画やライブストリーミングを簡単に行うことができます。また、様々なビデオソースやオーディオソースを組み合わせて、カスタマイズされた映像を作成できます。OBS Studioは、YouTubeなどのプラットフォーム用に動画を撮影したり、ライブストリーミングを行う際にも非常に便利です。

なぜOBSを学ぶ事が大事なのか?
 現代の教育やビジネス環境では、デジタルメディアやオンラインプレゼンテーションの使用が不可欠です。OBS Studioを使いこなせることで、プレゼンテーションやチームプロジェクトを効果的に行えるようになります。また、将来的にさまざまな職種や業界で働くことになるため、OBS Studioのようなビデオ録画およびストリーミングツールを使いこなす能力は、就職やキャリアアップにも役立ちます。さらに、OBS Studioを使って自分自身のチャンネルやコンテンツを作成することで、創造力やコミュニケーション力を向上させることもできます。

リンク集
 基本的な使い方を学んだら、必要に応じて作業動画などを作成しYouTubeにアップロードし、ブログに投稿していきましょう。分かりやすい動画付きで、必要な情報が整理されたブログは、それだけで作成者が「価値のある人間である」ことを他の人に知らせることができます。

図解優しいOBSの使い方 かなり詳しい使い方が解説されている
OBS Studio初心者向け使い方解説 個別に準備されておりより詳しく解説