グループLINEの真実(?)
LINEのトーク履歴から・各アカウントの投稿件数・各アカウントの投稿文字数をcsv出力し、JavaFXでグラフに出力してみました。身近な方がコロナ関連統計データのグラフ作成をしているのを見て、思いつきでつくってみました。【注:投稿件数カウント仕様】オレンジのグラフです。・iOSのLINEアプリから出力したテキストファイルに対応。・「を招待しました。」「が参加しました。」「が退会しました。」など、投稿ではないアクションはカウントから除外。・スタンプ投稿、写真投稿、ノート投稿、イベント作成等はカウント。【注:投稿文字数カウント仕様】青いグラフです。・iOSのLINEアプリから出力したテキストファイルに対応。・「を招待しました。」「が参加しました。」「が退会しました。」など、投稿ではないアクションはカウントから除外。・スタンプ投稿は6字、写真投稿は10字、ファイル投稿は30字、リンク貼り付けは20字と見なす。いや、これはあまり知っても役に立たないというか、わりと残酷な数字を突き付けられる感じもしますね~実行結果をいくつか見てみましょう~-----【その1、高校同期(3人)のトークルーム】まずは投稿件数。絶妙なバランスですね。ワイが一番少ないけど、僅差です。次に文字数の観点で見てみましょう。順位は変わらずですが、意外と差が出ました。投稿件数としても文字数としてもBさんがトップ。やはりBさんが何かを提案することが多いですね。高校は底辺校でしたが、彼らは一生ものの友人です。-----【その2:大学同期のトークルーム】投稿件数。1位はHさんの309件。私は288件で次点。文字数。順位の変動はあるものの、微差。字数では私が首位に。途中で履歴全消失しているので、これは大学3年の秋くらいからのデータ。もっと前を含めると私と2位の差が開く確率が高い気がする。ちなみに今は殆ど稼働していない残念なトークルーム。-----【その3:入社同期のトークルーム】投稿件数。私が1位。文字数。順位は変わらずですが、差が大きくなりました。私ってそんなにしゃべるタイプではないはずなんですが、他の2人もそんなにしゃべらないので相対的に首位になっています。複数回開いた同期会も全て私が言い出しっぺという(苦笑)ちなみにBさんが結構前に退職したので、今は全く動いていない残念なトークルーム。-----【その4:私がリーダーということになっているらしいトークルーム】投稿件数。私の投稿件数:1161件。どう考えても一番だとは思っていたけど、2位に余裕でダブルスコアとは。他の方もうちょっと発言してほしいかも~笑文字数。1位は変わらず私で61193字。Fさんが短文投稿、Dさんが長文投稿していることがわかります。また、KさんとJさんが入れ替わっていますね。-----【その5:一応私が管理者な感じだけど、特定の共有事項についてはみんな積極的に流してねなトークルーム】投稿件数。私、381件。ここでも2位にダブルスコア以上。文字数。私:21273字。Mさん:8679字で3位との落差が出る格好となりました。-----【その6:私が参加しているグループで最大人数のもの】リーダーのAAOさんが大変頑張っていらっしゃるので圧倒的1位になるだろうとは思っていましたが、ここまで極端な数字が出るとは思っておらず(AAO:3465件、145406字)。2位:453件のAANさん、13249字のAAKさん以下ですと下記です。やはり一部、長文投稿される方はいるものの、基本的な傾向として投稿件数と字数は相関関係にあるということがわかります。-----------------飲み会とか5人以上くらいから、何もしゃべらない人って出てくるというか口数の偏りが顕著になってくると思うんですが、グループLINEも同じだと思っています。これはどんなグループでもあっても宿命ですね。特に人数が増えれば増えるほど偏りが顕著になる、と体感的にも思っていました。もちろん回数ではなくて、発言内容の方がずーーーーーっと重要なわけではありますが、今回の解析で件数傾向を定量的に掴むことができました。って、把握して何かに活かせるわけではないので、あんまり意味はないのですが。。最後に一応ソースを載っけておきます。GitHubに登録するのが普通なのだろうけど、アカウント作成していないのでダサいけどベタ張り。。経験年数に対してスキルが低いので、勉強しないといけないのだが、、全然できていない現実。。。 投稿件数カウント import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.util.Map; import java.util.Map.Entry; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class PostRate { static Map<String, Integer> nameMap = new HashMap<String, Integer>(); public static void main(String[] args) { File readFile = new File("テキトー.txt"); String nameOfoutputFile = readFile.getName(); File outputFile = new File("フォルダ" + nameOfoutputFile + ".csv"); try (BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile, false))) { { BufferedReader br = new BufferedReader( new InputStreamReader(new FileInputStream(readFile), "UTF-8")); String line; while ((line = br.readLine()) != null) { if (checkLine(line)) { int index = line.indexOf("\t", 6); if (index != -1) { nameMap.merge(line.substring(6, index), 1, Integer::sum); } } } List<Entry<String, Integer>> entriesList = new ArrayList<Entry<String, Integer>>(nameMap.entrySet()); // 比較関数Comparatorを使用してMap.Entryの値を比較する(昇順) Collections.sort(entriesList, new Comparator<Entry<String, Integer>>() { public int compare(Entry<String, Integer> obj1, Entry<String, Integer> obj2) { return obj1.getValue().compareTo(obj2.getValue()); } }); for(Entry<String, Integer> entry : entriesList) { System.out.println(entry.getKey() + " : " + entry.getValue()); bw.write(entry.getKey() + ","); bw.write(String.valueOf(entry.getValue())); bw.newLine(); } bw.flush(); br.close(); System.out.println("終了"); } } catch (IndexOutOfBoundsException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private static boolean checkLine(String line) { if (line.length() < 5) { return false; } String regex_num = "^([01][0-9]|2[0-3]):[0-5][0-9]"; Pattern p = Pattern.compile(regex_num); Matcher m1 = p.matcher(line.substring(0, 5)); boolean result = m1.matches(); if (result) { return true; } else { return false; } } } 文字数カウント import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.util.Map; import java.util.Map.Entry; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ActivityAnalysys { static Map<String, Integer> postsMap = new HashMap<String, Integer>(); public static void main(String[] args) { File readFile = new File("テキトー.txt"); String nameOfoutputFile = readFile.getName(); File outputFile = new File( "C:\\Users\\circu\\Documents\\fileIOTest\\output\\CharsCount" + nameOfoutputFile + ".csv"); try (BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile, false))) { { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(readFile), "UTF-8")); String line; String accountName = ""; boolean isFirstLine = false; String afterCharsAccountName = ""; String emptyLine = ""; while ((line = br.readLine()) != null) { if (checkLine(line)) { int index = line.indexOf("\t", 6); // -1は自動メッセージ if (index == -1) { accountName = ""; continue; } // 名前取得 accountName = line.substring(6, index); // 名前より後の文字を取得 afterCharsAccountName = line.substring(++index, line.length()); isFirstLine = true; } // 空行はカウントしない if (!accountName.equals(emptyLine)) { int count = postsMap.containsKey(accountName) ? postsMap.get(accountName) : 0; int numOfFirstLinechars = 0; int numOfchars = 0; if (count == 0) { numOfFirstLinechars = checkPostContent(afterCharsAccountName); postsMap.put(accountName, numOfFirstLinechars); } else { if (isFirstLine) { numOfFirstLinechars = checkPostContent(afterCharsAccountName); postsMap.replace(accountName, count + numOfFirstLinechars); } else { numOfchars = checkPostContent(line); postsMap.replace(accountName, count + numOfchars); } } } isFirstLine = false; } List<Entry<String, Integer>> entriesList = new ArrayList<Entry<String, Integer>>(postsMap.entrySet()); // 比較関数Comparatorを使用してMap.Entryの値を比較する(昇順) Collections.sort(entriesList, new Comparator<Entry<String, Integer>>() { public int compare(Entry<String, Integer> obj1, Entry<String, Integer> obj2) { return obj1.getValue().compareTo(obj2.getValue()); } }); for (Entry<String, Integer> entry : entriesList) { System.out.println(entry.getKey() + " : " + entry.getValue()); bw.write(entry.getKey() + ","); bw.write(String.valueOf(entry.getValue())); bw.newLine(); } bw.flush(); br.close(); System.out.println("終了"); } } catch (IndexOutOfBoundsException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private static boolean checkLine(String line) { if (line.length() < 5) return false; String regex_num = "^([01][0-9]|2[0-3]):[0-5][0-9]"; Pattern p = Pattern.compile(regex_num); Matcher m1 = p.matcher(line.substring(0, 5)); boolean result = m1.matches(); if (result) { return true; } else { return false; } } private static int checkPostContent(String afterCharsAccountName) { if(afterCharsAccountName.equals("[写真]")) return 10; if(afterCharsAccountName.equals("[ファイル]")) return 30; if(afterCharsAccountName.contains("https://")) return 20; if(afterCharsAccountName.contains("http://")) return 20; return afterCharsAccountName.length(); } } グラフ描画 import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.chart.BarChart; import javafx.scene.chart.CategoryAxis; import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart; import javafx.stage.Stage; public class LinePostCountBarChart extends Application{ @Override public void start(Stage stage) { File readFile = new File("上記の出力ファイル.txt.csv"); final CategoryAxis xAxis = new CategoryAxis(); final NumberAxis yAxis = new NumberAxis(); stage.setTitle(readFile.getName()); // xAxis.setLabel("アカウント名"); final BarChart<String,Number> barChart = new BarChart<>(xAxis,yAxis); barChart.setTitle(readFile.getName()); XYChart.Series series = new XYChart.Series(); series.setName("解析結果"); try { BufferedReader br = new BufferedReader( new InputStreamReader(new FileInputStream(readFile), "SJIS")); String line; while ((line = br.readLine()) != null) { String[] strArray = line.split(","); series.getData().add(new XYChart.Data(strArray[0], Integer.valueOf(strArray[1]))); } br.close(); }catch(IOException e) { e.printStackTrace(); } Scene scene = new Scene(barChart,2100,600); barChart.getData().add(series); // barChart.setCategoryGap(10); stage.setScene(scene); scene.getStylesheets().add("ChartLine.css"); stage.show(); } public static void main(String[] args) { launch(args); } }