본문 바로가기

python

파이썬 2일차(파이썬 자료구조 개념설명, 리스트, 문자열 메소드,lambda, map)

# =============================================================================
# 
# # 파이썬 자료 구조
# 
# 1. 리스트
# - 기본 자료 구조(내장된)
# - R의 벡터와 비슷
# - 1차원
# - 서로 다른 데이터 타입 허용
 
# 2. 딕셔너리
# - 기본 자료 구조
# - R의 리스트와 비슷
# - key와 value 형태로 구성

# 3. 배열
# - numpy 모듈 지원 자료 구조
# - R의 배열과 동일
# - 다차원
# - 하나의 데이터 타입만 허용

 
# 4. 시리즈(내장되어 있지 않다는 것 말고 성격적으로 더 비슷하네)
# - pandas 모듈 지원 자료 구조 
# - 1차원 
# - R에서의 벡터와 동일
# - 서로 같은 데이터 타입만 허용
# - 데이터 프레임의 컬럼을 구성하는 기본 자료 구조 

# 5. 데이터프레임
# - pandas 모듈 지원 자료 구조
# - R에서의 데이터프레임과 동일
# - 행과 열의 구조(key(컬럼)-value)
# - 2차원

# =============================================================================

# 리스트 : 파이썬 기본 자료구조, 1차원, 서로 다른 데이터 타입허용

# 1. 생성

l1 = [1,2,3]
type(l1)     # class는 벡터면 벡터가 아닌 해당 안에 원소들의 타입을 반환했지만 다름

l2 = [1,2,3,'4']
l2
l3 = [1,2,3,[4,5]] ; l3  

#R에서는 벡터에 벡터를 중첩시켜도 그냥 나열된 상태로 나왔으나, 이것은 중첩이 됨(리스트는 서로 다른 데이터 타입을 허용해서 그런듯.)
#리스트안에 리스트를 넣을 수 있으나 1차원임. 
#중첩된 것의 색인은 2차원 비스무리하게 되긴 함.
l3[3][0]

type (l2)


# 리스트는 여러개를 묶거나 묶은채로 전달할 때 빈번히 사용됨

# 2. 색인
l1[0]
l1[0,2]   # 2차원으로 인식된다는 듯. 
l1[[0,2]] # 이것도 안된다. 동시에 여러개 색인 불가(리스트를 사용한 색인 불가)
l1[1:2]   # 두번째 원소에서 두번째 까지 추출  # 슬라이스 색인하면 차원축소 방지함.
          # 슬라이스 색인 : x1~x2까지 같은거
l1[1]     # 정수 하나 추출시 차원축소
l1[0:1]   #?
l1[0:2]   # 첫번째 원소에서 두번째 원소 추출  #0,1추출 2개 추출

# n:m => n~ (m-1)까지 추출

l1[-1]    # 뒤에서 첫번째 원소 추출


# [참고 : 시리즈에서의 리스트 색인]

from pandas import Series
s1 = Series(11)
s1[[0,2]]  #리스트를 사용한 색인 가능

# 3. 연산
l1 = [1,2,3]
l2 = [10,20,30]

l1 + 1          #파이썬의 주요한 특징, 벡터와 같은 반복적인 연산들 안됨
                #(사상 자체가 반복문을 확실히 프로그래밍하지 않으면 안되도록 함)
l1 + l2         #각각 연산이 되는게 아니고 합집합이 나옴
l1 * 3          #l1 + l1 + l1 이 되어서 우리가 아는 산술연산이 되는게 아님


#원소별로 값을 전달하고 싶다면 적용함수를 이용해야한다.

# math 모듈 내 함수
import math 

#파이썬 기본창에서는 math. 만써도 여러 함수들이 보이는데 스파이더에선 안된다고 하심
#내장된 함수 목록   #중요!!!
dir(math)

math.sqrt(9)    #제곱근
2**3            #거듭제곱 연산자
math.pow(2,3)   #거듭제곱 함수

# 문자열 관련 함수  (리스트에는 적용 안됨)

# 문자열 관련 표현식
# 1. 문자열 색인

v1 = 'abcde'
v1[1]         # b 출력
v1[0:3]       # abc 출력
#cf. In R : 문자열 자체가 하나의 원소라 str_sub이나 split없이 하나씩 알파벳을 추출하지 못했음

# 2. 다중 라인 문자열 생성 

# v2 = 'abcde
# 12345'         

# vsql = 'select *
#           from emp'
          
                            #줄바꿔서 입력 안됨

vsql ='''select *
           from emp'''
                            #'''를 이용해서 입력 가능 #현재 왜안되누? 두줄 모두 잡고 실행해야함

vsql
vsql ="""select *
           from emp"""

# 3. 문자열 메소드 (string)

s1 = 'abcde'
s2 = 'ABCde'
l1 = ['abc','ABC']

#1) 대소치환

s1.upper()
s2.lower()
s1.title() #camel표기법

#2) startswith : 문자열의 시작 여부 확인

s1.startswith(data,     #시작값 확인문자
              start,    #검사 시작 위치(생략가능)
              end)      #검사 끝 위치(생략가능)

s1.startswith('A')       #A로 시작합니까?

#문자열내에 문자 색인이 가능해서 굳이 이 메서드가 필요한가 싶음

s1[2] == 'c'             #3번째 글자가 c 입니까?
s1.startswith('c',2)     #3번째 글자가 c 입니까?

#3) endswith : 문자열의 끝 여부 확인
s1.endswith('e')

l1.startswith('a')        # l1과 startswith는 어울리지 않기에 attribute에러 메세지 출력함

#list의 원소는 문자열이니까 하나씩 뽑아서 전달하면 가능, 반복문이나 적용함수로 해결 가능할 듯

# 4) strip : 공백제거
' abcd '.rstrip()  # 오른쪽 공백 제거
' abcd '.lstrip()  # 왼쪽 공백 제거
' abcd '.strip()   # 양쪽 공백 제거

'abcde'.lstrip('a')  # 문자열을 전달하면 문자열 제거가능하나 a를 제거하다 a가 아닌 녀석을 만나면 멈춤(연속된 a 제거 가능) . 중간에 있는 a 제거 불가


# 5) replace : 치환

'abcde'.replace('a','A') #치환
'abcde'.replace('c','')  #제거

# 6) split : 분리            
'a;b;c'.split(';')  #출력 결과 리스트

# 7) find : 특정 문자의 위치값 리턴               # str_locate
'abcde'.find('a')   #0  #'a'의 위치 리턴
'abcde'.find('A')   #없으면 -1 리턴     #이걸 활용하면 특정값의 포함여부를 확인할수도 있음


'abcde fg 1234'.find('1234')


# 8) count : 포함 횟수 리턴
'abcabcaa'.count('a')  #4번 쓰임

# 9) 형(type) 확인

'abd'.isalpha()       #문자 구성 여부
'abd'.isnumeric()     #숫자 구성 여부
'abd'.isalnum()       #문자/숫자 구성여부


# 10) format : 포맷 변경
'{0:.2f}'.format(10)


#연습문제
ename = 'smith'
tel = '02)345-7839'
jumin = '901223-2223928'
vid = 'abc1234!'

# 1)ename의 두번째 글자가 m으로 시작하는지 여부
ename[1] == 'm'
ename.startswith('m',1)

# 2) tel 에서 국번 (345) 출력

tel.split(')')[1].split('-')[0]

#------------------another
vno1 = tel.find(')')    #정규식표현식이 불가능한 메서드 , 그래서 escape 문자도 필요 없음
vno2 = tel.find('-')
tel[3:6]
tel[vno1+1:vno2]

#마찬가지로 split의 경우도 정규식표현식이 불가능 
# 참고로 pandas에서 제공하는 split메서드는 정규식 지원됨  #  데이터프레임타입.split 
# 정규식 표현은 R에서 배운것과 동일한 규칙

# 3) jumin에서 여자 여부 출력

jumin.find('-2') != -1

#------------another
jumin[7] == 2        #false # 형일치 정확하게 해야함
jumin[7] == '2'      #true

# 4) vid에서 !가 포함되어 있는닌지 여부 출력

vid.find('!') != -1

#4. 문자열 결합  
'a' + 'b'     # '+'기호가 어떤타입에 붙느냐에 따라 다르게 적용 됨

# =============================================================================
# #cf. 연결 연산자 in ORACLE , in R
# #in ORACLE
# # 'a' || 'b'
# 
# #in R
# # stringr::str_c('a','b')
# # paste(c('a','b'))    # str_c(c('a','b'), collapse = '')  둘이 같은? 
# # paste('a','b', sep='')
# 
# =============================================================================

#5. 패턴확인(포함여부)

'abcde'.find('c')   #로 -1이면 포함x, 그 외값이면 포함

'c' in 'abcde'      #문자열 in    #일치가 아니고 패턴의 포함여부임 

ename = ['smith','allen','ward','scott']  

(ename == 'smith') or (ename == 'allen')

#조건 확인의 경우에도 각자에 전달 안된다
#?
ename in ['smith', 'allen']

#?

# 6. 문자열 길이 확인
len('abcde')


# 예제 - 지폐환산 (돈입력받고 5만원~1000원 단위로 돈바꾸고 나머지 잔돈까지)

# 다 작성후 한번에 여러줄 잡고 f9 
# 또는 파이썬 IDLE - File - new file - 여기에 코드 써놓고 F5키로 한번에 실행 
# F5 누르면 저장하라고 하는데 디폴트 디렉토리 아닌 곳에 저장해도 상관은 없음
total = int(input('지폐로 교환할 돈은 얼마?'))

c_50000 = total/50000     #몫은 // 이 정확하긴 함  # / 하고 나중에 %d로 형변환 해서 값 잘려서 정상적으로 출력되는것임
rest1 = total%50000       
c_10000 = rest1/10000     # %/% in R 
rest1 %= 10000            # %% in R
c_5000 = rest1/5000
rest1 %= 5000
c_1000 = rest1/1000
rest1 %= 1000

print("50000원 짜리 ==> %d장" %c_50000)       #print("50000원 짜리 ==> c_50000" ) #c_50000이 문자로 읽혀서, 뒤로 빼놓고 전달하는 방식을 취하는 것임 
print("10000원 짜리 ==> %d장" %c_10000)
print("5000원 짜리 ==> %d장" %c_5000)
print("1000원 짜리 ==> %d장" %c_1000)
print("잔돈 ==> %d원" %rest1)


#개선 사항 : 출력 가지런하게

total = int(input('지폐로 교환할 돈은 얼마?'))

c_50000 = total/50000     
rest1 = total%50000       
c_10000 = rest1/10000     
rest1 %= 10000            
c_5000 = rest1/5000
rest1 %= 5000
c_1000 = rest1/1000
rest1 %= 1000

print("50000원 짜리  ==> %2d장" %c_50000)      
print("10000원 짜리  ==> %2d장" %c_10000)
print("5000원 짜리   ==> %2d장" %c_5000)
print("1000원 짜리   ==> %2d장" %c_1000)
print("\n잔돈        ==> %d원" %rest1)

#\n 아니면 print자체가 개행의 의미가 있으므로 print()를 1000원과 잔돈 사이에 넣거나

# =============================================================================
# #[참고 : 함수와 메소드의 차이] 
# func1(x,y,z)  = x + y + z
# 
# func1(data,x,y)        #함수
# 
# data.func1(x,y)        #메소드  
# 
# #파이썬이 메소드를 선호하는 이유
# 
# from pandas import Series
# s1 = Series([1,2,3,4])
# 
# s1.order      #'Series' object has no attribute 'order'  // order를 적용할 수있는 속성이 없음 
# s1.map?       #시리즈타입에 map #메소드 사용 설명
# 
# # 메소드는 데이터형을 앞으로 뺀 형태라서 , 해당 데이터에 메소드의 적용의 적합여부를 메소드 실행 전에 확인 가능
# # 함수는 파싱을 하면서 데이터가 적절하지 않음을 파악 
# 
# =============================================================================



# 논리연산자

v1 = 100

(v1 > 50) and (v1 < 150)
(v1 > 50) & (v1 < 150)

(v1 > 50) or (v1 < 150)
(v1 > 50) | (v1 < 150)

#R과 비교하자면 문자로 표현이 가능해짐

not(v1 > 50)           # !(v1 > 50) 은 안됨

#리스트 조건 전달
l1 = [1,2,3,4,5]
l1 > 3        #리스트와 정수의 대소비교 불가
l1 == 1       #같다. 같지 않다는 벡터연산 불가

# ============================================================================= 
# #사용자 정의함수 # 1. 중복된 코드 줄이기 2. 적용함수에 함수명을 인자로 전달하고자
# 
# # 정식적인 사용자 정의함수
# #문법적인 묶음을 {} 가 아닌 : 을 사용함 
# # def f1 : ~~~~
# #추후에 배울것임
# 
# ============================================================================= 

#  lambda 를 사용한 사용자 정의함수 생성(축약형)
#- 축약형 사용자 정의 함수 생성 문법
#- 간단한 return 만 가능
#- 복잡한 프로그래밍 처리 불가 
# 함수명 = lambda input:output 

#[예시1]
# y = x+1

f1 = lambda x : x + 1
f1(5)

#[예시2]
#인자 2개
f2 = lambda x,y : x + y
f2(2,10)

#[예시3]
#인자의 default 값
f3 = lambda x, y=0 : x + y
f3(2,10)

#주의 

f4 = lambda x = 0, y : x + y   #앞에 input value만 디폴트값 선언 불가, 디폴트값을 정한 x 뒤에 오는 모든 변수들은 디폴트값을 가져야함  
#정확히는 모르지만 인자를 순서대로 전달하는것과 관련이있으려나 싶긴함



# =============================================================================

# 적용 함수 : map 
# - 1차원 적용함수 : 데이터 셋의 함수의 반복 적용 가능
# - 결과 출력시 list함수 사용 필요
# - 함수의 추가인자 전달 가능

map(func, *iterables)        # *변수이름  <- 가변형 변수이다



l1 + 1  # 벡터연산 불가

map(f1, l1)  # <map at 0x1a5fbb5ffc8> 적용해서 메모리에 값을 올려놓고 출력은 x
list(map(f1, l1)) #이렇게 하면 제대로 출력 됨 
# ============================================================================= 

# 예제) 다음 두 리스트의 각 원소의 합을 출력
l1 = [1,2,3,4,5]
l2 = [10,20,30,40,50]

l1 + l2       #리스트 연산 불가, 확장

#cf.Series 는 가능

f2 = lambda x,y : x + y
list(map(f2,l1,l2))

# 예제) 다음 리스트의 각 원소를 대문자로 변경
l3 = ['a','b','ab']
l3.upper()          #리스트에 적용 불가

f3 = lambda x : x.upper()

list(map(f3,l3))

#[연습문제]
#다음의 리스트를 생성
L1 = [1,2,3,4]
L2 = [10,20,30,40]
L3 = ['서울','부산','대전','전주']
L4 = ['abc@naver.com', 'a123@hanmail.net']

# 1. L2의 L1승 출력, 10^1, 20^2, ...

f1 = lambda x,y : x**y

list(map(f1, L2, L1))

# 2. L4의 값에 '시'를 붙여 출력

f2 = lambda x :  x+'시'
list(map(f2,L3))

# 3. L4에서 이메일 아이디만 출력 

f3 = lambda x : x.split('@')[0]
list(map(f3, L4))

#---------another

f3_1 = lambda x : x[0:x.find('@')]
list(map(f3_1,L4))


# 추가 문제  :
# split의 구분기호를 입력받고, 위치값을 전달 받아 분리된 것중 선택하여 추출 

f5 = lambda x,y,z : x.split(y)[z]

l1 = ['a;b;c', 'A;B;C']
#적용전에 스칼라에 대한 test부터
f5('a;b;c', ';', 1)
f5(l1,';',1)             #불가

list(map(f5,l1,';',1))   #'int' object is not iterable

#test 예시1
f6 = lambda x,y=';',z='0' : x.split(y)[z]     #디폴트값을 넣어주면 달라지려나 해서 해보심
list(map(f6,l1,';',1))                        #안됨 


# =============================================================================
#  [참고 - 함수의 인자전달 방식 in R]
# 
# sapply(vect , 함수 , 나머지인자1, 나머지인자2)
# 함수 앞에 있는 vect 만 하나씩 찢어서 전달함. 
# =============================================================================
#

# map (함수, 인자1, 인자2, 인자3) 

#함수 뒤에 있는 인자들 모두 세트로 묶어서 전부 하나씩 전달함. 근데 test 예시1 의 ;가 1개라서 찢어서 전달이 안되서 에러가 발생 하는 것.


list(map(f6,l1,[';',';'],[0,0]))  #이렇게 갯수 맞춰주면 원하던 결과 얻을 수 있음
list(map(f6,l1,[';',';'],[0,1]))  #갯수만 맞으면 이렇게 다르게 전달 가능한 점의 장점


#[예제]
# 다음의 리스트이 각원소가  a로 시작하는지 여부 확인

l1 = ['abc','ABC','bcd']
l1.startswith('a')  #불가

list(map(lambda x:x.startswith('a'),l1))

list[[True, False, False]]            #R처럼 색인이 불가능, list색인 불가능 하기 때문에 조건 색인도 불가능 

Series(l1)[[True, False, False]]      #시리즈는 리스트색인 가능(조건색인도 가능)


# 리스트의 특징 추가 정리
# 1. 생성
l1 = [1,2,3,4]
l2 = [1,2,3,4,5,6]
# 2. 색인
l1[0]   # 차원 축소되어서 첫번째 원소
l1[0:0] # 빈리스트만 나옴
l1[0:1] # 첫번째 원소 차원 축소 없이 출력
l1[[0,2]] #리스트로 1번째 3번째 출력 불가
l1[-1]  
l2[::2]    #시작값: 끝값+1: 증가값

# 3. 연산
l1 + 1                          #불가      
list(map(lambda x:x+1, l1))     #이렇게 해야함

# 4. 확장
l1 + l2          #두 리스트의 합집합
l1.append(5)     # 5 라는 원소 추가, 출력없이 바로 수정됨 

l1 + [7]         #추가해서 출력해서 보여줌 , 원본이 수정되는건 아님
l2.extend([7])   #리스트값으로 추가하여 원본객체를 즉시수정  #출력 x
# 위에 2개의 원리는 비슷

# 5. 수정
l1[0] = 10 ;l1
l1[1] = [20,30] ; l1        #2번째 원소를 해당 리스트로 대체  #출력 [10, [20, 30], 3, 4, 5]
l1[2:4] = [300,400] ; l1    #각각 수정

l2[7] = 8                   #out of range 에러 발생, 정의되지 않은 position에 값 할당 불가

#in R
#빈 벡터의 i번째 값이 수정이 된다고 하는데 , 내 기억에는 안됨 
# v1 <- c()
# for ... {
#   v[i] <- ...        
# }


# 6. 삭제
del(l1[1])  #값 삭제 후 원본 수정
del(l1)     #객체 자체 삭제 가능    
#들어가는 값 자체를 지워줌 
l2 = []     #객체는 유지, 원소만 전체 삭제

# =============================================================================
#  함수,메소드의 설명 보기
#  함수의 경우
round?            #함수 설명 나옴  # 왜 지랄인지 모르겠는데, 그냥 줄실행은 안되고 글자 블록잡아주고 f9눌러야 되네
#  메소드의 경우
append?           #메소드라서 설명 안나옴
l1.append?        #메소드는 적용할 데이터형식을 붙여서 질문을 해야 설명 찾아준다
# =============================================================================

round?
#왜인지는 모르겠는데 물음표 뒤에 스페이스로 빈공간만 들어가도 출력 안됨



# ============================================================================= 
#  실습문제  
# =============================================================================

# 1. 문자열, 찾을 문자열, 바꿀 문자열을 입력 받아 변경한 결과를 아래와 같이 출력
# 전 :
# 후 :

x = input("문자열 교체  \n 문자열 :")
y = input('찾을 문자열 :')
z = input('바꿀 문자열 :')

after_str = x.replace(y,z)
print('전 : %s' % x)
print('후 : %s' % after_str)

# 2. 이메일 주소를 입력받고 다음과 같이 출력
# 아이디 : a1234
# 메일엔진 : naver

e_mail = input('이메일 입력해주세요:')

print('아이디 : %s' %e_mail.split('@')[0])
print('메일엔진 : %s' %e_mail.split('@')[1].split('.')[0])

print('%s'% 'http://kic.com/'+e_mail.split('@')[0])

# 3. 2번을 활용하여 다음과 같은 홈페이지 주소 출력
# http://kic.com/a1234


# 4. num1='12,000' 의 값을 생성 후, 33으로 나눈 값을 소숫점 둘째짜리까지 표현

num1 = '12,000'
num1 = int(num1.replace(',', ''))
 
round(num1/33,2)

print('%.2f' % num1/33)       #이건 왜 안될까욤

# 5. 다음의 리스트 생성 후 연산
ename = ['smith','allen','king'] 
jumin = ['8812111223928','8905042323343','90050612343432']
tel=['02)345-4958','031)334-0948','055)394-9050','063)473-3853']
vid=['2007(1)','2007(2)','2007(3)','2007(4)']

# 1) ename에서 i를 포함하는지 여부 확인
f_find_judge = lambda x : x.find('i') != -1        # y라는 문자로 'i' 말고 다른 문자도 하고 싶지만 map에 적용되면 문제가 될수 있음

list(map(f_find_judge,ename))

# 2) jumin에서 성별 숫자 출력

f_jumin_gender = lambda x:x[6]

list(map(f_jumin_gender,jumin))

# 3) ename에서 smith 또는 allen 인지 여부 출력 [True,True,False]

list(map(lambda x:(x=='smith')or (x=='allen'),ename))

# 4) tel에서 다음과 같이 국번 XXX 치환 (02)345-4958 => 02)XXX-4958)

f_xxx = lambda x: x.replace(x[x.find(')')+1:x.find('-')],'XXX')

list(map(f_xxx, tel))

'abcde'.find('c')  # 2

# 5) vid 에서 각각 년도와 분기를 따로 저장

f_year = lambda x : x.split('(')[0]
year = list(map(f_year, vid))


f_quar = lambda x : x[x.find('(')+1:x.find(')')]
quar = list(map(f_quar,vid))

year 
quar

f_int = lambda x: int(x)
year = list(map(f_int, year))
quar = list(map(f_int, quar))