Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                g<div id="root"></div>
<script src="https://likr.github.io/eg-renderer/eg-renderer.js"></script>
              
            
!

CSS

              
                .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;
}
              
            
!

JS

              
                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
      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)
      }
      const visited = new Set()
      const queue = [id]
      while(queue.length > 0){
        const t = queue.shift()
        if(visited.has(t) === false){
          visited.add(t)
          for(const i of adjacencyList[t]){
            queue.push(i)
          }
        }
      }
      for (const node of this.props.data.nodes){
        if (visited.has(node.id) === true){
          node.fillOpacity = "1" 
          node.labelFillOpacity = "1"
        }else{
          node.fillOpacity = "0.15"
          node.labelFillOpacity = "0.15"
        }
      }
      for (const link of this.props.data.links){
        if (visited.has(link.source) === true){
          link.strokeOpacity = "1"
        }else{
          link.strokeOpacity = "0.15"
        }
      }
      this.renderer.current.update();
    })
  }

  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>

        </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"
            no-auto-centering
            />
        </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>Twitterにおけるハッシュタグ話題遷移ネットワーク可視化</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">ツイートした日時</li>
                <li align="left">ハッシュタグ</li>
              </ul>
              <h3>◎可視化方法</h3>
              <p align="left"> 上記のハッシュタグを含むツイートデータを使用するために、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"> 可視化結果から掘り下げた情報を得る手助けとして以下の3つの拡張機能を実装しています。</p>
              <ul>
                <li align="left">各ハッシュタグをホールドしながら動かすことで自由に動かすこと。リンクも連動して動くため、どのハッシュタグ間が繋がっているのか、見たいハッシュタグのみを移動させることも可能。</li>
                <li align="left">テキストボックスより検索したいキーワードを入力することで、入力したキーワードを含むハッシュタグを強調表示する。</li>
                <li align="left">クリックされたハッシュタグから到達可能なハッシュタグを強調表示する。</li>
              </ul>
              <div style={{ margin: "2em" }} className='renderer'>
                <Chart data={data} />
              </div>
              <div>
                <h3>◎考察</h3>
                <p align='left'> Twitterのハッシュタグを用いて話題遷移ネットワーク可視化を行った結果、東日本大震災後に放射線や原発事故に関する科学的に不適切または不正確な情報が広がったり、根拠のない言説が流布されるようになる過程には、話題が遷移していくうちに原発などから離れた政治的な話題に広がっていることや、差別的な意図がある造語を用いている話題に遷移していることが確認された。<br/> また「原発」や「震災」からかけ離れた話題に広がる経緯には、これらのタグを無理やりツイートに付けることで多くの人に見せようとしている意図が考えられたり、差別的な意図がある造語をハッシュタグにすることで、興味関心を引かせて不適切な内容を拡散しているように考えられた。</p>
                <p align="left"> ハッシュタグの遷移を見ていくと3つ以上のハッシュタグの遷移をしているものは少なく、概ね2つのハッシュタグを遷移していることが多いことが分かる。 原因として考えられる事が、実際に当時のツイートを検索したところ、genpatsuというハッシュタグだけつけて、内容は東電上層部の責任問題について述べていたり当時の総理大臣の応援しているだけの内容だったりと大雑把にハッシュタグを使用していることが確認でき、あまりハッシュタグはバリエーションを持っていないことからこの結果になったのではないかと考えられる。
                </p>
              </div>
            </div>
          </div>
        </section>


        <footer className="footer">
          <div className="content has-text-centered">
            <p>&copy;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"));
})();

              
            
!
999px

Console