이전 포스트에서 설명한 포장형(wrapper class)의 생성자를 이용하면 문자열을 기본형 값으로 바꿀 수 있다.


[표 1] 포장형의 기본형 값을 반환하는 객체 메쏘드

메소드명

기능

byteValue()

byte값을 반환한다.

shortValue()

short값을 반환한다.

intValue()

int값을 반환한다.

longValue()

long값을 반환한다.

floatValue()

float값을 반환한다.

doubleValue()

double값을 반환한다.


예를 들어서 다음과 같이 Integer 형 객체에서 다양한 기본형 값을 얻을 수 있다.


package tut02;
public class Tut02 {
   public static void main(String[] args) {
       int ia = new Integer("1234567").intValue();
       float fa = new Integer("1234567").floatValue();
       short sa = new Integer("1234567").shortValue(); //(*)

       System.out.println(ia);
       System.out.println(fa);
       System.out.println(sa);
   }
}
실행 결과
1234567
1234567.0
-10617


위의 (*)에서와 같이 손실 변환(lossy conversion)의 경우에도 별다른 경고나 오류를 발생하지 않는다. 따라서 같은 형의 데이터의 변환에만 쓰는 것이 바람직하다.


 포장형으로 부터 기본형 값을 얻는 메소드로 parse 로 시작하는 정적 메소드들이 있다. 이 메소드를 이용하면 정수형을 반환하는 메소드는 기수를 지정해 줄 수도 있다.


[표 2] 포장형의 기본형 값을 반환하는 메쏘드

메소드명

기능

Byte.parseByte(String)

Byte.parseByte(String, radix)

문자열을 byte값으로 변환한다.

기수 radix인 문자열을 byte값으로 변환한다.

Short.parseShort(String)

Short.parseShort(String, radix)

문자열을 short값으로 변환한다.

기수 radix인 문자열을 short값으로 변환한다.

Integer.parseInt(String)

Integer.parseInt(String, radix)

문자열을 int값으로 변환한다.

기수 radix인 문자열을 int값으로 변환한다.

Long.parseLong(String)

Long.parseLong(String, radix)

문자열을 long값으로 변환한다.

기수 radix인 문자열을 long값으로 변환한다.

Float.parseFloat(String)

문자열을 float값으로 변환한다.

Double.parseDouble(String)

문자열을 double값으로 변환한다.


실제 프로그램에서는 주로 [표 2]의 정적 메소드를 자주 사용하게 된다.


package tut02;
public class Tut02 {
   public static void main(String[] args) {
       int ia = Integer.parseInt("1234567");
       int ib = Integer.parseInt("11110010",2); //이진수
       int ic = Integer.parseInt("1ab3f",16); //십육진수

       System.out.println(ia);
       System.out.println(ib);
       System.out.println(ic);
   }
}
실행 결과
1234567
242
109375


 포장형 객체를 반환하는 valueOf() 이라는 정적 메소드도 있다.


[표 3] 문자열로 부터 포장형 객체를 반환하는 메쏘드

메소드명

기능

Byte.valueOf(String)

Byte.valueOf(String,radix)

Byte.valueOf(byte)

문자열을 Byte 객체로 변환

기수 radix인 문자열을 Byte 객체로 변환한다.

byte값을 Byte 객체로 박싱

Short.valueOf(String)

Short.valueOf(String,radix)

Short.valueOf(short)

문자열을 Short 객체로 변환

기수 radix인 문자열을 Short 객체로 변환한다.

short값을 Short 객체로 박싱

Integer.valueOf(String)

Integer.valueOf(String,radix)

Integer.valueOf(int)

문자열을 Integer 객체로 변환

기수 radix인 문자열을 Interger 객체로 변환한다.

int값을 Interger 객체로 박싱

Long.valueOf(String)

Long.valueOf(String,radix)

Long.valueOf(long)

문자열을 Long 객체로 변환

기수 radix인 문자열을 Long 객체로 변환한다.

long값을 Long 객체로 박싱

Float.valueOf(String)

Float.valueOf(float)

문자열을 Float 객체로 변환한다.

float값을 FLoat 객체로 박싱

Double.valueOf(String)

Double.valueOf(float)

문자열을 Double 객체로 변환한다.

float값을 Double 객체로 박싱


예를 들어서


int ia = Integer.valueOf("1234567");


이렇게 하면 자동 언박싱이 일어나서 변수 ia에 1234567이란 값이 저장된다. 하지만 parseInt()함수보다는 당연히 성능이 떨어진다.




Posted by 살레시오
,

 자바에는 int, double과 같은 기본형 자료형(primitive type)의 포장 클래스(wrapper class)가 있어서 기본형을 객체로 다루어야 할 경우에 사용할 수 있다. 다음 표에서와 같이 포장 클래스명은 기본형의 첫 문자를 대문자로 바꾸면 된다.( char형과 int형만 이름이 다르다.)


[표 1] 포장 클래스

기본형

포장 클래스

생성 예

boolean

Boolean

Boolean bA = new Boolean(true);

Boolean bB = new Boolean(“false”);

char

Character

Character cA = new Character(‘a’);

byte

Byte

Byte byA = new Byte(10);

Byte byB = new Byte(“127”);

short

Short

Short sA = new Short(1234);

Short sB = new Short(“1234”);

int

Integer

Integer iA = new Integer(1234);

Integer iB = new Integer(“1234”);

long

Long

Long lA = new Long(1234);

Long lB = new Long(“1234”);

float

Float

Float fA = new Float(12.34f);

Float fB = new Float(“12.34f”);

double

Double

Double dA = new Double(12.34);

Double dB = new Double(“12.34”);


생성자는 위와 같이 해당하는 기본형 값을 줄 수도 있고 문자열로 줄 수도 있다. 문자열로 주는 경우 해당 데이터형의 형식에 맞아야 한다.


 박싱(boxing)이란 기본형을 참조형으로 변환하는 것이고 언박싱(unboxing)이란 반대로 참조형을 기본형으로 바꾸는 것이다. 그리고 JDK 1.5부터는 이것을 자동으로 해주는 기능이 추가되었다.


package tut02;
public class Tut02 {
   public static void main(String[] args) {
       Integer iA = new Integer(123);
       Integer iB = new Integer(123);
       
       int ia = (int)iA; //(1) 언박싱(unboxing)
       int ib = iB; //(2) 오토언박싱(auto unboxing)
       Integer iC = (Integer)456; //(3)박싱(boxing)
       Integer iD = ia; //(4)오토 박싱(auto boxing)
   }
}


위 예세서 (1)은 명시적으로 언박싱을 해주는 것이고 (2)는 자동으로 수행해 주는 것이다. 박싱의 경우도 자동으로 처리해 준다. 오토박싱과 오토언박싱은 대응되는 자료형 사이에만 일어난다는 점도 유의해야 한다.


 포장 클래스 객체간 연산도 가능하며 기본형과 포장형 간 연산도 가능하다.


package tut02;
public class Tut02 {
   public static void main(String[] args) {
       Integer iA = new Integer(123);
       Integer iB = new Integer(123);
       
       int ia = (int)iA; //(1) 언박싱(unboxing)
       int ib = iB; //(2) 오토언박싱(auto unboxing)
       Integer iC = (Integer)456; //(3)박생(boxing)
       Integer iD = ia; //(4)오토 박싱(auto boxing)
       
       Integer iE = iA + iB; // 포장형끼리의 연산
       Integer iF = ia - ib; // 기본형끼리의 연산 결과를 오토박싱
       int ic = ia * iB; // 기본형과 포장형 간 연산
   }
}


자바에서는 연산자가 오버로딩되지 않는다는 점을 생각하면 의아할 것이다. 하지만 이 코드는 내부적으로 포장형인 피연산자가 오토언박싱 되어서 기본형 끼리의 연산으로 수행되는 것이라고 이해할 수 있다.


 비교 연산도 가능하지만 내용물의 동치 여부를 검사할 때 ==기호대신 equals() 메소드를 이용해야 한다.


package tut02;
public class Tut02 {
   public static void main(String[] args) {
       Integer ia = new Integer(123);
       Integer ib = new Integer(123);
       Integer ic = new Integer(456);
       System.out.println("ia>=ib:"+(ia>=ib));
       System.out.println("ib>=ic:"+(ib>=ic));
       System.out.println("ia==ib:"+(ia==ib));
       System.out.println("ia.equals(ib):"+ia.equals(ib));
   }
}


ia>=ib:true
ib>=ic:false
ia==ib:false
ia.equals(ib):true


왜냐면 포장형고 객체이기 때문에 ==연산은 두 객체 인스턴스의 참조(주소값)을 비교하게 되는 것이다. 내용물의 동치 여부는 문자열 객체와 마찬가지로 equals() 메소드를 이용해야 한다.




Posted by 살레시오
,

 증감 연산자 ++와 --는 한 개의 피연산자를 갖는 단항 연산자로서 피연산자를 1씩 증가 혹은 감소시키는 것이다. (증감연산자는 C/C++에서의 용법과 동일하다.) 반복문에서 많이 사용되며 피연산자로서 정수형과 실수형 모두 사용할 수 있으나 주로 정수형 변수에 사용된다.


int ia = 10;

ia++; //  ia = ia + 1 과 같다.


double da = 1.0;

da++; // da = da + 1과 같다.


이와 같이 증감 연산자는 수식을 간결하게 만들어주고 실행속도 면에서 좀 더 효율적이다. 이 연산자는 매우 자주 쓰이는 연산자이지만 잘못 사용한 경우에는 발견하기 힘든 오류을 발생시킬 수 있으므로 주의해서 사용해야 한다.


[표 1] 증감 연산자의 종류와 용법

형식

의미

++x

x를 먼저 1증가시킨 후 그 값을 사용

--x

x를 먼저 1감소시킨 후 그 값을 사용

x++

x값을 먼저 사용한 후 1 증가

x--

x값을 먼저 사용한 후 1 감소


위 표에서와 같이 증감연산자는 변수 앞에도 붙을 수 있고 뒤에 올수도 있다.  ++x 나 x++ 는 프로그램에서 다른 조건식이나 수식에 연결되지 않고 단일문으로 쓰일 경우에는 같은 결과를 얻지만 수식의 일부분으로 사용할 경우에는 그 의미가 달라질 수 있다. 다음 예를 보자. (package와 class이름은 각자 다를 수 있다.)


package tut02;
public class Tut02 {
   public static void main(String[] args) {
       int ia, ib;
       ia = ib = 1;
       ia++;
       ib--;
       System.out.println("ia:"+ia+",ib:"+ib);
   }
}


이 예에서 출력되는 ia와 ib값은 각각 2와 0이다. ia++, ib--은 각각 다음과 같다.


ia = ia + 1;
ib = ib - 1;


다음 예에서와 같이 증감연산자가 다른 연산자와 사용될 때에는 주의해야 한다.


package tut02;
public class Tut02 {
   public static void main(String[] args) {
       int  ia = 10, ib = 10, ix, iy;
       ia++;
       ++ib;
       System.out.println("ia:"+ia+", ib:"+ib);

       ix = --ia;
       iy = ib--;
       System.out.println("ia:"+ia+", ib:"+ib);
       System.out.println("ix:"+ix+", iy:"+iy);
   }
}


ia:11, ib:11
ia:10, ib:10
ix:10, iy:11


이 예제에서 ia에 저장되는 값과 ib에 저장되는 값은 각각 10과 11이다. ix=--ia 에서는 ix에 대입되기 전에 먼저 ia값이 1 감소하고 그 후에 그 값이 ix에 대입된다. iy=ib-- 에서는 iy에 ib값이 먼저 대입되고 ib값이 1 감소한다. 증감연산자가 앞에 붙느냐 혹은 뒤에 붙느냐에 따라 동작의 순서가 달라진다는 점에 유의해야 한다.

 관례적으로 증감연산자를 쓰지 말아야 할 경우가 있다.

  1. 하나의 변수가 수식 내에서 두 번 이상 사용될 경우에는 증감연산자를 사용하지 않는다. 예: y = x*2 + x++

  2. 한 변수가 어떤 함수의 인자로 두 번 이상 사용될 경우에는 이 변수에 증감 연산자 ++, --를 사용하지 않는다. 예: func(x*2, x++)

그 외에도 프로그램 작성자가 혼동의 여지가 있을 것 같다면 명확하게 작성하도록 유의해야 한다.



Posted by 살레시오
,

 산술 연산자는 덧셈, 뺄셈 등 산술 연산을 수행하는 연산자들이고 부호 연산자는 부호를 지정하는 것들이다. 산술 연산자는 피연산자가 두 개이므로 이항 연산자이고 부호 연산자는 피연산자가 하나이므로 단항 연산자에 해당된다. 산술 연산자는 다음과 같은 것들이 있다.


[표 1] 산술 연산자

기호

기능

용례

+

덧셈/부호

12+13, a+b, 12.3+45.6+c, +12, +d
-

뺄셈/부호

12-13, a-b, 12.3-45.6-c, -12, -d
*

곱셈

12*13, a*b, 12.3*45.6*c
/

나눗셈

12/13, a/b, 12.3/45.6/c
%

나머지

12%13, a%b


예를 들면 다음과 같다.


int ia = 11, ib = 22, ic, id, ie;
ic = 1a + ib;
id = ia – ib;
ie = ia * ib;
double da = 1.1, db=2.2;
da = ie*ie;
db = da/ia;


부호 연산자는 어떤 값의 부호를 지정하거나 바꾸는데 쓰인다. +, -기호는 부호 연산자로도 사용되며, 부호 연산자는 한 개의 피연산자를 요구하는 단항 연산자이다.  예를 들면 다음과 같다.


+1234   -1234   -temp   +a   -b


따라서 다음과 같은 표현식에서 첫 번째 ‘-’는 부호연산자이고 두번째 ‘-’는 산술연산자이다.


ia = -ib - ic;


 연산의 결과를 변수에 저장할 때는 결과 값이 그 변수의 허용 범위에 들어가는지 여부를 잘 판단해야 한다.


int ia = 2000000;
int ib = ia*10;
byte bya = 129; // 오류
short sa = 700000;//오류


나머지(%)연산의 예를 들면 다음과 같다.


10%5 // 0
3%2 // 1
8%3 // 2
3%5 // 3


한 가지 주의할 점은 %연산자의 피연산자는 정수형과 실수형 모두 사용 가능하다. (C/C++에서는 정수형만 사용가능하지만 자바는 그렇지 않다.)


double da=5.2, db=4.1;
System.out.println(da%db); // 1.1이 출력됨


 다음 예제는 다양한 숫자의 2로 나눈 나머지를 출력해 주는 프로그램이다.


package tut02;
public class Tut02 {
   public static void main(String[] args) {
       byte bya = 13;
       int ia = 23456;
       float fa = 78.567f;
       System.out.println(bya%2);
       System.out.println(ia%2);
       System.out.println(fa%2);
   }
}


1
0
0.56700134


 다음 예제는 두 수의 덧셈, 뺄셈, 곱셈, 나눗셈의 결과를 표시해 주는 프로그램이다.


package tut02;
public class Tut02 {
   public static void main(String[] args) {
       int ia = 23456, ib=789;
       int ir = ia + ib;
       System.out.println("ia + ib = "+ir);
       ir = ia - ib;
       System.out.println("ia - ib = "+ir);
       ir = ia * ib;
       System.out.println("ia * ib = "+ir);
       System.out.println("ia / ib = "+(ia/ib));
   }
}


ia + ib = 24245
ia - ib = 22667
ia * ib = 18506784
ia / ib = 29


 자바는 컴파일하는 시점에서 변수에 어떤 상수를 입력할 때 범위를 체크하여 허용 범위를 넘어선다면 에러를 발생시킨다. 또한 산술연산을 할 때 다음과 같이 자동 형 변환이 일어난다.


  • 두 피연산자 중 하나라도 double 형이면 다른 하나도 double 형으로 변환하고 결과도 double형이다.

  • 그렇지 않고 두 피연산자 중 하나라도 float 형이면 다른 하나도 float 형으로 변환하고 결과도 float형이다.

  • 그렇지 않고 두 피연산자 중 하나라도 long 형이면 다른 하나도 long 형으로 변환하고 결과도 long형이다.

  • 그렇지 않다면 두 피연산자를 모두 int 형으로 변환하고 결과도 int 형이다.


자바에서 산술연산에 대해서는 이 네 가지의 규칙을 따른다. (c/c++의 경우보다 훨씬 더 간결하다.) 특히 마지막 규칙 때문에 예를 들어서 두 개의 byte형이나  short 형의 연산의 결과는 int형이 된다. 따라서 다음과 같은 간단한 연산도 에러를 발생한다.


short sa = 10, sb=20;
short sc = -sa;
short sd = sa+sb; //에러


범위를 벗어나는 것도 아닌데 왜 에러가 발생하는지 처음에는 의아할 수 있다. -sa와  sa+sb의 연산 결과는 int 형이고 이것을 short 형에 대입하려고 하기 때문이다. (C/C++ 프로그래머는 처음에 혼동하기 쉽다.) 자바에서는 크기가 작은 자료형으로의 형변환(lossy conversion)은 무조건 명시적으로 해 주어야 한다. 따라서 다음과 같이 명시적으로 형변환을 해주어야 한다.


short sa = 10, sb=20;

short sc = (short)(-sa);

short sd = (short)(sa+sb);


이것은 byte형에 대해서도 마찬가지이다.




Posted by 살레시오
,

 맥시마(Maxima)로 미분방정식을 풀 수 있다. 이때 사용되는 대표적인 함수로서 ode2() 함수와 desolve() 함수가 있는데 이 중에서 ode2()함수를 이용하여 아래의 2계 선형 상미방을 풀어보자.

이것을 손으로 풀면 A4용지 한 페이지가 꽉 찰 정도로 풀이 과정이 꽤 복잡한 문제이다. ode2()함수의 첫번째 인자는 미분방정식을 입력해야 된다. wxMaxima에서는

y'(x) 은 'diff(y,x)
y''(x) 는 'diff(y,x,2)

로 입력한다. 그리고 ode2()의 두 번째 인자는 함수(이 경우는 y), 세 번째 인자는 독립변수(이 예제에서는 x이다.)를 넣어주면 된다.


위에서 %k1, %k2는 적분상수를 표시한다. 초기 조건(initial condition)을 입력하여 적분상수를 구하려면 다음과 같이 ic2()함수를 이용하면 된다.


이것으로부터 미방의 해는 y=sin(x) + 3x/25 - 8/25 + 3.1e-0.5x 임을 알 수 있다.


 이 해의 그래프를 그려보고 싶다면 wxplot2d()함수를 이용하면 된다.

위 에서 wxplot2d()의 첫 번째 인자로 도시하려는 함수를 입력해야 하는데 rhs(%o2)라고 되어 있다. 이것은 결과가 저장된 변수 %o2 의 우변(right-hand side)을 추출하는 함수이다. 그리고 두 번째 인자는 [x,0,20] 인데 x축의 범위를 설정하는 것이다.



Posted by 살레시오
,

 맥시마(Maxima)에서 라플라스 변환을 구하기 위한 함수로 laplace() 가 있다.

         문법 : laplace ( f , t , s ) 함수, 시간 변수, 라플라스 변수의 순으로 입력한다.

예를 들어서 1, eat, sin(wt), cosh(at) 함수들의 라플라스 변환을 구하면 다음과 같다.

약간 더 복잡한 함수로 eatcos(wt), tsin(wt), δ(t), t^1.5 의 라플라스 변환은 다음과 같다.

역라플라스 변환은 ilt() 함수를 이용하면 된다.

         문법 : ilt( F, s, t) 라플라스 함수, 라플라스변수, 시간변수 순서대로 입력한다.

예를 들면 다음과 같다.


이와 같이 맥시마를 이용하면 라플라스 변환과 역라플라스 변환 결과를 손쉽게 구할 수 있다.



Posted by 살레시오
,

 자바에서는 문자열(string)을 처리할 수 있는 String 클래스를 제공한다. 클래스에 대해서는 아직 설명하지 않았지만 문자열은 간단하게라도 알고 넘어가야 한다. 자바에서 문자열은 기본형이 아니라 참조형이고 큰따옴표를 이용하여 표현한다. 그리고 문자열 변수를 선언/초기화하는 것은 String은 클래스이지만 기본형의 변수를 선언하고 초기화하는 방법과 동일하다.


String name; //문자열 선언
name = “홍길동”; // 문자열 초기화

String title = “선생님”;//선언과 동시에 초기화


다음과 같이 화면에 출력할 수도 있다.


System.out.println(name); // name 문자열의 내용이 화면에 출력된다.


두 개의 문자열을 서로 연결하는 데 ‘+’연산자를 이용할 수도 있다.


String fullName = name + title; // 두 문자열 연걸
String sayHi = “안녕하세요. “ + name;
String sayHello = “안녕하세요. “ + name + title; //세 개 이상도 연결 가능


다음과 같이 System.out.println() 함수에 문자열 연결식을 직접 입력할 수도 있다.


System.out.println(“안녕하세요. “+name+”씨.”);


이렇게 하면 불필요한 문자열 변수를 생성하지 않고 직접 연결된 문자열을 화면에 출력할 수 있다.


 어떤 기본형 변수의 값을 출력할 때에도 System.out.println()함수를 사용할 수 있다.


double da = 11.2;
System.out.println(da);


함수 System.out.println() 는 문자열을 인자로 주어야 하는데 이경우 변수값 da는 내부적으로 문자열로 자동으로 변환되어서 System.out.println()에 그 문자열을 넘겨준다. 따라서 다음과 같이 연결하는 것도 가능하다.


double da = 11.2;
System.out.println(“da 값은 “ + da + “입니다.”);


이 경우 연결이 일어나기 전에 da값은 문자열로 변환되어 세 개의 문자열을 연결한 새로운 문자열이 System.out.println()에 인자로 넘어가는 것이다.




Posted by 살레시오
,

  numpy에는 배열(ndarray)과 행렬(matrix) 객체가 있는데 이들 중 어느 것을 사용해야 하는지 처음 공부할 때 혼동하기 쉽다. 언뜻 보기에는 사용법에 별 차이가 없어 보이고 행렬 연산에는 무조건 matrix를 사용해야 하는 것 아닌가 짐작했었는데 다음 사이트에 잘 정리가 되어 있어서 여기에 옮겨보도록 하겠다.

              numpy for MATLAB users

1. 결론 요약

  일단 결론부터 말하자면 ndarray 를 사용하는 것이 더 효율적이라고 명시되어 있다. 개인적으로 matrix 객체는 MATLAB 사용자의 편의를 위해서 (억지로) 만들어진 것 같은 느낌이 든다. (pylab 과 같은 모듈도 비슷한 이유로 만들어졌으나 파이썬 커뮤니티 안에서는 사용이 역시 권장되지 않는다.)

 ndarray를 사용해야 하는 이유는 다음과 같이 요약할 수 있다.

  • ndarray는 numpy에서 지원하는 표준형인 벡터/행렬/텐서 를 저장한다.

  • 많은 numpy 함수가 matrix가 아니라 ndarray를 반환한다.

  • 요소 간 연산과 선형대수 연산에 대해선 명확히 구분되어 있다.

  • 표준 벡터나 열벡터/행벡터를 표현할 수 있다.

ndarray를 사용할 경우의 한 가지 단점은 행렬의 곱셈을 수행할 때 ,dot() method를 사용해야한다는 점이다. 즉, ndarray 객체 A와 B를 행렬곱하려면 A.dot(B) 와 같이 수행해야 하며 A*B는 요소간 곱셈이 된다. 반면 matrix객체 A와 B는 단순히 A*B로 행렬곱이 수행된다.

2. 좀 더 자세한 설명

 numpy는 ndarray 와 matrix 둘 다 포함한다. ndarray는 다양한 종류의 수치 연산을 위해서 고안된 범용 n차 배열이다. 반면 matrix는 선형 대수 연산을 위해서 특별히 고안된 객체이다. 실용적인 관점에서 둘 사이의 차이점은 몇 가지 안된다.

 

  • 연산자 *, dot() 그리고 multiply()

    • ndarray 는 '*'는 요소간 곱셈이다. 행렬곱을 할때는 obj.dot() 메쏘드를 사용해야 한다.

    • 반면 matrix는 '*'이 행렬곱이다. 그리고 numpy.multiply() 함수가 요소간 곱이다.

  • 벡터와 1차 배열

    • ndarray 는 벡터 1xN, Nx1, 그리고 N크기의 1차원 배열이 모두 각각 다르다. obj[:,1] 는 크기가 n인 1차 배열을 반환한다. 그리고 1차원 배열의 전치는 작동하지 않는다. (역자 주 : ndarray로 벡터를 표현할 때는 반드시 2차 배열을 이용해야 한다.)

    • 반면 matrix 객체에서 1차 배열은 모두 1xn, 혹은 nx1 행렬(2차원 배열)로 상향 변환된다. ( matrix 객체는 내부적으로 항상 2차원 배열이다. )

  • ndarray는 고차원 배열이 가능하지만 matrix는 항상 2차원 배열 이다.

  • 편리한 attribute

    • ndarray 는 전치를 해주는 a.T attribute 가 있다.

    • matrix 도 m.H, m.I, m.A attribute들이 있으며 각각 복소전치, 역행렬, ndarray로의 변환이다.

  • 편리한 생성자

    • ndarray는 중첩된 리스트로 다차원 배열을 생성한다. 예) array( [ [1,2,3].[4,5,6] ] )

    • matrix는 MATLAB 의 문법을 지원한다. 예) matrix('[1 2 3; 4 5 6]')


좀 더  부가 설명을 하자면 ndarray를 이용하여 벡터(vector)를 표현할 때는 2차 배열로 정의해야 한다. 즉, 다음 세 가지는 모두 다르며 이 중  벡터는 두 번째와 세 번째 같이 생성해야 한다. (혼동하기 참 쉽다.)


a1 = np.array( [1, 2, 3] ) #크기 (3,)인 1차원 배열
a2 = np.array( [ [1, 2, 3] ] ) #크기 (1,3)인 2차원 배열 (행벡터)
a3 = np.array( [ [1], [2], [3] ] ) #크기 (3,1)인 2차원 배열 (열벡터)


여기서 a1.T 는 동작하지 않는다. 반면 a2.T 와 a3.T는 동작한다. 1차 배열은 행벡터나 열벡터 두 가지 모두로 취급되기도 한다.


 ndarray 객체를 사용하는데 있어서 장점과 단점은 다음과 같다.


  • 장점

    • 1차 배열은 행벡터나 열벡터 둘 다로 취급할 수 있다. dot(A,v) 에서 v는 열벡터로 다루어지고 dot(v,A)에서는 행벡터로 취급된다. 따라서 전치를 복잡하게 수행할 필요가 없다.

    • 요소 간 곱셈이 쉽다 (예: A*B) 사실 모든 산술 연산 (+ - * / ** 등등)이 요소 간 연산이다.

    • ndarray 는 numpy 의 가장 기본 객체이므로 연산의 속도, 효율성 그리고 numpy를 이용하는 외부 라이브러리의 반환형이 이것일 가능성이 높다.

    • 다차원 배열을 쉽게 구현한다.

    • tensor algebra 에 대한 장점이 있다.(?)

  • 단점

    • 행렬간 곱에 obj.dot() 멤버함수를 사용해야 하므로 번잡할 수 있다. 세 행렬 A, B, C의 행렬곱은 dot( dot(A,B),C) 이다


matrix 객체를 사용하는 데 있어서 장점과 단점은 다음과 같다.


  • 장점

    • MATLAB 행렬과 비슷하게 동작한다.

    • 행렬곱이 A*B 와 같이 '*' 연산자이므로 선형대수 연산이 좀 더 편하다.

  • 단점

    • matrix 의 차수는 항상 2이다. 벡터도 2차 배열로 저장된다. 3차 행렬을 구현하려면 ndarray를 이용하던가 matrix객체를 요소로 갖는 리스트(list)로 구현해야 한다.

    • ndarray 가 numpy의 표준 객체이기 때문에 어떤 함수는 인수로 matrix를 주어도 반환 객체는 ndarray 이기도 하다. numpy 에 포함된 함수는 절대 그렇지 않지만 제3자 라이브러리는 그럴 수 있다.

    • 요소간 곱은 multiply(A,B)함수를 이용해야 한다.

    • *은 행렬곱인데 반해 /는 요소간 연산이다. 즉, 연산자 오버로딩에 일관성이 결여되어 있다.


 개인적으로는 numpy를 사용하기로 결정되었다면 matlab에서의 개념은 모두 버리고 numpy에서 제공하는 객체에 빨리 익숙해져야한다는 의견이다. 그래서 matrix객체나 pylab 모듈을 사용하는 것이 권장되지 않는 것이다.




Posted by 살레시오
,

 넘파이에는 ndarray 라는 객체가 있는데 레퍼런스에는 그냥 배열(array)로 지칭한다. 아마 파이썬의 기본 객체인 리스트, 튜플, 딕셔너리 와 이름이 겹치지 않으므로 혼동의 여지가 없어서 그런 것 같다. 앞으로도 그냥 배열이라고 하면 numpy.ndarray 객체를 지칭하는 것으로 하겠다.


>>> import numpy as np

이 객체를 생성하는 대표적인 함수가 array() 함수인데 첫 번째 입력인수가 array-like 객체이다. array-like 객체는 다음과 같은 것들이 있다.

 

  • 리스트 (list)

  • 튜플 (tuple)

  • 배열 (ndarray) - 배열 자체도 array()함수의 첫 번째 인자로 올 수 있다.

  • 행렬 (matrix)


예를 들면 다음과 같다.


>>> np.array([1, 2, 3])
     array([1, 2, 3])

>>> np.array([1, 2, 3.0]) # upcasting
    array([ 1.,  2.,  3.])

>>> np.array([[1, 2], [3, 4]]) #More than one dimension:
    array([[1, 2],
          [3, 4]])
>>> np.array([1, 2, 3], ndmin=2) # Minimum dimensions 2
    array([[1, 2, 3]])

>>> np.array([1, 2, 3], dtype=complex) # Type provided:
    array([ 1.+0.j,  2.+0.j,  3.+0.j])

>>> np.array(np.mat('1 2; 3 4')) # Creating an array from sub-classes:
    array([[1, 2],
           [3, 4]])

>>> np.array(np.mat('1 2; 3 4'), subok=True)
    matrix([[1, 2],
            [3, 4]])

 넘파이로 코딩하면서 직접 확인한 몇 가지 사실들을 아래에 기록해 보았다. (혹시 오류가 있다면 댓글을 남져주시길 부탁드립니다.)


스칼라 상수(변수)로 생성된 배열은 0차 배열 이다.

스칼라로 배열을 생성할 경우 배열 객체의 차수는 0차이다. 즉, 스칼라는 0차 배열이다. shape은 empty tuple을 가지고 size는 1이다.


>>> x= 10
>>> a = np.array(x)
>>> a.ndim, a.shape, a.size
     (0, (), 1)


0차 배열이라는 용어가 생소하겠지만 배열에 스칼라도 포함된다는 것이 더 논리적인 것 같다. 만약 단일 수에서 1차 배열을 생성하려면 다음과 같이 리스트(혹은 튜플)로 만들어서 넘겨야 한다.

>>> a = np.array( [x] ) # 혹은 a = np.array( (x,) )
>>> a.ndim, a.shape, a.size
     (1, (1,), 1)


이것은 요소가 한 개인 1차 배열이 된다. 즉 np.array(10) 과 np.array([10])은 엄연히 구별된다. 전자는 0차 배열, 후자는 1차 배열이다.


2차 배열에서 크기가 다른 행은 리스트다.

이것은 예를 들어서 다음과 같은 배열 a와 b를 고려해 보자.


>>> a = np.array( [ [1,2], [3,4] ] )
>>> b = np.array( [ [1,2], [3,4,5] ] )


두 변수는 모두 배열이다. 하지만 배열 a의 각 요소 a[0] 과 a[1] 은 type 이 배열인 반면 b[0], b[1] 은 리스트이다. 즉, 변수 a 는 array of array 이고 변수 b는 array of list 이다. 따라서 a[0] 은 배열의 모든 속성을 사용할 수 있지만 b[0]는 리스트이기 때문에 그럴 수 없다.


 만약 크기가 다른 행들도 모두 배열로 만들고 싶다면 다음과 같이 하면 된다.


>>> a = np.array( [ np.array([1,2]) , np.array([1,2,3]) ] )


이것은 array of array 가 된다.


리스트와 배열의 연산 결과는 배열이다.

 리스트끼리의 산술 연산은 정의되어 있지 않으나 리스트와 배열 상호간 연산은 요소간 연산이 적용된다. 그리고 그 결과도 배열이다.


>>> [1,2,3]/2 # 에러가 발생한다.
>>> [1,2,3]/np.asarray([2])
    array([ 0.5,  1. ,  1.5])


위의 두 번째 명령의 결과를 보면 연산 결과도 배열임을 알 수 있다.



Posted by 살레시오
,

 넘파이의 ndarray(이하 배열)을 슬라이싱할 때 파이썬의 리스트(list)와 다르게 원본의 참조가 생성되는데 이를 뷰(view)라고 한다. 예를 들어서 다음과 같이 2차 배열 arr을 고려하자.


>>> arr=np.arange(8).reshape(2,4)
>>> arr
array([[0, 1, 2, 3],
      [4, 5, 6, 7]])


단순 대입문을 사용하여 arr2 = arr 이라는 명령은 arr 의 참조본이 arr2에 저장된다. 따라서 arr이 수정되면 arr2 도 같이 바뀐다. 이것은 리스트의 동작방식과 같다.

 그런데 arr[0] 라고 슬라이싱하면 첫 번째 행 전체가 선택된다.


>>> b=arr[0]
>>> b
array([0, 1, 2, 3])


여기서도 마찬가지로 b 배열은 arr 배열의 첫 행에 대한 복사가 아니고 뷰(참조의 개념)이기 때문에 원본이 바뀌면 이 참조본도 따라서 바뀌게 된다.

>>> arr[0]=10
>>> arr
array([[10, 10, 10, 10],
      [ 4,  5,  6,  7]])
>>> b
Out[127]: array([10, 10, 10, 10])


이 예에서 보면 arr 배열의 첫 행을 모두 10으로 바꾸었다. 배열의 슬라이싱에 스칼라를 대입하면 그 범위의 전체 요소에 대입된다. 그런데  b 배열도 같이 바뀐 것을 알 수 있다. 이는 리스트의 슬라이싱에서는 복사본이 생성되는 것과는 다른 동작이기 때문에 주의가 필요하다.  numpy는 대용량 데이터 처리를 염두에 두고 설계되었다. 대용량 데이터의 슬라이싱이 빈번하게 일어나는 복잡한 코드를 실행시키는데 있어서 복사가 남발되면 메모리 문제를 일으킬 소지가 많기 때문에 이렇게 설계된 것으로 짐작된다.


  만약 슬라이싱의 복사본을 생성하고 싶다면 copy() 속성을 이용하면 된다.


>>> b=arr[0].copy()
>>> b
array([0, 1, 2, 3])

이제 b 배열은 원본인 arr 과 독립적인 복사본이므로 원본이 바뀌어도 아무런 영향을 받지 않는다.

  비슷한 차이가 numpy.asarray() 함수와 numpy.array() 함수에도 존재하는데 두 함수의 동작은 거의 유사하다. 가장 큰 차이점은 copy 옵션의 초기값이 다르다는 것이다. 사실 asarray()는 내부적으로 다음과 같이 정의되어 있다.

def asarray(a, dtype=None, order=None):
   return array(a, dtype, copy=False, order=order)


참고로 array() 함수의 정의부는 다음과 같다.


numpy.array(object, dtype=None, copy=True, order=None, subok=False, ndmin=0)


따라서 만약 배열이나 행렬객체를 이용하여 다시 생성된 배열은 numpy.array()함수를 이용하였다면 복사본이, numpy.asarray()함수를 이용하였다면 참조본(전체 뷰)이 생성된다.


>>> arr = np.ones((3,4))
>>> arr
array([[ 1.,  1.,  1.,  1.],
      [ 1.,  1.,  1.,  1.],
      [ 1.,  1.,  1.,  1.]])
>>> arrB = np.asarray(arr) # 참조본 생성
>>> arrC = np.array(arr) #복사본 생성
>> arr[1]=np.pi # 원본 변경
>>> arr
array([[ 1.        ,  1.        ,  1.        ,  1.        ],
      [ 3.14159265,  3.14159265,  3.14159265,  3.14159265],
      [ 1.        ,  1.        ,  1.        ,  1.        ]])
>>> arrB # 참조본은 자동으로 변경된다.
array([[ 1.        ,  1.        ,  1.        ,  1.        ],
      [ 3.14159265,  3.14159265,  3.14159265,  3.14159265],
      [ 1.        ,  1.        ,  1.        ,  1.        ]])
>>> arrC # 복사본은 변경되지 않는다.
array([[ 1.,  1.,  1.,  1.],
      [ 1.,  1.,  1.,  1.],
      [ 1.,  1.,  1.,  1.]])



Posted by 살레시오
,