🌊

Python視点でR言語の文法を勉強する

2024/08/09に公開

🎯 目的

Pythonエンジニア視点で、R言語のコーディングで躓く点を明らかにする。

これまで1年ほどPythonで記述してきましたが、お仕事でRを使う機会が増えてきました。Python以外の言語もいくつか通ってきているので基本的なコーディングは問題なかったのですが、ところどころのポイントで躓いていました。

まだまだ深みには達していないものの、躓いたポイントを備忘録としてまとめます。

※ サンプルコードはClaude3.5に書いてもらっています。

👾 躓きポイント

1. 📦 パッケージの読み込み

Pythonではプロジェクト作成時にpipコマンドでパッケージをインストールすることが多いですが、Rではコードにinstall.packages()関数によってパッケージをインストールします。

パッケージによってはCRANを指定しなければインストールできないものもあります。

✓ Python

# コンソールでインストール
# pip install pandas

# パッケージ読込
import pandas as pd

✓ R言語

# CRANミラーを設定
options(repos="https://ftp.yz.yamagata-u.ac.jp/pub/cran/")

# パッケージが存在しない場合のみインストール
if (!require(dplyr)) install.packages("dplyr")

# パッケージ読込
library(dplyr)

2. 🧮 ベクターとリスト

Pythonでは単に列挙する配列はリスト、キーと値をセットとする配列は辞書(dict)と呼ばれます。一方でRでは単に列挙する配列はベクター、キーと値をセットとする配列はリストと呼ばれます。

LLMに質問する際に「リスト」を要求すると、Pythonでいうdictが返ってくることになります。

✓ Python

# リスト(単純な配列)
python_list = [1, 2, 3, 4, 5]

# 辞書(キーと値のペア)
python_dict = {"fruit1": "apple", "fruit2": "banana", "fruit3": "cherry"}

✓ R言語

# ベクター(単純な配列)
r_vector <- c(1, 2, 3, 4, 5)

# リスト(キーと値のペア)
r_list <- list(fruit1 = "apple", fruit2 = "banana", fruit3 = "cherry")

3. 🔍 配列のindex

Pythonでは配列のindexは0から始まりますが、Rでは配列のindexは1から始まります。

また、indexとして負の値-nを与えたとき、Pythonではで末尾からn番目の要素を参照しますが、Rではn番目の要素以外すべてを参照することになります。

✓ Python

# 配列とインデックスの使用
python_list = ["apple", "banana", "cherry", "date"]

print(python_list[0])   # 最初の要素: "apple"
print(python_list[2])   # 3番目の要素: "cherry"
print(python_list[-1])  # 最後の要素: "date"

# スライシングの例
print(python_list[1:3])  # ["banana", "cherry"]

✓ R言語

# 配列(ベクター)とインデックスの使用
r_vector <- c("apple", "banana", "cherry", "date")

print(r_vector[1])   # 最初の要素: "apple"
print(r_vector[3])   # 3番目の要素: "cherry"
print(r_vector[length(r_vector)])  # 最後の要素: "date"

# 負のインデックスの例(要素の除外)
print(r_vector[-1])  # c("banana", "cherry", "date")

# スライシングの例
print(r_vector[2:3])  # c("banana", "cherry")

4. 🔄 returnの挙動

Pythonではreturn文がなければNoneが返り、return xと書けばxが返ります。Rではreturn文がなければ最後に処理された結果が返り、return(x)と書けばxが返ります。

複数の値を返す場合、Pythonではタプルを、Rではリストとして返されます。

✓ Python

def python_function():
    a = 1
    b = 2
    c = a + b
    # return文なし

def python_function_with_return():
    a = 1
    b = 2
    c = a + b
    return c

def python_multiple_return():
    return 1, 2, 3  # タプルとして複数の値を返す

print(python_function())  # 出力: None  # Noneが返る
print(python_function_with_return())  # 出力: 3  # 指定されたc(3)を返す
print(python_multiple_return())  # 出力: (1, 2, 3)  # 複数の値がタプルとして返される

✓ R言語

r_function <- function() {
  a <- 1
  b <- 2
  c <- a + b
}

r_function_with_return <- function() {
  a <- 1
  b <- 2
  c <- a + b
  return(c)
}

r_multiple_return <- function() {
  return(list(x = 1, y = 2, z = 3))  # リストとして複数の値を返す
}

print(r_function())  # 出力: 3  # 最後に処理されたc(3)が返る 
print(r_function_with_return())  # 出力: 3  # 指定されたc(3)を返す
print(r_multiple_return())  # 出力: $x 1 $y 2 $z 3  # 複数の値がリストとして返される

5. 🛡️ tryCatchとグローバル変数

Pythonではエラー処理はtry-exceptで行いますが、RではtryCatch-errorを使います。

このとき、Pythonのtry-except内で定義した変数はスコープ外からアクセスできますが、RのtryCatch内で定義した変数はスコープ外からアクセスできません。(※ Rのif文内で定義した変数はスコープ外からアクセスできます。)

RでtryCatch内の変数を外から扱うには、 特別な演算子 <<- を使ってグローバル変数とするか、tryCatchを関数のように利用して1つの値を返す必要があります。

✓ Python

# Pythonの例
try:
    x = 10
    y = x / 0  # ゼロ除算エラーを発生させる
except ZeroDivisionError:
    error_value = "ゼロ除算エラーが発生しました"

print(x)  # 10が出力される
print(error_value)  # "ゼロ除算エラーが発生しました" が出力される

✓ R言語

tryCatch({
    x <- 10
    y <- x / 0  # ゼロ除算エラーを発生させる
}, error = function(e) {
    error_value <- "ゼロ除算エラーが発生しました"
})

print(x)  # エラー: オブジェクト 'x' が見つかりません
print(error_value)  # エラー: オブジェクト 'error_value' が見つかりません

✓ R言語 (グローバル変数を使用する例)

x <- NULL
error_value <- NULL

tryCatch({
    x <<- 10  # グローバル代入演算子を使用
    y <- x / 0
}, error = function(e) {
    error_value <<- "ゼロ除算エラーが発生しました"  # グローバル代入演算子を使用
})

print(x)  # 10が出力される
print(error_value)  # "ゼロ除算エラーが発生しました" が出力される

✓ R言語 (tryCatchを使用して1つの値を返す例)

result <- tryCatch({
    10 / 0  # ゼロ除算を試みる
}, error = function(e) {
    NA  # エラーが発生した場合はNAを返す
})

print(result)  # NAが出力される

6. 🖨️ 標準出力の書き方

Pythonではprint()で標準出力に文字列を出力でき、Rではprint()やcat()などの関数で文字列を出力できます。

print()は各引数を別々の行に出力し、デフォルトで引用符[1]付きで表示します。一方、cat()は引数を横並びに出力し、引用符なしで表示します。改行が行われないので、明示的に改行\nを指定する必要があります。

個人的には引用符が邪魔すぎて、catを使うことが多いです。

✓ Python

name = "Alice"
age = 30

print("Hello, World!")  # 出力: Hello, World!
print("My name is", name, ", and I am", age, "years old.")  # 出力: My name is Alice, and I am 30 years old.
print(f"My name is {name}, and I am {age} years old.")  # 出力: My name is Alice, and I am 30 years old.

✓ R言語

name <- "Bob"
age <- 25

print("Hello, World!")  # 出力: [1] "Hello, World!"
print(name, age)  # 出力: [1] "Bob" [2] 25

cat("Hello, World!\n")  # 出力: Hello, World!
cat("My name is", name, "and I am", age, "years old.\n")  # 出力: My name is Bob and I am 25 years old.

7. 🔗 paste()とpaste0()

Rには文字列結合のための関数としてpaste()paste0()が用意されています。

paste()はデフォルトで要素間にスペースを挿入し、paste0()はスペースを挿入しません。paste()のsep引数で区切り文字を指定でき、両関数ともcollapse引数でベクター内の要素を1つの文字列に結合できます。

✓ R言語

# paste()の基本使用
name <- "Alice"
greeting <- paste("Hello", name)
print(greeting)  # "Hello Alice"

# paste0()の使用
first_name <- "John"
last_name <- "Doe"
full_name <- paste0(first_name, last_name)
print(full_name)  # "JohnDoe"

# sep引数の使用
words <- c("R", "is", "awesome")
sentence <- paste(words, sep = "-")
print(sentence)  # "R-is-awesome"

# collapse引数の使用
fruits <- c("apple", "banana", "cherry")
fruit_list <- paste(fruits, collapse = ", ")
print(fruit_list)  # "apple, banana, cherry"

8. 🏷️ データフレームのカラム指定

Pythonではpandasをインストールすることでpd.DataFrame()を扱えるようになりますが、Rでは何もインストールせずとも標準でdata.frame()がサポートされています。

当然データフレームの扱いにも違いがあり、カラムの指定方法や長さの確認方法が異なります。

特に特徴的なのはカラムの指定方法で、Rではカラム名がAの場合、df$Aのように$を使って指定します。そのAが変数に格納されている場合、df[[col_name]]のように二重括弧を使って指定する必要があります。また、カラム名がアルファベットでない場合はdf$`名前のようにバッククォートで囲む必要があります。

✓ Python pandas (カラムの指定)

import pandas as pd

# データフレームの作成
df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6], '名前': ['太郎', '花子', '次郎']})

# カラムの指定(文字列)
print(df['A'])  # 出力: 0    1  1    2  2    3  Name: A, dtype: int64

# カラムの指定(インデックス)
print(df.iloc[:, 0])  # 出力: 0    1  1    2  2    3  Name: A, dtype: int64

# カラムの指定(変数内の文字列)
col_name = 'A'
print(df[col_name])  # 出力: 0    1  1    2  2    3  Name: A, dtype: int64

# 日本語カラム名の指定
print(df['名前'])  # 出力: 0    太郎  1    花子  2    次郎  Name: 名前, dtype: object

# 複数カラムの指定(文字列リスト)
print(df[['A', 'B']])  # 出力:    A  B  0  1  4  1  2  5  2  3  6

✓ R言語 (カラムの指定)

# データフレームの作成
df <- data.frame(A = c(1, 2, 3), B = c(4, 5, 6), "名前" = c('太郎', '花子', '次郎'))

# カラムの指定(文字列)
print(df$A)  # 出力: 1 2 3
print(df[["A"]])  # 出力: 1 2 3

# カラムの指定(インデックス)
print(df[, 1])  # 出力: 1 2 3

# カラムの指定(変数内の文字列)
col_name <- "A"
print(df[[col_name]])  # 出力: 1 2 3

# 日本語カラム名の指定(バッククォートを使用)
print(df$`名前`)  # 出力: "太郎" "花子" "次郎"

# 複数カラムの指定(文字列ベクター)
print(df[, c("A", "B")])  # 出力:   A B  1 1 4  2 2 5  3 3 6

9. 📏 データフレームの行列数の確認

行列数を確認する関数も異なります。
珍しくRの方が分かりやすい命名だと感じています。

✓ Python pandas (行列数の確認)

# 複数カラムの指定(インデックス)
print(df.iloc[:, [0, 1]])  # 出力:    A  B  0  1  4  1  2  5  2  3  6

# データフレームの長さ(行数)の確認
print(len(df))  # 出力: 3

# カラム数の確認
print(len(df.columns))  # 出力: 3

✓ R言語 (行列数の確認)

# 複数カラムの指定(インデックス)
print(df[, 1:2])  # 出力:   A B  1 1 4  2 2 5  3 3 6

# データフレームの長さ(行数)の確認
print(nrow(df))  # 出力: 3

# カラム数の確認
print(ncol(df))  # 出力: 3

おわり 🏁

おわり

Discussion