Movatterモバイル変換


[0]ホーム

URL:


ピーチ委員会

この広告は、90日以上更新していないブログに表示しています。

つばきファクトリーで本当ににらめっこが強いのは誰か

行くぜ!つばきファクトリー#76でにらめっこ対決をしていました。個人の対戦成績を列挙すると、

村田x o土居石井o x豫風小野x o秋山石井x o小野田村田o x八木小野x o小野田谷本o x福田豫風o x河西秋山o x村田土居x o小野谷本o x石井八木x o石井小野田o x小野福田o x村田福田o x土居小野田x o豫風河西o x秋山

です。これを個人の戦績に直すと、

1位谷本2勝0敗
2位小野田3勝1敗
3位豫風2勝1敗
3位秋山2勝1敗
3位福田2勝1敗
6位石井2勝2敗
6位河西1勝1敗
8位土居1勝2敗
9位村田1勝3敗
9位小野1勝3敗
11位八木0勝2敗

勝率で順位付けしています。
しかし、強い人と当たるとどうしても成績が悪くなりますよね。こういうときよくElo Ratingという指標が用いられますが、あまりに誤差が大きそうなのでMCMCを使ってレーティングを計算してみました。

1位谷本1764.4±111.3
2位豫風1619.3±76.6
3位小野田1619.3±76.5
4位石井1619.2±76.6
5位河西1556.0±77.0
6位福田1546.6±119.4
7位秋山1494.5±77.4
8位小野1433.9±80.3
9位土居1368.3±82.9
10位村田1297.5±90.3
11位八木1189.2±116.5

mean±σです。勝率とは違う順位になることが分かるでしょう。2~4位は同じ分布です。それから、分布は広くてこんな少ない対戦では本当の強さは分からないことが分かります。


# coding: utf-8from __future__import annotationsfrom itertoolsimport groupbyfrom collectionsimport defaultdictfrom fractionsimport Fractionimport sysimport pandasas pdimport numpyas npdefcalculate_frequencies(trace, players, num_bins=20):    frequencies = {}for playerin players:        samples = trace[player]        freq, edges = np.histogram(samples, bins=num_bins)        bin_centers = (edges[:-1] + edges[1:]) /2        frequencies[f"{player}_freq"] = freq        frequencies[f"{player}_bins"] = bin_centersreturn frequenciesdefexport_frequencies_to_excel(frequencies, filename='player_skill_frequencies.xlsx'):# 各プレイヤーのビンセンターを同じにするため、最小および最大範囲を計算    min_bin =min(min(freq)for freqin frequencies.values()if'_bins'in freq)    max_bin =max(max(freq)for freqin frequencies.values()if'_bins'in freq)    bin_centers = np.linspace(min_bin, max_bin,len(frequencies[next(iter(frequencies))]))    data = {}for playerin players:        freq = frequencies[f"{player}_freq"]        bins = frequencies[f"{player}_bins"]        data[f"{player}_freq"] = np.interp(bin_centers, bins, freq)        data[f"{player}_bins"] = bin_centers        df = pd.DataFrame(data)    df.to_excel(filename, index=False)print(f'Data exported to {filename}')defexport_frequencies_to_excel(frequencies, filename='player_skill_frequencies.xlsx'):    df = pd.DataFrame(frequencies)    df.to_excel(filename, index=False)print(f'Data exported to {filename}')defexport_trace_to_excel(trace, players, filename='player_skills.xlsx'):    data = {player: trace[player]for playerin players}    df = pd.DataFrame(data)    df.to_excel(filename, index=False)print(f'Data exported to {filename}')colors = {'谷本':'#e6e6fa','小野':'#50c878','小野田':'#ffdab9','秋山':'#ff6347','河西':'#800080','八木':'#ffa500','福田':'#4169e1','豫風':'#ffdb58','石井':'#ffffff','村田':'#ffb6c1','土居':'#00ff7f' }# -> (win, lose)defparse_result(line:str) ->tuple[str,str]:for i, cinenumerate(line):if cnotin'ox':continue        player1 = line[:i]        player2 = line[i+3:]if c =='o':return (player1, player2)else:return (player2, player1)return ('','')defread_results(path:str) ->list[tuple[str,str]]:withopen(path,'r')as f:return [ parse_result(line.rstrip())for linein f ]defadd(v:tuple[int,int], w:tuple[int,int]) ->tuple[int,int]:return (v[0] + w[0], v[1] + w[1])defdetermin_ranking(resutls:list[tuple[str,str]]                                ) ->list[tuple[int,str,int,int]]:    dic:dict[str,tuple[int,int]] = defaultdict(lambda : (0,0))for player1, player2in results:        dic[player1] = add(dic[player1], (1,0))        dic[player2] = add(dic[player2], (0,1))        players = [ (p, w, l)for p, (w, l)in dic.items() ]    players.sort(key=lambda v: Fraction(v[1], v[1]+v[2]), reverse=True)    ranks:list[tuple[int,str,int,int]] = []    rank =1for r, vin groupby(players, key=lambda v: Fraction(v[1], v[1]+v[2])):        ps =list(v)for p, w, lin ps:            ranks.append((rank, p, w, l))        rank +=len(ps)return ranksdefprint_ranks(ranks:list[tuple[int,str,int,int]]):for rank, player, win, losein ranks:        args = (rank, player, win, lose)print('|%d位|%s|%d勝%d敗|' % args)import pymc3as pmimport numpyas np# モデルの構築import pymc3as pmdefbuild_model(matches, players, initial_rating, sigma):    model = pm.Model()with model:# 各プレイヤーの実力を正規分布でモデル化        skills = { player: pm.Normal(player, mu=initial_rating, sigma=sigma)for playerin players }# 対戦結果の尤度関数を定義for i, (player1, player2)inenumerate(matches):            skill_diff = skills[player1] - skills[player2]            prob_win = pm.math.sigmoid(skill_diff)            pm.Bernoulli(f'{i}', p=prob_win, observed=1)return modeldefsample_model(model, num_samples, num_tune):    trace = pm.sample(num_samples, tune=num_tune, cores=2, model=model)return tracedefprint_rate(trace, players):    rates = [ (np.mean(trace.get_values(player)), player)for playerin players ]    rates.sort(reverse=True)for i, (rate, player)inenumerate(rates,1):print("%d位 %6s %.2f" % (i, player, rate))import matplotlib.pyplotas pltimport arvizas azfrom matplotlibimport font_managerdefplot_results(trace, players, path_img):# 日本語フォントを指定    font_path ='/usr/share/fonts/truetype/takao-gothic/TakaoPGothic.ttf'    font_prop = font_manager.FontProperties(fname=font_path)    plt.rcParams['font.family'] = font_prop.get_name()        plt.figure(figsize=(10,6))for playerin players:        az.plot_posterior(trace, var_names=[player],                                ref_val=1500, hdi_prob=0.94, ax=plt.gca())        plt.title('Skill Distributions of Players')    plt.xlabel('Skill Level')    plt.ylabel('Density')    plt.legend(players)    plt.savefig(path_img)    plt.close()print(f'Saved plot to {path_img}')path = sys.argv[1]results = read_results(path)players =list(set(pfor vin resultsfor pin v))ranks = determin_ranking(results)print_ranks(ranks)model = build_model(results, players,1500,200)trace = sample_model(model,2000,1000)print_rate(trace, players)plot_results(trace, players,"N-1.png")# 頻度の計算frequencies = calculate_frequencies(trace, players)# データのエクスポートexport_frequencies_to_excel(frequencies)

年の差ラジオのWOC

https://radio.rcc.jp/toshinosa/

ザ・ギース尾関高文とOCHA NORMA広本瑠璃の年の差ラジオをでゲームをやっていましたが、全く勝てそうになかったのに勝ちました。勝つ確率はどれくらいなのか算出します。

ルールとしては明確ではなかったですが、プレーヤー1は1イニングに1~20のどれかの点数が同確率で入る、プレーヤー2はアウトが1/2、単打が1/3、ホームランが1/6で起こるとします。イニングが終わって10点以上差がついていたらコールドです。

まず、ランダムに1億回プレイしてみましょう。

from __future__import annotationsfrom itertoolsimport *from collectionsimport Counterimport randomimport sysfrom enumimport Enumfrom typingimport List, TupleState = Tuple[int,int,int]classAction(Enum):    OUT =1    HIT =2    HOMERUN =3@staticmethoddefselect_randomly():        r = random.randrange(1,7)if r <=3:return Action.OUTelif r <=5:return Action.HITelse:return Action.HOMERUNdeftransit(s: State, a: Action) -> State:    (score, out, base) = sif a == Action.OUT:return (score, out +1, base)elif a == Action.HIT:if base ==3:return (score +1, out, base)else:return (score, out, base +1)else:return (score + base +1, out,0)defplay1_randamly() ->int:return random.randrange(1,21)defplay2_randomly() ->int:    s = (0,0,0)whileTrue:        s = transit(s, Action.select_randomly())if s[1] ==3:return s[0]defplay_randomly() ->str:    score1 =0    score2 =0for _inrange(9):        score1 += play1_randamly()        score2 += play2_randomly()if score1 >= score2 +10:return'Lose'elif score2 >= score1 +10:return'Win'if score1 > score2:return'Lose'elif score1 < score2:return'Win'else:return'Draw'N =int(sys.argv[1])c = Counter(play_randomly()for _inrange(N))print(c)
Counter({'Lose': 99860253, 'Win': 139604, 'Draw': 143})

勝つ確率は0.14%程度のようです。
次に、真面目に確率計算をします。最初にプレーヤー2が1イニングに何点入るかの確率を求めます。ただし、181点以上入ったら必ず勝つので、181点以上はまとめて181点とします。

from __future__import annotationsfrom functoolsimportreducefrom itertoolsimport *from collectionsimport Counterimport randomimport sysfrom enumimport Enumfrom typingimport List, TupleState = Tuple[int,int,int]classAction(Enum):    OUT =1    HIT =2    HOMERUN =3@staticmethoddefprobability(a: Action):if a == Action.OUT:return3 /6elif a == Action.HIT:return2 /6else:return1 /6deftransit(s: State, a: Action) -> State:    (score, out, base) = sif a == Action.OUT:return (score, out +1, base)elif a == Action.HIT:if base ==3:return (score +1, out, base)else:return (score, out, base +1)else:return (score + base +1, out,0)defprobs_inning(N:int) ->list[float]:    probs:list[float] = [0.0] * (N +2)    c: Dict[State,float] = Counter()    c[(0,0,0)] =1.0while c:        new_c: Dict[State,float] = Counter()for s0, p0in c.items():for actionin Action:                s1 = transit(s0, action)                score, out, base = s1                p1 = p0 * Action.probability(action)if out ==3:                    probs[score] += p1elif score > N:passelse:                    new_c[s1] += p1        c = new_cprint(1.0 -sum(probs[:11]))# 11点以上取る確率    probs[N+1] = probs[N]# 半分ずつ減っていくのでたぶんこれくらいreturn probsdefdecide_probs():    probs1 = [0.0] + [1/20] *20    probs2 = probs_inning(180)# 181点上なら必ず勝つ        probs = [0.0] *3# [Lose, Win, Draw]    scores = { (0,0):1.0 }for iinrange(9):        new_scores: Dict[tuple[int,int],float] = Counter()for (score1, score2), p0in scores.items():for s1, s2in product(range(1,21),range(182)):                prob1 = probs1[s1]                prob2 = probs2[s2]                new_score1 = score1 + s1                new_score2 = score2 + s2                p = p0 * prob1 * prob2if new_score1 >= new_score2 +10:                    probs[0] += p# Loseelif new_score2 >= new_score1 +10:                    probs[1] += p# Winelse:                    new_scores[(new_score1, new_score2)] += p        scores = new_scoresfor (score1, score2), pin scores.items():if score1 > score2:            probs[0] += p# Loseelif score1 < score2:            probs[1] += p# Winelse:            probs[2] += p# Drawprint("Lose: %.6f, Win: %.6f, Draw: %.2e" %tuple(probs))decide_probs()

まず、プレーヤー2が1イニングに11点以上取る確率は、0.6%程度です。
そして、

Lose: 0.998606, Win: 0.001392, Draw: 1.39e-06

ランダムにプレイしたときとほとんど変わらないですね。

ハロプロ楽曲大賞2019集計(3)

第18回ハロプロ楽曲大賞'19

投票者がどのユニットの曲に入れたかを、自分の推しのユニットごとに集計した。
兼任は当分配で。
グラフは、累積の割合。

f:id:aya_momo:20200105154524p:plain

どのユニットも、自ユニットに最も入れているのは変わらないが、その次に入れているユニットかを見てみると、BEYOOOOONDSヲタ以外はBEYOOOOONDSであることがわかる。

順位を表にすると分かりやすい。

f:id:aya_momo:20200105183431p:plain

以下に、全てのデータを示す。

,モーニング娘。,アンジュルム,Juice=Juice,カントリー・ガールズ,こぶしファクトリー,つばきファクトリー,BEYOOOOONDS,othersモーニング娘。,6635.5,1807.75,2677.25,546.5,1317.0,706.25,3858.5,811.25アンジュルム,220.75,2258.0,543.5,173.5,332.0,122.0,820.25,280.0Juice=Juice,262.5,409.0,1990.0,118.0,335.5,153.5,757.5,264.0カントリー・ガールズ,138.25,231.5,177.25,508.0,96.75,29.75,278.0,95.5こぶしファクトリー,55.5,110.5,182.5,53.0,1262.0,39.5,307.5,59.5つばきファクトリー,92.5,149.5,254.0,32.0,171.0,443.5,365.5,92.0BEYOOOOONDS,260.5,319.5,345.75,92.75,298.75,119.25,2861.75,146.75others,1068.0,1382.5,1384.75,715.5,695.75,247.5,1696.0,2185.0

ハロプロ楽曲大賞2019集計(2)

第18回ハロプロ楽曲大賞'19


前に記事にしたものだが、グラフにしてみた。

推し部門に入れた投票者を投票先の推しのユニットの推しだとみなして、上位楽曲はどのユニットの推しが投票したのか調べた。兼任メンバーは0.5ずつとした。グラフはポイントの累積割合。

f:id:aya_momo:20200104110432p:plain

1位はJuice=Juiceヲタよりモーニング娘。ヲタの方がポイントを入れている。
モーニング娘。ヲタは全体の41.5%という巨大勢力なので、こういうことも起こる。

ハロプロ楽曲大賞の投票者数の減少は本当にカントリー・ガールズヲタのせいなのか?

第18回ハロプロ楽曲大賞'19

ハロプロ楽曲大賞の投票人数が去年は一昨年に比べて約400人減ったという。推しメン部門では、3905人から3511人になった。
これについて、カントリー・ガールズのヲタが投票しなくなったから、という言説をどこかで見た。これを検証してみる。

まず、一昨年の得票数は、

森戸知沙希85票
船木結62票
梁川奈々美57票
山木梨沙54票
小関舞31票
嗣永桃子29票

と、全体で318票で、400票に満たない。

では、カントリー・ガールズヲタは、他のユニットのヲタより減少しているのだろうか。
一昨年と去年で同じIDの投票者を追いかければ分かるかと思ったが、実は同じ投票者でもIDは変わるらしい。そこで、ニックネームで追いかけることにした。
では、ニックネームはどれくらい重複するだろうか。

一昨年は、ニックネームは3610あり、ユニークな(一度しか現れない)ニックネームは3405。去年は、ニックネームは3287あり、ユニークなニックネームは3129。

そして、一昨年のユニークなニックネームの投票者3405人が去年どうしたかを調べた。

全体では、1729人が投票、1676人が不明(ニックネームを変えているかもしれないので)。

では、カントリー・ガールズヲタの投票者はどうしたのか調べた。

名前投票不明
梁川奈々美3021
小関舞818
嗣永桃子1312
森戸知沙希3634
山木梨沙3122
船木結2331
合計141138

全体の投票・不明の割合と変わらない。カントリー・ガールズヲタが投票しなかったという証拠は見つからなかった。

ついでに、各ヲタが推し変したかを調べた。

梁川奈々美 ->梁川奈々美 9 小林萌花 3山木梨沙 3 西田汐里 2 その他 13
小関舞 -> 小関舞 6 その他 2
嗣永桃子 ->嗣永桃子 8 その他 5
船木結 ->船木結 16 江口紗耶 2 その他 5
森戸知沙希 ->森戸知沙希 29野村みな美 2 その他 5
山木梨沙 ->山木梨沙 26 その他 5

ちなみに、一昨年上位は、

佐藤優樹 ->佐藤優樹 134 その他 26
小田さくら ->小田さくら 57 その他 9
宮本佳林 ->宮本佳林 65 その他 14
加賀楓 ->加賀楓 74 その他 13
和田彩花 ->和田彩花 19中西香菜 4上國料萌衣 4 その他 27

ニックネームで追うのもそんなに悪くないことが分かる。

ハロプロ楽曲大賞2019集計(1)

第18回ハロプロ楽曲大賞'19


上位の曲に、どのユニットのヲタが投票したかをポイントで集計してみた。
兼任の3人は、等分。

songモーニング娘。'19アンジュルムJuice=Juiceカントリー・ガールズこぶしファクトリーつばきファクトリーBEYOOOOONDSothers
「ひとりで生きられそう」って それってねえ、褒めているの?1266.0343.51066.584.0126.0136.0158.0401.5
「ひとりで生きられそう」って それってねえ、褒めているの? (New Vocal Ver.)66.7526.7539.55.011.518.017.015.0
ニッポンノD・N・A!805.75260.0231.087.2582.0108.5391.0268.0
眼鏡の男の子673.0211.5158.045.560.058.0481.0187.0
青春Night1296.589.589.060.016.035.073.0147.0
赤いイヤホン378.75641.25133.064.045.046.062.5208.0
都営大江戸線六本木駅で抱きしめて480.0147.25120.075.7575.594.0312.0200.0
人生Blues1033.056.585.034.018.024.551.5127.5
I surrender 愛されど愛873.037.7532.012.2510.014.034.587.5
恋してみたくて840.2528.034.528.758.012.020.597.0
微炭酸240.580.5342.535.017.059.529.0117.0
消せやしないキモチ178.7566.2583.031.0279.531.554.5124.5
人生、すなわちパンタ・レイ153.25419.049.539.7510.021.533.5107.0
全然起き上がれないSUNDAY225.5291.564.540.511.519.055.578.5
ふわり、恋時計259.068.7571.514.7515.0203.552.079.0
One Summer Night ~真夏の決心~161.7582.7558.5190.022.516.031.582.0
ハルウララ179.2569.056.016.25164.047.541.564.0
三回目のデート神話195.051.7573.013.7519.5194.031.539.0
元年バンジージャンプ117.2527.7568.021.516.514.5212.066.0
弱気女子退部届126.2554.534.5152.2516.515.027.5103.5
検索

引用をストックしました

引用するにはまずログインしてください

引用をストックできませんでした。再度お試しください

限定公開記事のため引用できません。

読者です読者をやめる読者になる読者になる

[8]ページ先頭

©2009-2025 Movatter.jp