티스토리 뷰

아래 글에 이어 작성된 글입니다.

 

 

[인공지능] 딥러닝과 Python 라이브러리

본격적으로 인공지능을 실습하기 전 Python의 여러 라이브러리를 소개합니다. Python(파이썬)은 머신러닝 프로젝트의 언어로 많이 선택되는 대표적인 언어입니다. 파이썬의 여러 라이브러리를 이

munak.tistory.com

 

기본적인 Numpy의 사용법과 연습문제 풀이를 진행합니다. 

 


 

 

Numpy

Numpy(넘파이)는 과학 및 공학 분야에서 널리 사용되는 파이썬의 오픈소스 라이브러리로 기존 파이썬에서 제공하는 리스트보다  빠른 계산 속도를 가진 ndarray(다차원 배열) 배열을 사용할 수 있도록 해줍니다. 인공지능의 반은 잘 정제된 데이터이기 때문에 좋은 품질의 데이터를 모으고 또 그 데이터를 이해하는 것이 굉장히 중요합니다. 넘파이는 이에 특화된 라이브러리입니다.

 

 

NumPy

Powerful N-dimensional arrays Fast and versatile, the NumPy vectorization, indexing, and broadcasting concepts are the de-facto standards of array computing today. Numerical computing tools NumPy offers comprehensive mathematical functions, random number g

numpy.org

 

 

 

 

Numpy 적재하기

파이썬 코드에서 넘파이를 사용하기 위해서는 라이브러리를 적재해야 합니다. 보통 as명령어를 사용해 np로 별칭을 붙힌 후 사용합니다.

 

import numpy as np

 

 

 

 

Numpy 배열 생성하기

배열은 넘파이의 중심이 되는 데이터 구조로  array( ) 함수로 생성할 수 있습니다. 함수 안에 리스트를 넣으면 ndarray 객체로 변환해 줍니다. 이때 배열의 요소는 모두 같은 타입을 가져야 합니다. 

 

각 요소는 인덱스로 접근할 수 있습니다. 다차원 배열의 경우 콤마(,) 를 사용해 인덱스를 표기할 수 있습니다.

 

import numpy as np
# 배열 선언하기
a = np.array([1,2,3]) 	# array([1,2,3])

a[0] # 1


# 다차원 배열 선언하기
b = np.array([[1,2,3],[4,5,6],[7,8,9]])  # array([[1,2,3],
						# [4,5,6],
						# [7,8,9]])
b[0, 2] # 3
b[0][2] # 3

 

 

 

 

 

Numpy 배열의 속성

넘파이 배열(ndarray)은 일반적으로 동일한 타입의 요소들이 저장되며 그 크기가 고정된 컨테이너입니다. 배열의 차원 및 항목 수는 형상(shape)에 의해 정의됩니다. 배열의 형상은 각 차원의 크기를 지정하는 정수 튜플 형태이며 이때 차원을 넘파이에서는 axes(축)이라고 부릅니다. ndarry 클래스는 다음과 같은 속성을 가집니다. (아래 속성에 대해 확실히 알고 넘어가야 후에 헷갈리지 않습니다.)

 

속성 설명
ndim 축의 개수                                                                         ex. 2차원 배열인 경우 ndim은 2이다.
shape 배열의 형상, 형상은 정수 튜플로 나타낸다.             ex. n개의 행과 m개의 열이있는 행렬의 경우 (n,m)
size 배열 안에 있는 요소들의 총 개수
dtype 배열 요소들의 자료형                                                   ex. numpy.int32, numpy.int16, numpy.float64 등
itmesize 배열 요소들의 크기, 단위는 바이트 이다.                ex. float64의 경우 8
data 실제 데이터가 저장되는 메모리 블럭의 주소

 

import numpy as np
a = np.array([[0,1,2],[3,4,5],[6,7,8]])

a.shape  # (3,3)
a.ndim   # 2
a.dtype  # dtype('int32')
a.itemsize  # 8
a.size   # 9

 

 

 


 

 

자료형 지정하기

지정하지 않은 디폴트로 np.float64 실수 타입으로 만들어 집니다. 명시적으로 np.int32, np.unicode_ 등을 지정할 수 있습니다.

c = np.array([[1, 2], [3, 4]], dtype=complex)
# array([[1.+0.j, 2.+0.j],
#       [3.+0.j, 4.+0.j]])

 

 

 

1이나 0으로 채워진 배열 생성하기

인수로  배열의 형상을 주어 1이나 0으로 채워진 배열을 생성할 수 있습니다.

np.zeros((3, 4))
# array([[0., 0., 0., 0.],
#       [0., 0., 0., 0.],
#       [0., 0., 0., 0.]])


np.ones((2, 3, 4), dtype=np.int16)
# array([[[1, 1, 1, 1],
#        [1, 1, 1, 1],
#        [1, 1, 1, 1]],
#
#       [[1, 1, 1, 1],
#        [1, 1, 1, 1],
#        [1, 1, 1, 1]]], dtype=int16)

 

 

 

연속되는 값으로 생성하기

arrage( ) 함수를 사용하면 연속되는 값을 가지는 배열을 쉽게 만들수 있습니다. 

 

### arange(시작값, 종료값, 간격)

np.arange(5)
# array([0, 1, 2, 3, 4])

np.arange(1, 6)
# array([1, 2, 3, 4, 5])

np.arange(10, 30, 5)
# array([10, 15, 20, 25])

np.arange(0, 2, 0.3)  # it accepts float arguments
# array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8])

 

 

 

균일한 간격의 값으로 생성하기

linspace( )를 사용해 균일한 간격으로 지정된 개수만큼의 요소를 가지는 배열을 생성할 수 있습니다. 

np.linspace(0, 2, 9)                   # 9 numbers from 0 to 2
# array([0.  , 0.25, 0.5 , 0.75, 1.  , 1.25, 1.5 , 1.75, 2.  ])

 

 

 

정렬하기 

sort( )를 사용해 배열의 요소를 정렬할 수 있습니다. 이때 축의 종류 및 순서를 지정할 수 있습니다.

a = np.array([[1,4],[3,1]])
np.sort(a)                # sort along the last axis
# array([[1, 4],
#       [1, 3]])

np.sort(a, axis=None)     # sort the flattened array
# array([1, 1, 3, 4])

np.sort(a, axis=0)        # sort along the first axis
# array([[1, 1],
#       [3, 4]])

 

 

 

형상(shape) 변경하기

reshape를 이용하면 행렬안의 데이터 개수는 유지한 채로 배열의 차원을 변경할 수 있습니다. 

 

a = np.arange(6)                    # 1d array
# array([0, 1, 2, 3, 4, 5])


b = np.arange(12).reshape(4, 3)     # 2d array
# array([[0, 1, 2],
#	[3, 4, 5],
#	[6, 7, 8],
#	[9, 10, 11]])


c = np.arange(24).reshape(2, 3, 4)  # 3d array
# array([[[0, 1, 2, 3],
#  [4, 5, 6, 7],
#  [8, 9, 10, 11]],

# [[12, 13, 14, 15],
#  [16, 17, 18, 19],
#  [20, 21, 22, 23]]])

 

이때 인수로 -1을 전달하면 데이터 개수에 맞춰 자동으로 배열의 형태가 결정됩니다.

 

a = np.arange(12)
a.reshape(6, -1)
# array([[0, 1],
# 	[2, 3],
#	[4, 5],
#	[6, 7], 
#	[8, 9],
#	[10, 11]])

 

 

 

 

평탄화

flatten( ) 를 사용하면 다차원 배열을 1차원 배열로 변환(평탄화)할 수 있습니다. ravel( )도 같은 기능을 가지고 있습니다! 다만 부모 배열에도 영향을 미치기 때문에 주의해서 사용해야 합니다.

 

# a = np.arange(12)
# a.reshape(6, -1)
a.flatten() # array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

 

 

 


 

 

슬라이싱

파이썬의 리스트에서와 같이 넘파이 배열에서도 인덱싱과 슬라이싱을 사용할 수 있습니다. 

 

import numpy as np

a = np.arange(10)
# array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

 

 

 

생략 시 각각 첫 인덱스와 마지막 인덱스로 지정됩니다.

# 슬라이싱

a[1:3]  # 인덱스 1에서 인덱스 2까지 
# array([1, 2])


a[:3]  # 인덱스 0에서 인덱스 3까지 
# array([0, 1, 2])

a[1:]  # 인덱스 1에서 마지막 인덱스 까지 
# array([1, 2, 3, 4, 5, 6, 7, 8, 9])

 

# 마이너스의 경우 뒤에서 부터 센다
# -1 = (마지막 인덱스) -1
# -2 = (마지막 인덱스) -2 
# ... 

a[:-1]  # 인덱스 0에서 인덱스 -1까지 
# array([0, 1, 2, 3, 4, 5, 6, 7, 8])

a[3:-2]  # 인덱스 3에서 인덱스 -2까지 
# array([3, 4, 5, 6, 7])

 

 

 

2차원 배열의 경우 다음과 같이 활용할 수 있습니다.

data = np.arange(16).reshape(-1,4)
# array([[ 0,  1,  2,  3],
#       [ 4,  5,  6,  7],
#       [ 8,  9, 10, 11],
#       [12, 13, 14, 15]])


data[0]       # array([0, 1, 2, 3])
data[1,:]     # array([4, 5, 6, 7])
data[:,2]     # array([ 2,  6, 10, 14])


data[0:2, 0:2] # array([[0, 1],
               #        [4, 5]])

data[0:2, 2:4] # array([[2, 3],
               #        [6, 7]])



# ::2의 의미는 시작 인덱스 부터 2씩 건너 뛰며 요소를 선택하라는 의미이다.
data[::2, ::2]   # array([[ 0,  2],
                 #         [ 8, 10]])

data[1::2, 1::2] # array([[ 5,  7],
                 #        [13, 15]])

 

 

 

 

논리적인 인덱싱(Logical indexing)

논리적인 인덱싱이란 배열에 특정 조건을 주어 값을 추려내는 것을 말합니다. 이를 잘 이용하면 원하는 값을 쉽게 뽑아낼 수 있습니다.    

# 논리적인 인덱싱
ages = np.array([18, 19, 25, 30, 28])


# 나이가 20 보다 큰 사람 확인하기
ages > 20
# array([False, False,  True,  True,  True])


# 20 보다 큰 요소 뽑아내기
ages[ages > 20]
# array([25, 30, 28])


#  20 보다 크고 30 작은 요소 뽑아내기
ages[(ages > 20) & (ages < 30)]

 

 

 

 

기본 연산

넘파이 배열에는 + 연산자나 * 연산자 등 수학적인 연산자를 적용할 수 있습니다. 

 

arr1 = np.arange(1,4)     # array([1, 2, 3])
arr2 = np.arange(4,7)     # array([4, 5, 6])

arr1 + arr2   # array([5, 7, 9])
arr1 * arr2   # array([ 4, 10, 18])
arr1 - arr2   # array([-3, -3, -3])
arr1 ** arr2  # array([ 1, 32, 729])

 

 

 

 

여러 계산 함수들

sum( )는 요소들의 합을, min( )은 요소들 중 최소값을, max( ) 눈 요소들 중 최대 값을, mean은 평균값을 계산해 줍니다. 이때 axis 속성을 지정하여 각 행이나, 열에 대해서도 쉽게 계산할 수 있습니다.

a = np.array([[1,2,3],[4,5,6],[7,8,9]])  # array([[1, 2, 3],
                                                # [4, 5, 6],
                                                # [7, 8, 9]])
a.sum() # 45
a.min() # 1
a.max() # 9
a.mean() # 5.0

# 각 열의 합
a.sum(axis=0) # array([12, 15, 18])

# 각 열의 평균값
a.mean(axis=0) # array([4., 5., 6.])

# 각 행의 평균값
a.mean(axis=1) # array([2., 5., 8.])

 

 

 

 

+ 브로드 캐스팅

배열과 단일 숫자 사이에서 연산을 하는 경우(벡터와 스칼라 사이의 연산하려고 할때) 넘파이는 자동으로 스칼라를 적절한 벡터로 확장시켜 연산을 진행합니다.

np.arange(5) * 2 # array([0, 2, 4, 6, 8])

 

 

 

 

행렬 곱셈

행렬 곱셈(내적)을 하기 위해서는 *연산자가 아닌 @나 dot( )를 사용하여야 합니다. 

arr1 = np.arange(1,4)     # array([1, 2, 3])
arr2 = np.arange(4,7)     # array([4, 5, 6])

arr1 * arr2    # array([ 4, 10, 18])
arr1 @ arr2    # 32
arr1.dot(arr2) # 32

 

 

 

전치 행렬 구하기

속성 T나 transpose( ) 함수를 이용해 쉽게 전치 행렬을 계산할 수 있습니다.

a = np.array([[1,2,3],[4,5,6],[7,8,9]])   #array([[1, 2, 3],
                                                # [4, 5, 6],
                                                # [7, 8, 9]])
a.T # array([[1, 4, 7],
    #        [2, 5, 8],
    #        [3, 6, 9]])

# a.transpose()

 

 


 

난수 생성하기

넘파이의 random 함수들을 이용하면 다양한 분포의 난수 배열을 쉽게 생성할 수 있습니다.

 

 

Random sampling (numpy.random) — NumPy v1.24.dev0 Manual

Numpy’s random number routines produce pseudo random numbers using combinations of a BitGenerator to create sequences and a Generator to use those sequences to sample from different statistical distributions: BitGenerators: Objects that generate random n

numpy.org

 

 

동일한 값을 계속 얻고자 한다면  seed를 설정해주면 됩니다.

 

# 시드 100으로 설정하기
np.random.seed(100)

 

 

 

 

균일 분포 난수 생성하기

rand( ) 함수를 사용하면 입력한 행, 열을 가지는 0.0과 1.0 사이의 난수 배열을 얻을 수 있습니다.

 

# np.random.seed(100)
np.random.rand(5) # array([0.54340494, 0.27836939, 0.42451759, 0.84477613, 0.00471886])

np.random.rand(2,5) #  array([[0.54340494, 0.27836939, 0.42451759, 0.84477613, 0.00471886],
                    #         [0.12156912, 0.67074908, 0.82585276, 0.13670659, 0.57509333]])

 

 

 

특정 범위에 있는 난수를 생성하려면 수식을 조금 추가하면 됩니다.

 

# a과 b사이의 난수 생성하기
# (b-a) * np.random.rand(3) + a

# 10과 20사이의 난수 생성하기
(20-10) * np.random.rand(3) + 10

 

 

 

 

정수 값을 가지는 난수

randint() 함수를 사용하면 정수 값을 가지는 난수 배열을 얻을 수 있습니다.

 

# np.random.randint(시작값, 종료값, size=크기)
np.random.randint(1,7, size=10) # 1부터 6까지의 값을 가지는 크기 10 배열 구하기

 

 

 

정규분포 난수 생성

randn( ) 함수를 사용하면 정규 분포를 따르는 난수 배열을 생성할 수 있습니다.

# 크기 5의 정규분포 난수 배열 생성하기
np.random.randn(5)

 

 

normal( ) 함수 또한 정규 분포 난수를 생성하는데 사용되며, 정규분포 평균과 표준편차를 인수로 지정할 수 있습니다.

 

# 크기 5의 평균 0, 표준편차를 0.1로 가지는 정규분포 난수 배열 생성하기
np.random.normal(loc=0.0, scale = 0.1, size = 5)

 

 

 

+ 고유 항목 및 개수 얻기

unique( )를 사용하면 배열에서 고유한 요소를 쉽게 추출할 수 있습니다. return_counts를 True로 설정하면 빈도수를 확인할 수 있습니다.

 

# 1부터 6까지의 값을 가지는 크기 100 배열 생성
a = np.random.randint(1,7, size=100) 

np.unique(a) # 고유값 추출
np.unique(a, return_counts = True) # 빈도수 출력

 

 


 

연습문제

위 내용을 바탕으로 [딥러닝 express]의 연습문제 2장을 풀어보았습니다. (개인적인 풀이기에 오류가 있을 수 있습니다!)

 

 

 

 

01. 다음 코드의 최종 결과를 쓰시오.

import numpy as np

my_vector = np.array([1, 2, 3, 4, 5, 6])
selection = my_vector % 2 == 0

my_vector[selection]

 

답 : [2, 4, 6]

논리적인 인덱싱 my_vector %2 == 0 (짝수이면 Ture)에 의해 selection은 [Fasle, True, Fasle, True, Fasle, True,]를  가진 boolean형 배열이 된다. 이를 인덱스로 지정하여 my_vector에 보냈으니 출력은 [2, 4, 6]이다.
 

 

 

 

 

 

02. 다음 코드의 최종 결과를 쓰시오.

import numpy as np

first_matrix = np.array([[1, 2, 3], [4, 5, 6]])
second_matrix = np.array([1, 2, 3])

first_matrix + second_matrix

 

답 : [[2, 4, 6], [5, 7, 9]]

넘파이는 다른 크기, 차원을 가진 행렬에도 산술 연산이 가능하도록 자동으로 스칼라를 확장하는 기능(브로드캐스팅)을 가지고 있기에 second_matrix가 [[1, 2, 3], [1, 2, 3]]로 확장되어 연산이 진행된다. 따라서 정답은 [[1+1, 2+2, 3+3], [4+1, 5+2, 6+3]] = : [[2, 4, 6], [5, 7, 9]]이다.

 

 

 

 

 

 

03. 크기가 10의 널 벡터를 생성하고 다섯 번째 요소는 1로 설정하는 코드를 작성하라.

import numpy as np

array = np.zeros(10)  # np.zeros를 사용하면 크기가 10인 널벡터를 생성할 수 있다.
array[4] = 1  # 원하는 요소를 1로 설정한다.
print(array)  # [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]

 

 

 

04. 10에서 19까지의 값을 가지는 1차원 배열을 생성하라.

import numpy as np

# arange를 사용하면 원하는 범위의 연속되는 값을 가진 배열을 생성할 수 있다.
# arange(시작값, 종료값, 간격)
array = np.arange(10, 20)
print(array)  # [10 11 12 13 14 15 16 17 18 19]

 

 

 

05. 0부터 9까지의 값으로 넘파이 1차원 배열을 채우고, 이 배열을 거꾸로 하는 문장을 작성하라.

import numpy as np

array = np.arange(0, 10)  # arange 를 이용해 0~9 값을 가지는 1차원 배열 생성
array = array[::-1]  # 슬라이싱 : 처음부터 끝까지 -1칸 간격으로 (역순으로) 변환
print(array)  # [9 8 7 6 5 4 3 2 1 0]

 

 

 

 

06. 0부터 8까지의 값을 가지고 크기가 3x3인 행렬을 생성하라.

import numpy as np
# arange를 통해 0~8 값을 가지는 1차원 리스트 생성
# reshape를 통해 3x3 배열로 형태 변경
array = np.arange(9).reshape(3, 3) 
print(array) ###[[0 1 2]
             ### [3 4 5]
             ### [6 7 8]]

 

 

 

 

07. 난수로 채워진 3x3 넘파이 배열을 생성해 보자.

import numpy as np

# random.rand(행, 열)을 이용한 난수(0.0~1.0)로 이루어진 
# 2차원 넘파이 배열을 생성할 수 있다.
array = np.random.rand(3, 3)
print(array)

 

 

 

 

 

08. 임의의 값으로 10x10 배열을 만들고 최소값과 최대값을 찾아보자.

import numpy as np

# random.rand(행, 열)을 이용하 난수(0.0~1.0)로 이루어진
# 2차원 넘파이 배열을 생성할 수 있다.
array = np.random.rand(10, 10)

# min와 max 메소드로 최소, 최대 값을 찾을 수 있다.
print(array.min());
print(array.max());

 

 

 

 

 

 

09. 배열의 테두리에 1, 내부에 0을 가진 3x3 크기의 2차원 배열을 생성해보자. 슬라이싱을 이용한다.

import numpy as np

array = np.ones((3,3))  # 1로 가득 채워진 3x3 넘파이 배열을 생성한다.
array[1:-1,1:-1] = 0    # 1부터 -1(끝부터 1개)까지, 즉 [1][1] 요소를 0으로 바꾼다.
print(array)   #[[1. 1. 1.]
               # [1. 0. 1.]
               # [1. 1. 1.]]

 

 

 

 

10. 5x5 행렬을 만들어 체스 보드 패턴으로 채워보자.

import numpy as np

array = np.zeros((5,5))  # 1로 가득 채워진 5x5 넘파이 배열을 생성한다.
array[1::2, ::2] = 1    # 행은 1부터 2간격 씩, 열은 0부터 2간격 씩 선택해 1로 변경
array[::2, 1::2] = 1    # 행은 0부터 2간격 씩, 열은 1부터 2간격 씩 선택해 1로 변경
print(array)    #[[0. 1. 0. 1. 0.]
                # [1. 0. 1. 0. 1.]
                # [0. 1. 0. 1. 0.]
                # [1. 0. 1. 0. 1.]
                # [0. 1. 0. 1. 0.]]

 

 

 

 

 

11. 3x3 난수로 행렬을 만들고 평균값과 표준 편차로 행렬을 정규화하여 보자.

import numpy as np
# random.rand(행, 열)을 이용한 난수(0.0~1.0)로 이루어진 
# 2차원 넘파이 배열을 생성할 수 있다.
array = np.random.rand(3, 3)    
mean =  np.mean(array)  # mean을 사용해 평균 구하기
std = np.std(array)     # std를 사용해 표준 편차 구하기
array = (array - mean)/std  # 정규화 
print(array)

 

 

 

 

 

12. 넘파이를 사용하여 0에서 9까지의 값을 가진 벡터를 만들고 5에서 8 사이의 숫자 부호를 반전시켜보자.

import numpy as np

array = np.arange(10) # arange를 통해 0~9 값을 가지는 1차원 리스트 생성
# 논리적인 인덱싱으로 5~8값을 선택후 *-1로 부호 반전
array[(array >= 5) & (array <= 8)] *= -1 
print(array) # [ 0  1  2  3  4 -5 -6 -7 -8  9]

 

 

 

 

 

13. 넘파이로 3x3 크기의 2차원 배열을 생성하고, 모든 요소의 합, 각열의 합, 각 행의 합을 계산해보자.

import numpy as np
# arange를 통해 0~8 값을 가지는 1차원 리스트 생성
# reshape를 통해 3x3 배열로 형태 변경
array = np.arange(9).reshape(3, 3) 
print("원본배열 :")
print(array)
print("모든 요소의 합 : ", array.sum()) #  sum으로 모든 요소의 합 출력
print("각 열의 합 : ",array.sum(axis = 0))  # axis을 0으로 지정하여 열 지정
print("각 행의 합 : ",array.sum(axis = 1))  # axis을 0으로 지정하여 행 지정

 

원본배열 :
[[0 1 2]
 [3 4 5]
 [6 7 8]]
 
모든 요소의 합 :  36
각 열의 합 :  [ 9 12 15]
각 행의 합 :  [ 3 12 21]

 

 

 

 

 

 

14. 주어진 두 벡터의 내적을 계산하기 위해 넘파이 프로그램을 작성해보자.

import numpy as np
# 넘파이 배열 선언
arr1 = np.array([4, 5])
arr2 = np.array([7, 10])
print(arr1)
print(arr2)
print("백터의 내적", np.dot(arr1,arr2)) # dot메소드로 내적 계산
print("백터의 내적", arr1 @ arr2)       # @ 연산자로 내적 계산

 

[ 4  5]
[ 7 10]
백터의 내적 78
백터의 내적 78

 

 

 

 

 

15. [2, 0, 3, 6, 8, 12, 10, 9, 18, 20, 22]와 같은 데이터를 이용하여 다음과 같은 선 그래프를 그려보자.

import numpy as np
import matplotlib.pyplot as plt # 맵플롯립 탑재

# 넘파이 배열 선언
x = np.arange(13)   # x축 데이터 삽입
y = np.array([2, 0, 3, 6, 4, 6, 8, 12, 10, 9, 18, 20, 22]) # y축 데이터 삽입

plt.plot(x, y)  # 그래프 그리기
plt.show()      # 그래프 보이기

 

 

 

 

 

 

[인공지능] Numpy로 경사하강법 구현하기

[인공지능] Numpy 기본 사용 + 딥러닝 express 연습문제 2장 아래 글에 이어 작성된 글입니다. [인공지능] 딥러닝과 Python 라이브러리 본격적으로 인공지능을 실습하기 전 Python의 여러 라이브러리를

munak.tistory.com

 

 

 

감사합니다.

 

 

 


 

공부한 내용을 복습/기록하기 위해 작성한 글이므로 내용에 오류가 있을 수 있습니다.

댓글
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Total
Today
Yesterday