데이터와 통계분석에 활용되는 R을 웹 언어에서 실행한다면, 강력한 시너지 효과를 기대할 수 있을 것입니다. 그러나 많은 개발자들의 도전에도 불구하고 이것은 쉽지 만은 않은 과제였는데, 그 이유는 바로 R을 Input File 없이 커맨드라인에서 실행할 수 있는 방법이 녹록치 않았기 때문입니다.
이번 포스팅에서 소개하는 PHP 클래스는, 인자로 받은 R 스크립트를 임시파일로 만들어 쉘 커맨드로 R을 실행한 결과를 반환합니다. 이를 통해서 PHP에서 R 스크립트를 문자열로 만들어 그대로 실행할 수 있습니다. 본 클래스는 리눅스 OS에서 작동하는 것을 전제로 합니다.
클래스 R_script 소개
final class R_script { private static $pathTmp = '/tmp/'; private static $pathRbin = '/usr/bin/R'; static function run($s) { // Setting File Names $sKey = 'r-php-script-' . md5(time() . rand()); $fInput = self::$pathTmp . $sKey; // Write an Input File $fp = fopen($fInput, 'w+'); fwrite($fp, $s); fclose($fp); // Run the R Binary $res = shell_exec(self::$pathRbin . ' --slave -q --no-save < ' . $fInput); // Delete Files unlink($fInput); // Return return $res; } }
클래스를 구성하는 소스코드는 간결합니다. 먼저 임시파일의 이름을 설정하고, 인자 $s로 입력받은 R 스크립트를 임시파일에 작성합니다. 그 후에는 쉘 커맨드로 R을 실행하여 결과를 받은 후, 임시파일을 지우는 것으로 마무리됩니다.
R의 실행파일 경로는 /usr/bin/R로 지정되어 있으나, 서버환경에 따라서 경로가 다른 경우에는 $pathRbin 멤버변수를 수정하여 사용할 수 있습니다.
$s = ' x <- c(14,26,68,47,33,95); y <- c(10,32,89,57,75,80); mean(x); mean(y); cor(x, y, method="pearson"); '; echo R_script::run($s); // 출력결과 /* [1] 47.16667 [1] 57.16667 [1] 0.7844031 */
실제 클래스를 사용하는 예제는 위와 같습니다. 2개의 벡터를 각각 x와 y에 담고, 각 벡터의 평균과 함께 피어슨 상관계수를 구하는 코드입니다. 출력한 결과는 코드 가장 아래의 주석과 같습니다.
$s = " x <- c(14,26,68,47,33,95); y <- c(10,32,89,57,75,80); cat(mean(x)); cat('\n') cat(mean(y)); cat('\n') cat(cor(x, y, method='pearson')); "; echo R_script::run($s); // 출력결과 /* 47.16667 57.16667 0.7844031 */
행 머리에 [1]이 붙어 이용하시기에 불편하시다면, R 스크립트 상에서 cat( ) 함수로 로직을 감싸면 결과만 출력할 수 있습니다. 다만 이 때는 개행문자가 별도로 붙지 않기 때문에, 여러 개의 결과를 출력할 때에는 개행문자 또한 따로 cat( ) 함수로 출력해야 할 것입니다. PHP 상에서 문자열 함수나 정규표현식을 활용하여 제거하는 방법도 있습니다만, 만약 각괄호 안의 숫자가 정상적인 출력 안에 포함된다면 예상하지 못한 문제가 발생할 수 있어 별도의 처리는 하지 않았습니다.
위 방법은 임시파일을 이용하고, 특히 쉘 커맨드를 직접 실행한다는 점에서 최선의 방법은 아닙니다. 만약 R 스크립트 문자열 중의 일부가 GET 또는 POST 파라미터로 이루어진다면, 보안위협에 그대로 노출될 수 있는 위험도 있습니다. 실행속도 또한 만족스러운 수준은 아닙니다. 그러나 매우 간편하게 PHP 소스코드 상에서 R의 다양한 연산을 활용할 수 있다는 점에서 실시간성이 필요하지 않은 통계분석 도구를 구현할 때 활용할만한 가치가 있을 것입니다.
활용예제 : 두 배열의 피어슨 상관계수를 구하는 함수
function pearsonCor($a, $b) { // Check if It is an Array if(!is_array($a) || !is_array($b)) { return false; } // Check Dimensions if(count($a) != count($b)) { return false; } // Make All Elements to Float $a = array_map('floatval', $a); $b = array_map('floatval', $b); return floatval(R_script::run(' a <- c(' . implode(',', $a) . '); b <- c(' . implode(',', $b) . '); cat(cor(a, b, method="pearson")) ')); } $a = array(14, 26, 68, 47, 33, 95); $b = array(10, 32, 89, 57, 75, 80); echo pearsonCor($a, $b);
선형분석에서 자주 사용하는 계수라고 한다면, 피어슨 상관계수를 역시 빠뜨릴 수 없을 것입니다. 두 데이터 집합의 움직임이 얼마나 동질적인지를 밝힐 때 사용하는 계수인데, 금번 포스팅에서 소개한 클래스를 활용하여 피어슨 상관계수를 구하는 함수를 구현한다면 위와 같은 코드가 될 것입니다.
다만 PHP에서 피어슨 상관계수를 구하는 함수는 이미 PECL에 stats_stat_correlation( )라는 이름으로 존재하고, 작동속도도 이 함수가 월등히 빠르기 때문에 굳이 위 방법을 사용할 이유는 없습니다. 위 코드는 단지 이해하기 쉬운 활용예제로써 소개한 것입니다. 항상 그렇듯 문제를 해결할 때는 해당 언어의 기본스펙에서 먼저 답을 구하는 것이 순리라는 사실을 잊지 말아야 겠습니다.
좋은글 감사합니다.
window님, 감사합니다. 앞으로도 좋은 글로 찾아 뵙겠습니다.
Thanks for finally writing about > PHP 소스코드에서 R 스크립트 실행하기
– Epiloum 개발노트 < Loved it!
Yes! Finally something about 하나히어링.