シェルスクリプトマガジン

Pythonあれこれ(Vol.97掲載)

著者:飯尾 淳

本連載では「Pythonを昔から使っているものの、それほど使いこなしてはいない」という筆者が、いろいろな日常業務をPythonで処理することで、立派な「蛇使い」に育つことを目指します。その過程を温かく見守ってください。皆さんと共に勉強していきましょう。第27回では、異文化間交流教育プロジェクトで得られたオンライン交流データを分析する、筆者の研究例を紹介します。

シェルスクリプトマガジン Vol.97は以下のリンク先でご購入できます。

図2 必要なライブラリをインポートするコード

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib
import re, os, math
from graphviz import Graph
from IPython.display import Image, display, display_png
from sklearn.linear_model import LinearRegression

図4 mk_relations()関数を定義するコード

def mk_relations(df):
  # 関係性を抽出する
  prev = []
  relations = {}
  f = df.columns
  for i, row in df.iterrows():
    # 発言文字数が「0」ではない発言者を抽出
    d = dict(zip(f, list(row)))
    speakers = list(filter(lambda k: d[k] != 0, d.keys()))
    # 前行のスピーカーと今の行のスピーカーの関係を抽出
    # どちらかもしくは両方が空行のときはスキップ
    if len(prev)*len(speakers) > 0:
      for p_speaker in prev:
        for n_speaker in speakers:
          r = tuple(sorted([p_speaker, n_speaker]))
          if r not in relations.keys():
            relations[r] = 1
          else:
            relations[r] += 1
    prev = speakers
  return relations

図5 ファイルからターンテイキングの状況を抽出するコード

filename = 'data/file00.xlsx'
df = pd.read_excel(filename).drop(['Unnamed: 0'], axis=1)
d = mk_relations(df)
d

図7 conv_dict()関数を定義するコード

def conv_dict(d):
  v = d.values()
  return dict(zip(d.keys(), map(lambda x: x/max(v), v)))

図9 count_talk()関数を定義するコード

def count_talk(df):
  retvar = {}
  for (name, col) in df.T.iterrows():
    retvar[name] = len(list(filter(lambda x: x > 0, col)))
  return retvar

図11 render_graph()関数を定義するコード

def render_graph(filename):
  print(f'[source] {filename}')
  (basename, extention) = re.match(r'(.*)\.(.*)', filename).groups()
  pathname = 'data/'+filename
  outputfile = f'graphs/{basename}'
  # データフレームの準備
  df = pd.read_excel(pathname).drop(['Unnamed: 0'], axis=1)
  names = df.columns
  # 発言の関係性と発言回数の抽出
  relations = conv_dict(mk_relations(df))
  talk_counts = count_talk(df)
  talks = conv_dict(talk_counts)
  # グラフ描画に関する定数など
  ARROW_WIDTH = 3
  colors     = ['white', 'azure', 'lavender', 'slateblue', 'navy']
  textcolors = ['black', 'black', 'black',    'white',     'white']
  # dot属性の準備
  dot = Graph(format='png')
  dot.attr('node', shape='ellipse')
  dot.attr('node', fontname='Arial')
  # ノードの追加
  for n in talks.keys():
    idx = int(talks[n]*(len(colors)-1))
    dot.node(n, f'{n} ({talks[n]:4.2f})',
      style='filled', fillcolor=colors[idx], fontcolor=textcolors[idx])
  # エッジの追加
  for e in relations.keys():
    (src, dst) = e
    dot.edge(src, dst, penwidth=f'{relations[e]*ARROW_WIDTH:5.3f}')
  # サマリー表の作成
  df_summary = pd.DataFrame({'ID': talk_counts.keys(),
                             '発言回数': talk_counts.values()})
  # レンダリング
  display(df_summary)
  dot.render(outputfile)
  display_png(Image(f’graphs/{basename}.png'))

図15 calc_num_cv()関数を定義するコード

def calc_num_cv(filename):
  print(f'[source] {filename}')
  (basename, extention) = re.match(r'(.*)\.(.*)', filename).groups()
  pathname = 'data/'+filename
  # データフレームの準備
  df = pd.read_excel(pathname).drop(['Unnamed: 0'], axis=1)
  names = df.columns
  num = len(names)
  # 発言回数の抽出
  talk_counts = count_talk(df)
  # サマリー表の作成
  df_summary = pd.DataFrame({'ID': talk_counts.keys(),
                             '発話回数': talk_counts.values()})
  # 平均・標準偏差・変動係数の計算
  mu = sum(df_summary['発話回数'])/num # 平均値
  sd = math.sqrt(sum((df_summary['発話回数'] - mu) *
         (df_summary['発話回数'] - mu))/(num-1)) # 不偏標準偏差
  print(f'参加者数 = {num}')
  print(f'平均 = {mu:4.2f}')
  print(f'不偏標準偏差 = {sd:4.2f}')
  print(f'変動係数 = {(sd/mu):6.4f}')
  return num, sd/mu

図16 すべてのデータから参加者数と変動係数を計算するコード

files = sorted(os.listdir('data'))
table = []
Xlabel = '参加者数'
Ylabel = '変動係数'
for filename in files:
  num, cv = calc_num_cv(filename)
  table.append({ 'Name': filename, Xlabel: num, Ylabel: cv })