<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>SciPy on Grayrecord Technow Blog</title>
    <link>https://technow.grayrecord.com/tags/scipy/</link>
    <description>Recent content in SciPy on Grayrecord Technow Blog</description>
    <image>
      <title>Grayrecord Technow Blog</title>
      <url>https://technow.grayrecord.com/images/Grayrecord-technow.png</url>
      <link>https://technow.grayrecord.com/images/Grayrecord-technow.png</link>
    </image>
    <generator>Hugo -- 0.161.1</generator>
    <language>ja</language>
    <lastBuildDate>Tue, 19 Aug 2025 22:54:38 +0900</lastBuildDate>
    <atom:link href="https://technow.grayrecord.com/tags/scipy/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>SciPyのF分布ppfが抱える数値精度問題</title>
      <link>https://technow.grayrecord.com/post/inaccurate-results-for-extreme-arguments/</link>
      <pubDate>Tue, 19 Aug 2025 22:54:38 +0900</pubDate>
      <guid>https://technow.grayrecord.com/post/inaccurate-results-for-extreme-arguments/</guid>
      <description>&lt;h2 id=&#34;概要&#34;&gt;概要&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;scipy.stats.f.ppf&lt;/code&gt;（F分布のパーセント点関数）が、特定の条件下で不正確な結果を返す可能性があるという問題についての報告です。具体的には、確率が1に非常に近い値（裾の領域）と大きな自由度の組み合わせにおいて、ppf関数の結果と、累積分布関数（cdf）を最適化して得られる結果との間に大きな乖離が見られます。&lt;/p&gt;
&lt;p&gt;この問題は2021年頃に発見されましたが、最近のバージョンでも同様の現象が確認されたため、改めて報告するものです。&lt;/p&gt;
&lt;p&gt;なお、本件は、既に、https://github.com/scipy/scipy/issues/20835 で報告されており、恐らく、scipy 1,17で対策はマージされると思います。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scipy/scipy/pull/22699&#34;&gt;ENH: special: boostify F distribution functions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;問題の詳細&#34;&gt;問題の詳細&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;scipy.stats.f.ppf&lt;/code&gt; に欠陥がある可能性が疑われます。最適化関数と累積分布関数（cdf）の組み合わせで得られる結果が、ppf関数の戻り値と一致しません。&lt;/p&gt;
&lt;h3 id=&#34;再現コード&#34;&gt;再現コード&lt;/h3&gt;
&lt;p&gt;以下のコードは、&lt;code&gt;sps.f.ppf&lt;/code&gt; を直接呼び出した場合と、&lt;code&gt;spo.brentq&lt;/code&gt; を使って &lt;code&gt;sps.f.cdf&lt;/code&gt; から逆関数を求めた場合の結果を比較します。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;scipy.stats&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;sps&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;scipy.optimize&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;spo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# ppf関数を直接呼び出す&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sps&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ppf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.99999995&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;50000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;loc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scale&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# cdfと最適化関数brentqを使って逆関数を求める&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;spo&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;brentq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;lambda&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sps&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cdf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;50000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.99999995&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1e10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;コードの出力&#34;&gt;コードの出力&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;3333.8385803475894
29.725915444860586
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;出力結果からわかるように、2つの計算結果は大きく異なっています。&lt;/p&gt;
&lt;h3 id=&#34;エラーメッセージ&#34;&gt;エラーメッセージ&lt;/h3&gt;
&lt;p&gt;この問題では、エラーメッセージは出力されません。コードは正常に終了しますが、得られる結果の信頼性に疑問があります。&lt;/p&gt;
&lt;h3 id=&#34;原因の考察&#34;&gt;原因の考察&lt;/h3&gt;
&lt;p&gt;このPythonコードと出力は、F分布のパーセント点関数（ppf）と、それを最適化関数（&lt;code&gt;brentq&lt;/code&gt;）を使って求めるアプローチとの間の数値的な不安定性を示唆しています。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;sps.f.ppf(0.99999995, 1, 50000)&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;これはF分布のパーセント点関数（ppf）を直接呼び出し、累積確率が&lt;code&gt;0.99999995&lt;/code&gt;となるF値を求めます。この計算では、F値が &lt;strong&gt;3333.83&amp;hellip;&lt;/strong&gt; という非常に大きな値になっています。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;spo.brentq(lambda x: sps.f.cdf(x, 1, 50000) - 0.99999995, 1, 1e10)&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;こちらは&lt;code&gt;scipy.optimize&lt;/code&gt;モジュールの&lt;code&gt;brentq&lt;/code&gt;関数を使い、同じ問題を数値的に解いています。&lt;code&gt;brentq&lt;/code&gt;は、与えられた関数の値が0になる点（根）を見つけるための堅牢なアルゴリズムです。ここでは、「&lt;code&gt;sps.f.cdf(x, 1, 50000) - 0.99999995&lt;/code&gt;」という式が0になる&lt;code&gt;x&lt;/code&gt;、つまり累積確率が&lt;code&gt;0.99999995&lt;/code&gt;になるF値を探索しています。&lt;/p&gt;
&lt;p&gt;この計算では、F値が &lt;strong&gt;29.72&amp;hellip;&lt;/strong&gt; という、ppfの直接呼び出しとは全く異なる、より妥当と思われる値が得られます。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;この乖離は、&lt;code&gt;scipy.stats.f.ppf&lt;/code&gt;が特定の条件下で正確な答えを返さない可能性があることを示しています。&lt;/p&gt;
&lt;p&gt;F分布のような統計分布の逆関数（ppf）を、極端な確率値（この例では1に非常に近い&lt;code&gt;0.99999995&lt;/code&gt;）で計算する場合、数値的な精度の問題が発生しやすくなります。特に自由度が大きい場合、分布の裾（テール）の部分が非常に平坦になるため、計算過程でのわずかな丸め誤差などが、結果として得られるF値の大きな違いにつながることがあります。&lt;/p&gt;
&lt;p&gt;一方、&lt;code&gt;brentq&lt;/code&gt;は指定された範囲内で根を探索する、より頑健な数値計算アルゴリズムです。関数の振る舞いをより正確に追跡できるため、このようなケースではより信頼性の高い結果を生成すると考えられます。&lt;/p&gt;
&lt;h3 id=&#34;問題の可視化&#34;&gt;問題の可視化&lt;/h3&gt;
&lt;p&gt;この問題は、以下のコードで&lt;code&gt;scipy.special.fdtri&lt;/code&gt;（F分布の逆関数）の挙動をプロットすることで、より明確に可視化できます。自由度&lt;code&gt;d2&lt;/code&gt;が50000と大きい場合に、確率&lt;code&gt;p&lt;/code&gt;が0に近づく（つまり累積確率が1に近づく）領域で、関数の値が急激に変動していることがわかります。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;numpy&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;np&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;scipy&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;special&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;plt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;d1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;50000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;logspace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# fdtriは1-pを引数に取るため、pが0に近づくと累積確率が1に近づく&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;fdtri&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;special&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fdtri&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;d1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;plt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;semilogx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fdtri&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;plt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;xlabel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;p (1 - Cumulative Probability)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;plt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ylabel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;F-value (fdtri)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;plt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;fdtri behavior for extreme probabilities (d1=1, d2=50000)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;plt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;grid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;plt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;show&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img alt=&#34;F分布の逆関数における極端な値での変動&#34; loading=&#34;lazy&#34; src=&#34;https://technow.grayrecord.com/images/chart-scipy-001.png&#34;&gt;&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
