A Pen by EntranceExit on CodePen.
Created
November 29, 2019 07:33
-
-
Save moriakijp/84fc8fd2e1fd61dabb10860411a16711 to your computer and use it in GitHub Desktop.
Twitter:原発に関連するハッシュタグ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <div id="root"></div> | |
| <script src="https://likr.github.io/eg-renderer/eg-renderer.js"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class Chart extends React.Component { | |
| constructor(props) { | |
| super(props); | |
| this.renderer = React.createRef(); | |
| this.wrapper = React.createRef(); | |
| this.state={ | |
| keyword:'' | |
| } | |
| } | |
| componentDidMount(){ | |
| const wrapper = this.wrapper.current; | |
| this.renderer.current.width = wrapper.clientWidth | |
| this.renderer.current.height = wrapper.clientWidth | |
| window.addEventListener("resize", ()=>{ | |
| const wrapper = this.wrapper.current; | |
| this.renderer.current.width = wrapper.clientWidth | |
| this.renderer.current.height = wrapper.clientWidth | |
| }) | |
| this.renderer.current.addEventListener('nodeclick', (event) => { | |
| const { id } = event.detail | |
| alert(id) | |
| const adjacencyList = {} | |
| const data = this.props.data; | |
| for (const node of data.nodes){ | |
| adjacencyList[node.id] = [] | |
| } | |
| for (const link of data.links){ | |
| adjacencyList[link.source].push(link.target) | |
| } | |
| }) | |
| } | |
| componentDidUpdate(prevProps) { | |
| if (this.props.data !== prevProps.data) { | |
| const data = this.props.data; | |
| const { Graph } = egraph; | |
| const graph = new Graph(); | |
| const nodeWidth = d3.scaleSqrt() | |
| .domain([0, d3.max(data.nodes, (node) => node.frequency)]) | |
| .range([0,45]) | |
| .nice() | |
| const nodeHeight = d3.scaleSqrt() | |
| .domain([0, d3.max(data.nodes, (node) => node.frequency)]) | |
| .range([0,45]) | |
| .nice() | |
| const indices = new Map() | |
| for (const node of data.nodes) { | |
| node.width = nodeWidth(node.frequency) | |
| node.height = nodeHeight(node.frequency) | |
| node.label = node.id | |
| node.fillColor = "orange" | |
| indices.set(node.id, graph.addNode(node)); | |
| } | |
| for (const link of data.links) { | |
| link.strokeColor = "#ccc" | |
| graph.addEdge(indices.get(link.source), indices.get(link.target), link); | |
| } | |
| const { ManyBodyForce, LinkForce, PositionForce } = egraph; | |
| const manyBodyForce = new ManyBodyForce(); | |
| manyBodyForce.strength(()=>-125) | |
| const linkForce = new LinkForce(); | |
| const positionForce = new PositionForce(); | |
| positionForce.x(()=>0) | |
| positionForce.y(()=>0) | |
| const { Simulation } = egraph; | |
| const simulation = new Simulation(); | |
| simulation.add(manyBodyForce.force()); | |
| simulation.add(linkForce.force()); | |
| simulation.add(positionForce.force()); | |
| const positions = simulation.start(graph); | |
| data.nodes.map((node, i) => { | |
| Object.assign(node, positions[i]); | |
| }); | |
| this.renderer.current.load(data); | |
| } | |
| } | |
| render() { | |
| return ( | |
| <div > | |
| <div className='kensaku'> | |
| <form | |
| className="change" | |
| onSubmit={(event) => { | |
| event.preventDefault() | |
| for (const node of this.props.data.nodes) { | |
| if (node.id.includes(this.refs.keyword.value)){ | |
| node.fillOpacity = "1" | |
| node.labelFillOpacity = "1" | |
| }else{ | |
| node.fillOpacity = "0.15" | |
| node.labelFillOpacity = "0.15" | |
| } | |
| } | |
| for (const link of this.props.data.links){ | |
| if (link.source.includes(this.refs.keyword.value)){ | |
| link.strokeOpacity = "1" | |
| }else{ | |
| link.strokeOpacity = "0.15" | |
| } | |
| } | |
| this.renderer.current.update(); | |
| }} | |
| > | |
| <div> | |
| 検索したいタグを入力してください | |
| </div> | |
| <input ref='keyword' /> | |
| <button> | |
| 検索 | |
| </button> | |
| </form> | |
| </div> | |
| <div ref={this.wrapper}> | |
| <eg-renderer ref={this.renderer} | |
| node-id-property="id" | |
| default-node-stroke-width="0" | |
| default-node-label-font-size="6" | |
| default-link-target-marker-shape = "triangle" | |
| width="2000" | |
| height="2000" /> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| } | |
| class App extends React.Component { | |
| constructor(props) { | |
| super(props); | |
| this.state = { | |
| data: null | |
| }; | |
| } | |
| componentDidMount() { | |
| const url = | |
| "https://raw.githubusercontent.com/EntranceExit/Zemi-zone/master/20001edgesEX%2B%2B%2B.json"; | |
| window | |
| .fetch(url) | |
| .then(response => response.json()) | |
| .then(data => { | |
| this.setState({ data }); | |
| }); | |
| } | |
| render() { | |
| const { data } = this.state; | |
| return ( | |
| <div className='aller'> | |
| <header className="header"> | |
| <div className="content has-text-centered"> | |
| </div> | |
| </header> | |
| <section className="section"> | |
| <div className="container"> | |
| <div className="content has-text-centered"> | |
| <h3>2019年度尾上ゼミ 情報科学研究 課題制作</h3> | |
| <h2>ハッシュタグの話題遷移ネットワーク可視化</h2> | |
| <p align="left"> 本研究では、Twitterの原発や放射線に関するtweetデータに使用されているハッシュタグについて可視化を行っています。</p> | |
| <h3>◎データ説明</h3> | |
| <p align="left"> 本研究で使用しているデータは、Twitter社から提供された2011年の3月1日から2013年4月1日までの約3年間の原発や放射線などに関連したtweetデータを使用しています。tweetデータには下記が含まれており、ファイル形式はcsvファイルです。</p> | |
| <ul> | |
| <li align="left">ユーザID</li> | |
| <li align="left">tweetした日時</li> | |
| <li align="left">ハッシュタグ</li> | |
| </ul> | |
| <h3>◎可視化方法</h3> | |
| <p align="left"> 上記のハッシュタグを含むtweetデータを使用するために、Colab上にてファイルを読み込み、可視化する妨げになると考えられるハッシュタグの取捨選択を行います。(スパムと思われるハッシュタグなどが多く見受けられるため。) <br/> 次にユーザIDごとにハッシュタグの出現頻度行列とハッシュタグの遷移頻度行列を作成し、それらを用いてハッシュタグそれぞれの遷移確率を求めます。その後、networkxというパッケージを使い、グラフのデータセットを作成します。<br/>また遷移確率の求め方などは<a href="https://esa-storage-tokyo.s3-ap-northeast-1.amazonaws.com/uploads/production/attachments/8704/2019/05/22/35056/ed0b90fd-3d5d-4693-971d-7f3774ba4d26.pdf" target="_blank">風間一洋, 鳥海不二夫, 榊剛史, 栗原聡, 篠田孝祐, & 野田五十樹. Twitter のイベントの因果関係の分析.(2014)</a>を参考に行っています。<br/>実際にデータセットを作る過程はこちらのリンク先で確認することができます。<a href="https://colab.research.google.com/drive/1RzdusUAVuiX-BfA3406Tw3OkqlK92hKg?hl=ja#scrollTo=e5oIvJCZ7oM-" target="_blank">https://colab.research.google.com/drive/1RzdusUAVuiX-BfA3406Tw3OkqlK92hKg?hl=ja#scrollTo=e5oIvJCZ7oM-</a></p> | |
| <p align="left">※出現頻度:ここでは各ハッシュタグの出現する回数のこと。<br/> 遷移頻度:ここではあるハッシュタグAがあるハッシュタグBへ移動している回数のこと。<br/> 遷移確率:ここではあるハッシュタグAがあるハッシュタグBへ移動する確率のこと。</p> | |
| <p align="left"> 作成したデータセットをGithubのリポジトリにアップロードした後、Reactを適用したCodePen上にて可視化を行います。また可視化する上でeg-renderer(<a href="https://www.npmjs.com/package/eg-renderer" target="_blank">https://www.npmjs.com/package/eg-renderer</a>)というネットワーク可視化をするプログラムを使用しています。 | |
| </p> | |
| <h3>◎可視化説明</h3> | |
| <p align="left"> 以下の可視化はハッシュタグの出現頻度の多いものを元にタグとタグの間の遷移関係についてネットワーク可視化を施したものです。</p> | |
| <ul> | |
| <li align="left">ノードの大きさ:該当するハッシュタグの出現頻度の多さによって変動し、大きいほど多いということになります。</li> | |
| <li align="left">エッジの矢印:ハッシュタグ間における遷移方向。矢印の方向に向かうほど新しい話題になります。</li> | |
| </ul> | |
| <h3>◎使用方法</h3> | |
| <p align="left"> 各ノードはホールドしながら動かすことで自由に動かすことができます。エッジも連動して動くため、どのノード間が繋がっているのか、見たいノードのみを移動させることも可能です。また、テキストボックスより検索したいキーワードを入力することで、キーワードが当てはまるタグが強調表示させることができます。</p> | |
| <div style={{ margin: "2em" }} className='renderer'> | |
| <Chart data={data} /> | |
| </div> | |
| <div> | |
| <h3>◎考察</h3> | |
| <p align='left'> 以前、熱中症というハッシュタグで組み合わせを調べた際にはあまり見受けられなかった表記ゆれと思われるハッシュタグが多く見られる。(※genpatsuとgenpatu、nhkとNHKなど) また、ローマ字表記と漢字表記で分けられているタグも見受けられる。(※genpatsuと原発、fukusimaと福島など)<br/> これらの原因として前者は単純な入力ミスも考えられるが、後者の場合は外国人にも目を通してもらいたい場合や、逆に日本人に拡散したい情報の場合などには漢字でハッシュタグを使用しているとも考えられる。</p> | |
| <p align="left"> ハッシュタグの遷移を見ていくと3つ以上のハッシュタグの遷移をしているものは少なく、概ね2つのハッシュタグを遷移していることが多いことが分かる。 原因として考えられる事が、実際に当時のtweetを閲覧したところ、genpatsuというハッシュタグだけつけて、内容は東電上層部の責任問題について述べていたり当時の総理大臣の応援しているだけの内容だったりと大雑把にハッシュタグを使用していることが確認でき、あまりハッシュタグはバリエーションを持っていないことからこの結果になったのではないかと考えられる。 | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <footer className="footer"> | |
| <div className="content has-text-centered"> | |
| <p>©2019 Deguchi Takuya</p> | |
| </div> | |
| </footer> | |
| </div> | |
| ); | |
| } | |
| } | |
| class Root extends React.Component { | |
| constructor(props) { | |
| super(props); | |
| this.state = { | |
| error: null | |
| }; | |
| } | |
| static getDerivedStateFromError(error) { | |
| return { error }; | |
| } | |
| componentDidCatch(error, info) { | |
| console.error(error.toString()); | |
| } | |
| render() { | |
| const { error } = this.state; | |
| if (error != null) { | |
| return ( | |
| <div className="hero is-danger is-fullheight"> | |
| <div class="hero-body"> | |
| <div class="container"> | |
| <h1 class="title">{error.toString()}</h1> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| return <App />; | |
| } | |
| } | |
| (async () => { | |
| await egraph( | |
| "https://s3-us-west-2.amazonaws.com/s.cdpn.io/2004014/egraph_wasm_bg.wasm" | |
| ); | |
| ReactDOM.render(<Root />, document.getElementById("root")); | |
| })(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <script src="https://unpkg.com/react@16/umd/react.development.js"></script> | |
| <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> | |
| <script src="https://unpkg.com/d3@5/dist/d3.min.js"></script> | |
| <script src="https://unpkg.com/d3-sankey@0/dist/d3-sankey.min.js"></script> | |
| <script src="https://unpkg.com/topojson@3/dist/topojson.js"></script> | |
| <script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/2004014/egraph_wasm.js"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| .kensaku{ | |
| margin: 15px; | |
| border: 1px solid #aaa; | |
| padding: 20px 20px; | |
| } | |
| .search{ | |
| margin: 25px; | |
| border: 1px solid #aaa; | |
| padding: 20px 40px; | |
| } | |
| .renderer{ | |
| margin: 25px; | |
| border: 1px solid #aaa; | |
| padding: 20px 40px; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <link href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.4/css/bulma.min.css" rel="stylesheet" /> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment