ilovechoonsik
[STARTERS 4기 TIL] 중간 평가 대비 Python, SQL 복습 (230309,10) 본문
📖 오늘 내가 배운 것
중간 평가 대비해서 Python, SQL 헷갈리는 개념들 총 정리!
1.1 데이터와 정보
구분 | 내용 | 예시 |
데이터 | 객관적 사실을 수집하여 모아놓은 자료 | 블로그 방문기록 |
정보 | 데이터 가공하여 의미 도출된 것 | 일일 방문자 수, 성별 방문자 수, 시간대별 방문자 수 |
지식 | 정보에 개인적 경험을 결합시킨 새로운 지식 | 방문자가 많은 성별/나이에 대한 원인 파악 |
지혜 | 지식에 아이디어가 결합된 창의적 산물 | 방문자가 많은 성별/나이를 타겟으로 운영 전략 수립 |
1. Python
1 람다 표현식
- 매개변수와 수식으로 이루어진 함수!
lambda 매개변수1, 매개변수2, .... : 수식
2.1 subset 생성할 때 데이터 찾는, 뽑는 방법들
정규식!
라이브러리 불러오기
import re
메타문자
[ ] |
문자클래스 | [ ] 사이의 문자들과 매치 |
- | [From - To] | ex) [a-zA-Z] = 알파벳 모두 [0-9] = 모든 숫자 |
\d | 숫자 | = [0-9] |
\D | 숫자가 아닌 것 | = [^0-9] |
\s | 공백 | = [ \n\t\r\f\v] |
\S | 공백이 아닌 것 | = [^ \n\t\r\f\v] |
\w | 문자+숫자+_ | = [a-zA-Z0-9_] |
\W | 문자+숫자+_가 아닌 것 | =[^a-zA-Z0-9_] |
\ | 이스케이프 \와 함께 사용하면 일반 문자로 인식된다. |
|
. | \n를 제외한 모든 문자 | . = 모든 문자 |
[.] | []안에 .을 넣을 경우 그냥 '.'으로 인식 | [.] = 온점. |
| | or | |
^ | [] 바깥에 있을 경우 문자열의 시작과 매치 re.MULTILINE과 함께 쓰인다면 각 줄의 첫 문자와 일치 |
ex) ^a = a로 시작하는 |
[^] | [] 안에 있을 경우 [] 안의 정규표현식을 부정(not)한다. | ex) [^123] = 123이 아닌 것 |
$ | 문자열의 끝과 매치 re.MULTILINE과 함께 쓰인다면 각 줄의 끝 문자와 일치 |
|
* | 반복 (0 ~ ∞) | ex) mizy* = miz / mizy / mizyyyyy |
+ | 반복 (1 ~ ∞) | ex) mizy+ = mizy / mizyy |
? | 있어도 되고 없어도 된다. (0 또는 1) | ex) miz?y = miy / mizy |
{m} | m번 반복 | ex) mizy{3} = mizyyy |
{x, y} | x ~ y번 반복 | ex) mizy{2, 4} = mizyy / mizyyy |
(1) str, find 사용 - boolean indexing
# 서울시 구별 인구수 시각화
# 구 (11 로 시작하면 구 이기 때문에 str과 문자열 함수 find 사용하여 True로 반환받는다
# 그 값이 0 보다 클 것이기 때문에 조건을 걸고 boolean indexing!
df_seoul = df[df['행정구역'].str.find('구 (11')>0].copy()
# 구 별로 데이터를 그룹화 할 것이기 때문에 str-split을 통해 분리하고
# 그 중 str[1]번째 데이터를 가져온다,
df_seoul['구'] = df_seoul['행정구역'].str.split(' ').str[1]
# 동별 인구수 시각화
# 송파구 동별 인구수 시각화
df_song['동'] = df_song['행정구역'].str.split(' ').str[2].str.split('(').str[0]
df_song
(2) str.contains - boolean indexing
- 컬럼.str.contains(문자열) : 문자열이 포함된 데이터 추출
df_sido = df[df['행정구역'].str.contains('00000000')].copy()
(3) groupby 후 값없어서 생략된 연도 포함시키기
# 연도별 폭염일수 : 연도로 그룹핑하여 일수 카운트
df_seoul_hotday_count = df_seoul_hotday.groupby(df_seoul_hotday['일시'].dt.year)['일시'].count().to_frame()
# 폭염일이 없는 날을 포함시키기 위하여 1911~2020년의 연도 데이터프레임 생성
df_years = pd.DataFrame(range(1911,2021))
df_years.index = df_years[0]
df_years
# 연도별 폭염일수, 연도 데이터프레임 concat
df_seoul_hotday_count = pd.concat([df_seoul_hotday_count,df_years], axis=1)
df_seoul_hotday_count
(4) str -> int 자료형 일괄 변경
for i in range(101):
df.iloc[:,i] = df.iloc[:,i].str.replace(',','').astype('int64')
# df의 모든행의 100까지의 열을 각각 돌면서 , 을 뺴버리고 int64로 변경!
# 이걸 df.iloc[:,i] i에 맞춰 전부 적용
(5) index 가지고 데이터 삭제하기
계약이 된 상태인 행들만 보고 싶다! 해제사유발생한 행(notnull())은 날려줘라~
df = df.drop(index=df[df['해제사유발생일'].notnull()].index)
(6) 컬럼의 중복되지 않는 고유한 값들만 보려면 + 볼 수 있는 행 수 늘리기 (=저번 🥓)
df_store_class = df_store[['상권업종대분류명','상권업종중분류명','상권업종소분류명']].drop_duplicates().sort_values(['상권업종대분류명','상권업종중분류명','상권업종소분류명'])
# 상권업종 분류표 모두보기(최대 행 수 지정)
pd.options.display.max_rows=20
2. map 함수
- map은 리스트나 튜플의 각 요소를 지정된 함수로 처리해 주는 함수이다.
- 원본리스트를 변경하지 않고 새 리스트를 생성한다.
- list(map(함수, 리스트))
- tuple(map(함수, 튜플))
- 람다 표현식에 조건부 표현식 사용
- lambda 매개변수들 : 식1 if 조건식 else 식2
# 아래 리스트에서 짝수는 float로 바꾸고, 홀수는 str로 바꾸기
l1 = [1,2,3,4,5,6,7,8,9,10]
list(map((lambda x:float(x) if x%2==0 else str(x)),l1))
# 들어온 매개변수 x가 짝수라면 -> float(x)
# else -> str(x)
['1', 2.0, '3', 4.0, '5', 6.0, '7', 8.0, '9', 10.0]
3. 클래스, 객체 class
4. 데이터 프레임 만드는 방법
1. 리스트로 만들기 pd.DataFrame(2차원리스트, columns=컬럼리스트, index=인덱스리스트)
2. 딕셔너리로 만들기 pd.DataFrame(딕셔너리, index=인덱스리스트)
3. csv, excel 등등 문서 읽어오
미리 보기
- head(n) : 가장 위 n개 행
- tail(n) : 가장 뒤 n개 행
- sample(n) : 랜덤 n개 행 (비율로 보려면 frac=비율(0.n))
- nlargest(갯수,컬럼명) : 컬럼에서 지정한 개수의 높은 순 보기 (컬럼의 데이터가 숫자형일 때 사용할 수 있다.)
- nsmallest(갯수,컬럼명) : 낮은 순 보기 (컬럼의 데이터가 숫자형일 때 사용할 수 있다.)
요약 보기
- shape : 행, 열 크기 보기
- len(데이터프레임) : 데이터 갯수 보기
- columns : 컬럼명 보기
- index : 인덱스 보기
- dtypes : 데이터 자료형 보기 (판다스에서는 문자열의 데이터타입이 object)
- info() : 데이터프레임 정보 보기 (데이터프레임의 총 샘플 갯수, 컬럼 수, 컬럼 별 정보 등)
- 컬럼.unique() : 컬럼의 유니크한 데이터 뽑기 (개수는 nuique)
- 컬럼.value_counts() : 컬럼의 유니크한 값의 갯수
- describe() : 요약통계 보기
5 조건에 따라 데이터 추출하기
- boolean index '&' , '|' ,'~', '^'
- 컬럼.isin(값리스트)
- 컬럼.isnull() --> 해당 컬럼의 값이 null인 데이터 추출
- 컬럼.notnull() --> 해당 컬럼의 값이 null이 아닌 데이터 추출
6. 행 데이터 추출
loc | iloc | ||
df.loc[인덱스] | 시리즈 형태로 추출 | df.iloc[행번호] | 시리즈 형태로 추출 |
df.loc[인덱스리스트] | 데이터프레임 형태로 추출 | df.iloc[행번호리스트] | 데이터프레임 형태로 추출 |
df.loc[인덱스명슬라이스] | 행 전체 추출, 슬라이스 시에도 인덱스 이름 사용해야 함 | df.iloc[행번호슬라이스] | 슬라이스로 범위 지정 추출 |
# 인덱스가 i1,i3,i5인 행 추출 df.loc[['i1','i3','i5']] # 인덱스가 i3인 행을 데이터프레임 형태로 추출 df.loc[['i3']] # 인덱스가 i3인 행 이후의 모든 행 추출 df.loc['i3':] |
# 짝수 행번호의 데이터 추출 df.iloc[::2] # 홀수 행번호의 데이터 추출 df.iloc[1::2] # 1,3,5행 슬라이스 df.iloc[1:6:2] # 마지막 2개 행 추출하기(리스트) df.iloc[[-2,-1]] |
7. 행, 열 데이터 추출
행 데이터 추출에서 사용했던 기법을 ,로 구분시켜 컬럼에 대해서도 사용가능
loc[인덱스명, 컬럼명] (리스트 및 슬라이싱도 가능)
iloc[인덱스 번호, 컬럼 번호] (리스트 및 슬라이싱도 가능)
loc | iloc | ||
# 인덱스 i1의 kor점수 df.loc['i1','kor'] # 인덱스 i1,i3,i5의 name, kor df.loc[['i1','i3','i5'],['name', 'kor']] # 모든 행에서 'name','math' 가져오기 df.loc[:,['name','math']] # 인덱스 i7 이후의 행에서 'name' 가져오기(데이터프레임) df.loc['i7':,['name']] |
# 0번째 행, 0번째 열 df.iloc[0,0] # 1,3,4번째 행 name, eng df.iloc[[1,3,4],[0,2]] # 1,3,5번째 행 0,2번째 열 슬라이싱 df.iloc[1:6:2, :3:2] # 마지막행 1,3열 슬라이싱 df.iloc[-1,1:4:2] # 모든행, 1,2열 df.iloc[:,[1,2]] |
8 열 추가/삭제/변경
표로 열과 행이 각각 어떤 경우에 어떤 함수 쓰는지 정리 -> append, rename, drop
사용법 | 예시 | |
열 추가 | df[column] = 추가할데이터 # 컬럼이 존재하면 추가, 존재하지 않으면 수정된다. |
# 학생 번호 추가하기 (1부터 시작하여 1씩 증가) df['no'] = range(1,len(df)+1) |
열 수정 | df[column] = 수정할데이터 | # 학생 번호 수정하기 (100부터 시작하여 1씩 증가) df['no'] = df['no']+99 |
열 삭제 | df.drop(columns=삭제할컬럼리스트, inplace=True) # 존재하지 않는 열은 삭제할 수 없다. |
# no, sum column 삭제 df.drop(columns=['no','sum'], inplace=True) |
컬럼명 한번에 수정 | df.columns = 컬럼명리스트 # 컬럼명리스트의 항목 수는 컬럼 수와 동일해야한다. |
#'이름','국어','영어','수학' df.columns=['이름','국어','영어','수학'] |
특정 컬렴명만 수정 | df.rename(columns={'현재컬럼명1':'바꿀컬럼명1','현재컬럼명2':'바꿀컬럼명2',...}) | # 이름-->성명 df = df.rename(columns={'이름':'성명'}) |
9 행 추가/삭제/변경
사용법 | 예시 | |
행 추가 후 인덱스 재지정 | df.append(추가할데이터, ignore_index=True) # 추가할 데이터는 딕셔너리 형태로 전달 : {컬럼1:값1, 컬럼2:값2,...} # 데이터프레임의 끝에 행 추가 # 기존 인덱스는 무시하고, 인덱스가 새롭게 생성된다. |
new_value = {'name':'Python','kor':80,'eng':90,'math':100} df = df.append(new_value, ignore_index=True) # ignore_index=True 기존 인덱스 무시하고 새로 생성하겠다는 뜻 |
인덱스 지정하여 추가/수정 | df.loc[인덱스] = 추가할데이터 df.loc[인덱스] = 수정할데이터 # 인덱스가 존재하면 해당 인덱스의 데이터가 수정된다. # 인덱스가 존재하지 않으면 데이터프레임의 끝에 데이터가 추가된다. |
# 인덱스 35에 추가 df.loc[35] = ['aaa',70,80,90] # 인덱스 30에 추 df.loc[30] = ['ccc',60,70,80] |
행 삭제 | df.drop(index=[삭제할인덱스리스트], inplace=True) | # 30,34,35 삭제 df.drop(index=[30,34,35], inplace=True) |
전체 인덱스명 변경 | df.index = 인덱스명리스트 인덱스명리스트의 항목 수는 인덱스 수와 동일해야한다. |
# range(100,3100,100) df.index = range(100,3100,100) |
특정 인덱스명 변경하 | df.rename(index={'현재컬럼명1':'바꿀컬럼명1','현재컬럼명2':'바꿀컬럼명2',...}) | # 100-->'a', 200-->'b' df.rename(index={100:'a',200:'b'}, inplace=True) |
10 함수로 컬럼의 데이터 변경하기
- 컬럼.apply(함수명)
- 컬럼.apply(함수명, 매개변수명=매개변수값)
매개변수명을 명시해주어야 한다. - 적용할 함수가 미리 정의되어 있어야 한다.
예시1
# 1. df['math']의 모든점수에 5점 더하기.(--> 105점?)
df_copy['math'] = df.math+5
df_copy
# 2. df['math']의 모든점수에 5점 더하기. 100점이 넘을 수 없다.
def plus5(x):
score = x+5
if score>=100:
score=100
return score
df_copy['math'] = df['math'].apply(plus5)
df_copy
# plus5의 매개변수 x는 df['math'] 데이터를 받는 매개변수!
# 때문에 apply 할 때는 함수명만 전달해주면 된다~
11 행/열 데이터 집계하기
- 데이터프레임.apply(함수명, axis=0) : 열단위로 함수가 적용된다.
- 데이터프레임.apply(함수명, axis=1) : 행단위로 함수가 적용된다.
# 합계 구하기
def get_sum(x):
return x.sum()
# 행, 열 단위 데이터가 들어오면 합계 구해서 return
행 단위 적용 - 학생 별 점수 합계
df_copy['sum'] = df.apply(get_sum, axis=1)
열 단위 적용 - 과목 별 점수 합계
df_copy.loc['sum'] = df.apply(get_sum, axis=0)
12 결측치
NaN, 값이 없는 것! : 통계값을 구할 때 데이터의 개수에 영향을 끼치므로 적절한 처리 필요
df.isnull().sum() : 널값 개수
df.info() : 전체 정보
dropna() : 삭제
fillna() : 대치
방법 | 문법 | 예시 | |
결측치 삭제 | df.dropna() : 결측치가 존재하는 모든 행 삭제 df.dropna(axis=1) : 결측치가 존재하는 모든 열 삭제 |
||
결측치 대치 |
특정 값으로 | df.fillna(특정값) | df.fillna(0) -> 전부 0 으로 |
이전 값으로 | df.fillna(method='ffill') | 이전 값 없으면 대치 X, 그대로 NaN | |
다음 값으로 | df.fillna(method='bfill') | 다음 값 없으면 대치 X, 그대로 NaN | |
컬럼별로 대치할 값 지정 | df.fillna({'컬럼명1':값1, '컬럼명2':값2,...}) | df.fillna({'A':0,'B':1,'C':2,'D':3}) |
📌 결측치가 포함된 데이터의 통계값?
- 결측치는 없는 데이터로 간주된다.
df['A'].mean() # 3.0
df['B'].mean() # 3.0
13 자료형 종류
int64 : 정수형
float64 : 실수형
bool : 부울형
object : 문자열
category : 카테고리
datetime64 : 날짜, 시간
14 자료형 처리
방법 | 문법 | 예시 |
자료형 확인 | 데이터프레임 : df.dtypes 시리즈 : series.dtype 시리즈 데이터 타입 혼합 -> object 시리즈 정수와 실수 혼합 -> float64 |
시리즈의 경우 : df['int'].dtype 혼합의 경우 : type(df.loc[행,열]) |
자료형 변환 | df.astype('자료형') series.astype('자료형') |
전체 자료형 변환 : df = df.astype('자료형') object에서 int로 : df = df.astype('float').astype('int') object인 데이터를 바로 int로 바꾸는 건 불가능! float으로 바꾸고 int로 넘긴다 특정 컬럼의 자료형 변환 : df['col'] = df['col'].astype('자료') |
# 숫자형으로 변환 | pd.to_numeric(col, errors='ignore') : 숫자로 변경할 수 없는 값이 있으면 작업하지 않음 pd.to_numeric(col, errors='coerce') : 숫자로 변경할 수 없는 값이 NaN으로 설정 pd.to_numeric(col, errors='raise') : 숫자로 변경할 수 없는 값이 있으면 에러발생(default) |
- pd.to_numeric(s2, errors='ignore') 0 1.0 1 2 2 a dtype : object - pd.to_numeric(s2, errors='coerce') 0 1.0 1 2.0 3 NaN dtype : float64 |
# 시계열 데이터로 변환 | pd.to_datetime(col) | - astype df['출생'] = df['출생'].astype('datetime64') - to_datetime df['사망'] = pd.to_datetime(df['사망']) |
(2) 카테고리형 다루기
목적 | 사용법 | 예시 |
카테고리형으로 변환 | 컬럼.astype('category') | # 자료형 변환하기 df['grade'] = df['grade'].astype('category') |
카테고리 요소 이름 바꾸기 | 컬럼.cat.categories = 카테고리리스트 | # 1234 등급을 ABCD로 df['grade'].cat.categories = ['A','B','C','D'] |
카테고리 추가 | 컬럼.cat.set_categories(카테고리리스트) | # F 끼워넣기 df['grade'] = df['grade'].cat.set_categories(['A','B','C','D','F']) |
데이터 용량을 절약할 수 있다는 장점!
(1) 시계열 데이터?
Pandas에서 날짜와 시간을 다루는 자료형
목적 | 사용법 | 예시 |
컬럼을 datetime 자료형으로 변경 | pd.to_datetime(컬럼) | # 출생, 사망 컬럼을 datetime 자료형으로 변경하기 df['출생'] = pd.to_datetime(df['출생']) |
연, 월, 일 분기 추출 | 컬럼.dt.year 컬럼.dt.month 컬럼.dt.day 컬럼.dt.quarter |
year : 1962, 1953 ... month : 1, 2, 3 ... day : 20, 21 ... quarter : 1,2,3,4 |
날짜 계산 | 긴 날짜 - 짧은 날 | # 연산 시 최소 단위로 결과값 반환 df['생존일수'] = df['사망']-df['출생'] : 24213 days # 생존기간 컬럼 만들기 df['생존기간'] = df['사망'].dt.year-df['출생'].dt.year : 56 |
요일, 월이름 추출 | 컬럼.dt.strftime('%a') : 요약된 요일이름 컬럼.dt.strftime('%A') : 긴 요일이름 컬럼.dt.strftime('%w') : 숫자요일(0:일요일) 컬럼.dt.strgtime('%b') : 요약된 월이름 컬럼.dt.strftime('%B') : 긴 월이름 |
%a : Thu, Fri ... %b : Jan, Feb ... |
datetime 자료형을 인덱스로 만들어 사용 | df.index = df['컬럼'] | # 출생 컬럼을 인덱스로 만들기 df.index = df['출생'] |
# 1955년 출생한 사람 데이터 추출하기
df.loc['1955']
# 1955년 2월 출생한 사람 데이터 추출하기
df.loc['1955-02']
15 데이터프레임 연결하기 concat
- pd.concat(데이터프레임리스트)
데이터프레임이 동일한 컬럼명 기준으로 행으로 연결된다. - pd.concat(데이터프레임리스트, axis=1)
데이터프레임이 동일한 인덱스 기준으로 열로 연결된다
7.2 공통된 열 기준으로 연결하기 merge
- pd.merge(left,right,on=기준컬럼,how=연결방법)
- 2개의 데이터프레임을 연결한다.
16 행/열 형태 변경 (melt, pivot)
행을 열로(값으로) 보내기! melt
- 데이터프레임.melt()
- pd.melt(데이터프레임)
열을 행으로 보내기! pivot
- df.pivot( index=인덱스로 사용할 컬럼, columns=컬럼으로 사용할 컬럼, values=값으로 사용할 컬럼 )
샘플데이터 생성
# 샘플데이터
df = pd.read_csv('data/scores.csv')
df = df.head(2)
df = df.melt(id_vars = 'name', var_name='subject',value_name='score')
def get_grade(x):
if x>=90: grade='A'
elif x>=80: grade='B'
elif x>=70: grade='C'
elif x>=60: grade='D'
else: grade='F'
return grade
df['grade'] = df['score'].apply(get_grade)
df = df.sort_values('name')
df
예시 1. 인덱스=name, 컬럼=subject, values=score
df.pivot(index='name', columns='subject',values='score')


적용 전 / 적용 후
예시 2. 인덱스=name, 컬럼=subject, valyes=[score, grade]
df.pivot(index='name', columns='subject',values=['score','grade'])


적용 전 / 적용 후
17 분석하며 사용
중복 제거 drop_duplicates
# 고유 특정 문자열이 포함되어있는지 확인 : .str.contains(문자열)품목 목록
df_items =df[['품목 번호','품목 이름']].drop_duplicates()
df_items = df_items.sort_values('품목 이름')
# 고유한 시장/마트의 목록
df_market = df[['시장/마트 번호','시장/마트 이름','자치구 이름','시장유형 구분(시장/마트) 이름']].drop_duplicates()
문자열 포함 확인 str.contains
# 2021-06 데이터 이용
df_sam = df[ (df['품목 이름'].str.contains('삼겹살')) & (df['년도-월']=='2021-06') & (df['실판매규격'].str.contains('600g')) ]
df_sam
to_datetime 에러 처리 errors = coerce 에러 유발하는 녀석들 NULL로
# errors = coerce 사용해서 에러 유발하는 녀석들 NULL 처리
df['반납일시'] = pd.to_datetime(df['반납일시'], errors='coerce')
- datetime 함수
dt.date
dt.year 등으로 뽑기
#요일컬럼 추가 : strftime('%a') -> 14.서울시 공공자전거
df_207['대여요일'] = df_207['대여일시'].dt.strftime('%a')
18 시각화
plt, subplot : https://ilovechoonsik.tistory.com/216?category=1158849
그래프 강조, seaborn : https://ilovechoonsik.tistory.com/218
목적에 따라 사용되는 그래프 종
시각화 목적 | 그래프 종류 |
시간에 따른 변화 | plot |
크기 비교/순위 | bar(h) |
데이터 비율 | pie |
데이터 분포 표현 | histogram, boxplot, violinplot, heatmap |
데이터 관계 | scatter |
지역 | hexagonal, folium |
산점도
plt.scatter(x,y,s=,c=,cmap)
s : 사이즈, 사이즈 기
c : 색상 or cmap 사용 시 cmap 색상 기준
히트맵
plt.pcolor(df, cmap=~)
sns.heatmap
히스토그램
plt.hist
2.2 bar
용도 | 파리미터 |
막대 폭 지정 | 세로 width = 0~1사이의 실수(default:0.8) 가로 height = 0~1사이의 실수(default:0.8) |
막대 테두리 | edgecolor = 테두리 색상 linewidth = 테두리 두께 |
막대 색상 | color |
막대 패턴 | hatch='/', '', '|', '-', '+', 'x', 'o', 'O', '.', '*' (패턴기호 개수로 밀도 조정) |
막대 위치 지정 | align = center/edge (default=center) edge로 지정하면 막대의 왼쪽 끝과 틱을 맞춘다. 막대의 오른쪽 끝과 틱을 맞추려면 width를 음수로 지정한다. |
19 subplot
4가지 방법
1. 도화지 그려놓고 냅다 때려박기
2. 도화지 그려놓고 위치 지정하기
3. 행,열로 쪼개서 그리기 [행번호][열번호] 접근
4. 객체 생성할 때 (전체행개수, 전체열개수, 순서 지정)
1. 냅다 떄려박기
1) 전체 그래프의 크기를 정한다. (정하지 않으면 디폴트 크기로 지정된다.)
plt.figure(figsize=(x사이즈, y사이즈))
2) 그래프를 그려 넣을 격자를 지정한다.(전체행개수,전체열개수,그래프순서)
plt.subplot(전체행개수,전체열개수,그래프순서)
3) 격자에 그래프를 하나씩 추가한다.
plt.figure(figsize=(9,6))
plt.subplot(221)
plt.plot(df1['x'],df1['y'],'o')
plt.subplot(222)
plt.plot(df2['x'],df2['y'],'+')
plt.subplot(223)
plt.plot(df3['x'],df3['y'],'*')
plt.subplot(224)
plt.plot(df4['x'],df4['y'],'d')
1. pyplot으로 subplot 그리기
- figure 객체를 변수에 받는다.
- figure객체의 suptitle(제목)메소드로 전체 그래프의 제목을 표시한다.
- figure객체의 tight_layout()메소드로 그래프의 간격, 너비를 최적화한다.
# plt.figure() : figure(도화지) 객체 생성
# fig에 전체 도화지 객체 담기
fig = plt.figure(figsize=(9,6), facecolor='ivory')
plt.subplot(221)
plt.plot(df1['x'],df1['y'],'o')
plt.title('ax1')
plt.subplot(222)
plt.plot(df2['x'],df2['y'],'+')
plt.title('ax2')
plt.subplot(223)
plt.plot(df3['x'],df3['y'],'*')
plt.title('ax3')
plt.subplot(224)
plt.plot(df4['x'],df4['y'],'d')
plt.title('ax4')
fig.suptitle('Amscombe', size=20)
fig.tight_layout() # figure 간 간격 최적화
2. 위치, 크기 지정해서 subplot 그리기
2.1 figure, axes
- figure : 그림이 그려지는 캔버스
- axes : 하나의 그래프 (각 subplot을 그리기 위한 틀!) 각각 객체로 받아 사용
2.2 위치, 크기 지정하여 그래프 그리기
1) figure 객체를 생성한다.
fig = plt.figure(figsize=(가로길이,세로길이))
2) figure객체의 add_axes 메소드로 위치와 크기를 지정하여 axes 객체를 생성한다.
ax1 = fig.add_axes([left, bottom, width, height])
# left, bottom : 상대적인 시작 위치 (figsize의 크기를 1이라고 했을 때 상대적 위치)
# width, height : 상대적인 크기(figsize의 크기를 1이라고 했을 때 상대적 크기)
3) axes에 그래프를 그린다.
ax1.plot(x,y)
4) axes에 제목 추가.
ax1.set_title(제목)
# plt~ 해서 그렸던 그래프와 다르게 객체 지향으로 그린다.
2.3 위치와 크기를 자유롭게 지정하여 axes 객체 만들기
- add_axes를 사용하면, 서브플롯의 크기와 위치를 자유롭게 지정할 수 있다.
- 그래프를 겹쳐그리거나, 크기가 각각 다른 그래프를 그릴 수 있다.
# 1)figure 객체를 생성한다.
fig = plt.figure(figsize=(8,6))
# 2) figure객체의 add_axes 메소드로 위치와 크기를 지정하여 axes 객체를 생성한다.
ax1 = fig.add_axes([0,0.5,0.4,0.4])
ax2 = fig.add_axes([0.5,0.5,0.4,0.4])
ax3 = fig.add_axes([0,0,0.4,0.4])
ax4 = fig.add_axes([0.5,0,0.4,0.4])
# 3) axes에 그래프를 그린다.
ax1.plot(df1['x'],df1['y'],'o')
ax2.plot(df2['x'],df2['y'],'r^')
ax3.plot(df3['x'],df3['y'],'k*')
ax4.plot(df4['x'],df4['y'],'m+')
# 4) axes에 제목 추가.
ax1.set_title('ax1')
ax2.set_title('ax2')
ax3.set_title('ax3')
ax4.set_title('ax4')
3. axes를 행, 열로 쪼개어 서브플롯 그리기
- plt.subplots() 함수를 호출하면 figure, axes 객체를 생성하여 튜플 형태로 반환한다.
fig, ax = plt.subplots()
1) axes 객체를 행,열로 쪼개어 생성하기
fig, ax = plt.subplots(nrows=행개수, ncols=열개수,figsize=(가로사이즈,세로사이즈))
2) axes[행번호][열번호] 형태로 접근하여 그래프 그리기
3) 서브플롯간 축을 공유할 수 있다.
sharex=True, sharey=True
3.1 예시
# 1) axes 객체를 행,열로 쪼개어 생성하기
# 3) 서브플롯간 축을 공유할 수 있다.
fig,ax = plt.subplots(nrows=2, ncols=2, figsize=(8,6), sharex=True, sharey=True, facecolor='pink')
# 2) axes[행번호][열번호] 형태로 접근하여 그래프 그리기
ax[0][0].plot(df1['x'],df1['y'],'o')
ax[0][1].plot(df2['x'],df2['y'],'^')
ax[1][0].plot(df3['x'],df3['y'],'*')
ax[1][1].plot(df4['x'],df4['y'],'d')
# 4) 각 그래프에 제목 추가
ax[0][0].set_title('ax1')
ax[0][1].set_title('ax2')
ax[1][0].set_title('ax3')
ax[1][1].set_title('ax4')
# 4) 각 그래프에 그리드 추가
ax[0][0].grid(ls=':')
ax[0][1].grid(ls=':', color='pink')
ax[1][0].grid(ls=':', color='skyblue')
ax[1][1].grid(ls=':', color='green', alpha=0.5)
# 6) 그래프 전체 제목
fig.suptitle('Anscombe', size=20)
# 7) 그래프 간격, 크기 최적화
fig.tight_layout()
4. 전체 행 열과 그래프 순서에 따라 서브플롯 그리기
1) figure 객체를 생성한다.
fig=plt.figure()
2) 서브플롯을 그릴 axes 객체를 생성한다.
ax = fig.add_subplot(전체행개수,전체열개수,순서)
3) axes 객체에 그래프를 그린다.
ax.plot(x,y)
4) 축 공유하기 : 어떤 axes의 축을 공유할 것인지 지정한다.
sharex=axes객체, sharey=axes객체
4.1 예시
# 1) figure 객체를 생성한다.
fig = plt.figure(figsize=(9,6), facecolor='ivory')
# 2) 서브플롯을 그릴 axes 객체를 생성한다.
ax1 = fig.add_subplot(2,2,1)
ax2 = fig.add_subplot(2,2,2, sharex=ax1, sharey=ax1)
ax3 = fig.add_subplot(2,2,3, sharex=ax1, sharey=ax1)
ax4 = fig.add_subplot(2,2,4)
# 3) axes 객체에 그래프를 그린다.
# 4) 축 공유하기 : 어떤 axes의 축을 공유할 것인지 지정한다.
ax1.plot(df1['x'],df1['y'],'o', label='ax1')
ax2.plot(df2['x'],df2['y'],'^', label='ax2')
ax3.plot(df3['x'],df3['y'],'*', label='ax3')
ax4.plot(df4['x'],df4['y'],'+', label='ax4')
# 4) 틱 변경하기
ax4.set_xticks(range(1,20,1))
ax4.set_yticks(range(1,15,1))
# 5) 범례 표시하기
ax1.legend(loc=2)
ax2.legend(loc=2)
ax3.legend(loc=2)
ax4.legend(loc=2)
# 6) figure 제목 추가하기
fig.suptitle('Anscombe', size=20)
# 8) 그래프 크기,간격 최적화하기
fig.tight_layout()
plt.show()
# 그래프를 이미지로 저장하려면?
- fig.savefig(파일명, dpi=해상도)
- 해상도 default : 100
- str.find!
# 구 (11 로 시작하면 구 이기 때문에 str과 문자열 함수 find 사용하여 True로 반환받는다
# 그 값이 0 보다 클 것이기 때문에 조건을 걸고 boolean indexing!
df_seoul = df[df['행정구역'].str.find('구 (11')>0].copy()
- ,삭제 및 데이터 타입 한번에 변경
for i in range(101):
df.iloc[:,i] = df.iloc[:,i].str.replace(',','').astype('int64')
mutable vs immutable -> 주피터 깊은복사와얇은복사
- 얕은복사(shallow copy) : 객체의 메모리를 새로 할당. 하위 객체의 메모리는 그대로.
- 깊은복사(deep copy) : 객체와 하위 객체들의 메모리를 새로 할당.
2. SQL
DML : 데이터 조작어 - 검색 및 수정
SELECT INSERT UPDATE DELETE MERGE
DDL : 데이터 구조 다루기 - 구조 생성 변경 삭제
CREATE ALTER, DROP, RENAME
DCL : 데이터에 대한 권한 관리
GRANT REVOKE
아래 결과는?
-- NULL인 문자열 결합
SELECT CONCAT("전화번호: ", phone )
FROM customer;
-> NULL 결합시 결과값 NULL
내장함수 - 제곱
power(숫자, 제곱)
MOD(m,n)함수 : m을 n으로 나누어 나머지 값 반환
FLOOR(n) : 주어진 값보다 작거나 같은 최대 정수를 구하는 함수
CEIL(n) : 주어진 값보다는 크지만 가장 근접하는 최솟값을 구하는 함수
- 문자열 결합
CONCAT(문자열, 문자열,…): 공백 없이 문자열들만 결합한다.
CONCAT(구분자,문자열, 문자열,…): 구분자로 문자열을 결합한다
SELECT GROUP_CONCAT(묶을 컬럼 [, 데이터1, 데이터2 ...])
FROM 테이블명
- 컬럼에서 NULL이 아닌 모든 값을 콤마(,)로 합쳐 하나의 문자열로 가져오는 함수
- 컬럼에 추가로 데이터1,2를 붙인 다음에 결합
- 문자열 길이
LENGTH(문자열): 문자열의 Byte 길이
CHAR_LENGTH(문자열): 문자의 개수
- 대소문자
UPPER(문자열) : 대문자로 변환한다.
LOWER(문자열) : 소문자로 변환한다.
- 문자열 추출
-- 안녕하세요 문자열에서 2~3 번 인덱스 문자를 추출
select substring('안녕하세요', 2,3);
-- 안.녕.하.세.요 문자열에서 . 를 만난후 2번째 까지
select substring_index('안.녕.하.세.요', '.', 2);
-- 안.녕.하.세.요 문자열에서 . 를 만난후 뒤에서 -3번째 까지
select substring_index('안.녕.하.세.요', '.', -3);
SELECT LEFT('안녕하세요', 3);
SELECT RIGHT('안녕하세요', 3);
- 날짜 자료형 다루기
YEAR(날짜), MONTH(날짜), DAY(날짜), HOUR(시간), MINUTE(시간), SECOND(시간),
MICROSECOND(시간), LAST_DAY(날짜)
- 날짜/시간 증감 함수
date를 기준으로 INTERVAL expr 만큼 더하거나 뺀다
DATE_ADD(date,INTERVAL expr)
DATE_SUB(date,INTERVAL expr)
ADDDATE(date,INTERVAL expr),
SUBDATE(date,INTERVAL expr)
time를 기준으로 expr 만큼 더하거나 뺀다
ADDTIME(time, expr),
SUBTIME(time, expr)
날짜 date에 type 형식으로 지정한 expr값을 더하거나 뺀다
DATE_ADD()와 ADDDATE()는 같은 동작이고, DATE_SUB()와 SUBDATE()는 같은 의미이다
- 날짜/시간 사이의 차이와 월/요일/주 값
DATEDIFF(날짜1, 날짜2) 날짜1-날짜2의 차이를 반환
TIMEDIFF(시간1, 시간2 ) 시간1-시간2의 차이를 반환
DAYOFWEEK(날짜) 요일(1: 일~7: 토) 반환
MONTHNAME( ) 월의 영문(January ~December) 반환
DAYOFYEAR(날짜) 1년 중 몇 번째 날(1~366)인지를 반환
TIME_TO_SEC(시간) 시간을 초 단위로 반환
- 날짜 형식 반환
DATE_FORMAT(날짜,format) 날짜를 format 형식으로 반환한다.
- 데이터 형식 변환 함수
CAST(expression AS 데이터형식 [(길이)])
CONVERT(expression, 데이터형식 [(길이)])
BINARY
CHAR
DATE
DATETIME
TIME
SIGNED {INTEGER}
UNSIGNED {INTEGER}
• 구매 가격을 정수로 출력
SELECT CAST(AVG(saleprice) AS SIGNED INTEGER)
AS '평균 구매 가격' FROM orders;
SELECT CONVERT(AVG(saleprice), SIGNED INTEGER) AS '평균 구매 가격'
FROM orders;
SELECT CAST('2020-10-19 12:35:29.123' AS DATE)AS 'DATE'; > 2021-01-20
SELECT CAST('2020-10-19 12:35:29.123' AS TIME)AS 'TIME'; > 10:10:30
SELECT CAST('2020-10-19 12:35:29.123' AS DATETIME)AS 'DATETIME'; 2021-~~~ 10:10 ~~
- 상관 부속질의(correlated subquery)
• 상위 부속질의의 투플을 이용하여 하위 부속질의를 계산함.
즉 상위 부속질의와 하위 부속질의가 독립적이지 않고 서로 관련을 맺고 있음.
예) 출판사별로 출판사의 평균 도서 가격보다 비싼 도서를 구하시오.
SELECT b1.bookname
FROM Book b1
WHERE b1.price > (SELECT avg(b2.price)
FROM Book b2
WHERE b2.publisher=b1.publisher);
- From 절에서 서브쿼리 사용하기
• FROM절 안에 쓴 서브 쿼리의 결과는 뷰처럼 취급
• 인라인 뷰(Inline View)라고도 한다.
- NULL 관련 처리 방안
IFNULL(컬럼명, 컬럼 NULL일 때 사용 값)
- NULL 값이란?
아직 지정되지 않은 값
NULL 값은 ‘0’, ‘’ (빈 문자), ‘ ’ (공백) 등과 다른 특별한 값
NULL 값은 비교 연산자로 비교가 불가능함
NULL 값의 연산을 수행하면 결과 역시 NULL 값으로 반환됨
집계 함수를 사용할 때 null 주의할 점
‘NULL+숫자’ 연산의 결과는 NULL
집계 함수 계산 시 NULL이 포함된 행은 집계에서 빠짐
해당되는 행이 하나도 없을 경우
SUM, AVG 함수의 결과는 NULL이 되며, COUNT 함수의 결과는 0
- IF
IF(조건, 참일때 값, 거짓일때 )
SELECT IFNULL(column_name, '대체할 값') FROM [table_name];
SELECT IFNULL(column_name, IFNULl(column_name, '대체할 값')) FROM [table_name];
SELECT ISNULL(is_discount, 0) AS result
CASE value 혹은 조건 WHEN value1 THEN result1 WHEN value2 THEN result2 … [ELSE else_result] END
- JOIN
INNER - 겹치는 것
LEFT, RIGHT - 한 쪽에만 존재하는 거 + 겹치는 경우 해당 부분만
CROSS - 상호 조인이라고도 불리며, 테이블의 모든 행들과 조인 대상 테이블의 모든 행을
조인시키는 기능이다.
- 집합 연산자는 두 개의 SELECT 문의 결과에 대해 합집합, 교집합, 차집합을 구하는 연산자이다.
• 결합하는 SELECT문의 결과는 열의 수나 각각의 데이터형이 똑같아야 한다.
연산자 설명
Table1 UNION Table2 Table1 과 Table2의 합집합
Table1 MINUS Table2 Table1 과 Table2의 차집합
Table1 INTERSECT Table2 Table1 과 Table2의 교집합
* MySQL에는 MINUS, INTERSECT 연산자 지원이 안되고 IN과 NOT IN 연산자로 구현할 수 있다
- WITH , VIEW 차이
WITH는 단일사용할 쿼리내에 정의되어 있는경우, 해당 쿼리문안에서만 실행된다.
VIEW는 한번 생성하면 DROP할 때까지 계속 존재한다.
- VIEW 생성 CREATE VIEW 구문 사용
CREATE VIEW v_orders AS
SELECT ordered, 0.custid, usrname, O.bookid, saleprice, orderdate
FROM ustomer C, Orders O, Book B
WHERE C.custid = O.custid and B.bookid = O.bookid;
DROP VIEW view_name
- 집계함수
• 값의 합,평균,개수,최대.최소 값에 대한 함수를 "집계함수"라 호칭함
SUM(속성이름) 속성 값들을 합계를 낸다
AVG(속성이름) 속성 값들의 평균을 낸다
COUNT({속성이름] | *}) 속성 혹은 모든 행의 개수를 센다
MAX(속성이름) 속성 값들 중 최대값을 산출한다
MIN(속성이름) 속성 값들 중 최소값을 산출한다
POWER(X, Y) X 값의 Y제곱을 계산한다
- 수치형 집계함수
ABS(숫자) 숫자 절대값 출력
CEILING(숫자) 숫자보다 크거나 같은 최소 정수 값
FLOOR(숫자) 숫자 값 보다 작은 정수 중 가장 큰 수! 실수는 무조건 버림, 음수일 경우는 제외
ROUND(숫자,자릿수) 숫자를 소수점 이하 자릿수에서 반올림(자릿수는 양수,0,음수)
GREATEST(숫자1,숫자2,...) 주어진 수 중 제일 큰 수 리턴
LEAST(숫자1,숫자2,...) 주어진 수 중 제일 작은 수 리턴
- 기간 집계함수
DAYOFYEAR(날짜) 일년을 기준으로 날짜까지의 날짜 수
DAYOFWEEK(날짜) 요일(1: 일~7: 토) 반환
WEEKOFYEAR(날짜) 날짜의 주 수(0~53) 를 반환한다. WEEK(date,3) 호환
YEARWEEK(날짜[, mode]) 주 범위 0~53 를 반환한다. mode(0~7)로 주 지정
MONTHNAME(날짜) 월의 영문(January ~December) 반환
WEEK(날짜[,mode]) 일년 중 몇 번째 주
QUARTER(날짜) 날짜가 4분기 중에서 몇 분기인지를 반환
DATE_FORMAT(날짜,format) dt 속성를 날짜형식 format으로 반환
- mode 값
0 Sunday 0-53 With a Sunday in this year
1 Monday 0-53 With 4 or more days this year
2 Sunday 1-53 With a Sunday in this year
3 Monday 1-53 With 4 or more days this year
4 Sunday 0-53 With 4 or more days this year
5 Monday 0-53 With a Monday in this year
6 Sunday 1-53 With 4 or more days this year
7 Monday 1-53 With a Monday in this year
- FORMAT -> 문자열 형태의 소수점 자리수 맞
- 데이터의 분포에 대한 함수 사용
• STD, STDDEV, VARIANCE 함수
내장함수 설명
STD(expr) expr 의 표준편차를 반환
VARIANCE(expr) expr 의 분산을 반환
- 순위 함수
RANK(속성) 공동 순위만큼 건너뜀 (ex: 1,2,2,4,5 ...)
DENSE_RANK(속성) 공동 순위를 뛰어넘지 않음 (ex: 1,2,2,3,4 ...)
ROW_NUMBER(속성) 공동 순위를 무시함 (ex: 1,2,3,4,5 ...)
- 순위 함수에 PARTION BY 사용
• 그루핑해서 순위를 매기고자 할 때
SELECT B.bookname,
ROW_NUMBER () OVER (PARTITION BY O.CUSTID ORDER BY O.SALEPRICE)AS RANKING
FROM BOOK B, ORDERS O
WHERE B.BOOKID=O.BOOKID
GROUP BY 1;
- 소계 ROLL UP
말 그대로 소계
GROUPBY ~ WITH ROLLUP
- exist
조건을 만족하는 데이터가 있으면 True. T/F 여부만 확인하여 T인 데이터만 추출
use bookstore;
SELECT *
FROM customer;
select mod(9,2) from dual;
SELECT CONCAT(username,':',phone)
FROM customer;
SELECT bookname, ":", publisher FROM book;
SELECT GROUP_CONCAT(username, ":", phone) AS "전화"
FROM customer;
SELECT substring('abcdef',2,3);
SELECT b1.bookname
FROM Book b1
INNER JOIN Book b2 ON b1.publisher = b2.publisher
WHERE b1.price > (SELECT avg(b2.price)
FROM Book b2
WHERE b2.publisher=b1.publisher);
SELECT username
FROM Customer
WHERE address LIKE '대한민국%'
OR custid IN (SELECT custid FROM Orders);
#25000원 이상 주문한 고개
SELECT c.username
, c.address
, b.bookname
FROM (SELECT custid, bookid, saleprice
FROM orders
WHERE saleprice >= 25000) o
INNER JOIN customer c ON o.custid = c.custid
INNER JOIN book b ON o.bookid = b.bookid;
#도서를 주문하지 않은 고객 id, 이름
SELECT *
FROM customer c
LEFT JOIN (SELECT DISTINCT custid
FROM orders) o ON o.custid = c.custid
WHERE o.custid IS NULL;
use bookstore;
#Q1. 가격이 가장 싼 책을 구매한 고객의 리스트를 구하시오.
SELECT *
FROM book;
SELECT username
FROM Customer c
INNER JOIN Orders o ON c.custid = o.custid
WHERE o.saleprice = (SELECT Max(price) FROM book);
#Q2. 출판사별 판매량 합계와 소계 구하기
SELECT IF(GROUPING(publisher), '소계', IFNULL(publisher,'-'))
, sum(saleprice)
FROM book b
INNER JOIN Orders o ON b.bookid = o.bookid
GROUP BY publisher WITH ROLLUP;
#Q3. 출판사 및 책이름 합치기
SELECT CONCAT(publisher,':',bookname)
FROM book;
#Q4. 배송 날짜가 주문 날짜 3일 뒤라고 가정할 때, orders 테이블에 deliverydate를 추가하시오.
SELECT *
, DATE_ADD(orderdate, INTERVAL 3 DAY) deliverydate
FROM orders;
#Q5. 책을 2권이상 산 고객의 이름과 구매총수량을 나타내시오
SELECT c.username
, COUNT(o.custid) cnt
, sum(saleprice) ts
FROM Orders o
INNER JOIN Customer c ON o.custid = c.custid
GROUP BY 1
HAVING cnt >= 2;
#Q6. salesprice를 만원 단위로 집계한다면?
SELECT (FLOOR(saleprice / 10000) * 10000) '1만원 단위'
, COUNT(*)
FROM Orders
GROUP BY 1
ORDER BY 1;
# 고객별 총 주문횟수, 총구매액, 평균구매액, 최소/최대구매액 구하기
# 구매하지 않은 고객 포함하기
# 구매하지 않은 고객은 집계 결과 0으로 표현하기
# 총 구매액 순으로 순위 매기기(RANK)
SELECT username AS 고객
, IFNULL(COUNT(o.orderid), 0) AS 주문횟수
, IFNULL(SUM(o.saleprice), 0) AS 총구매액
, IFNULL(ROUND(AVG(o.saleprice),1), 0) AS 평균구매액
, IFNULL(MIN(o.saleprice), 0) AS 최소구매액
, IFNULL(MAX(o.saleprice), 0) AS 최대구매액
, RANK() OVER (ORDER BY SUM(o.saleprice) DESC) 순위
FROM customer c
LEFT JOIN orders o ON c.custid = o.custid
GROUP BY username
ORDER BY 총구매액 DESC;
#고객별 총 구매 금액에 따라 고갱등급을 나눕니다
#60000보다 크면 최우수 고객
#40000보다 크면 우수 고객
#30000보다 크면 고객
# 등급이아니라 총 구매액에 따른 순위를 매기시오
# 등급이아니라 총 구매액에 따른 순위를 매기시오
SELECT username
, CASE
WHEN sum(saleprice) > 60000 THEN '최우수'
WHEN sum(saleprice) > 40000 THEN '우수'
WHEN sum(saleprice) > 30000 THEN '고객'
ELSE 'ab'
END AS 'ab'
, RANK() OVER (ORDER BY sum(saleprice) DESC) 순위
FROM Orders o
INNER JOIN Customer c ON o.custid = c.custid
GROUP BY 1;
##################################################
# 고객별 saleprice 랭킹
##################################################
select c.username
, b.bookname
, o.saleprice
, RANK() OVER(PARTITION BY username ORDER BY o.saleprice DESC) 순위
from orders o, customer c, book b
where o.custid=c.custid
and o.bookid=b.bookid;
##################################################
# 지역-도서별 판매 수량
# 지역별 판매수량 소계
##################################################
select substring_index(address,' ',1) as 지역,
b.bookname as 도서명,
count(*) 총판매수량 ,
sum(o.saleprice) 총판매금액
from customer c, orders o, book b
where c.custid = o.custid
and o.bookid = b.bookid
group by 1,2 WITH ROLLUP;
# 지역별 총판매금액 소계를 넣고 싶다 -> GROUP BY WITH ROLLUPX
💪🏻 좋았던 점, 앞으로 개선해야 할 점 (추가로 배워야 할 점)
📌 중간 평가를 대비하기 위해 지금까지 배웠던 내용을 전체적으로 복습했다!
기억 안 나는 부분이 생각보다 많았는데.. 복습을 지속적으로 수행해야 할 필요가 느껴졌다🐿️
#유데미, #유데미코리아, #유데미부트캠프, #취업부트캠프, #부트캠프후기, #스타터스부트캠프, #데이터시각화 #데이터분석 #태블로
'STARTERS 4기 🚉 > TIL 👶🏻' 카테고리의 다른 글
[STARTERS 4기 TIL] 프로젝트 기반 태블로 실전 트레이닝 #2 (230314) (0) | 2023.03.15 |
---|---|
[STARTERS 4기 TIL] 프로젝트 기반 태블로 실전 트레이닝 #1 (230313) (0) | 2023.03.13 |
[STARTERS 4기 TIL] Python 시계열 #2 (230308) (0) | 2023.03.09 |
[STARTERS 4기 TIL] Python 시계열 #1 (230307) (0) | 2023.03.08 |
[STARTERS 4기 TIL] Tableau 고급 #2 (230306) (0) | 2023.03.06 |