からっぽのしょこ

読んだら書く!書いたら読む!同じ事は二度調べ(たく)ない

【R】π/2 - θ(余角)の可視化

はじめに

 円関数(三角関数)の定義や性質、公式などを可視化して理解しようシリーズです。

 この記事では、一般角に対する余角と円関数の関係のグラフを作成します。

【前の内容】

www.anarchive-beta.com

【他の内容】

www.anarchive-beta.com

【この記事の内容】

π/2 - θ(余角)の可視化

 和が直角になる角(余角・complementary angle)について、単位円における定義をグラフで確認します。また、変数(偏角)を直角から引いたときの円関数(circular functions)・三角関数(trigonometric functions)との関係を確認します。
 度数法と弧度法(角度とラジアン)の関係や単位円における偏角については「【R】円周の作図 - からっぽのしょこ」を参照してください。

 利用するパッケージを読み込みます。

# 利用パッケージ
library(tidyverse)

 この記事では、基本的に パッケージ名::関数名() の記法を使うので、パッケージの読み込みは不要です。ただし、作図コードについてはパッケージ名を省略するので、ggplot2 を読み込む必要があります。
 また、ネイティブパイプ演算子 |> を使います。magrittr パッケージのパイプ演算子 %>% に置き換えられますが、その場合は magrittr を読み込む必要があります。

定義式の確認

 まずは、余角の定義と円関数との関係を数式で確認します。
 各関数についてはそれぞれの記事を参照してください。

 和が直角となる2つの角度

 \displaystyle
\theta + \tau
    = \frac{\pi}{2}

について、一方の角度  \theta に対して他方の角度  \tau を余角と呼びます。
  \tau について式を整理すると、余角の計算式が得られます。

 \displaystyle
\tau
    = \frac{\pi}{2} - \theta

 変数  \theta, \tau はラジアン(弧度法の角度)で、鋭角  0 \lt \theta \lt \frac{\pi}{2} (度数法の角度だと  0^{\circ} \lt \theta^{\circ} \lt 90^{\circ} )の範囲外も扱います。 \pi は円周率です。

 sin関数とcos関数に関して、2つの角度は、次の関係になります。

 \displaystyle
\begin{aligned}
\sin \left(
    \frac{\pi}{2} - \theta
\right)
   &= \cos \theta
\\
\cos \left(
    \frac{\pi}{2} - \theta
\right)
   &= \sin \theta
\end{aligned}

 sin関数はcos関数の値、cos関数はsin関数の値と一致します。
 この関係を用いると、sin関数またはcos関数を用いて定義される関数

 \displaystyle
\begin{aligned}
\tan x
   &= \frac{\sin x}{\cos x}
\\
\cot x
   &= \frac{\cos x}{\sin x}
\\
\sec x
   &= \frac{1}{\cos x}
\\
\csc x
   &= \frac{1}{\sin x}
\end{aligned}

は、次の関係になります。

 \displaystyle
\begin{aligned}
\tan \left(
    \frac{\pi}{2} - \theta
\right)
   &= \frac{
          \sin (\frac{\pi}{2} - \theta)
      }{
          \cos (\frac{\pi}{2} - \theta)
      }
    = \frac{\cos \theta}{\sin \theta}
    = \frac{1}{\tan \theta}
    = \cot \theta
\\
\cot \left(
    \frac{\pi}{2} - \theta
\right)
   &= \frac{
          \cos (\frac{\pi}{2} - \theta)
      }{
          \sin (\frac{\pi}{2} - \theta)
      }
    = \frac{\sin \theta}{\cos \theta}
    = \frac{1}{\cot \theta}
    = \tan \theta
\\
\sec \left(
    \frac{\pi}{2} - \theta
\right)
   &= \frac{1}{\cos (\frac{\pi}{2} - \theta)}
    = \frac{1}{\sin \theta}
    = \csc \theta
\\
\csc \left(
    \frac{\pi}{2} - \theta
\right)
   &= \frac{1}{\sin (\frac{\pi}{2} - \theta)}
    = \frac{1}{\cos \theta}
    = \sec \theta
\end{aligned}

 sin関数とcos関数が入れ替わるため、tan関数とcot関数、sec関数とcsc関数がそれぞれ入れ替わります。

直角三角形と角の関係

 一般角を扱う前に、直角三角形における(鋭角に対する)余角の定義を確認しておきます。

・直角三角形と余角の関係

直角三角形と余角の関係

 三角形の内角の和は  180^{\circ} なので、直角三角形の2つの鋭角の1つを  \theta^{\circ} とするともう一方(余角)は  90^{\circ} - \theta^{\circ} で表わせます。

・長方形(直角)と余角の関係

長方形と余角の関係

 直角を2つの鋭角に分割したとき、1つを  \theta^{\circ} とするともう一方(余角)は  90^{\circ} - \theta^{\circ} で表わせます。
 長方形の対角線(直角三角形の斜辺・単位円の動径)で分割された2つの三角形は合同です。

・余角を基準とした場合

長方形と余角の関係

  90^{\circ} - \theta^{\circ} を基準(偏角)とすると、 90^{\circ} - (90^{\circ} - \theta^{\circ}) = \theta^{\circ} が余角になり、直角三角形の縦横(対辺と隣辺)が入れ替わります。

・余角の座標

余角と座標の関係

 半径  r の円周上の点に関して、 \theta^{\circ} を偏角とする点  (r \cos \theta, r \sin \theta) \tau^{\circ} = 90^{\circ} - \theta^{\circ} を偏角とする点  (r \cos \tau, r \sin \tau) は、 y = x の直線(恒等関数)に対して線対称な位置関係になります。

 ただしここまでの図は、基準となる角度が鋭角  0^{\circ} \lt \theta^{\circ} \lt 90^{\circ} の場合に限定しています。次では、一般角(全ての角度)に拡張した場合を考えます。

単位円と関数の関係

 次は、単位円における偏角(単位円周上の点)と円関数(sin・cos)の関係を可視化します。
 角度の符号の反転については「【R】-θ(負角)の可視化 - からっぽのしょこ」を参照してください。

グラフの作成

 変数を固定して、単位円における偏角のグラフを作成します。

・作図コード(クリックで展開)

 基準となる角度を指定し対象の角度を計算して、円周上の点の描画用のデータフレームを作成します。

# 点用のラジアンを指定
theta <- 2/6 * pi

# 余角を計算
tau <- 0.5*pi - theta

# 円周上の点の座標を作成
point_df <- tibble::tibble(
  t = c(theta, -theta, tau), 
  x = cos(t), 
  y = sin(t), 
  type = c("main", "sub", "target") # 角度カテゴリ
)
point_df
# A tibble: 3 × 4
       t     x      y type  
   <dbl> <dbl>  <dbl> <chr> 
1  1.05  0.5    0.866 main  
2 -1.05  0.5   -0.866 sub   
3  0.524 0.866  0.5   target

 変数(ラジアン)  \theta を指定して、直角との差  \tau = \frac{\pi}{2} - \theta を計算します。
 3つの偏角  t = \theta, -\theta, \tau について、単位円の円周上の点の座標  (x, y) = (\cos t, \sin t) を計算します。

 偏角を示す線分の描画用のデータフレームを作成します。

# 半径線の終点の座標を作成
radius_df <- dplyr::bind_rows(
  # 始線
  tibble::tibble(
    x = 1, 
    y = 0, 
    type = "main" # 角度カテゴリ
  ), 
  # 動径線
  point_df |> 
    dplyr::select(x, y, type)
)
radius_df
# A tibble: 4 × 3
      x      y type  
  <dbl>  <dbl> <chr> 
1 1      0     main  
2 0.5    0.866 main  
3 0.5   -0.866 sub   
4 0.866  0.5   target

 原点と点  (1, 0) を結ぶ線分(始線)、原点と点  (\cos t, \sin t) を結ぶ線分(動径線)用に、円周上の点の座標を格納します。原点  (0, 0) の座標は、作図時に引数に直接指定します。

 角マークの描画用のデータフレームを作成します。

# 角マークの座標を作成
ds <- 0.005
angle_mark_df <- tibble::tibble(
  t    = c(theta, -theta, tau), 
  type = c("main", "sub", "target"), # 角度カテゴリ
  d    = c(0.15, 0.25, 0.35) # マークサイズを指定
) |> 
  dplyr::reframe(
    u = seq(from = 0, to = t, length.out = 600), .by = dplyr::everything()
  ) |> # 円弧用のラジアンを作成
  dplyr::mutate(
    x = (d + ds*u) * cos(u), 
    y = (d + ds*u) * sin(u)
  )
angle_mark_df
# A tibble: 1,800 × 6
       t type      d       u     x        y
   <dbl> <chr> <dbl>   <dbl> <dbl>    <dbl>
 1  1.05 main   0.15 0       0.15  0       
 2  1.05 main   0.15 0.00175 0.150 0.000262
 3  1.05 main   0.15 0.00350 0.150 0.000525
 4  1.05 main   0.15 0.00524 0.150 0.000787
 5  1.05 main   0.15 0.00699 0.150 0.00105 
 6  1.05 main   0.15 0.00874 0.150 0.00131 
 7  1.05 main   0.15 0.0105  0.150 0.00157 
 8  1.05 main   0.15 0.0122  0.150 0.00184 
 9  1.05 main   0.15 0.0140  0.150 0.00210 
10  1.05 main   0.15 0.0157  0.150 0.00236 
# ℹ 1,790 more rows

 3つの偏角  t を示す角マークとして、 0 \leq u \leq t のラジアンを作成して、係数が  d の螺旋の座標  (x, y) = (d u \cos u, d u \sin u) を計算します。この例では、ノルムの基準値 d と間隔用の係数 ds でサイズを調整します。ds0 にすると、半径が  d の円弧の座標  (x, y) = (d \cos u, d \sin u) になります。

 角ラベルの描画用のデータフレームを作成します。

# 角ラベルの座標を作成
angle_label_df <- tibble::tibble(
  d = c(0.1, 0.3, 0.4), # ラベル位置を指定
  u = 0.5 * c(theta, -theta, tau), 
  x = d * cos(u), 
  y = d * sin(u), 
  angle_label = c("theta", "-theta", "tau")
)
angle_label_df
# A tibble: 3 × 5
      d      u      x      y angle_label
  <dbl>  <dbl>  <dbl>  <dbl> <chr>      
1   0.1  0.524 0.0866  0.05  theta      
2   0.3 -0.524 0.260  -0.15  -theta     
3   0.4  0.262 0.386   0.104 tau  

 角マークの中点に角ラベルを配置することにします。 u = \frac{t}{2} のラジアンを作成して、円弧上の点の座標を計算します。原点からのノルム d で表示位置を調整します。

 直角マークの描画用のデータフレームを作成します。

# 直角マークの座標を作成
d <- 0.1
rightangle_mark_df <- tibble::tibble(
  u = c(-theta, 0.25*pi-theta, tau), 
  x = c(d, sqrt(2)*d, d) * cos(u), 
  y = c(d, sqrt(2)*d, d) * sin(u)
)
rightangle_mark_df
# A tibble: 3 × 3
       u      x       y
   <dbl>  <dbl>   <dbl>
1 -1.05  0.05   -0.0866
2 -0.262 0.137  -0.0366
3  0.524 0.0866  0.05  

  -\theta, \tau の動径線は直角に交わるので、組み合わせて正方形になる2つの線分の座標を計算します。一辺の長さ d でサイズを調整します。

 関数を示す線分の描画用のデータフレームを作成します。

# 関数の描画順を指定
fnc_level_vec <- c("sin", "cos")

# 関数線分の座標を作成
fnc_seg_df <- tibble::tibble(
  fnc = c(
    "sin", "cos", "sin", "cos", 
    "sin", "cos"
  ) |> 
    factor(levels = fnc_level_vec), # 関数カテゴリ
  type = c(
    "main", "main", "main", "main", 
    "target", "target"
  ), # 角度カテゴリ
  x_from = c(
    cos(theta), 0, 0, 0, 
    0, cos(tau)
  ), 
  y_from = c(
    0, 0, 0, sin(theta), 
    0, 0
  ), 
  x_to = c(
    cos(theta), cos(theta), 0, cos(theta), 
    cos(tau), cos(tau)
  ), 
  y_to = c(
    sin(theta), 0, sin(theta), sin(theta), 
    0, sin(tau)
  )
)
fnc_seg_df
# A tibble: 6 × 6
  fnc   type   x_from y_from  x_to  y_to
  <fct> <chr>   <dbl>  <dbl> <dbl> <dbl>
1 sin   main    0.5    0     0.5   0.866
2 cos   main    0      0     0.5   0    
3 sin   main    0      0     0     0.866
4 cos   main    0      0.866 0.5   0.866
5 sin   target  0      0     0.866 0    
6 cos   target  0.866  0     0.866 0.5  

 各線分(の長さ)に対応する関数カテゴリを fnc 列として線の描き分けなどに使います。線分の描画順(重なり順や色付け順)を因子レベルで設定します。
 各線分の始点の座標を x_from, y_from 列、終点の座標を x_to, y_to 列として、完成図を見ながら頑張って格納します。

 関数ラベルの描画用のデータフレームを作成します。

# 関数ラベルの座標を作成
fnc_label_df <- tibble::tibble(
  fnc = c(
    "sin", "cos", 
    "sin", "cos"
  ) |> 
    factor(levels = fnc_level_vec), # 関数カテゴリ
  x = c(
    cos(theta), 0.5*cos(theta), 
    0.5*cos(tau), cos(tau)
  ), 
  y = c(
    0.5*sin(theta), 0, 
    0, 0.5*sin(tau)
  ), 
  fnc_label = c(
    "sin~theta", "cos~theta", 
    "cos~tau", "sin~tau"
  ), 
  a = c(
    90, 0, 
    0, 90
  ), 
  h = 0.5, 
  v = c(
    -0.5, -0.5, 
    1.5, 1.5
  )
)
fnc_label_df
# A tibble: 4 × 7
  fnc       x     y fnc_label     a     h     v
  <fct> <dbl> <dbl> <chr>     <dbl> <dbl> <dbl>
1 sin   0.5   0.433 sin~theta    90   0.5  -0.5
2 cos   0.25  0     cos~theta     0   0.5  -0.5
3 sin   0.433 0     cos~tau       0   0.5   1.5
4 cos   0.866 0.25  sin~tau      90   0.5   1.5

 偏角と関数の組み合わせごとに1つの線分の中点に関数名を表示することにします。
 ラベルの表示角度を a 列、表示角度に応じた左右の表示位置を h 列、上下の表示位置を v 列として指定します。

 各種ラベルの表示用の文字列を作成します。

# ラベル用の文字列を作成
def_label <- paste0(
  "list(", 
  "tau == frac(pi, 2) - theta, ", 
  "sin(frac(pi, 2) - theta) == cos~theta, ", 
  "cos(frac(pi, 2) - theta) == sin~theta", 
  ")"
)
angle_label_vec <- paste(
  c("theta", "-theta", "tau"), 
  c(theta/pi, -theta/pi, tau/pi) |> 
    round(digits = 2), 
  sep = " == "
) |> 
  paste("* pi")
fnc_label_vec <- paste(
  c("sin~theta", "cos~theta"), 
  c(sin(theta), cos(theta)) |> 
    round(digits = 2), 
  sep = " == "
)
def_label; angle_label_vec
[1] "list(tau == frac(pi, 2) - theta, sin(frac(pi, 2) - theta) == cos~theta, cos(frac(pi, 2) - theta) == sin~theta)"
[1] "theta == 0.33 * pi"   "-theta == -0.33 * pi" "tau == 0.17 * pi"   

 サブタイトル用の変数ラベル、凡例用の関数ラベルを作成します。
 ラベルとして数式(ギリシャ文字や記号)を表示する場合は、expression() の記法を用います。その際に、オブジェクト(プログラム上の変数)を使う場合は、文字列として作成しておき parse()text 引数に渡します。

 単位円と角度(ラジアン)目盛の描画用のデータフレームを作成します。

# 円周の座標を作成
circle_df <- tibble::tibble(
  t = seq(from = 0, to = 2*pi, length.out = 361), # 1周期分のラジアン
  r = 1, # 半径
  x = r * cos(t), 
  y = r * sin(t)
)

# 半円(範囲π)における目盛数(分母の値)を指定
tick_num <- 6

# 角度目盛の座標を作成:(補助目盛有り)
d <- 1.1
rad_tick_df <- tibble::tibble(
  # 座標用
  i     = seq(from = 0, to = 2*tick_num-0.5, by = 0.5), # 目盛番号(分子の値)
  t_deg = i/tick_num * 180, # 度数法の角度
  t_rad = i/tick_num * pi,  # 弧度法の角度(ラジアン)
  r     = 1, # 半径
  x     = r * cos(t_rad), 
  y     = r * sin(t_rad), 
  major_flag = i%%1 == 0, # 主・補助フラグ
  grid       = dplyr::if_else(major_flag, true = "major", false = "minor"), # 目盛カテゴリ
  # ラベル用
  rad_label  = dplyr::if_else(
    major_flag, true = paste0("frac(", i, ", ", tick_num, ") ~ pi"), false = ""
  ), # ラジアンラベル
  label_x = d * x, 
  label_y = d * y, 
  a = t_deg + 90, 
  h = 1 - (x * 0.5 + 0.5), 
  v = 1 - (y * 0.5 + 0.5), 
  tick_mark = dplyr::if_else(major_flag, true = "|", false = "") # 目盛指示線用
)

 詳しくは「円周の作図」を参照してください。

 単位円における3つの偏角のグラフを作成します。

# グラフサイズを設定
axis_size <- 1.5

# 単位円における偏角と関数線分を作図
ggplot() + 
  geom_segment(data = rad_tick_df, 
               mapping = aes(x = 0, y = 0, xend = x, yend = y, linewidth = grid), 
               color = "white", show.legend = FALSE) + # θ軸目盛線
  geom_text(data = rad_tick_df, 
            mapping = aes(x = x, y = y, angle = a, label = tick_mark), 
            size = 2) + # θ軸目盛指示線
  geom_text(data = rad_tick_df, 
            mapping = aes(x = label_x, y = label_y, label = rad_label, hjust = h, vjust = v), 
            parse = TRUE) + # θ軸目盛ラベル
  geom_segment(mapping = aes(x = c(-Inf, 0), y = c(0, -Inf), 
                             xend = c(Inf, 0), yend = c(0, Inf)), 
               arrow = arrow(length = unit(10, units = "pt"), ends = "last")) + # x・y軸線
  geom_path(data = circle_df, 
            mapping = aes(x = x, y = y), 
            linewidth = 1) + # 円周
  geom_segment(data = radius_df, 
               mapping = aes(x = 0, y = 0, xend = x, yend = y, linetype = type), 
               linewidth = 1, show.legend = FALSE) + # 半径線
  geom_path(data = rightangle_mark_df, 
            mapping = aes(x = x, y = y)) + # 直角マーク
  geom_path(data = angle_mark_df, 
            mapping = aes(x = x, y = y, linetype = type), 
            show.legend = FALSE) + # 角マーク
  geom_text(data = angle_label_df, 
            mapping = aes(x = x, y = y, label = angle_label), 
            size = 5, parse = TRUE) + # 角ラベル
  geom_point(data = point_df, 
             mapping = aes(x = x, y = y, shape = type), 
             size = 4, show.legend = FALSE) + # 円周上の点
  geom_segment(data = fnc_seg_df, 
               mapping = aes(x = x_from, y = y_from, xend = x_to, yend = y_to, 
                             color = fnc, linetype = type), 
               linewidth = 1) + # 関数線分
  geom_text(data = fnc_label_df, 
            mapping = aes(x = x, y = y, label = fnc_label, color = fnc, 
                          hjust = h, vjust = v, angle = a), 
            parse = TRUE, show.legend = FALSE) + # 関数ラベル
  scale_color_hue(labels = parse(text = fnc_label_vec), name = "function") + # 凡例表示用
  scale_shape_manual(breaks = c("main", "sub", "target"), 
                     values = c("circle", "diamond open", "circle open")) + # 補助点用
  scale_linetype_manual(breaks = c("main", "sub", "target"), 
                        values = c("solid", "dashed", "twodash"), 
                        labels = parse(text = angle_label_vec), 
                        name = "angle") + # 補助線用, 凡例表示用
  scale_linewidth_manual(breaks = c("major", "minor"), 
                         values = c(0.5, 0.25)) + # 主・補助目盛線用
  guides(linetype = guide_legend(override.aes = list(linewidth = 0.5))) + 
  theme(legend.text.align = 0) + 
  coord_fixed(ratio = 1, 
              xlim = c(-axis_size, axis_size), 
              ylim = c(-axis_size, axis_size)) + 
  labs(title = "complementary angle", 
       subtitle = parse(text = def_label), 
       x = expression(x == r ~ cos~theta), 
       y = expression(y == r ~ sin~theta))

単位円における余角と三角関数の関係

 基準となる偏角  \theta を黒色の丸印と実線、符号の反転した角  -\theta を白抜きの四角印と破線、直角との差(余角)  \tau = \frac{\pi}{2} - \theta を白抜きの丸印と鎖線で示します。直角との差は、左に  90^{\circ} = \frac{\pi}{2} 回転することに対応します。

アニメーションの作成

 変数を変化させて、単位円における偏角のアニメーションを作成します。
 作図コードについては「diff_right_angle.R at anemptyarchive/Mathematics · GitHub」を参照してください。

・偏角と関数の関係

 偏角  \theta が大きくなると左に回転し、逆に  -\theta が小さくなるので右に回転します。  変数  \theta を直角  \frac{\pi}{2} から引く(  -\theta の点から左に  \frac{\pi}{2} 回転する)と、sin関数がcos関数  \sin (\frac{\pi}{2} - \theta) = \cos \theta に、cos関数がsin関数  \cos (\frac{\pi}{2} - \theta) = \sin \theta に入れ替わるのを確認できます。

・偏角と座標の関係

 直線  y = x に対して線対称に変化します。

 この記事では、直角との差を確認しました。次の記事では、二直角との差を確認します。

おわりに

 円関数の記事を書きながら余角ってなんだっけと調べてみたところ、鋭角の場合のみの図解が出てきて、なんで皆はこの図で分かるんだと深夜に一般角の場合の図を作り始めました。なんとか単位円上で説明できたと思われるので記事にしました。
 1枚の図で解説するのは難しかったので、この記事でも鋭角の場合の図も追加で作りました。記事中の図で一番の萌えポイントは、線形代数シリーズで得た知識(回転行列)を駆使してy = xの直線に直角マークの描けたところです。射影ベクトルでも描けるかもしれません。

 余談ですが、機械翻訳で余角を英語にして日本語に戻すと補角になりとても混乱しました。覚えられる気がしないのでもう「coangle」でいいんじゃないですか。
 日本語の用語でも角度と角の使い分けが不明のまま雰囲気で書きました。

 2024年4月2日は、Juice=Juiceの元リーダーの宮崎由加さんの30歳のお誕生日です。

 とっても可愛いのと同時に強く優しい人柄で尊敬します。個人的に(タイミング的に難しいとは思いつつも)ハロプロリーダーになってほしかったなぁとずっと思ってます。

【次の内容】

www.anarchive-beta.com