からっぽのしょこ

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

【R】sec関数の定義の可視化

はじめに

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

 この記事では、R言語でsecant関数のグラフを作成します。

【前の内容】

www.anarchive-beta.com

【他の記事一覧】

www.anarchive-beta.com

【この記事の内容】

sec関数の定義の可視化

 sec関数(正割関数・セカント関数・secant function)の定義をグラフで確認します。sec関数は、円関数(circular functions)・三角関数(trigonometric functions)の1つです。
 sec関数の形状(振幅・周期・位相・切片)の変化については各パラメータの記事や「sec関数の波形の可視化」を参照してください。

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

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

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

定義式の確認

 まずは、sec関数の定義式を確認します。
 cos関数については「【R】cos関数の定義の可視化 - からっぽのしょこ」を参照してください。

 sec関数は、cos関数の逆数で定義されます。

 \displaystyle
\sec x
    = \frac{1}{\cos x}

 ただし、 n を整数として  x = (\frac{1}{2} + n) \pi = \frac{2 n + 1}{2} \pi のとき、 \cos x = 0 であり、0除算になるため定義できません。変数  x はラジアン(弧度法の角度)、 \pi は円周率です。

曲線の形状

 続いて、sec関数の曲線のグラフを作成します。

曲線の作成

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

 変数の範囲を指定して、座標計算用のベクトルを作成します。

# 変数(ラジアン)の範囲を指定
theta_vec <- seq(from = -2*pi, to = 2*pi, length.out = 1001)
head(theta_vec)
[1] -6.283185 -6.270619 -6.258053 -6.245486 -6.232920 -6.220353

 変数(ラジアン)  \theta の範囲を指定して、数値ベクトルを作成します。範囲を  2 \pi の倍数にすると周期性を確認しやすくなります。円周率  \pipi で扱えます。

 曲線の描画用のデータフレームを作成します。

# 閾値を指定
threshold <- 4

# 曲線の座標を作成
curve_df <- tibble::tibble(
  t     = theta_vec, 
  sec_t = 1/cos(t), 
  cos_t = cos(t)
) |> 
  dplyr::mutate(
    sec_t = dplyr::if_else(
      (sec_t >= -threshold & sec_t <= threshold), true = sec_t, false = NA_real_
    )
  ) # 閾値外を欠損値に置換
curve_df
# A tibble: 1,001 × 3
       t sec_t cos_t
   <dbl> <dbl> <dbl>
 1 -6.28  1    1    
 2 -6.27  1.00 1.00 
 3 -6.26  1.00 1.00 
 4 -6.25  1.00 0.999
 5 -6.23  1.00 0.999
 6 -6.22  1.00 0.998
 7 -6.21  1.00 0.997
 8 -6.20  1.00 0.996
 9 -6.18  1.01 0.995
10 -6.17  1.01 0.994
# ℹ 991 more rows

 変数  \theta と関数  \sec \theta の値をデータフレームに格納します。比較用に、 \cos \theta の値も格納しておきます。cos関数は cos() で、sec関数は cos() を使って計算できます。
 ただし範囲によっては、 \sec \theta が発散するので、閾値 threshold を指定しておき、閾値外の値を(数値型の)欠損値 NA に置き換えます。

 ラジアン軸目盛の表示用のベクトルを作成します。装飾用の処理です。

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

# 目盛番号(分子の値)の範囲を設定
tick_min <- floor(min(theta_vec) / pi) * tick_num
tick_max <- ceiling(max(theta_vec) / pi) * tick_num

# 目盛番号(分子の値)を作成
tick_vec <- tick_min:tick_max

# 目盛位置を作成
rad_break_vec <- tick_vec/tick_num * pi

# 目盛ラベルを作成
rad_label_vec <- paste(round(rad_break_vec/pi, digits = 2), "* pi")
head(tick_vec); head(rad_break_vec); head(rad_label_vec)
[1] -4 -3 -2 -1  0  1
[1] -6.283185 -4.712389 -3.141593 -1.570796  0.000000  1.570796
[1] "-2 * pi"   "-1.5 * pi" "-1 * pi"   "-0.5 * pi" "0 * pi"    "0.5 * pi" 

 ラジアン  \theta に関する軸目盛ラベルを  \frac{i}{n} \pi を小数にした形で表示することにします。 n は半周期(  \pi 間隔の範囲)における目盛数、 i は(負の数を含む)目盛番号に対応します。
 theta_vec の最小値・最大値に対して、 \theta = \frac{i}{n} \pi i について整理した  i = \frac{\theta}{\pi} n を計算して、最小番号から最大番号までの整数を作成します。整数にするために、小数部分に関して最小値は floor() で切り捨て、最大値は ceiling() で切り上げておきます。
 作成した目盛番号に対応する目盛位置(ラジアン)を  \theta = \frac{i}{n} \pi で計算します。

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

 漸近線の描画用のベクトルを作成します。

# 漸近線の範囲を設定:(π単位で切り捨て・切り上げ)
theta_lower <- (floor(min(theta_vec) / pi) - 0.5) * pi
theta_upper <- (ceiling(max(theta_vec) / pi) + 0.5) * pi

# 漸近線の位置を作成
asymptote_break_vec <- seq(from = theta_lower, to = theta_upper, by = pi)

# 漸近線のラベルを作成
asymptote_label_vec <- paste(round(asymptote_break_vec/pi, digits = 2), "* pi")
head(asymptote_break_vec); head(asymptote_label_vec)
[1] -7.853982 -4.712389 -1.570796  1.570796  4.712389  7.853982
[1] "-2.5 * pi" "-1.5 * pi" "-0.5 * pi" "0.5 * pi"  "1.5 * pi"  "2.5 * pi" 

  \sec \theta が発散する位置に漸近線と目盛ラベルを表示することにします。
 ラジアン軸目盛と同様に、theta_vec の最小値・最大値を用いて、ラジアン  \theta = (n + \frac{1}{2}) \pi (  n は整数)とラベルを作成します。

 sec関数のグラフを作成します。

# 関数曲線を作図
ggplot() + 
  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_vline(xintercept = asymptote_break_vec, 
             linetype = "twodash") + # 漸近線
  geom_line(data = curve_df, 
            mapping = aes(x = t, y = sec_t, linetype = "sec"), 
            linewidth = 1, na.rm = TRUE) + # sec曲線
  geom_line(data = curve_df, 
            mapping = aes(x = t, y = cos_t, linetype = "cos"), 
            linewidth = 1) + # cos曲線
  scale_linetype_manual(breaks = c("sec", "cos"), 
                        values = c("solid", "dotted"), 
                        labels = c(expression(sec~theta), expression(cos~theta)), 
                        name = "function") + # 凡例表示用
  scale_x_continuous(breaks = rad_break_vec, 
                     labels = parse(text = rad_label_vec), 
                     sec.axis = sec_axis(trans = ~., 
                                         breaks = asymptote_break_vec, 
                                         labels = parse(text = asymptote_label_vec), 
                                         name = "asymptote")) + # ラジアン軸目盛
  guides(linetype = guide_legend(override.aes = list(linewidth = 0.5))) + # 凡例の体裁
  theme(legend.text.align = 1) + # 図の体裁
  coord_fixed(ratio = 1, 
              xlim = c(min(theta_vec), max(theta_vec))) + # 描画範囲
  labs(title = "secant function", 
       subtitle = expression(sec~theta == frac(1, cos~theta)), 
       x = expression(theta), 
       y = expression(f(theta)))

sec関数のグラフ

 sec曲線を実線、cos曲線を点線で、また漸近線を鎖線で示します。
 横軸(変数)  \theta はラジアン(弧度法の角度)、 \pi は円周率です。

  n を整数として、 \theta 軸が  (\frac{1}{2} + n) \pi の点(  \frac{1}{2} \pi を中心に  \pi 間隔)で、 \cos \theta = 0 であり、 \sec \theta が発散するのを確認できます。

 次からは、単位円との関係を見ていきます。

単位円の作成

 円関数の可視化に利用する単位円(unit circle)のグラフを確認します。
 円の作図や度数法と弧度法(角度とラジアン)の関係については「【R】円周の作図 - からっぽのしょこ」を参照してください。

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

 単位円の描画用のデータフレームを作成します。

# 円周の座標を作成
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)
)
circle_df
# A tibble: 361 × 4
        t     r     x      y
    <dbl> <dbl> <dbl>  <dbl>
 1 0          1 1     0     
 2 0.0175     1 1.00  0.0175
 3 0.0349     1 0.999 0.0349
 4 0.0524     1 0.999 0.0523
 5 0.0698     1 0.998 0.0698
 6 0.0873     1 0.996 0.0872
 7 0.105      1 0.995 0.105 
 8 0.122      1 0.993 0.122 
 9 0.140      1 0.990 0.139 
10 0.157      1 0.988 0.156 
# ℹ 351 more rows

 1周分のラジアン  0 \leq \theta \leq 2 \pi を作成して、単位円の円周のx座標  x = \cos \theta とy座標  y = \sin \theta を計算します。

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

# 半円(範囲π)における目盛数(分母の値)を指定
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"), # 目盛カテゴリ
  # ラベル用
  deg_label  = dplyr::if_else(
    major_flag, true = paste0(round(t_deg, digits = 1), "*degree"), false = ""
  ), # 角度ラベル
  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 = "") # 目盛指示線用
)
rad_tick_df
# A tibble: 24 × 16
       i t_deg t_rad     r         x     y major_flag grid  deg_label  rad_label
   <dbl> <dbl> <dbl> <dbl>     <dbl> <dbl> <lgl>      <chr> <chr>      <chr>    
 1   0       0 0         1  1   e+ 0 0     TRUE       major "0*degree" "frac(0,…
 2   0.5    15 0.262     1  9.66e- 1 0.259 FALSE      minor ""         ""       
 3   1      30 0.524     1  8.66e- 1 0.5   TRUE       major "30*degre… "frac(1,…
 4   1.5    45 0.785     1  7.07e- 1 0.707 FALSE      minor ""         ""       
 5   2      60 1.05      1  5   e- 1 0.866 TRUE       major "60*degre… "frac(2,…
 6   2.5    75 1.31      1  2.59e- 1 0.966 FALSE      minor ""         ""       
 7   3      90 1.57      1  6.12e-17 1     TRUE       major "90*degre… "frac(3,…
 8   3.5   105 1.83      1 -2.59e- 1 0.966 FALSE      minor ""         ""       
 9   4     120 2.09      1 -5   e- 1 0.866 TRUE       major "120*degr… "frac(4,…
10   4.5   135 2.36      1 -7.07e- 1 0.707 FALSE      minor ""         ""       
# ℹ 14 more rows
# ℹ 6 more variables: label_x <dbl>, label_y <dbl>, a <dbl>, h <dbl>, v <dbl>,
#   tick_mark <chr>

 「曲線の作図」のときと同様に、目盛番号と対応するラジアンを作成して、円周上の座標とラベル用の文字列を作成します。ただし、補助目盛も作成します。主目盛と補助目盛を major_flag 列または grid 列で描き分けます。
 目盛ラベル用の座標を label_x, label_y 列として、表示位置を原点からのノルム(半径) d で調整します。

 単位円と角度目盛のグラフを作成します。

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

# 単位円を作図
ggplot() + 
  geom_segment(data = rad_tick_df, 
               mapping = aes(x = 0, y = 0, xend = x, yend = y), 
               linetype = "dotted") + # 角度目盛線
  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 = deg_label, hjust = h, vjust = v), 
            parse = TRUE) + # 角度目盛ラベル
  geom_path(data = circle_df, 
            mapping = aes(x = x, y = y), 
            linewidth = 1) + # 円周
  coord_fixed(ratio = 1, 
              xlim = c(-axis_size, axis_size), 
              ylim = c(-axis_size, axis_size)) + # アスペクト比
  labs(title = "unit circle", 
       subtitle = expression(r == 1), 
       x = expression(x == r ~ cos~theta), 
       y = expression(y == r ~ sin~theta))

単位円のグラフ

 原点を中心とする半径  r の円周の座標は  (x, y) = (r \cos \theta, r \sin \theta) です。半径が  r = 1 の円を単位円と呼びます。(半径や開始位置に関わらず)偏角が  2 \pi (度数法だと  360^{\circ} )で1周します。
 度数法の角度  \theta^{\circ} と弧度法の角度  \theta \theta = \frac{2 \pi}{360^{\circ}} \theta^{\circ} の関係です。

 このグラフ上に円関数の値を線分として描画します。

単位円と関数の関係

 次は、単位円における偏角(単位円周上の点)と円関数(sec・sin・cos・tan)の関係を可視化します。
 各関数についてはそれぞれの記事を参照してください。

グラフの作成

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

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

 変数を指定して、円周上の点の描画用のデータフレームを作成します。

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

# 円周上の点の座標を作成
point_df <- tibble::tibble(
  t = theta, 
  x = cos(t), 
  y = sin(t)
)
point_df
# A tibble: 1 × 3
      t     x      y
  <dbl> <dbl>  <dbl>
1  5.24   0.5 -0.866

 変数  \theta を指定して、単位円の円周上の点の座標  (x, y) = (\cos \theta, \sin \theta) を計算します。

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

# 半径線の終点の座標を作成
radius_df <- tibble::tibble(
  x = c(1, cos(theta)), 
  y = c(0, sin(theta))
)
radius_df
# A tibble: 2 × 2
      x      y
  <dbl>  <dbl>
1   1    0    
2   0.5 -0.866

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

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

# 角マークの座標を作成
d  <- 0.2
ds <- 0.005
angle_mark_df <- tibble::tibble(
  u = seq(from = 0, to = theta, length.out = 600), 
  x = (d + ds*u) * cos(u), 
  y = (d + ds*u) * sin(u)
)
angle_mark_df
# A tibble: 600 × 3
         u     x       y
     <dbl> <dbl>   <dbl>
 1 0       0.2   0      
 2 0.00874 0.200 0.00175
 3 0.0175  0.200 0.00350
 4 0.0262  0.200 0.00525
 5 0.0350  0.200 0.00700
 6 0.0437  0.200 0.00875
 7 0.0524  0.200 0.0105 
 8 0.0612  0.200 0.0122 
 9 0.0699  0.200 0.0140 
10 0.0787  0.200 0.0157 
# ℹ 590 more rows

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

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

# 角ラベルの座標を計算
d <- 0.3
angle_label_df <- tibble::tibble(
  u = 0.5 * theta, 
  x = d * cos(u), 
  y = d * sin(u)
)
angle_label_df
# A tibble: 1 × 3
      u      x     y
  <dbl>  <dbl> <dbl>
1  2.62 -0.260  0.15

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

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

# ラベル用の文字列を作成
var_label <- paste0(
  "list(", 
  "r == 1, ", 
  "theta == ", round(theta/pi, digits = 2), " * pi", 
  ")"
)
fnc_label_vec <- paste(
  c("sec~theta", "sin~theta", "cos~theta", "tan~theta"), 
  c(1/cos(theta), sin(theta), cos(theta), tan(theta)) |> 
    round(digits = 2), 
  sep = " == "
)
var_label; fnc_label_vec
[1] "list(r == 1, theta == 1.67 * pi)"
[1] "sec~theta == 2"     "sin~theta == -0.87" "cos~theta == 0.5"  
[4] "tan~theta == -1.73"

 サブタイトル用の変数ラベル、凡例用の関数ラベルを作成します。
 expression() の記法では、等号は "=="、複数の(数式上の)変数は "list(変数1, 変数2)" で並べて表示できます。

 ここまでは、共通の処理です。ここからは、2つの方法で図示します。

パターン1

 1つ目の方法では、動径上を伸びる線分としてsec関数を可視化します。

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

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

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

# 符号の反転フラグを設定
rev_flag <- sin(theta) < 0

# 関数線分の座標を作成
fnc_seg_df <- tibble::tibble(
  fnc = c(
    "sec", "sec", 
    "sin", 
    "cos", 
    "tan", "tan"
  ) |> 
    factor(levels = fnc_level_vec), # 関数カテゴリ
  x_from = c(
    0, ifelse(test = rev_flag, yes = 0, no = NA), 
    cos(theta), 
    0, 
    1, ifelse(test = rev_flag, yes = 1, no = NA)
  ), 
  y_from = c(
    0, ifelse(test = rev_flag, yes = 0, no = NA), 
    0, 
    0, 
    0, ifelse(test = rev_flag, yes = 0, no = NA)
  ), 
  x_to = c(
    1, ifelse(test = rev_flag, yes = 1, no = NA), 
    cos(theta), 
    cos(theta), 
    1, ifelse(test = rev_flag, yes = 1, no = NA)
  ), 
  y_to = c(
    ifelse(test = rev_flag, yes = -tan(theta), no = tan(theta)), ifelse(test = rev_flag, yes = tan(theta), no = NA), 
    sin(theta), 
    0, 
    tan(theta), ifelse(test = rev_flag, yes = -tan(theta), no = NA)
  ), 
  line_type = c(
    "main", "sub", 
    "main", 
    "main", 
    "main", "sub"
  ) # 補助線用
)
fnc_seg_df
# A tibble: 6 × 6
  fnc   x_from y_from  x_to   y_to line_type
  <fct>  <dbl>  <dbl> <dbl>  <dbl> <chr>    
1 sec      0        0   1    1.73  main     
2 sec      0        0   1   -1.73  sub      
3 sin      0.5      0   0.5 -0.866 main     
4 cos      0        0   0.5  0     main     
5 tan      1        0   1   -1.73  main     
6 tan      1        0   1    1.73  sub     

 各線分に対応する関数カテゴリを fnc 列として線の描き分けなどに使います。線分の描画順(重なり順)や色付け順を因子レベルで設定します。
 各線分の始点の座標を x_from, y_from 列、終点の座標を x_to, y_to 列として、完成図を見ながら頑張って格納します。偏角が  \pi \lt \theta \lt 2 \pi (  \sin \theta が負の値)のとき、座標計算や補助線に用いる  \tan \theta と符号が異なるので、それぞれy座標の符号を反転させた線分を追加します。
 元の関数(線分)と符号を反転させた関数(線分)を、線の種類で描き分けることにします。線の種類を区別する文字列を line_type 列とします。文字列自体は自由です。

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

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

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

 単位円におけるsec関数のグラフを作成します。

# グラフサイズの下限値・上限値を指定
axis_lower <- 1.5
axis_upper <- 3

# グラフサイズを設定
axis_x_size <- 1.5
axis_y_size <- max(axis_lower, abs(tan(theta))) |> min(axis_upper)

# 単位円における関数線分を作図
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_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), 
               linewidth = 1) + # 半径線
  geom_path(data = angle_mark_df, 
            mapping = aes(x = x, y = y)) + # 角マーク
  geom_text(data = angle_label_df, 
            mapping = aes(x = x, y = y), 
            label = "theta", parse = TRUE, 
            size = 5) + # 角ラベル
  geom_text(mapping = aes(x = 0.5*cos(theta+0.1), y = 0.5*sin(theta+0.1)), 
            label = "r", parse = TRUE, 
            size = 5) + # 半径ラベル:(θ + αで表示位置を調整)
  geom_point(data = point_df, 
             mapping = aes(x = x, y = y), 
             size = 4) + # 円周上の点
  geom_vline(xintercept = 1, linetype = "dashed") + # 補助線
  geom_segment(data = fnc_seg_df, 
               mapping = aes(x = x_from, y = y_from, xend = x_to, yend = y_to, 
                             color = fnc, linetype = line_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_linetype_manual(breaks = c("main", "sub"), 
                        values = c("solid", "twodash")) + # 符号の反転用
  scale_linewidth_manual(breaks = c("major", "minor"), values = c(0.5, 0.25)) + # 主・補助目盛線用
  guides(linetype = "none") + 
  theme(legend.text.align = 0) + 
  coord_fixed(ratio = 1, 
              xlim = c(-axis_x_size, axis_x_size), 
              ylim = c(-axis_y_size, axis_y_size)) + 
  labs(title = "circular functions", 
       subtitle = parse(text = var_label), 
       x = expression(x == r ~ cos~theta), 
       y = expression(y == r ~ sin~theta))

単位円の偏角とsec関数の関係

 偏角(始線から動径までの反時計回りの角度)を  \theta として、単位円(  r = 1 の円)の円周上の点の座標は  (x, y) = (\cos \theta, \sin \theta) です。
 sec関数の定義式  \sec \theta = \frac{1}{\cos \theta} や、この図の相似な直角三角形から、 \cos \theta : 1 = 1 : \sec \theta なのが分かります。 \theta に関する直角三角形について、隣辺(  \cos \theta に対応する辺)が1になるように拡大したときの斜辺(  r に対応する辺)と言えます。
 よって、sec関数の値は、「原点と点  (\cos \theta, \sin \theta) を通る半直線」と「  x = 1 の直線」の交点  (1, \tan \theta) と原点を結ぶ線分の符号付きの長さです。(この図では、)符号はy座標の符号と対応します。
 (y座標の)符号を反転した線分を破線で示します。

パターン2

 2つ目の方法では、x軸線上を伸びる線分としてsec関数を可視化します。

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

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

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

# 関数線分の座標を作成
fnc_seg_df <- tibble::tibble(
  fnc = c(
    "sec", 
    "sin", "sin", 
    "cos", "cos", 
    "tan"
  ) |> 
    factor(levels = fnc_level_vec), # 書き分け用
  x_from = c(
    0, 
    cos(theta), abs(cos(theta))*cos(theta), 
    0, 0, 
    cos(theta)
  ), 
  y_from = c(
    0, 
    0, abs(cos(theta))*sin(theta), 
    0, 0, 
    sin(theta)
  ), 
  x_to = c(
    1/cos(theta), 
    cos(theta), ifelse(cos(theta) >= 0, yes = 1, no = -1), 
    cos(theta), abs(cos(theta))*cos(theta), 
    1/cos(theta)
  ), 
  y_to = c(
    0, 
    sin(theta), 0, 
    0, abs(cos(theta))*sin(theta), 
    0
  ), 
  w = c(
    "bold", 
    "normal", "normal", 
    "thin", "normal", 
    "normal"
  ) # 重なり対策用
)
fnc_seg_df
# A tibble: 6 × 6
  fnc   x_from y_from  x_to   y_to w     
  <fct>  <dbl>  <dbl> <dbl>  <dbl> <chr> 
1 sec     0     0      2     0     bold  
2 sin     0.5   0      0.5  -0.866 normal
3 sin     0.25 -0.433  1     0     normal
4 cos     0     0      0.5   0     thin  
5 cos     0     0      0.25 -0.433 normal
6 tan     0.5  -0.866  2     0     normal

 「パターン1」と同様に、線分の座標を格納します。

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

# 関数ラベルの座標を作成
fnc_label_df <- tibble::tibble(
  fnc = c("sec", "sin", "cos", "tan") |> 
    factor(levels = fnc_level_vec), # 色用
  x = c(
    0.5 / cos(theta), 
    cos(theta), 
    0.5 * cos(theta), 
    0.5 * (1/cos(theta) + cos(theta))
  ), 
  y = c(
    0, 
    0.5 * sin(theta), 
    0, 
    0.5 * sin(theta)
  ), 
  fnc_label = c("sec~theta", "sin~theta", "cos~theta", "tan~theta"), 
  a = c(0, 90, 0, 0), 
  h = c(0.5, 0.5, 0.5, -0.2), 
  v = c(1, -0.5, -0.5, 0.5)
)
fnc_label_df
# A tibble: 4 × 7
  fnc       x      y fnc_label     a     h     v
  <fct> <dbl>  <dbl> <chr>     <dbl> <dbl> <dbl>
1 sec    1     0     sec~theta     0   0.5   1  
2 sin    0.5  -0.433 sin~theta    90   0.5  -0.5
3 cos    0.25  0     cos~theta     0   0.5  -0.5
4 tan    1.25 -0.433 tan~theta     0  -0.2   0.5

 線分の中点の座標とラベル用の文字列などを格納します。

 単位円におけるsec関数のグラフを作成します。

# グラフサイズの下限値・上限値を指定
axis_lower <- 1.5
axis_upper <- 3

# グラフサイズを設定
axis_x_min  <- min(-axis_lower, 1/cos(theta)) |> max(-axis_upper)
axis_x_max  <- max(axis_lower, 1/cos(theta)) |> min(axis_upper)
axis_y_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_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), 
               linewidth = 1) + # 半径線
  geom_path(data = angle_mark_df, 
            mapping = aes(x = x, y = y)) + # 角マーク
  geom_text(data = angle_label_df, 
            mapping = aes(x = x, y = y), 
            label = "theta", parse = TRUE, 
            size = 5) + # 角ラベル
  geom_text(mapping = aes(x = 0.5*cos(theta+0.1), y = 0.5*sin(theta+0.1)), 
            label = "r", parse = TRUE, 
            size = 5) + # 半径ラベル:(θ + αで表示位置を調整)
  geom_point(data = point_df, 
             mapping = aes(x = x, y = y), 
             size = 4) + # 円周上の点
  geom_hline(yintercept = 0, linetype = "dashed") + # 補助線
  geom_segment(data = fnc_seg_df, 
               mapping = aes(x = x_from, y = y_from, xend = x_to, yend = y_to, 
                             color = fnc, linewidth = w)) + # 関数線分
  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_linewidth_manual(breaks = c("bold", "normal", "thin", "major", "minor"), 
                         values = c(1.5, 1, 0.5, 0.5, 0.25)) + # 線の重なり対策用, 主・補助目盛線用
  guides(color = guide_legend(override.aes = list(linewidth = 1)), 
         linewidth = "none") + 
  theme(legend.text.align = 0) + 
  coord_fixed(ratio = 1, 
              xlim = c(axis_x_min, axis_x_max), 
              ylim = c(-axis_y_size, axis_y_size)) + 
  labs(title = "circular functions", 
       subtitle = parse(text = var_label), 
       x = expression(x == r ~ cos~theta), 
       y = expression(y == r ~ sin~theta))

単位円の偏角とsec関数の関係

 動径(原点と円周上の点を結ぶ線分)を隣辺、x軸線の一部を斜辺としたときのパターン1の図と言えます。文字通り首を捻って見てください。
 原点を  O = (0, 0)、円周上の点を  P = (\cos \theta, \sin \theta) として、「動径に対する点  P を通る垂線」と「  y = 0 の直線」の交点を  Q = (\frac{1}{\cos \theta}, 0) とします。sec関数の値は、点  O, Q を結ぶ線分の符号付きの長さ(点  Q のx座標)です。

アニメーションの作成

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

・パターン1

  \theta = (\frac{1}{2} + n) \pi (  n は整数)(この図だと  \frac{3}{6} \pi, \frac{9}{6} \pi の目盛位置)のとき  \cos \theta = 0 なので、動径が垂直になり直線  x = 1 と平行(交点ができない)なため、 \sec \theta を描画(定義)できないのを確認できます。

・パターン2

 こちらの図だと、動径の垂線が水平になり直線  y = 0 と平行なため、 \sec \theta を描画(定義)できないのが分かります。

単位円と曲線の関係

 最後は、単位円と関数曲線のグラフを作成して、変数(ラジアン)と座標の関係を可視化します。
 作図コードについては「sec_definition.R」を参照してください。

変数と座標の関係

 変数に応じて移動する円周上の点と曲線上の点のアニメーションを作成します。

点の座標

 円周上の点とsec関数曲線上の点の関係を可視化します。

 単位円におけるsec関数の値に関して、y座標の正負に応じて線分を回転し、y軸の値(y軸線上の線分)に変換しています。
  \cos \theta = 0 のとき曲線が不連続になり、その前後で符号(線分の向き)が変わるのを確認できます。

 円周上の点とcos関数・sec関数曲線上の点の関係を可視化します。

 左下図によってx座標(横方向の線分)からy座標(縦方向の線分)に変換(回転)し、cos関数とsec関数の曲線を並べて比較します。
 cos関数の値は円周上の点のx座標(符号付きの幅)です。

点の推移

 円周上を周回した際のsec関数の推移を可視化します。

 単位円を1周する  2 \pi の間隔で、曲線の形状が一致するのを確認できます。

 円周上を周回した際のcos関数・sec関数の推移を可視化します。


 この記事では、sec関数の定義を確認しました。次の記事からは、各種パラメータによる波形への影響を確認していきます。または、csc関数の定義を確認します。

参考書籍

  • 『三角関数(改定第3版)』(Newton別冊)ニュートンプレス,2022年.

おわりに

 sec関数を表す線分を円周上の点に合わせて描画すると座標が反転していることに気付いたものの、対応策が思い付かず作業を中断していました。符号(座標)を反転させた他の関数の線分と両方載せればいいや、で解決しました(?)。正直いまいち納得できていません。

 にしてもどんどん図が盛りだくさんになって、既に読めたものじゃなかったのに更に酷くなりました。アニメーションの作図コードはGitHubに上げてそっちを見てでよかった気がします。
 そろそろ効率的なコーディング能力やら(?)を上げないとキツくなってきました。具体的にそれが何なのか分かっていませんが。

 さて投稿日の2023年6月28日に、元エビ中の柏木ひなたさんがソロデビューしましたー🎊🎉

 エビ中での活動を観るのはギリ間に合わなかったのですが、ソロデビューのタイミングには間に合って良かったです。デビューおめでとうございます!!!!!!!!!!

  • 2024.03.23:加筆修正しました。

 座標が反転するのではなく、下半分の範囲ではtanとsecの符号が異なるだけでした。secの符号はcosの影響のみですが、tanはsinの影響も受けて符号が決まるので、sinが負の値になる範囲で、tanとsecの符号が異なります。図だけで考えていたらわけ分からなくなりました。

 「アニメーションの作図コードはGitHubに上げてそっちを見て」にしました。手元で再現したい方は、そっちを見てください。

【次の内容】

www.anarchive-beta.com