6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PythonAdvent Calendar 2023

Day 3
記事投稿キャンペーン 「2024年!初アウトプットをしよう」

プログラミング言語の人気ランキングをQiitaAPIで調べて纏める(PythonとFlutterによる実装の例)

Last updated at Posted at 2024-01-20

はじめに

どのプログラミング言語を勉強したらいいか?人気な言語はどれ?気になる人もたくさんいるでしょう。ランキングしようとしても色んな基準があって一概と答えることはできないが、ここではQiitaでの記事数とフォロワー数でランキングを纏めてみたいと思います。参考になれば幸いです。

纏める方法はQiitaAPIを通じてデータを取得して扱うことです。主にPythonを使いますが、Flutterを使ってスマホで表示するアプリも作ってみたので、ここでもコードを載せて説明します。

結果発表

扱う対象について

ここで扱うのは言語の名前であるQiitaでのタグです。全部65言語あります。

勿論実際にもっとたくさん言語があるはずですが、ここでQiitaで記事数が50以上だけ扱うので、あまり誰も知らないマイナー言語は含まれません。(私が使うIDL言語も記事数9しかないので入れていません。ALGOLAdaも)

できるだけたくさんの言語を入れようとしていますが、もしまだどこかの人気言語を見落としていたら指摘して頂ければ追加します。

ここで扱うのはプログラミング言語だけなので、SQLHTMLCSSLaTeXなどは対象外です。

SASCoqなど独特な言語を使うソフトウェアはプログラミング言語リストに入ることもあるようですが、厳密にはプログラミング言語の名前ではなくソフトウェアの名前なので対象外です。

VBAVB(Visual Basic)言語の用途で言語自体を差すのではないので対象外ですが、実際にVBAタグの数が圧倒的にVBやVB.netより多いので、VBの人気はVBAタグで考慮した方がいいかもしれません。

一文字の名前の言語はC言語D言語R言語がありますが、ここではCRはそのままタグの名前として定着していますが、D言語はdlangというタグが使われています。clangというタグもありますが、これはC言語のことではなく、別の意味なので紛らわしい気もします。

Mindなでしこプロデルという日本語プログラミング言語も含まれます。

全部の言語のタグ纏め

以下の表は2024年1月20日時点でQiitaAPIから取得したデータから纏めたもの。名前の順で並んでいます。後ろにある括弧はこの65の言語の中での順位。

言語 記事数 フォロワー数
ActionScript 261 (46位) 235 (40位)
AppleScript 489 (38位) 168 (46位)
assembly 154 (55位) 67 (52位)
AWK 560 (34位) 287 (36位)
BASIC 200 (49位) 9 (64位)
brainfuck 75 (63位) 17 (61位)
C 5250 (12位) 34130 (8位)
C# 16168 (7位) 49961 (6位)
C++ 11123 (10位) 40338 (7位)
Carbon 119 (59位) 7 (65位)
Clojure 691 (27位) 563 (26位)
cobol 153 (56位) 71 (51位)
CoffeeScript 967 (23位) 17071 (12位)
Crystal 296 (44位) 207 (43位)
Dart 2844 (18位) 876 (23位)
Delphi 686 (28位) 229 (41位)
dlang 511 (37位) 228 (42位)
ECMAScript 560 (35位) 282 (37位)
Elixir 4469 (14位) 1301 (21位)
Elm 674 (29位) 565 (25位)
Erlang 564 (33位) 430 (29位)
F# 399 (40位) 337 (33位)
Forth 80 (62位) 22 (59位)
Fortran 661 (30位) 332 (34位)
GLSL 602 (32位) 436 (28位)
Go 12869 (9位) 8217 (17位)
Groovy 608 (31位) 267 (38位)
Haskell 2251 (19位) 14395 (14位)
Haxe 178 (52位) 190 (45位)
Java 23712 (5位) 88248 (3位)
JavaScript 54947 (2位) 147065 (2位)
JScript 177 (53位) 54 (54位)
Julia 1477 (21位) 1061 (22位)
Kotlin 7086 (11位) 3326 (19位)
lisp 429 (39位) 400 (30位)
Lua 729 (26位) 376 (32位)
MATLAB 1274 (22位) 735 (24位)
mind 175 (54位) 16 (62位)
Nim 382 (42位) 378 (31位)
Objective-C 4067 (16位) 23653 (10位)
OCaml 199 (50位) 257 (39位)
Pascal 232 (48位) 20 (60位)
Perl 1832 (20位) 18229 (11位)
PHP 28955 (4位) 81755 (4位)
Prolog 385 (41位) 124 (49位)
purescript 263 (45位) 164 (47位)
Python 78028 (1位) 167011 (1位)
Q# 58 (64位) 48 (56位)
R 4714 (13位) 2547 (20位)
Ruby 40765 (3位) 67430 (5位)
Rust 4281 (15位) 3639 (18位)
Scala 3353 (17位) 15669 (13位)
Scheme 247 (47位) 11966 (15位)
Scratch 381 (43位) 158 (48位)
Smalltalk 144 (57位) 62 (53位)
solidity 917 (24位) 330 (35位)
SuperCollider 52 (65位) 53 (55位)
Swift 21976 (6位) 10768 (16位)
Tcl 97 (61位) 40 (57位)
TypeScript 13663 (8位) 27267 (9位)
VB 101 (60位) 88 (50位)
VB.Net 734 (25位) 462 (27位)
VBScript 554 (36位) 199 (44位)
なでしこ 198 (51位) 25 (58位)
プロデル 121 (58位) 14 (63位)

記事数ランキング

Qiitaで最も記事数が多い言語トップ40を棒グラフに書いて纏めます。

kijirank.png

やはり一番人気なのはPythonで次はJavaScriptですね。これは記事数だけでなく、フォロワー数を見ても同じ結果です。

フォロワー数ランキング

フォロワー数で並べてまた棒グラフを見たらこうなります。

folrank.png

記事数と比べたら大体同じような傾向ですが、違うところも多いです。Rubyは記事数は3番なのに、フォロワーは5番となっています。逆にPerlやCoffeeScriptなどはフォロワー数のわりに記事数は少ない。更にSchemeはフォロワー数だけすごく多いが、記事数ランキングに入らないのです。

記事数とフォロワー数の分布図

記事数とフォロワーの関係を可視化するために分布図も描きます。ただし多すぎないようにここでは記事数25番までだけ表示します。

kiji-fol.png

Pythonによる実装

以上QiitaAPIに接続してデータを取ってグラフに書いたのは全部、一番人気と言われるPythonです。

Pythonを使うとどの作業も簡単にできてしまって本当に便利ですね。人気になるのも当然な結果でしょう。

ここでは今回使ったコードも載せます。

データ取得

まずデータを取得して.csvに保存するコードです。requestspandasを使ったら簡単にできます。

import requests
import pandas as pd

lis_df = []
for i in range(1,51):
    req = requests.get('https://qiita.com/api/v2/tags?page=%d&per_page=100&sort=count'%i)
    lis_df.append(pd.DataFrame(req.json())[['id','items_count','followers_count']])
pd.concat(lis_df,ignore_index=True).set_index('id').to_csv('qiita_tag.csv')

ここで得られるのは最も記事数が多い5000のタグです。その中に今回の対象であるプログラミング言語の前であるタグが含まれます。

棒グラフと分布図

データを手に入れた後、次は纏めてmatplotlibでグラフを描きます。

import pandas as pd
import matplotlib.pyplot as plt

# 扱う言語のリスト
gengo_lis = 'ActionScript AppleScript assembly AWK BASIC brainfuck C C# C++ Carbon Clojure cobol CoffeeScript Crystal Dart Delphi dlang ECMAScript Elixir Elm Erlang F# Forth Fortran GLSL Go Groovy Haskell Haxe Java JavaScript JScript Julia Kotlin lisp Lua MATLAB mind Nim Objective-C OCaml Pascal Perl PHP Prolog purescript Python Q# R Ruby Rust Scala Scheme Scratch Smalltalk solidity SuperCollider Swift Tcl TypeScript VB VB.Net VBScript なでしこ プロデル'.split(' ')
# 言語の名前であるタグだけ抽出する
df = pd.read_csv('qiita_tag.csv',index_col='id').loc[gengo_lis]

# フォロー数の棒グラフ
y = range(40)
plt.figure(figsize=[6,7.5],dpi=100)
ax = plt.axes(ylim=[min(y)-0.5,max(y)+0.5])
df.sort_values('followers_count',inplace=True)
plt.barh(y,df['followers_count'][-40:],color='#AAEE44',ec='k')
plt.yticks(y,['%s: %6s'%x for x in df['followers_count'][-40:].items()])
plt.title('フォロー数',family='MS Gothic')
plt.semilogx()
plt.tight_layout()
plt.savefig('folrank.png')

# 記事数の棒グラフ
plt.figure(figsize=[6,7.5],dpi=100)
ax = plt.axes(ylim=[min(y)-0.5,max(y)+0.5])
df.sort_values('items_count',inplace=True)
plt.barh(y,df['items_count'][-40:],color='#EEAA44',ec='k')
plt.yticks(y,['%s: %6s'%x for x in df['items_count'][-40:].items()])
plt.title('記事数',family='MS Gothic')
plt.semilogx()
plt.tight_layout()
plt.savefig('kijirank.png')

# 記事数とフォロワー数を比較する散布図
plt.figure(figsize=[6,6],dpi=100)
plt.xlabel('記事数',family='MS Gothic')
plt.ylabel('フォロー数',family='MS Gothic')
plt.scatter(df['items_count'][-25:],df['followers_count'][-25:],s=6,fc='r')
for gengo in df.index[-25:]:
    plt.text(df['items_count'][gengo],df['followers_count'][gengo],gengo,ha='right',va='top',family='MS Gothic',size=9)
plt.loglog()
plt.grid(ls=':',which='both')
plt.tight_layout()
plt.savefig('kiji-fol.png')
plt.close()

なお、ランキングの順位はこう書いて得られます。

df['items_count_rank'] = (-df['items_count']).argsort().argsort()+1
df['followers_count_rank'] = (-df['followers_count']).argsort().argsort()+1

Flutterによる実装

できたもの

最近Flutterを勉強し始めて随分と気に入ったので、この実装もFlutterで何かしたくてこんなものを作ることになりました。

まずできたアプリを.apkファイルにデプロイしてスマホに入れてインストールして起動したらまずは空っぽですが、「データをロードする」ボタンを押したらQiitaAPIからタグのデータを読み込んで纏めて並べてこういう表が出てきます。スライドして下の方も見られます。

このアプリは3タブに分けられて、次のタブを押したら記事数の順番で並んでいる棒グラフになります。

そしてフォロワー数の順番で並んでいる棒グラフ。

一度ロードしたらデータは保存されるので、閉じて再びアプリを起動してもデータはそのままで、もう一度ロードする必要はないが、時間が経ってまた新しいデータに更新したい場合またボタンを押します。

実装のコード

ここではFlutterやDartなどの基本を省略します。コードの中で色々解説を書きます。

使うパッケージ

今回の実装で必要なのはこの2つのパッケージです。

httpはQiitaAPIに接続してデータを取得するためであり、shared_preferencesは取得したデータを保存するためです。

AndroidManifest.xml

Androidでhttpパッケージが使えるようにするにこの部分を追加する必要があります。

android\app\src\main\AndroidManifest.xml
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

pubspec.yml

今回使う2つのパッケージをpubspec.ymlに追加します。

pubspec.yml
  shared_preferences: ^2.2.2
  http: ^1.2.0

main.dart

コードは分けずに全部ただ一つのファイルに纏めているので長いです。分けてもいいが、面倒くなるのでこのままでいい。

lib\main.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

void main() {
  runApp(const QiiApp());
}

class QiiApp extends StatelessWidget {
  const QiiApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const DefaultTabController(length: 3, child: QiiPage()),
    );
  }
}

class QiiPage extends StatefulWidget {
  const QiiPage({super.key});

  @override
  State<QiiPage> createState() => _QiiPageState();
}

class _QiiPageState extends State<QiiPage> {
  // 扱うプログラミング言語の名前のリスト
  List gengoLis =
      'ActionScript AppleScript assembly AWK BASIC brainfuck C C# C++ Carbon Clojure cobol CoffeeScript Crystal Dart Delphi dlang ECMAScript Elixir Elm Erlang F# Forth Fortran GLSL Go Groovy Haskell Haxe Java JavaScript JScript Julia Kotlin lisp Lua MATLAB mind Nim Objective-C OCaml Pascal Perl PHP Prolog purescript Python Q# R Ruby Rust Scala Scheme Scratch Smalltalk solidity SuperCollider Swift Tcl TypeScript VB VB.Net VBScript なでしこ プロデル'
          .split(' ');

  // 必要なウィジェットのリスト変数を宣言しておく
  List<TableRow> tableRowLis = [];
  List<Row> kijiRankBar = [];
  List<Stack> folRankBar = [];

  // タグのデータを並べてウィジェットに入れる
  void setQdata(qdata) {
    // 言語リストに含まれるものだけ取得する
    List qTagLis1 = qdata.where((q) => gengoLis.contains(q['id'])).toList();
    // リストをコピーする
    List qTagLis2 = [...qTagLis1];
    List qTagLis3 = [...qTagLis1];

    // 名前で並び替える
    qTagLis1
        .sort((a, b) => a['id'].toUpperCase().compareTo(b['id'].toUpperCase()));
    // 記事数で並び替える
    qTagLis2.sort((a, b) => b['n_kiji'].compareTo(a['n_kiji']));
    // フォロワー数で並び替える数で並び替える
    qTagLis3.sort((a, b) => b['n_fol'].compareTo(a['n_fol']));

    int nKijiMax = 0; // 記事数の最大値
    int nFolMax = 0; // フォロワー数の最大値
    TextStyle style =
        const TextStyle(fontWeight: FontWeight.bold, fontSize: 18);
    // 全体のデータを表示する表の行
    tableRowLis = [
      // ヘッダの行
      TableRow(children: <Widget>[
        TableCell(
          child: Container(
            padding: const EdgeInsets.all(3),
            alignment: Alignment.center,
            child: Text('言語', style: style),
          ),
        ),
        TableCell(
          child: Container(
            padding: const EdgeInsets.all(3),
            alignment: Alignment.center,
            child: Text('記事', style: style),
          ),
        ),
        TableCell(
          child: Container(
            padding: const EdgeInsets.all(3),
            alignment: Alignment.center,
            child: Text('フォロワー', style: style),
          ),
        ),
      ])
    ];

    // 表に行を入れていく
    for (Map q in qTagLis1) {
      // 最大値の更新
      if (q['n_kiji'] > nKijiMax) nKijiMax = q['n_kiji'];
      if (q['n_fol'] > nFolMax) nFolMax = q['n_fol'];

      tableRowLis.add(TableRow(
        children: <Widget>[
          // 言語の名前
          TableCell(
            child: Container(
              padding: const EdgeInsets.fromLTRB(10, 3, 10, 3),
              child: Text('${q['id']}'),
            ),
          ),
          // 記事数
          TableCell(
            child: Container(
              padding: const EdgeInsets.fromLTRB(10, 3, 10, 3),
              alignment: Alignment.centerRight,
              child: Text('${q['n_kiji']}'),
            ),
          ),
          // フォロワー数
          TableCell(
            child: Container(
              padding: const EdgeInsets.fromLTRB(10, 3, 10, 3),
              alignment: Alignment.centerRight,
              child: Text('${q['n_fol']}'),
            ),
          )
        ],
      ));
    }

    // 記事数順で表す行のリスト
    kijiRankBar = [];
    for (Map q in qTagLis2) {
      kijiRankBar.add(Row(
        children: [
          Container(
            // 言語の名前を書く左の部分
            color: const Color(0xFFDDDDEE),
            width: 100,
            height: 28,
            child: Padding(
              padding: const EdgeInsets.all(3.0),
              child: Text(q['id']),
            ),
          ),
          Flexible(
            // 記事数によって長くなる棒
            child: FractionallySizedBox(
              widthFactor: q['n_kiji'] / nKijiMax,
              child: Container(
                color: const Color(0xFFEEAA44),
                height: 26,
              ),
            ),
          ),
          Container(
            // 棒の右側に記事数
            padding: const EdgeInsets.all(3.0),
            child: Text('${q['n_kiji']}'),
          ),
        ],
      ));
    }

    // フォロワー数順で表す行のリスト
    folRankBar = [];
    for (Map q in qTagLis3) {
      folRankBar.add(Stack(
        children: [
          Container(
            // 棒の後ろの灰色
            color: const Color(0xFFDDDDEE),
            height: 26,
          ),
          Container(
            // フォロワー数によって長くなる棒
            color: const Color(0xFFAAEE44),
            width: MediaQuery.of(context).size.width * q['n_fol'] / nFolMax,
            height: 26,
          ),
          SizedBox(
            // 言語の名前とフォロワー数
            height: 28,
            child: Padding(
              padding: const EdgeInsets.all(3.0),
              child: Text('${q['id']} (${q['n_fol']})'),
            ),
          ),
        ],
      ));
    }
  }

  // QiitaAPIからデータを取得する関数
  void loadQiitaTag() async {
    List qdata = []; // タグのデータわ収めるリスト
    try {
      for (int i = 1; i < 51; i++) {
        // QiitaAPIにアクセス
        var response = await http.get(Uri.parse(
            'https://qiita.com/api/v2/tags?page=$i&per_page=100&sort=count'));

        for (Map q in jsonDecode(response.body)) {
          // 読み込まれたデータをリストに収めていく
          qdata.add({
            'id': q['id'],
            'n_kiji': q['items_count'],
            'n_fol': q['followers_count'],
          });
        }
      }

      // shared_preferencesを起動する
      SharedPreferences prefs = await SharedPreferences.getInstance();
      // データを保存する
      await prefs.setString('qdata', jsonEncode(qdata));
      // 表示のために配置する
      setState(() {
        setQdata(qdata);
      });
    } catch (e) {
      // 何かエラーが出る場合そのエラーメッセージを表示する
      setState(() {
        tableRowLis = [
          TableRow(children: <Widget>[Text('$e')])
        ];
      });
    }
  }

  // shared_preferencesで保存されたデータを読み込んで配置する
  void readQdata() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    setState(() {
      setQdata(jsonDecode(prefs.getString('qdata') ?? '[]'));
    });
  }

  // アプリを起動した時に実行する関数
  @override
  void initState() {
    super.initState();
    readQdata();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
          backgroundColor: const Color(0xFF88FFCC),
          title: const TabBar(
            // 上のメニューの部分
            isScrollable: true,
            indicatorColor: Color(0xFFCC9966),
            tabs: [
              Tab(text: '全体'),
              Tab(text: '記事数順'),
              Tab(text: 'フォロワー数順'),
            ],
            labelStyle: TextStyle(fontSize: 20),
          )),
      body: TabBarView(children: [
        // 全体のデータを表すタブ
        ListView(
          children: [
            // 押したらQiitaAPIに接続してデータを取得するボタン
            ElevatedButton(
              onPressed: loadQiitaTag,
              child: const Text(
                'データをロードする',
                style: TextStyle(fontSize: 20),
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(3.0),
              child: Table(
                border: TableBorder.all(),
                children: tableRowLis,
              ),
            )
          ],
        ),
        // 記事数のタブ
        ListView(
          children: kijiRankBar,
        ),
        // フォロワー数のタブ
        ListView(
          children: folRankBar,
        ),
      ]),
    );
  }
}

参考になった記事

他にプログラミング言語ランキング関連の記事

ランキングの基準はそれぞれ色々違って、時間によっても変わっていくので、この記事以外にも色んな基準と色んな時期に書いた記事を読んで比べて参考にしてみましょう。

以下Qiitaで見つけた他のランキング関連の記事:

6
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?