반응형

Gapminder 데이터셋은 세계 각 국가에 대한 시간에 따른 다양한 경제 지표 및 사회 지표를 수집한 데이터입니다. 이 데이터셋은 Gapminder Foundation에서 수집하고 제공하며, 세계의 국가들에 대한 주요 지표들의 시계열 데이터를 담고 있습니다. Gapminder 데이터셋에 포함된 주요 변수들로는 국내총생산(Gross Domestic Product, GDP), 인구, 기대수명, 출생률, 사망률, 대륙 등이 있습니다. 이 데이터셋은 연도별로 국가들의 상황을 기록하고 있어, 다양한 시각화 및 데이터 분석에 활용됩니다.

 

 

 

(1) 데이터 불러오기

# 라이브러리 불러오기
import pandas as pd
from gapminder import gapminder

# 데이터 불러오기
data = gapminder.copy()

# 데이터 크기 확인
data.shape

 

[결과값]

(1704, 6)

 

(2) 데이터 살펴보기

#데이터 정보 요약
data.info()

 

[결과값]

전체 행의 수는 1704이고, 총 6개의 열을 가지고 있습니다. 데이터 타입은 문자열(object), 정수(int64), 실수(float64)로 구성되어 있고 누락된 값은 없습니다.

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1704 entries, 0 to 1703
Data columns (total 6 columns):
 #   Column     Non-Null Count  Dtype  
---  ------     --------------  -----  
 0   country    1704 non-null   object 
 1   continent  1704 non-null   object 
 2   year       1704 non-null   int64  
 3   lifeExp    1704 non-null   float64
 4   pop        1704 non-null   int64  
 5   gdpPercap  1704 non-null   float64
dtypes: float64(2), int64(2), object(2)
memory usage: 80.0+ KB

 

(3) 여러가지 그래프를 그려보자

# 미국의 GDP 시계열 데이터
usa_data = data[data['country'] == 'United States']
plt.figure(figsize=(12, 6))
plt.plot(usa_data['year'], usa_data['gdpPercap'], marker='o')
plt.title('GDP per Capita Over Time (United States)')
plt.xlabel('Year')
plt.ylabel('GDP per Capita')
plt.show()

 

이 그래프를 통해 미국의 GDP per Capita가 연도에 따라 어떻게 변화했는지를 시각적으로 확인할 수 있습니다. 경제 성장이나 감소, 특정 시기의 변동성 등을 파악할 수 있습니다.

  1. X 축 (Year): 그래프의 x 축은 연도를 나타냅니다. 1952년부터 2007년까지의 미국의 GDP per Capita 변화를 보여줍니다.
  2. Y 축 (GDP per Capita): 그래프의 y 축은 GDP per Capita를 나타냅니다. 이 값은 해당 연도의 미국의 GDP를 인구 수로 나눈 값으로, 1인 당 평균 GDP를 의미합니다.
  3. 그래프의 형태: 그래프는 각 연도별로 미국의 GDP per Capita를 나타내는 점(line plot)으로 표현되어 있습니다. 각 점은 해당 연도의 GDP per Capita를 나타냅니다.
  4. Marker='o': 각 데이터 포인트는 동그라미(o)로 표시되어 있습니다.
  5. 그래프 제목: "GDP per Capita Over Time (United States)"라는 제목을 추가하였습니다.
  6. X 축 레이블 (Year): X 축에는 "Year"라는 레이블이 붙어 있어, X 축이 연도를 나타내는 것을 알려줍니다.
  7. Y 축 레이블 (GDP per Capita): Y 축에는 "GDP per Capita"라는 레이블이 붙어 있어, Y 축이 GDP per Capita를 나타내는 것을 알려줍니다.

 

 

# Top 10국가의 1인당 GDP 시각화
plt.figure(figsize=(14, 8))
sns.barplot(x='gdpPercap', y='country', data=top_gdp_countries, palette='viridis')
plt.title('Top 10 Countries by GDP per Capita (2007)')
plt.xlabel('GDP per Capita')
plt.ylabel('Country')
plt.show()

 

 

위 코드는 2007년 기준으로 상위 10개 국가의 1인당 GDP를 막대 그래프로 시각화하여 1인당 GDP가 높은 국가들을 한 눈에 볼 수 있는 장점이 있습니다.

 

 

#기대수명 vs. 1인당 GDP 산점도 그래프
plt.figure(figsize=(12, 8))
sns.scatterplot(x='gdpPercap', y='lifeExp', data=data_2007, hue='continent', palette='Set2', size='pop', sizes=(20, 2000))
plt.title('Life Expectancy vs. GDP per Capita (2007)')
plt.xlabel('GDP per Capita')
plt.ylabel('Life Expectancy')
plt.legend(title='Continent', loc='upper left')
plt.show()

 

  1. plt.figure(figsize=(12, 8)): 새로운 그림(figure)을 생성하고, 그림의 크기를 설정합니다. 이 경우에는 가로 12인치, 세로 8인치의 크기로 설정했습니다.
  2. sns.scatterplot(x='gdpPercap', y='lifeExp', data=data_2007, hue='continent', palette='Set2', size='pop', sizes=(20, 2000)): seaborn 라이브러리의 scatterplot 함수를 사용하여 산점도 그래프를 생성합니다. 
    • x='gdpPercap': x 축에는 1인당 GDP를 사용합니다.
    • y='lifeExp': y 축에는 기대수명을 사용합니다.
    • data=data_2007: 그래프에 사용할 데이터는 2007년의 데이터로 한정됩니다.
    • hue='continent': 대륙별로 데이터를 구분하여 다른 색상으로 표시합니다.
    • palette='Set2': 그래프에 사용할 색상 팔레트를 'Set2'로 지정합니다.
    • size='pop': 각 점의 크기는 해당 국가의 인구 크기로 나타냅니다.
    • sizes=(20, 2000): 인구 크기에 따라 표시되는 점의 크기 범위를 설정합니다.
  3. plt.legend(title='Continent', loc='upper left'): 대륙별로 다른 색상으로 표시된 점들에 대한 범례(legend)를 설정합니다. 범례의 위치는 왼쪽 상단으로 설정되어 있습니다.
  4. plt.show(): 그래프를 표시합니다.
# 상자 그림 (Box Plot) - 대륙 별 기대수명 (2007)
sns.boxplot(data=data[data['year'] == 2007], x='continent', y='lifeExp', palette='Set2')
plt.title('Box Plot - Life Expectancy by Continent (2007)')
plt.show()

 

위 코드는 2007년 기준으로 대륙별 기대수명에 대한 상자 그림(Box Plot)을 생성하는 코드입니다. 이 그래프를 통해 대륙별로 기대수명의 분포를 시각적으로 확인할 수 있습니다. 각 대륙의 중앙값, 사분위수, 이상치 등이 상자 그림으로 표현되어 있습니다.

반응형
Posted by 마르띤
,
반응형

Google에서 bubble chart로 검색을 해 보았다.

 

A bubble chart is a type of chart that displays three dimensions of data. Each entity with its triplet (v1, v2, v3) of associated data is plotted as a disk that expresses two of the vi values through the disk's xy location and the third through its size.

 

해석을 하자면 버블 차트는 데이터의 세 가지 차원을 표현하는 차트 중 하나입니다. 각 엔터티는 연관된 데이터의 세 값 (v1, v2, v3)을 가지며, 이를 디스크로 표현하여 디스크의 xy 위치로 두 개의 vi 값과 크기로 세 번째 값을 나타냅니다.

 

개념과 그래프의 예시는 아래 위키피디아 링크를 가보면 된다.

https://en.wikipedia.org/wiki/Bubble_chart

 

Bubble chart - Wikipedia

From Wikipedia, the free encyclopedia Type of chart Bubble chart displaying the relationship between poverty and violent and property crime rates by state. Larger bubbles indicate higher percentage of state residents at or below the poverty level. Trend su

en.wikipedia.org

 

seaborn 패키지와 함께 그릴 수 있는 예제도 검색하면 쉽게 찾아볼 수 있다. 

https://python-graph-gallery.com/bubble-plot-with-seaborn/

 

Bubble Plot with Seaborn | The Python Graph Gallery

How to draw a bubble plot using the scatterplot function of seaborn library

python-graph-gallery.com

 위 링크의 예시를 하나 가져와보자. gapminder 데이터 패키지를 설치하면 쉽게 그려볼 수 있다. gapminder는 국가별 경제 수준과 의료 수준 동향을 정리한 DataSet으로 분석에 다양하게 쓰인다. 

 

# 라이브러리 불러오기
import matplotlib.pyplot as plt
import seaborn as sns
from gapminder import gapminder # import data set 

# 사이즈 크기 설정
plt.rcParams['figure.figsize'] = [8, 8]

# 데이터 가져오기
data = gapminder.loc[gapminder.year == 2007]
 
# 버블차트를 그리기
sns.scatterplot(data=data, x="gdpPercap", y="lifeExp", size="pop", legend=False, sizes=(20, 2000))

# 그래프 출력
plt.show()

 

[결과값]

파이썬 공부를 해 본 사람이라면 한번씩은 봤을 그 그래프다!

(1) 버블차트를 그리는 방법은 다양하다.

import matplotlib.pyplot as plt
import numpy as np

# 데이터 생성
num_points = 30
x = np.random.rand(num_points)
y = np.random.rand(num_points)
sizes = np.random.rand(num_points) * 100  # 크기 정보

# 버블 차트 그리기
plt.scatter(x, y, s=sizes, alpha=0.5)

# 그래프 설정
plt.title('Bubble Chart Example')
plt.xlabel('X-axis')
plt.ylabel('Y-axis')
plt.grid(True)

# 그래프 표시
plt.show()

 

[결과값]

버블 차트(Bubble Chart)는 산점도(Scatter Plot)와 유사하지만, 각 데이터 포인트에 크기 정보를 추가한 그래프입니다. 각각의 데이터 포인트는 x축, y축으로 위치가 지정되고, 동시에 해당 데이터 포인트의 크기가 버블의 크기로 표현됩니다. 파이썬에서 버블 차트를 그리기 위해 주로 matplotlib 라이브러리를 사용합니다. 이 코드에서는 plt.scatter 함수를 사용하여 각 데이터 포인트를 표현하고, s 인자를 통해 크기 정보를 전달합니다. alpha 인자는 투명도를 나타내며, 0에서 1 사이의 값을 가집니다. num_points 변수를 통해 데이터 포인트의 개수를 조절하고, sizes 배열을 통해 각 데이터 포인트의 크기를 랜덤하게 생성합니다. 그 결과로 더 많은 버블이 표현된 그래프가 나타납니다. 데이터의 특성에 따라서 원하는 개수나 크기로 조절하실 수 있습니다.

 

(2) 버블차트의 색상을 다양하게 그려보자

import matplotlib.pyplot as plt
import numpy as np

# 데이터 생성
num_points = 30
x = np.random.rand(num_points)
y = np.random.rand(num_points)
sizes = np.random.rand(num_points) * 100  # 크기 정보
colors = np.random.rand(num_points)  # 색상 정보

# 버블 차트 그리기
plt.scatter(x, y, s=sizes, c=colors, cmap='viridis', alpha=0.5)

# 컬러바 추가
plt.colorbar()

# 그래프 설정
plt.title('Colored Bubble Chart Example')
plt.xlabel('X-axis')
plt.ylabel('Y-axis')
plt.grid(True)

# 그래프 표시
plt.show()

 

[결과값]

위 코드에서는 c 매개변수를 통해 각 데이터 포인트의 색상을 설정하고, cmap 매개변수로는 사용할 컬러 맵을 지정합니다. 여기서는 'viridis' 컬러 맵을 사용하였습니다. 그리고 plt.colorbar() 함수를 사용하여 컬러바를 추가하였습니다. 컬러 맵은 matplotlib에서 제공하는 여러 가지 컬러 스키마 중에서 선택할 수 있습니다. 

 

반응형
Posted by 마르띤
,
반응형

(1) boxplot은 왜 쓸까?

박스 플롯(Box Plot)은 통계적 데이터의 요약된 시각화 도구로, 데이터의 중앙 경향, 분포, 이상치를 효과적으로 보여주는 차트입니다. 다른 이름으로는 상자 수염 그림(Box-and-Whisker Plot)이라고도 불립니다. 박스 플롯은 다음과 같은 주요 구성 요소로 이루어져 있습니다:

 

1. 상자 (Box):

- 상자의 하단 변은 데이터의 1사분위수(Q1)를 나타냅니다.

- 상자의 상단 변은 데이터의 3사분위수(Q3)를 나타냅니다.

- 상자의 중앙에 수평선은 데이터의 중앙값(median)을 나타냅니다.

 

2. 수염 (Whiskers):

- 수염은 데이터의 최솟값과 최댓값을 나타냅니다. 일반적으로 1.5배의 사분위 범위(IQR)를 벗어나는 값은 이상치로 간주됩니다.

 

3. 이상치 (Outliers):

- 수염 부분을 벗어나는 점은 이상치로 간주됩니다.

 

박스 플롯은 데이터의 분포를 쉽게 이해하고, 여러 그룹 간의 비교를 통해 통계적 특성을 시각적으로 비교할 수 있는 강력한 도구입니다. 특히, 중앙값과 사분위수를 통해 데이터의 중심 경향과 분포를 한눈에 파악할 수 있어 다양한 분야에서 널리 사용되고 있습니다.

 

(2) boxplot 한 개를 그려보자

# 라이브러리 불러오기
import matplotlib.pyplot as plt
import numpy as np

# 예제 데이터 생성
np.random.seed(10) # 난수 생성 시드 설정
data = np.random.normal(0, 1, 100) # 평균이 0이고 표준 편차가 1인 정규 분포에서 100개의 데이터 생성

# 그래프 사이즈 조정
fig = plt.figure(figsize =(10, 7))

# 박스 플롯 그리기
plt.boxplot(data)

# 그래프 표시
plt.show()

 

[결과값]

위 코드를 실행하면 평균이 0이고 표준 편차가 1인 정규 분포에서 생성된 데이터에 대한 박스 플롯이 그려진 그래프 창이 표시됩니다. 

 

(3) boxplot 여러 개를 그려보자

# 라이브러리 불러오기
import matplotlib.pyplot as plt
import numpy as np

# 예제 데이터 생성
np.random.seed(10)

data_1 = np.random.normal(10, 5, 100)
data_2 = np.random.normal(9, 10, 100)
data_3 = np.random.normal(8, 15, 100)
data_4 = np.random.normal(7, 20, 100)
data = [data_1, data_2, data_3, data_4]

# 그래프 사이즈 및 간격 조정
fig = plt.figure(figsize =(10, 7))
ax = fig.add_axes([0, 0, 1, 1])

# 박스 플롯 그리기
bp = ax.boxplot(data)

# 그래프 표시
plt.show()

 

[결과값]

네 개의 상자 플롯이 그려져 있으며, 각각의 상자 플롯은 다른 그룹에 해당합니다. 각 그룹의 중심 경향과 분포를 나타내기 위해 상자의 위치와 크기가 다릅니다. 그래프 상단의 표시는 각 그룹의 중앙값(median)을 나타냅니다. 수염은 각 그룹의 데이터의 전체 분포를 나타냅니다. 이상치가 몇몇 그룹에 존재합니다.

 

이번 포스팅이 파이썬 공부에 작은 도움이 되었기를 바랍니다.

반응형
Posted by 마르띤
,
반응형

(1) 리스트 다루기

fruits = ["사과", "바나나", "딸기", "포도"]
print("과일 목록:", fruits)

 

[결과값]

과일 목록: ['사과', '바나나', '딸기', '포도']

 

파이썬 리스트는 순서가 있는 데이터의 컬렉션으로, 여러 값을 담을 수 있는 가변(mutable)한 자료형입니다. 리스트는 대괄호 []를 사용하여 정의되며, 각 항목은 콤마 ,로 구분됩니다.

 

(2) 딕셔너리 활용하기

student = {"이름": "철수", "나이": 15, "성적": 90}
print("학생 정보:", student)

 

[결과값]

학생 정보: {'이름': '철수', '나이': 15, '성적': 90}

 

리스트는 순수가 있는 데이터의 컬렉션으로 각 항목은 콤마로 구분. 딕셔너리는 순서가 없는 데이터의 컬렉션으로 각 항목은 키와 밸류의 한 상으로 구분, 중괄호를 사용하고, 각 쌍은 콤마로 구분된다.

 

(3) 제곱근

import math

result = math.sqrt(25)
print("제곱근:", result)

 

[결과값]

제곱근: 5.0

 

(4) 예외처리

try:
    result = 10 / 0
except ZeroDivisionError:
    print("0으로 나눌 수 없습니다.")

 

[결과값]

0으로 나눌 수 없습니다.

 

1.    try 블록:

-       try 키워드 아래의 블록은 예외가 발생할 수 있는 코드를 포함합니다.

-       여기서는 10 / 0이라는 수식이 있습니다. 이는 0으로 나누는 연산으로, 이는 ZeroDivisionError 예외를 발생시킵니다.

 

2.    except 블록:

-       except 키워드는 try 블록에서 발생한 예외를 처리하기 위한 블록을 나타냅니다.

-       ZeroDivisionError 0으로 나누려고 할 때 발생하는 예외입니다. 이 예외가 발생하면 해당 블록이 실행됩니다.

-       여기서는 예외가 발생했을 때 출력할 메시지를 print("0으로 나눌 수 없습니다.")로 지정했습니다.

 

3.    실행 흐름:

-       try 블록의 코드가 실행됩니다.

-       예외가 발생하면 프로그램의 흐름이 except 블록으로 이동하고, 지정된 메시지가 출력됩니다.

-       예외가 발생하지 않으면 except 블록은 건너뛰어집니다.

 

 

이 코드는 0으로 나누는 오류에 대한 처리를 담고 있습니다. 예외 처리를 통해 프로그램이 비정상적인 상황에서도 적절히 대응할 수 있도록 도와줍니다.

 

try:
    result = 10 / 2
except ZeroDivisionError:
    print("0으로 나눌 수 없습니다.")

 

예외가 발생하지 않으므로 except 블록은 건너뛰어지고 아무 결과값도 나오지 않는다.

 

이번 포스팅이 많은 파이썬 공부에 작은 도움이 되었기를 바랍니다. 

반응형
Posted by 마르띤
,
반응형

(1) 변수 x에 5를 할당하고, 변수 y에 3을 할당한 후, 두 변수를 더한 값을 출력하는 코드를 작성하세요.

x = 5
y = 3
result = x + y
print(result)

 

(2) 사용자로부터 이름을 입력 받아 환영 메시지를 출력하는 코드를 작성하세요.

name = input("이름을 입력하세요: ")
print("안녕하세요, " + name + "님!")

 

(3) 1부터 10까지의 숫자 중에서 짝수만 출력하는 코드를 작성하세요.

for i in range(1, 11):
    if i % 2 == 0:
        print(i)

1. for 루프: for i in range(1, 11):은 1부터 10까지의 숫자를 반복하는 루프를 나타냅니다. range(1, 11)은 1부터 10까지의 범위를 생성합니다.

2. 짝수 확인: if i % 2 == 0:는 현재의 i가 짝수인지를 확인합니다. 여기서 %는 나머지를 계산하는 연산자이며, i % 2가 0이면 i는 짝수입니다.

3. 짝수 출력: 짝수인 경우에만 print(i)가 실행되어 현재의 i를 출력합니다.

 

(4) 주어진 리스트에서 최대값을 찾는 함수를 작성하세요.

# 정의
def find_max(lst):
    max_value = max(lst)
    return max_value

# 사용 예시
numbers = [3, 8, 1, 5, 10]
result = find_max(numbers)
print("최대값:", result)

1. 함수 정의: find_max 함수는 리스트를 받아 해당 리스트에서 최대값을 찾아 반환하는 함수입니다. 함수는 max 내장 함수를 활용합니다.

2. 사용 예시: numbers라는 리스트를 만들고, 이 리스트를 find_max 함수에 전달하여 최대값을 찾습니다.

3. 결과 출력: 최대값을 변수 result에 저장하고, 이를 화면에 출력합니다.

 

이 코드는 임의의 숫자 리스트에서 최대값을 찾는 함수를 정의하고, 이 함수를 사용하여 주어진 예시 리스트의 최대값을 찾아 출력하는 예시입니다. 함수를 활용하면 언제든지 다른 리스트에 대해서도 최대값을 쉽게 찾을 수 있습니다.

 

 

 

이번 포스팅이 많은 파이썬 공부에 작은 도움이 되었기를 바랍니다. 

2024년에는 행복한 데이터 과학자 되세요!  :-)

반응형
Posted by 마르띤
,
반응형

1. G마켓 크롤링

지난 번 웹  크롤링을 통해 올리브영의 랭킹 정보를 긁어 모았고, 이번에는 G 마켓의 랭킹 정보를 크롤링 해 보려 한다.

https://kmrho1103.tistory.com/entry/Python-%EC%9B%B9%ED%81%AC%EB%A1%A4%EB%A7%81-%EC%98%AC%EB%A6%AC%EB%B8%8C%EC%98%81-%EB%9E%AD%ED%82%B9-%ED%81%AC%EB%A1%A4%EB%A7%81

 

[Python] 웹 크롤링 - 올리브영 랭킹 크롤링

0. 웹 크롤링이 뭐야? 크롤링은 인터넷 상에서 웹 페이지를 순회하고, 웹 사이트에서 정보를 추출하는 자동화된 프로세스를 말합니다. 크롤링은 웹 크롤러 또는 스파이더라고 불리는 소프트웨어

kmrho1103.tistory.com

 

2. G마켓 랭킹

https://category.gmarket.co.kr/listview/L100000005.aspx

 

G마켓-화장품/향수

컨실러란? 기미,잡티,주근깨,여드름흔적등을 감쪽같이 커버하여 깨끗한피부연출을 해주는 제품.국소부위용과 얼굴전체커버용 닫기

category.gmarket.co.kr

위 주소로 들어가면 아래 처럼 G마켓에서 잘 팔리는 상품들의 랭킹 정보를 볼 수 있다.

 

3. 파이썬 코드

import pandas as pd
import requests
from bs4 import BeautifulSoup

url = requests.get('https://www.gmarket.co.kr/n/best?jaehuid=200011415&viewType=C&largeCategoryCode=100000005')
html = BeautifulSoup(url.text)

products = []
for i in html.find_all('div',class_='thumb'):
    products.append(i.find('img')['alt']) # 이미지 태그의 'alt' 속성에 상품 이름이 들어 있다
    
price = []
for i in html.find_all('div',class_='s-price'):
    price.append(i.text.split('할인가')[1].split('원')[0])
    
df = pd.DataFrame({'상품명':products,'가격':price})
df

 

가격 태그 정보를 좀 더 분석해보면 깔끔하게 가져올 수 있을 것 같다는 생각이 드는데 좀 더 공부해야겠다.

 

 

 

 

 

반응형
Posted by 마르띤
,
반응형

0. 개요

보스턴 주택 가격 데이터셋은 머신 러닝과 회귀 분석 연습용으로 널리 활용되는 데이터셋 중 하나입니다. 이 데이터셋은 미국 매사추세츠주 보스턴 내 다양한 지역의 주택 가격과 주택 가격에 영향을 미치는 여러 가지 특성들을 포함하고 있습니다. 주로 회귀 분석의 예제로 사용되며, 집값을 예측하는 모델을 만들기 위한 데이터로 활용됩니다. 이 데이터셋은 Scikit-learn 라이브러리에 기본으로 내장되어 있어서 불러와서 사용할 수 있는데, 해당 파일을 블로그에도 첨부하였으니 자유롭게 다운하셔도 됩니다. 주요 칼럼값은 아래와 같습니다.

 

- CRIM: 지역별 1인당 범죄율

- ZN: 25,000 평방피트당 주거용 토지 비율

- INDUS: 비소매상업지역 면적 비율

- CHAS: 찰스 강 인접 여부 (1: 강 인접, 0: 강 미인접)

- NOX: 일산화질소 농도 RM: 주택당 평균 방 개수

- AGE: 1940년 이전에 건축된 주택의 비율

- DIS: 5개의 보스턴 고용 센터와의 거리에 대한 가중치

- RAD: 방사형 고속도로 접근성 지수

- TAX: $10,000당 재산세율

- PTRATIO: 학생-교사 비율

- B: 1000(Bk - 0.63)^2, 여기서 Bk는 지역별 흑인 비율

- LSTAT: 저소득 계층의 비율

- MEDV: 주택 가격의 중앙값

 

이 데이터셋을 활용하여 다양한 회귀 모델을 학습하고 집값을 예측하는 연습을 할 수 있습니다. 데이터의 다양한 특성을 이해하고 활용하여 모델을 개선하며, 데이터 분석 및 예측 능력을 향상시킬 수 있는 좋은 예제 중 하나입니다.

 

1. 파이썬 코드 - 회귀분석

#라이브러리 불러오기
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

#데이터 불러오기
boston_data = pd.read_csv('HousingData.csv')

#결측치 확인 및 제거
boston_data.isnull().sum()
boston_data = boston_data.dropna(axis=0)

#데이터 분할
X=boston_data.drop('MEDV',axis=1)
y=boston_data['MEDV']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 선형 회귀 모델 생성 및 학습
model = LinearRegression()
model.fit(X_train, y_train)

#테스트 데이터에 대한 예측
y_pred = model.predict(X_test)

# 평가: 평균 제곱 오차(Mean Squared Error) 계산
mse = mean_squared_error(y_test, y_pred)
print(f'Mean Squared Error: {mse:.2f}')

 

2. 탐색적 데이터 분석(EDA)

1) 데이터 구조

boston_data.head()

CRIM부터 MEDV까지 총 14개의 칼럼값을 가지고 있습니다.

 

2) 상관 분석

# 상관 분석 시각화
correlation_matrix = boston_data.corr()
plt.figure(figsize=(12, 8))
sns.heatmap(correlation_matrix, annot=True)
plt.title('Correlation Matrix')
plt.show()

- 'RM' (방 개수)은 'MEDV' (주택 가격)와 양의 상관 관계를 가지는 밝은 색을 보여줍니다. 이는 방의 개수가 증가할수록 주택 가격도 증가하는 경향을 나타냅니다.

- 'LSTAT' (저소득 계층의 비율)은 'MEDV'와 음의 상관 관계를 가집니다. 이는 저소득 계층의 비율이 높을수록 주택 가격이 낮아지는 경향을 나타냅니다.

- 'RAD' (방사형 고속도로 접근성 지수)와 'TAX' ($10,000당 재산세율)는 서로 강한 상관 관계를 보입니다. 

 

 

3) 주택 가격 분포

# 주택 가격 분포
plt.figure(figsize=(8, 6))
sns.histplot(data=boston_data, x='MEDV', bins=30, kde=True)
plt.title('Distribution of Housing Prices (MEDV)')
plt.show()

4) 방 개수와 주택 가격간 관계

# 방 개수와 주택 가격 분포
plt.figure(figsize=(8, 6))
sns.scatterplot(data=boston_data, x='RM', y='MEDV')
plt.title('Relationship between Rooms (RM) and Housing Prices (MEDV)')
plt.show()

3. 결측값 확인

파이썬에서 결측값을 확인하고 처리하는 방법은 데이터 전처리 과정에서 중요한 부분입니다.

boston_data.isnull().sum()

 

결측값을 지우는 방법도 있고, 평균이나 최빈값으로 대체하는 방법도 있는데 나는 지우는 방법을 선택하였습니다.

boston_data = boston_data.dropna(axis=0)

 

4. 모델 성능 평가

1) 성능 평가

# 평가: 평균 제곱 오차(Mean Squared Error) 계산
mse = mean_squared_error(y_test, y_pred)
print(f'Mean Squared Error: {mse:.2f}')
Mean Squared Error: 31.45
 

 

파이썬 머신러닝에서 모델 성능 평가는 매우 중요한 이유가 있습니다. 모델의 성능을 평가하는 것은 모델이 얼마나 좋은 예측을 하는지를 이해하고 개선하는 데 도움을 주는 핵심적인 단계입니다.

 - 비즈니스 의사 결정에 활용: 머신러닝 모델은 비즈니스 의사 결정을 지원하는 데 사용될 수 있습니다. 모델의 예측 능력이나 신뢰도를 평가함으로써, 예를 들어 상품 판매 예측, 금융 리스크 평가 등과 같은 중요한 결정에 도움을 줄 수 있습니다.

- 성능 개선: 성능 평가를 통해 모델의 약점이나 부족한 부분을 파악하고 개선할 수 있습니다. 예측의 정확도를 높이거나 편향을 줄이는 등 모델을 보다 강력하게 만들기 위한 방법을 찾을 수 있습니다.

- 과적합 및 과소적합 판단: 모델 성능 평가는 과적합(Overfitting)이나 과소적합(Underfitting)과 같은 문제를 판단하는데 도움을 줍니다. 훈련 데이터에 대한 예측 능력과 테스트 데이터에 대한 예측 능력을 비교하여 모델의 일반화 능력을 평가할 수 있습니다.

- 모델 해석: 모델의 성능 평가 결과는 모델이 어떤 특성을 중요하게 생각하는지, 어떤 특성이 예측에 큰 영향을 주는지 등을 파악하는 데 도움을 줍니다.

 

2) 시각화 - 실제값과 예측값을 비교하는 그래프

#라이브러리 
import matplotlib.pyplot as plt

#그래프 설정
plt.figure(figsize=(10, 6))
plt.scatter(y_test, y_pred, color='blue')
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], linestyle='--', color='red', linewidth=2)
plt.xlabel('Actual Price')
plt.ylabel('Predicted Price')
plt.title('Actual vs Predicted House Prices')
plt.show()

 

HousingData.csv
0.03MB

반응형
Posted by 마르띤
,
반응형

오늘은 Selenium으로 네이버 로그인 하는 연습을 해 보자.

Selenium이란?

웹 애플리케이션을 테스트하거나 웹 브라우징을 자동화하기 위한 도구와 라이브러리다. 주로 웹 페이지를 제어하고 상호작용하며 웹 사이트를 테스트하거나 데이터를 수집하는 등의 작업에 사용된다. Selenium은 다양한 프로그래밍 언어로 지원되며, 파이썬에서도 많이 사용된다. Selenium의 주요 기능과 특징은 다음과 같다:
1. 웹 브라우저 제어: Selenium을 사용하여 웹 브라우저(예: Chrome, Firefox, Edge 등)를 자동으로 제어할 수 있다. 이를 통해 웹 페이지를 열거나 조작할 수 있다.
2. 웹 페이지 상호작용: Selenium을 통해 웹 페이지의 버튼 클릭, 입력 필드 입력, 폼 제출 등의 상호작용을 자동으로 수행할 수 있다.
3. 웹 스크레이핑 및 데이터 수집: Selenium을 이용하여 웹 페이지의 데이터를 추출하고 원하는 정보를 수집할 수 있다.
4. 웹 테스트 자동화: 웹 애플리케이션의 테스트를 자동화하는 데 사용된다. 웹 페이지의 기능과 동작을 테스트하고 결과를 확인하는데 활용된다.
5. 다양한 언어와 테스트 프레임워크 지원: Selenium은 다양한 프로그래밍 언어와 테스트 프레임워크를 지원하며, 파이썬에서는 selenium 라이브러리를 사용하여 Selenium을 활용할 수 있다.

Selenium은 웹 페이지 상호작용을 자동화하거나 웹 애플리케이션을 테스트하는 데 유용한 강력한 도구로, 웹 개발자나 테스트 엔지니어 등이 자동화 작업을 수행할 때 많이 활용된다.

 

네이버 사이트를 로그인 하기 위한 전체 파이썬 코드는 아래와 같다. (아래 코드는 네이버가 봇임을 감지하고 로그인이 되지 않습니다. 이를 회피하기 위한 코드는 맨 아래 내용을 확인해 주세요)

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get('https://nid.naver.com/nidlogin.login?mode=form&url=https://www.naver.com/')

#ID 입력
id = driver.find_element(By.CSS_SELECTOR, "#id")
id.click()
id.send_keys('ID를 입력하세요')

#PW 입력
pw = driver.find_element(By.CSS_SELECTOR, "#pw")
pw.click()
pw.send_keys('PW를 입력하세요')

#로그인
login_btn = driver.find_element(By.CSS_SELECTOR, "#log\.login")
login_btn.click()

 

1. 필요한 패키지 불러오기

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By

 

2. 네이버 사이트 불러오기

www.naver.com에서 로그인 부튼을 누르면 아래와 같은 화면으로 넘어간다. 이 부분의 URL을 불러와야 한다. 주소는 아래와 같다. 

 

 

네이버

네이버에 로그인 하고 나를 위한 다양한 서비스를 이용해 보세요

nid.naver.com

3. 네이버 로그인 정보 확인하기 - ID / PW

자 여기서 윈도우에서는 F12, 맥에서는 control키와 클릭을 함께 눌러 태그 정보를 확인해 보자.

 

네이버 화면을 키고 ID 부분의 태그를 알아보니 id="id"를 확인할 수 있다. Selenium에서는 find_element_by_id 함수를 이용해서 id 값을 가져올 수 있다.

 

위는 pw의 태그 정보를 볼 수 있다.  id/pw 부분 모두 우클릭을 통해 copy selector를 하여 css copy를 하자. 해당 파이썬 코드는 아래와 같다.

#ID 입력
id = driver.find_element(By.CSS_SELECTOR, "#id")
id.click()
id.send_keys('ID를 입력하세요')

#PW 입력
pw = driver.find_element(By.CSS_SELECTOR, "#pw")
pw.click()
pw.send_keys('PW를 입력하세요')

 

자! ID와 PW를 입력하였으면 이제 로그인 버튼을 눌러줘야 한다.

네이버의 로그인 부분에서 우클릭을 하고 css copy를 하면 아래와 같다.

login_btn = driver.find_element(By.CSS_SELECTOR, "#log\.login")
login_btn.click()

자 이제 모든게 마무리 되었다. 그러면 실행을 해 보자. 그랬더니!! 동작을 한다.

잠시 후 크롬 창 하나가 뜨면서 네이버 화면이 떴고, 아이디와 패스워드 항목란에 입력이 되면서 로그인 버튼이 자동으로 눌리더니 네이버에서는 사람이 아닌 봇임을 감지하고 아래 처럼 화면이 떴다.

혹시나 해서 id/pw 입력란에 time.sleep 함수를 사용해서 입력 시간을 늦춰보았지만 소용은 없었다. 네이버에서이를 잘 탐지 하였다. 방법은 뭘까? 이른바 복붙 신공이다. 아이디와 비번을 입력/복사하는 형태로 로그인 하는 방법이다.

이 방법은 통할까 싶었는데 다행히 잘 통하였다. 그 방법은 아래와 같다.

 

처음 코드와 달리 pyperclip을 불러왔고, time은 id/pw 입력시간 간 간격을 두기 위해 추가로 불러왔다.

 

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By

import pyperclip
import time

driver = webdriver.Chrome()
driver.get('https://nid.naver.com/nidlogin.login?mode=form&url=https://www.naver.com/')

#ID 입력
id = driver.find_element(By.CSS_SELECTOR, "#id")
id.click()
pyperclip.copy('ID를 입력하세요')
id.send_keys(Keys.COMMAND,'v') #윈도우면 command 대신 ctrl
time.sleep(3)

#PW 입력
pw = driver.find_element(By.CSS_SELECTOR, "#pw")
pw.click()
pyperclip.copy('PW를 입력하세요')
pw.send_keys(Keys.COMMAND,'v') #윈도우면 command 대신 ctrl
time.sleep(3)

#로그인
login_btn = driver.find_element(By.CSS_SELECTOR, "#log\.login")
login_btn.click()

다행히 잘 로그인이 된다. 

반응형
Posted by 마르띤
,
반응형

1. 삼성전자 주식 정보를 파이썬으로 크롤링해 보자

네이버 주식에서 삼성전자 주가 정보를 크롤링 해보려 한다. 23년 8월 4일 금요일 종가 기준, 삼성전자의 주가는 68,300원이다. 
 
삼성전자라고 검색했을 때 나오는 종합 정보 가격 그러니까 전일/시가/고가/저가/거래량을 한번에 볼 수 있는 곳 말고, 아래 시세 정보를 통해 봐야 크롤링하기 더 편하다. 그 이유로는 아래 종합 정보 가격에서는 삼성전자 주가 정보 68,300원이 em 태그 아래 각각의 span 태그에 들어가 있는데 우리가 원하는 가격 68,300원이 아니라 6,8,3,0,0이 떨어져있다. 크롤링을 할수는 있겠지만 크롤링 하고 합치고 하는 과정이 번거롭기도 하고, 굳이 이렇게 번거롭게 크롤링 하지 않아도 된다. 그 이유는 시세 정보에서 크롤링을 쉽게할 수 있기 때문이다.
 

 
시세 정보 링크는 아래와 같다.

 

네이버 증권

국내 해외 증시 지수, 시장지표, 뉴스, 증권사 리서치 등 제공

finance.naver.com

네이버 증권 시세 정보를 들어가서 현재가 정보를 클릭하며 보면 반갑게 68,300원 가격 정보가 온전히 들어가 있는 것을 볼 수 있다. strong 태그 아래 class 정보는 tah p11 . 자 이제 크롤링을 위한 준비는 다 마쳤다. 삼성전자 주가 정보를 이제 크롤링 해 보자.
 

 
<파이썬 코드>

import requests
from bs4 import BeautifulSoup

url = requests.get('https://finance.naver.com/item/sise.naver?code=005930')
html = BeautifulSoup(url.text)
price = html.find('strong', class_='tah p11').text
print(price)
68,300

삼성전자 종목 하나만 크롤링 하는건 매우 간단하다. 필요한 라이브러리를 불러오고 URL을 읽어 낸 다음, 프라이스 정보에 tag 정보와 class정보만 올바르게 입력하면 된다. 주소 링크에서 code = 005930라고 써져 있는데 이는 삼성전자의 코드 정목을 뜻 한다. 만약 삼성전자 한개가 아니라 LG전자의 주가 정보도 함께 크롤링 하고 싶으면 이 코드 정보를 수정 하면 된다. 
 

2. 삼성전자를 포함한 다른 회사의 주식 정보도 함께 크롤링 해 보자

 
 아래는 크롤링 코드이다.

import requests
from bs4 import BeautifulSoup

codes = ['005930','066570']

for i in codes:
    url = requests.get(f'https://finance.naver.com/item/sise.naver?code={i}')
    html = BeautifulSoup(url.text)
    price = html.find('strong', class_='tah p11').text
    print(price)

몇가지 주의 사항으로는 기존 URL 주소에서 코드 넘버를 입력한 부분에 i를 입력해주고, https:// 앞 format의 f를 입력해주면 된다. 그리고 이 전체 코드를 for문에 적용해주면 쉽게 삼성전자와 LG전자의 주가 정보를 크롤링 할 수 있게 된다. 결과값은 아래와 같다.

68,300
103,600

 

참고로 f-string은 파이썬 3.6 이상 버전에서 도입된 문자열 포맷팅 방식 중 하나로, 문자열 안에서 변수나 표현식을 간단하게 포맷팅할 수 있는 방법이다. f-string은 문자열 앞에 f 또는 F를 붙여서 사용하며, 중괄호 {} 안에 변수나 표현식을 넣어서 사용한다. 아래는 f-string의 기본적인 사용법과 예시이다:

 

name = "Alice"
age = 30

# 변수를 f-string으로 포맷팅
formatted_string = f"My name is {name} and I am {age} years old."
print(formatted_string)

# 수식과 표현식을 f-string으로 포맷팅
x = 10
y = 20
result = f"The sum of {x} and {y} is {x + y}."
print(result)

f-string 내부의 중괄호 {} 안에 변수 이름이나 표현식을 넣으면 해당 부분이 실제 값으로 치환된다. 이렇게 하면 변수나 수식을 문자열에 쉽게 삽입할 수 있다.

 

크롤링 하고 나서 보니, 크롤링은 하였지만 결과에 대해서는 조금 하찮아보이기도 한다. 이 정보 자체로는 아무것도 할 수 없을테니. 다만 이런 기초 학습을 통해 네이버 주식 정보에서 대한민국에 상장된 모든 코스피 시세 정보도 크롤링 할 수 있으니, 조금씩 조금씩 공부하면서 실력이 늘도록 도움이 되었으면 한다. 나에게도 , 그리고 이 블로그 글을 읽을 누군가를 위해서도 조금씩 성장하는 즐거움을 주기 위해 크롤링 한 내용들을 기록으로 남기려 한다.

 

파이썬 공부를 하면서 느낀건데 매일 매일 하면 조금씩 는다. 그것도 아주 조금씩. 그런데 조금이라도 안 하면 금방 까먹는다. 하루에 한 보 전진하다가, 갑자기 2보 후퇴 하는 느낌이 든다. 파이썬 공부를 하면서 조금씩 성장하는 과정은 물론이고, 그 과정에서 느껴지는 즐거움은 큰 의미를 가진다. 작은 목표를 달성하거나 새로운 기술을 습득하는 등의 성장은 자기 성취감을 준다. 이러한 감정은 자신에게 긍정적인 자아 이미지를 형성하고 자신감을 높여줄 뿐만 아니라 작은 단계로 성장하면서 지속적인 동기 부여를 유지할 수 있다. 큰 목표를 이루기까지의 길은 멀고 힘들 수 있지만, 작은 성취들은 그 과정을 재미있고 가치있게 만들어준다. 성장하는 과정에서 새로운 경험과 지식을 얻게 된다. 새로운 것을 배우고 시도함으로써 세상을 더 풍부하게 느낄 수 있다.그리고  작은 도전과 과제들을 해결하면서 문제 해결 능력이 향상된다. 결국 엉덩이 무겁게 버티는 사람이 성장하는 느낌인데 이 능력은 삶의 다양한 상황에서 도움이 되며, 어려운 문제에 더 자신감 있게 접근할 수 있게 해준다. 결국 이러한 작은 성장들이 쌓이면서 지속적인 발전을 경험하게 된다. 이는 더 나은 방향으로 성장하기 위한 계획을 세우는 데 도움을 줄 수 있다.

 

작은 성장들은 큰 변화를 만들어낼 수 있는 첫 단계입니다. 제게 하는 말이기도 하지만 이 블로그를 읽는 모든 분들이 이러한 성장의 순간들을 즐기며 더 나은 미래를 향해 나아갈 수 있기를 바랍니다.

반응형
Posted by 마르띤
,
반응형

제목 그대로 R vs 파이썬, 무엇이 더 좋을까?

 

나도 파이썬을 배우기 전에는 R을 먼저 공부하였다. 2016년으로 기억한다. 당시는 방통대 통계학에 편입하여 공부하고 있었는데, 교재에서 많은 데이터들이 R을 다루기도 하였고, 인터넷으로 검색을 해보니 R 강의도 많았다.

 

파이썬이 없었던 것은 아니였지만, 처음 교재로 R을 접하다보니 자연스롭게 노출된 언어를 먼저 공부하게 되었다. 당시에도 R이냐 파이썬이냐를 두고 갑론을박을 했던 기억이 난다. 기억으로는 통계를 할거면 R, 통계를 넘어 개발까지 고민한다면 파이썬을 공부하라는 전문가의 말도 있었고, 어떤 프로그램 언어에 채용 공고가 많은지 보라는 의견도 있었다. 실제로 지금도 해당 내용은 인터넷 검색이 쉽게 되는데 당연하지만 파이썬의 채용공고가 더 많다. 그리고 캐글. 캐글은 데이터쟁이들의 놀이터인데 압도적인 비율로 파이썬을 사용한 결과값을 많이 제출하였고, R만 알던 나에게 파이썬도 해야겠구나...라고 생각하게 만들었던 생각이 난다. 

 

문득 챗 GPT는 뭐라고 말할지 궁금해졌다. 두 프로그램 언어 중 무엇이 좋은지 물어보았다.

둘 다 데이터 분석과 머신러닝 분야에서 매우 인기 있는 프로그래밍 언어이며, 각각의 장단점이 있습니다. 어떤 언어를 배울지 선택하는 것은 주로 개인의 용도, 선호도, 배우기 쉬운 언어 등을 고려해야 합니다. 아래는 각 언어의 특징과 강점을 설명해드리겠습니다:
R 언어:
  1. 통계 및 데이터 분석에 최적화: R은 통계 분석과 데이터 시각화를 위해 설계된 언어로, 데이터 분석가와 통계학자들에게 매우 인기가 있습니다.
  2. 풍부한 패키지와 라이브러리: R은 CRAN (Comprehensive R Archive Network)을 통해 수많은 패키지를 제공하여 데이터 분석에 필요한 다양한 기능을 지원합니다.
  3. 데이터 시각화: R은 ggplot2 등의 라이브러리를 통해 풍부하고 멋진 데이터 시각화를 제공합니다.
  4. 통계적 테스트 및 모델링: 통계적 가설 검정과 회귀 분석 등 다양한 통계적 기법을 지원합니다.
Python:
  1. 다목적 프로그래밍 언어: Python은 데이터 분석뿐만 아니라 웹 개발, 자연어 처리, 인공지능, 자동화 등 다양한 분야에서 사용되는 다목적 프로그래밍 언어입니다.
  2. 머신러닝 라이브러리: Python은 Scikit-learn, TensorFlow, PyTorch 등 머신러닝 및 딥러닝 라이브러리가 풍부하며, 머신러닝 프로젝트에 유용합니다.
  3. 데이터 처리 라이브러리: Pandas와 NumPy를 통해 데이터를 쉽게 처리하고 분석할 수 있습니다.
  4. 커뮤니티 지원: Python은 넓은 커뮤니티와 다양한 온라인 자료들이 있어 학습과 문제 해결에 유용합니다.
따라서, 데이터 분석과 통계에 초점을 맞추고 싶다면 R을 선택하는 것이 좋을 수 있습니다. 반면에, 데이터 분석 외에도 다른 영역에서도 활용하고 싶다면 Python을 선택하는 것이 유용할 수 있습니다. 또한, 두 언어를 모두 배우는 것도 가능하며, 상황에 따라 적절하게 선택하여 활용하는 것도 좋은 전략입니다.

 

R의 장점으로는 통게 및 데이터 분석에 최적화 되있다는 점이다. 통계적 가설 검정을 많이 했던 기억이 난다. 그리고 일본 저자가 쓴 교재를 통해 시각화 공부도 많이 했었다. 파이썬은 역시나 데이터 분석 외 이야기들이 많이 나온다. R만으로 머신러닝이 되지 않는건 아닌데, 파이썬은 R에서 제공하지 못하는 많은 분석 외적 영역을 제공해주곤 하였다. 그리고 이게 지금 내가 파이썬을 공부하는 가장 큰 이유인데 유저가 많다는 것이다. 당연히 구글 검색을 하면 관련된 결과가 더 많이 나와 공부하기 쉬운 환경이다.

 

물어보다 파이썬으로 했을 때 R 대비 장점을 한번 더 물어보았다.

파이썬은 다목적 프로그래밍 언어로 다양한 분야에서 사용되며, R에 비해 다음과 같은 장점들이 있어 파이썬만으로 해결하기 쉬운 것들이 있습니다:
  1. 웹 개발: Django, Flask 등의 웹 프레임워크를 사용하여 웹 애플리케이션을 개발할 수 있습니다.
  2. 자연어 처리(NLP): Natural Language Toolkit(NLTK) 등의 라이브러리를 통해 텍스트 데이터를 분석하고 처리할 수 있습니다.
  3. 컴퓨터 비전: OpenCV와 같은 라이브러리를 사용하여 이미지 및 비디오 처리, 객체 감지, 얼굴 인식 등을 수행할 수 있습니다.
  4. 자동화 및 스크립팅: 간단한 작업부터 복잡한 프로세스까지 자동화할 수 있는 강력한 스크립팅 언어입니다.
  5. 인공지능과 머신러닝: TensorFlow, PyTorch, Scikit-learn 등의 라이브러리를 통해 딥러닝 모델과 머신러닝 모델을 구축할 수 있습니다.
  6. 빅데이터 처리: PySpark를 사용하여 대규모 데이터를 처리하고 분석할 수 있습니다.
  7. 게임 개발: Pygame을 사용하여 2D 게임을 만들 수 있습니다.
  8. 네트워크 프로그래밍: 소켓 프로그래밍을 통해 네트워크 애플리케이션을 구축할 수 있습니다.
  9. 데이터베이스 연동: 다양한 데이터베이스와 연동하여 데이터를 읽고 쓸 수 있습니다.
물론 이러한 분야들 중에 일부는 R에서도 패키지를 통해 지원되기도 합니다. 하지만 파이썬은 다양한 분야에서 폭넓게 사용되기 때문에, 더 다양한 프로젝트와 활용 가능성이 있습니다. 따라서, 파이썬만으로도 R을 대체할 수 있는 경우가 많습니다. 다만, 특정 분석 기법이나 시각화에서 R이 더 편리할 수도 있으니 상황에 맞게 선택하는 것이 중요합니다.

 

반응형
Posted by 마르띤
,