├── 004-Dask
├── dask-worker-space
│ ├── global.lock
│ └── purge.lock
└── tutorial-01.py
├── 020-kinesis-single-shard
├── requirements.txt
├── consumer.py
└── producer.py
├── 022-kinesis-data-analytics
├── requirements.txt
├── consumer.py
└── producer.py
├── 204-Kubernetes-Redis
└── redis-custom-config
│ ├── redis.conf
│ ├── kustomization.yaml
│ └── redis-pod.yaml
├── README.md
├── 101-GeoHash
├── images
│ ├── lat-lng.jpg
│ ├── geohash-size.png
│ └── law-of-cosine.svg
├── map.html
└── 02 Lat Lng - Addition, Angle.ipynb
├── 200-Kubernetes
├── app
│ ├── fasion_model.h5
│ ├── requirements.txt
│ └── app.py
├── sample
│ ├── sample_1139.jpg
│ ├── sample_11518.jpg
│ ├── sample_12352.jpg
│ ├── sample_12924.jpg
│ ├── sample_13180.jpg
│ ├── sample_1421.jpg
│ ├── sample_14467.jpg
│ ├── sample_15419.jpg
│ ├── sample_16078.jpg
│ ├── sample_16742.jpg
│ ├── sample_16975.jpg
│ ├── sample_17027.jpg
│ ├── sample_17101.jpg
│ ├── sample_17764.jpg
│ ├── sample_18043.jpg
│ ├── sample_18313.jpg
│ ├── sample_18788.jpg
│ ├── sample_19007.jpg
│ ├── sample_19098.jpg
│ ├── sample_19177.jpg
│ ├── sample_19442.jpg
│ ├── sample_19482.jpg
│ ├── sample_20091.jpg
│ ├── sample_22950.jpg
│ ├── sample_23330.jpg
│ ├── sample_25021.jpg
│ ├── sample_25536.jpg
│ ├── sample_25643.jpg
│ ├── sample_27647.jpg
│ ├── sample_27825.jpg
│ ├── sample_27936.jpg
│ ├── sample_28251.jpg
│ ├── sample_28550.jpg
│ ├── sample_29020.jpg
│ ├── sample_2919.jpg
│ ├── sample_31021.jpg
│ ├── sample_3152.jpg
│ ├── sample_33108.jpg
│ ├── sample_33165.jpg
│ ├── sample_33193.jpg
│ ├── sample_33271.jpg
│ ├── sample_34045.jpg
│ ├── sample_3480.jpg
│ ├── sample_36605.jpg
│ ├── sample_37196.jpg
│ ├── sample_3767.jpg
│ ├── sample_37793.jpg
│ ├── sample_38070.jpg
│ ├── sample_38479.jpg
│ ├── sample_3880.jpg
│ ├── sample_41126.jpg
│ ├── sample_41669.jpg
│ ├── sample_41819.jpg
│ ├── sample_42013.jpg
│ ├── sample_42528.jpg
│ ├── sample_4256.jpg
│ ├── sample_42662.jpg
│ ├── sample_43296.jpg
│ ├── sample_4354.jpg
│ ├── sample_43814.jpg
│ ├── sample_44314.jpg
│ ├── sample_4496.jpg
│ ├── sample_45115.jpg
│ ├── sample_45910.jpg
│ ├── sample_45926.jpg
│ ├── sample_46011.jpg
│ ├── sample_46449.jpg
│ ├── sample_46682.jpg
│ ├── sample_47082.jpg
│ ├── sample_4712.jpg
│ ├── sample_48604.jpg
│ ├── sample_49798.jpg
│ ├── sample_49844.jpg
│ ├── sample_49963.jpg
│ ├── sample_50107.jpg
│ ├── sample_50488.jpg
│ ├── sample_50863.jpg
│ ├── sample_51212.jpg
│ ├── sample_51335.jpg
│ ├── sample_52312.jpg
│ ├── sample_52636.jpg
│ ├── sample_52970.jpg
│ ├── sample_53360.jpg
│ ├── sample_53374.jpg
│ ├── sample_54723.jpg
│ ├── sample_54962.jpg
│ ├── sample_55104.jpg
│ ├── sample_55535.jpg
│ ├── sample_55641.jpg
│ ├── sample_56036.jpg
│ ├── sample_56392.jpg
│ ├── sample_5707.jpg
│ ├── sample_58386.jpg
│ ├── sample_6135.jpg
│ ├── sample_6197.jpg
│ ├── sample_6689.jpg
│ ├── sample_8744.jpg
│ ├── sample_9688.jpg
│ ├── sample_9758.jpg
│ └── sample_9852.jpg
├── deployment.yaml
├── Dockerfile
├── README.md
├── 01-Kubernetes.ipynb
└── 02-Generate-Fashion-MNIST-Sample-Images.ipynb
├── .gitignore
├── 100-PyQT
├── 01-simple-example
│ └── main.py
├── 02-widgets
│ └── main.py
└── 03-QThread
│ ├── qthread.py
│ ├── signal_with_list.py
│ └── signal_with_python_object.py
├── 005-ray
├── script.py
├── submit.py
└── 10-ray-serving-tutorial
│ └── app.py
├── 002-Pyspark
├── macdonald
│ ├── README.md
│ └── check_connection.py
└── 01 Tutorial.ipynb
├── 202-Kubernetes-deploy-nginx
├── README.md
└── deployment.yaml
├── 003-Shared-Memory
├── shared-memory-list.py
├── shared-memory-bytearray.py
├── shared-string.py
└── shared_memory_queue.py
├── 203-Kubernetes-Service-MySQL
└── deployment.yaml
├── 006-pyarrow
├── test_dataset.py
├── pyarrow_torch.py
└── pyarrow-tutorial.ipynb
└── 010-Pyspark
└── 01 Tutorial.ipynb
/004-Dask/dask-worker-space/global.lock:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/004-Dask/dask-worker-space/purge.lock:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/020-kinesis-single-shard/requirements.txt:
--------------------------------------------------------------------------------
1 | Faker==4.9.0
--------------------------------------------------------------------------------
/022-kinesis-data-analytics/requirements.txt:
--------------------------------------------------------------------------------
1 | Faker==4.9.0
--------------------------------------------------------------------------------
/204-Kubernetes-Redis/redis-custom-config/redis.conf:
--------------------------------------------------------------------------------
1 | maxmemory 2mb
2 | maxmemory-policy allkeys-lru
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # code-snippet
2 |
3 | 바로 가져가서 사용할 수 있는 코드들을 예제와 함께 제공을 합니다.
4 |
5 | 주로 Python 코드들을 지원하고 있습니다.
6 |
7 |
--------------------------------------------------------------------------------
/101-GeoHash/images/lat-lng.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/101-GeoHash/images/lat-lng.jpg
--------------------------------------------------------------------------------
/101-GeoHash/images/geohash-size.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/101-GeoHash/images/geohash-size.png
--------------------------------------------------------------------------------
/200-Kubernetes/app/fasion_model.h5:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/app/fasion_model.h5
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_1139.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_1139.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_11518.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_11518.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_12352.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_12352.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_12924.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_12924.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_13180.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_13180.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_1421.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_1421.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_14467.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_14467.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_15419.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_15419.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_16078.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_16078.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_16742.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_16742.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_16975.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_16975.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_17027.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_17027.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_17101.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_17101.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_17764.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_17764.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_18043.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_18043.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_18313.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_18313.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_18788.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_18788.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_19007.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_19007.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_19098.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_19098.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_19177.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_19177.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_19442.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_19442.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_19482.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_19482.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_20091.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_20091.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_22950.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_22950.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_23330.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_23330.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_25021.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_25021.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_25536.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_25536.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_25643.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_25643.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_27647.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_27647.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_27825.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_27825.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_27936.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_27936.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_28251.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_28251.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_28550.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_28550.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_29020.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_29020.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_2919.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_2919.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_31021.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_31021.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_3152.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_3152.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_33108.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_33108.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_33165.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_33165.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_33193.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_33193.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_33271.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_33271.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_34045.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_34045.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_3480.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_3480.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_36605.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_36605.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_37196.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_37196.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_3767.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_3767.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_37793.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_37793.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_38070.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_38070.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_38479.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_38479.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_3880.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_3880.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_41126.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_41126.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_41669.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_41669.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_41819.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_41819.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_42013.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_42013.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_42528.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_42528.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_4256.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_4256.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_42662.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_42662.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_43296.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_43296.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_4354.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_4354.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_43814.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_43814.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_44314.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_44314.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_4496.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_4496.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_45115.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_45115.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_45910.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_45910.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_45926.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_45926.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_46011.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_46011.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_46449.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_46449.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_46682.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_46682.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_47082.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_47082.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_4712.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_4712.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_48604.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_48604.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_49798.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_49798.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_49844.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_49844.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_49963.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_49963.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_50107.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_50107.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_50488.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_50488.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_50863.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_50863.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_51212.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_51212.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_51335.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_51335.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_52312.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_52312.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_52636.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_52636.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_52970.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_52970.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_53360.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_53360.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_53374.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_53374.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_54723.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_54723.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_54962.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_54962.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_55104.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_55104.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_55535.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_55535.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_55641.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_55641.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_56036.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_56036.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_56392.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_56392.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_5707.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_5707.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_58386.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_58386.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_6135.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_6135.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_6197.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_6197.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_6689.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_6689.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_8744.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_8744.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_9688.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_9688.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_9758.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_9758.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/sample/sample_9852.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndersonJo/code-snippet/master/200-Kubernetes/sample/sample_9852.jpg
--------------------------------------------------------------------------------
/200-Kubernetes/app/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask==1.1.2
2 | Keras==2.4.3
3 | h5py==2.10.0
4 | tensorflow-cpu==2.4.0
5 | Pillow==7.2.0
6 | numpy==1.18.5
7 | opencv-python==4.4.0.42
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Python
2 | *.pyc
3 |
4 | # Project
5 | .idea
6 | .ipynb_checkpoints
7 |
8 | # Useless Files
9 | _SUCCESS
10 | *.crc
11 |
12 | # Data
13 | *.parquet
14 | *.orc
15 |
--------------------------------------------------------------------------------
/204-Kubernetes-Redis/redis-custom-config/kustomization.yaml:
--------------------------------------------------------------------------------
1 | configMapGenerator:
2 | - name: example-redis-config
3 | files:
4 | - redis.conf
5 | resources:
6 | - redis-pod.yaml
7 |
--------------------------------------------------------------------------------
/100-PyQT/01-simple-example/main.py:
--------------------------------------------------------------------------------
1 | from PyQt5.QtWidgets import QApplication, QLabel
2 | import sys
3 |
4 | app = QApplication(sys.argv)
5 | label = QLabel('Hello World')
6 | label.show()
7 | app.exec()
8 |
--------------------------------------------------------------------------------
/005-ray/script.py:
--------------------------------------------------------------------------------
1 | import ray
2 |
3 |
4 | @ray.remote
5 | def hello_world():
6 | print(ray.cluster_resources())
7 | return "hello world"
8 |
9 |
10 | ray.init()
11 | print(ray.get(hello_world.remote()))
12 |
--------------------------------------------------------------------------------
/002-Pyspark/macdonald/README.md:
--------------------------------------------------------------------------------
1 | # 1. Tutorial
2 |
3 | ## 1.1 Preparation
4 |
5 | S3 Bucket 하나 만들고, mcdonalds_dataset.csv 업로드 합니다.
6 | 예제에서의 S3 Bucket 이름은 data-emr-tutorial입니다.
7 |
8 | ```bash
9 | $ aws s3 cp mcdonalds_dataset.csv s3://data-emr-tutorial/data/
10 | $ aws s3 ls data-emr-tutorial/data/
11 | ```
12 |
13 | ## 1.2 Run Script
14 |
15 |
--------------------------------------------------------------------------------
/004-Dask/tutorial-01.py:
--------------------------------------------------------------------------------
1 | from dask.distributed import Client, progress
2 | import dask.array as da
3 |
4 | client = Client(processes=False, threads_per_worker=4, n_workers=1, memory_limit='2GB')
5 | x = da.random.random((10000, 10000), chunks=(1000, 1000))
6 | y = x + x.T
7 | z = y[::2, 5000:].mean(axis=1)
8 |
9 | print(x)
10 | print(z.compute())
11 | print(x.shape)
--------------------------------------------------------------------------------
/202-Kubernetes-deploy-nginx/README.md:
--------------------------------------------------------------------------------
1 | # Tutorial
2 |
3 | ## Deployment
4 |
5 | 먼저 deployment.yaml 파일을 디플로이 시킵니다.
6 |
7 | ```bash
8 | kubectl apply -f deployment.yaml
9 | ```
10 |
11 | 적용뒤에 라벨을 확인홥니다.
12 |
13 | ```bash
14 | kubectl describe deployments.apps nginx-deployment
15 | k get pods -l app=nginx
16 | ```
17 |
18 | 이후에 replicas 를 1로 변경해준다음에 `kuberctl apply -f deployment.yaml` 실행해서 업데이트 해줍니다.
19 | pods 을 확인해서 1개가 terminating되고 있는지 확인합니다.
20 |
--------------------------------------------------------------------------------
/202-Kubernetes-deploy-nginx/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
2 | kind: Deployment
3 | metadata:
4 | name: nginx-deployment
5 | spec:
6 | selector:
7 | matchLabels:
8 | app: nginx
9 | replicas: 2 # tells deployment to run 2 pods matching the template
10 | template:
11 | metadata:
12 | labels:
13 | app: nginx
14 | spec:
15 | containers:
16 | - name: nginx
17 | image: nginx:latest
18 | ports:
19 | - containerPort: 80
--------------------------------------------------------------------------------
/002-Pyspark/macdonald/check_connection.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from random import random
3 | from operator import add
4 |
5 | from pyspark.sql import SparkSession
6 |
7 | spark = SparkSession \
8 | .builder \
9 | .appName("PythonPi") \
10 | .getOrCreate()
11 |
12 | partitions = int(sys.argv[1]) if len(sys.argv) > 1 else 2
13 | n = 100000 * partitions
14 |
15 |
16 | def f(_: int) -> float:
17 | x = random() * 2 - 1
18 | y = random() * 2 - 1
19 | return 1 if x ** 2 + y ** 2 <= 1 else 0
20 |
21 |
22 | count = spark.sparkContext.parallelize(range(1, n + 1), partitions).map(f).reduce(add)
23 | print("Pi is roughly %f" % (4.0 * count / n))
24 |
25 | spark.stop()
26 |
--------------------------------------------------------------------------------
/022-kinesis-data-analytics/consumer.py:
--------------------------------------------------------------------------------
1 | from boto import kinesis as boto_kinesis
2 |
3 |
4 | def main():
5 | kinesis = boto_kinesis.connect_to_region('us-east-2')
6 |
7 | shard_id = 'shardId-000000000000' # Shard는 1개ch만 갖고 있음
8 | shard_it = kinesis.get_shard_iterator('AndersonStream', shard_id, 'LATEST')['ShardIterator']
9 | print('Latest Shard Iterator:', shard_it)
10 |
11 | while True:
12 | _out = kinesis.get_records(shard_it, limit=10)
13 | records = _out['Records']
14 |
15 | for r in records:
16 | print(r['Data'])
17 |
18 | shard_it = _out['NextShardIterator']
19 | if not records:
20 | break
21 |
22 |
23 | if __name__ == '__main__':
24 | main()
25 |
--------------------------------------------------------------------------------
/020-kinesis-single-shard/consumer.py:
--------------------------------------------------------------------------------
1 | import json
2 | from pprint import pprint
3 |
4 | from boto import kinesis as boto_kinesis
5 |
6 |
7 | def main():
8 | kinesis = boto_kinesis.connect_to_region('us-east-2')
9 |
10 | shard_id = 'shardId-000000000003' # Shard는 1개ch만 갖고 있음
11 | shard_it = kinesis.get_shard_iterator('AndersonStream', shard_id, 'LATEST')['ShardIterator']
12 | print('Latest Shard Iterator:', shard_it)
13 |
14 | while True:
15 | _out = kinesis.get_records(shard_it, limit=10)
16 | records = _out['Records']
17 |
18 | for r in records:
19 | print(r['Data'])
20 |
21 | shard_it = _out['NextShardIterator']
22 | if not records:
23 | break
24 |
25 |
26 | if __name__ == '__main__':
27 | main()
28 |
--------------------------------------------------------------------------------
/200-Kubernetes/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: myapp
5 | spec:
6 | type: NodePort
7 | ports:
8 | - protocol: TCP
9 | port: 80
10 | targetPort: 5000
11 | selector:
12 | app: myapp
13 | ---
14 | apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
15 | kind: Deployment
16 | metadata:
17 | name: myapp-deployment
18 | spec:
19 | selector:
20 | matchLabels:
21 | app: myapp
22 | replicas: 1 # tells deployment to run 2 pods matching the template
23 | template:
24 | metadata:
25 | labels:
26 | app: myapp
27 | spec:
28 | containers:
29 | - name: myapp
30 | image: myapp:latest
31 | imagePullPolicy: Never
32 | ports:
33 | - containerPort: 5000
--------------------------------------------------------------------------------
/200-Kubernetes/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:20.04
2 | MAINTAINER Anderson "a141890@gmail.com"
3 |
4 | ARG DEBIAN_FRONTEND=noninteractive
5 | ENV TZ=Asia/Seoul
6 | RUN apt-get update -y && \
7 | apt-get install -y python3-pip python3-dev libgl1-mesa-dev libgl1-mesa-glx libglib2.0-0 \
8 | build-essential cmake git pkg-config libgtk-3-dev \
9 | libavcodec-dev libavformat-dev libswscale-dev libv4l-dev \
10 | libxvidcore-dev libx264-dev libjpeg-dev libpng-dev libtiff-dev \
11 | gfortran openexr libatlas-base-dev python3-dev python3-numpy \
12 | libtbb2 libtbb-dev libdc1394-22-dev
13 |
14 | COPY ./app /app
15 | WORKDIR /app
16 | RUN pip3 install -r requirements.txt
17 |
18 | ENTRYPOINT [ "python3"]
19 | CMD [ "app.py" ]
--------------------------------------------------------------------------------
/204-Kubernetes-Redis/redis-custom-config/redis-pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: redis
5 | spec:
6 | containers:
7 | - name: redis
8 | image: redis:5.0.4
9 | command:
10 | - redis-server
11 | - "/redis-master/redis.conf"
12 | env:
13 | - name: MASTER
14 | value: "true"
15 | ports:
16 | - containerPort: 6379
17 | resources:
18 | limits:
19 | cpu: "0.1"
20 | volumeMounts:
21 | - mountPath: /redis-master-data
22 | name: data
23 | - mountPath: /redis-master
24 | name: config
25 | volumes:
26 | - name: data
27 | emptyDir: {}
28 | - name: config
29 | configMap:
30 | name: example-redis-config
31 | items:
32 | - key: redis-config
33 | path: redis.conf
34 |
--------------------------------------------------------------------------------
/005-ray/submit.py:
--------------------------------------------------------------------------------
1 | from ray.job_submission import JobSubmissionClient, JobStatus
2 | import time
3 |
4 | client = JobSubmissionClient("http://localhost:8265")
5 | job_id = client.submit_job(
6 | entrypoint="python script.py",
7 | runtime_env={
8 | 'working_dir': './' # 이게 있어야지 script.py 파일이 클러스터에 업로드 / 내부적으로 _upload_working_dir_if_needed 함수 호출
9 | }
10 | )
11 | print(job_id)
12 |
13 |
14 | def wait_until_status(job_id, status_to_wait_for, timeout_seconds=5):
15 | start = time.time()
16 | while time.time() - start <= timeout_seconds:
17 | status = client.get_job_status(job_id)
18 | print(f"status: {status}")
19 | if status in status_to_wait_for:
20 | break
21 | time.sleep(1)
22 |
23 |
24 | wait_until_status(job_id, {JobStatus.SUCCEEDED, JobStatus.STOPPED, JobStatus.FAILED})
25 | logs = client.get_job_logs(job_id)
26 | print(logs)
27 |
--------------------------------------------------------------------------------
/003-Shared-Memory/shared-memory-list.py:
--------------------------------------------------------------------------------
1 | from multiprocessing.shared_memory import ShareableList
2 | import numpy as np
3 | from multiprocessing import Process
4 |
5 |
6 | def daemon_run(name):
7 | a = ShareableList(name=name)
8 | data = np.array(a)
9 | print(f'[Processor] data: {data[:4]} | size: {len(data)}') # [Processor] data: [255 11 0 100] | size: 4096
10 | for i, v in enumerate(['def', -9999999999, 0.123456789123456, 8889999]):
11 | a[i] = v
12 |
13 |
14 | def main():
15 | # Shared Memory 생성
16 | a = ShareableList(['abc', 9999999, -100, 0.123456789])
17 |
18 | p = Process(target=daemon_run, args=(a.shm.name,)) # 프로세서를 열고, shared memory 를 읽어서 출력한다.
19 | p.start()
20 | p.join()
21 |
22 | data = np.array(a)
23 | print(f'[Main] data: {data[:4]} | size: {len(a)}') # [Main] data: [1 2 3 4] | size: 10
24 |
25 |
26 | if __name__ == '__main__':
27 | main()
28 |
--------------------------------------------------------------------------------
/200-Kubernetes/app/app.py:
--------------------------------------------------------------------------------
1 | from tempfile import gettempdir
2 |
3 | import cv2
4 | import numpy as np
5 | from flask import Flask, request, jsonify
6 | from keras.models import load_model
7 |
8 | app = Flask(__name__)
9 | model = load_model('fasion_model.h5')
10 |
11 |
12 | @app.route('/')
13 | def hello_world():
14 | return 'Hello! Anderson!'
15 |
16 |
17 | @app.route('/predict', methods=['POST'])
18 | def predict():
19 | tmp_dir = gettempdir()
20 | f = request.files["image"]
21 | f.save(tmp_dir + '/img.jpg', cv2.IMREAD_COLOR)
22 | img = cv2.imread(tmp_dir + '/img.jpg', cv2.IMREAD_COLOR)[:, :, 0]
23 | img = np.expand_dims(img, axis=0)
24 | pred_y = model.predict(img)[0]
25 | pred_label = int(np.argmax(pred_y))
26 | prob = float(pred_y[pred_label])
27 | return jsonify({'prediction': pred_label, 'prob': prob})
28 |
29 |
30 | if __name__ == '__main__':
31 | app.run(debug=True, host='0.0.0.0')
32 |
--------------------------------------------------------------------------------
/100-PyQT/02-widgets/main.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from typing import Optional
3 |
4 | from PyQt5.QtCore import QObject
5 | from PyQt5.QtWidgets import *
6 |
7 |
8 | class WidgetGallery(QDialog):
9 |
10 | def __init__(self, parent: Optional[QObject] = None):
11 | super(WidgetGallery, self).__init__(parent)
12 | self.originalPalette = QApplication.palette()
13 | styleComboBox = QComboBox()
14 | styleComboBox.addItems(QStyleFactory.keys())
15 |
16 | styleLabel = QLabel("&Style:")
17 | styleLabel.setBuddy(styleComboBox)
18 |
19 | topLayout = QHBoxLayout()
20 | topLayout.addWidget(styleLabel)
21 |
22 | mainLayout = QGridLayout()
23 | mainLayout.addLayout(topLayout, 0, 0, 1, 2)
24 | self.setLayout(mainLayout)
25 |
26 |
27 | if __name__ == '__main__':
28 | app = QApplication(sys.argv)
29 | gallery = WidgetGallery()
30 | gallery.show()
31 | sys.exit(app.exec())
32 |
--------------------------------------------------------------------------------
/003-Shared-Memory/shared-memory-bytearray.py:
--------------------------------------------------------------------------------
1 | from multiprocessing.shared_memory import SharedMemory
2 | import numpy as np
3 | from multiprocessing import Process
4 |
5 |
6 | def daemon_run(name):
7 | shm = SharedMemory(name=name)
8 | data = np.array(shm.buf)
9 | print(f'[Processor] data: {data[:4]} | size: {len(data)}') # [Processor] data: [255 11 0 100] | size: 4096
10 | shm.buf[:4] = bytearray([1, 2, 3, 4])
11 |
12 |
13 | def main():
14 | # Shared Memory 생성
15 | shm = SharedMemory(create=True, size=1024*1024*8)
16 | shm.buf[:4] = bytearray([255, 11, 0, 100]) # 값은 [0~256) 사이의 값만 가능
17 |
18 | p = Process(target=daemon_run, args=(shm.name,)) # 프로세서를 열고, shared memory 를 읽어서 출력한다.
19 | p.start()
20 | p.join()
21 |
22 | data = np.array(shm.buf)
23 | print(f'[Main] data: {data[:4]} | size: {len(shm.buf)}') # [Main] data: [1 2 3 4] | size: 10
24 |
25 | import ipdb
26 | ipdb.set_trace()
27 |
28 |
29 | if __name__ == '__main__':
30 | main()
31 |
--------------------------------------------------------------------------------
/203-Kubernetes-Service-MySQL/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: mysql
5 | spec:
6 | ports:
7 | - port: 3306
8 | selector:
9 | app: mysql
10 | clusterIP: None
11 | ---
12 | apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
13 | kind: Deployment
14 | metadata:
15 | name: mysql
16 | spec:
17 | selector:
18 | matchLabels:
19 | app: mysql
20 | strategy:
21 | type: Recreate
22 | template:
23 | metadata:
24 | labels:
25 | app: mysql
26 | spec:
27 | containers:
28 | - image: mysql:5.6
29 | name: mysql
30 | env:
31 | # Use secret in real usage
32 | - name: MYSQL_ROOT_PASSWORD
33 | value: 1234
34 | ports:
35 | - containerPort: 3306
36 | name: mysql
37 | volumeMounts:
38 | - name: mysql-persistent-storage
39 | mountPath: /var/lib/mysql
40 | volumes:
41 | - name: mysql-persistent-storage
42 | persistentVolumeClaim:
43 | claimName: mysql-pv-claim
--------------------------------------------------------------------------------
/022-kinesis-data-analytics/producer.py:
--------------------------------------------------------------------------------
1 | import json
2 | import random
3 | from time import sleep
4 |
5 | from boto import kinesis as boto_kinesis
6 | from faker import Faker
7 |
8 |
9 | def generate_data(faker):
10 | return {'name': faker.name(),
11 | 'age': random.randint(10, 20),
12 | 'gender': random.choice(['M', 'F']),
13 | 'score': random.choice(range(40, 70, 5)),
14 | 'job': faker.job()}
15 |
16 |
17 | def main():
18 | faker = Faker()
19 | kinesis = boto_kinesis.connect_to_region('us-east-2')
20 | print('Connected')
21 |
22 | if 'AndersonStream' not in kinesis.list_streams()['StreamNames']:
23 | kinesis.create_stream('AndersonStream', 1)
24 | print('AndersonStream Stream has been created')
25 |
26 | while True:
27 | sleep(1)
28 | print(kinesis.list_streams())
29 | if 'AndersonStream' in kinesis.list_streams()['StreamNames']:
30 | kinesis = boto_kinesis.connect_to_region('us-east-2')
31 | break
32 |
33 | for _ in range(50):
34 | data = generate_data(faker)
35 | res = kinesis.put_record('AndersonStream', json.dumps(data), 'partitionkey' + str(random.choice([0, 1])))
36 | print('PUT', data)
37 | print(' ', res['SequenceNumber'], '\n')
38 |
39 | # kinesis.delete_stream('AndersonStream')
40 |
41 |
42 | if __name__ == '__main__':
43 | main()
44 |
--------------------------------------------------------------------------------
/101-GeoHash/images/law-of-cosine.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/003-Shared-Memory/shared-string.py:
--------------------------------------------------------------------------------
1 | from multiprocessing.shared_memory import SharedMemory
2 | import numpy as np
3 | from multiprocessing import Process
4 |
5 |
6 | def daemon_run(in_name, out_name):
7 | in_shm = SharedMemory(name=in_name)
8 | out_shm = SharedMemory(name=out_name)
9 | xb = in_shm.buf[:1024].tobytes()
10 | text = xb.decode('euc-kr').strip()
11 | print(f'[Processor] data: {text} | byte: {len(xb)}') # [Processor] data: [255 11 0 100] | size: 4096
12 | assert text == '한글1234 ABC %^&'
13 |
14 | xb = '프로세서에서 리턴된 스트링 1234!'.encode('euc-kr')
15 | out_shm.buf[:len(xb)] = xb
16 |
17 |
18 | def main():
19 | # Shared Memory 생성
20 | shm1 = SharedMemory(create=True, size=1024 * 1024 * 64)
21 | shm2 = SharedMemory(create=True, size=1024 * 1024 * 64)
22 |
23 | shm1.buf[:] = (' ' * len(shm1.buf)).encode('euc-kr')
24 | shm2.buf[:] = (' ' * len(shm2.buf)).encode('euc-kr')
25 |
26 | x = '한글1234 ABC %^&'
27 | xb = x.encode('euc-kr')
28 | shm1.buf[:len(xb)] = xb # 값은 [0~256) 사이의 값만 가능
29 | p = Process(target=daemon_run, args=(shm1.name, shm2.name)) # 프로세서를 열고, shared memory 를 읽어서 출력한다.
30 | p.start()
31 |
32 | ob = shm2.buf[:1024].tobytes()
33 | text = ob.decode('euc-kr').strip()
34 | print(f'[Main ] data: {text} | byte: {len(ob)}') # [Main] data: [1 2 3 4] | size: 10
35 |
36 | assert text == '프로세서에서 리턴된 스트링 1234!'
37 |
38 | shm1.close()
39 | shm2.close()
40 | p.join()
41 |
42 |
43 | if __name__ == '__main__':
44 | main()
45 |
--------------------------------------------------------------------------------
/020-kinesis-single-shard/producer.py:
--------------------------------------------------------------------------------
1 | import json
2 | import random
3 | from time import sleep
4 |
5 | from boto import kinesis as boto_kinesis
6 | from faker import Faker
7 |
8 |
9 | def generate_data(faker):
10 | return {'name': faker.name(),
11 | 'age': random.randint(10, 20),
12 | 'gender': random.choice(['M', 'F']),
13 | 'score': random.choice(range(40, 70, 5)),
14 | 'data': {'id': random.randint(0, 10000),
15 | 'type': random.choice(['a', 'b', 'c'])}}
16 |
17 |
18 | def main():
19 | faker = Faker()
20 | kinesis = boto_kinesis.connect_to_region('us-east-2')
21 | print('Connected')
22 |
23 | if 'AndersonStream' not in kinesis.list_streams()['StreamNames']:
24 | kinesis.create_stream('AndersonStream', 1)
25 | print('AndersonStream Stream has been created')
26 |
27 | while True:
28 | sleep(1)
29 | print(kinesis.list_streams())
30 | if 'AndersonStream' in kinesis.list_streams()['StreamNames']:
31 | kinesis = boto_kinesis.connect_to_region('us-east-2')
32 | break
33 | i = 0
34 | while True:
35 | i += 1
36 | data = generate_data(faker)
37 | data['i'] = i
38 | res = kinesis.put_record('AndersonStream', json.dumps(data), 'partitionkey' + str(random.randint(0, 10)))
39 | print(f'{i:2}', data)
40 | print(' ', res, '\n')
41 |
42 | # kinesis.delete_stream('AndersonStream')
43 |
44 |
45 | if __name__ == '__main__':
46 | main()
47 |
--------------------------------------------------------------------------------
/200-Kubernetes/README.md:
--------------------------------------------------------------------------------
1 | # Tutorial
2 |
3 | ## Preparation
4 |
5 | 1. 먼저 [Kaggle Fahsion MNIST](https://www.kaggle.com/zalando-research/fashionmnist) 에서 데이터를 다운로드 받습니다.
6 |
7 |
8 | ## Postman 설정
9 |
10 | - POST 설정
11 | - body -> form-data
12 | - key: image
13 | - key 에서 파일로 변경
14 | - value: 파일 업로드
15 |
16 | ## Docker 확인
17 |
18 | ```bash
19 | docker build -t myapp .
20 | docker run -p 5000:5000 myapp
21 | ```
22 |
23 | Daemon으로도 실행
24 |
25 | ```bash
26 | docker run -d -p 5000:5000 --name myapp myapp:latest
27 | ```
28 |
29 | Postman에서 확인 합니다.
30 |
31 | ## Docker Hub로 올리기
32 |
33 | 태그걸어주고 Docker Hub에 올립니다.
34 |
35 | ```bash
36 | docker tag myapp andersonjo/myapp
37 | docker push andersonjo/myapp
38 | ```
39 |
40 | ## Docker Hub에서 Pull 안하고 Minikube Image 사용하는 방법
41 |
42 | ```bash
43 | eval $(minikube docker-env)
44 | ```
45 |
46 | 그 다음 build 를 해줍니다.
47 |
48 | ```bash
49 | docker build -t myapp .
50 | ```
51 |
52 | Minikube안에서 실행을 합니다.
53 |
54 | ```bash
55 | kubectl run myapp-kube --image=myapp:latest --image-pull-policy=Never
56 | ```
57 |
58 | pods을 확인 하고, Postman에서도 확인합니다.
59 |
60 | ```bash
61 | kubectl get pods
62 | kubectl port-forward myapp-kube 5000:5000
63 | ```
64 |
65 | ## stateful 로 해보기
66 |
67 | 아래의 두개의 명령이 되어 있어야 합니다.
68 |
69 | ```bash
70 | eval $(minikube docker-env)
71 | docker build -t myapp .
72 | ```
73 |
74 | 배포합니다.
75 |
76 | ```bash
77 | kubectl apply -f deployment.yaml
78 |
79 | kubectl port-forward svc/myapp 5000:80
80 | ```
81 |
82 | 최종적으로 Minikube로 서비스 포트를 열수 있습니다.
83 |
84 | ```bash
85 | minikube service myapp
86 | ```
--------------------------------------------------------------------------------
/005-ray/10-ray-serving-tutorial/app.py:
--------------------------------------------------------------------------------
1 | from ray import serve
2 | from starlette.requests import Request
3 | from transformers import M2M100ForConditionalGeneration, M2M100Tokenizer
4 |
5 |
6 | @serve.deployment(num_replicas=2, ray_actor_options={"num_cpus": 1, "num_gpus": 0})
7 | class Translator:
8 | def __init__(self):
9 | # Load model
10 | self.tokenizer = M2M100Tokenizer.from_pretrained("facebook/m2m100_1.2B")
11 | self.tokenizer.src_lang = 'en'
12 |
13 | self.model = M2M100ForConditionalGeneration.from_pretrained("facebook/m2m100_1.2B")
14 | self.model.eval()
15 |
16 | def translate(self, text: str) -> str:
17 | dest_lang_id = self.tokenizer.get_lang_id('ko')
18 | encoded_src = self.tokenizer(text, return_tensors="pt")
19 | generated_tokens = self.model.generate(**encoded_src,
20 | forced_bos_token_id=dest_lang_id,
21 | max_length=200,
22 | use_cache=True)
23 | result = self.tokenizer.batch_decode(generated_tokens, skip_special_tokens=True)[0]
24 | return result
25 |
26 | async def __call__(self, http_request: Request) -> str:
27 | korean_text: str = await http_request.json()
28 | return self.translate(korean_text)
29 |
30 |
31 | translator = Translator.bind()
32 |
33 | # if __name__ == '__main__':
34 | # translator = Translator()
35 | # print(translator.translate('self-belief and hard work will always earn you success'))
36 |
--------------------------------------------------------------------------------
/100-PyQT/03-QThread/qthread.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from datetime import datetime
3 | from multiprocessing import Process, Queue
4 |
5 | import numpy as np
6 | from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot, QCoreApplication
7 | from PyQt5.QtWidgets import QMainWindow, QApplication
8 |
9 |
10 | def producer(que: Queue):
11 | data = ''.join([str(np.random.rand()) for _ in range(20)])
12 | for i in range(100000):
13 | que.put(data, block=False)
14 |
15 |
16 | class Consumer(QThread):
17 | poped = pyqtSignal(str)
18 |
19 | def __init__(self, que: Queue):
20 | super().__init__()
21 | self.que = que
22 |
23 | def run(self):
24 | while True:
25 | data = self.que.get()
26 | self.poped.emit(data)
27 |
28 |
29 | class MyWindow(QMainWindow):
30 | def __init__(self, que):
31 | super().__init__()
32 | self.setWindowTitle('Test Haha')
33 | self.setGeometry(200, 200, 300, 200)
34 | self.statusBar().showMessage('Hello!')
35 | self.statusBar().setStyleSheet('border:1px solid #333333;')
36 |
37 | self.consumer = Consumer(que)
38 | self.consumer.poped.connect(self.process_data)
39 | self.consumer.start()
40 | self.cnt = 0
41 |
42 | @pyqtSlot(str)
43 | def process_data(self, data):
44 | self.cnt += 1
45 | self.statusBar().showMessage(str(self.cnt))
46 | if self.cnt >= 100000:
47 | QCoreApplication.instance().quit()
48 |
49 |
50 | if __name__ == '__main__':
51 | start_dt = datetime.now()
52 | que = Queue()
53 | p = Process(name='producer', target=producer, args=(que,), daemon=True)
54 | p.start()
55 |
56 | # Main Application
57 | app = QApplication(sys.argv)
58 | window = MyWindow(que)
59 | window.show()
60 | app.exec_()
61 |
62 | print((datetime.now() - start_dt).total_seconds())
63 |
--------------------------------------------------------------------------------
/100-PyQT/03-QThread/signal_with_list.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from datetime import datetime
3 | from multiprocessing import Process, Queue
4 |
5 | import numpy as np
6 | from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot, QCoreApplication
7 | from PyQt5.QtWidgets import QMainWindow, QApplication
8 |
9 |
10 | def producer(que: Queue):
11 | data = ''.join([str(np.random.rand()) for _ in range(20)])
12 | for i in range(100000):
13 | que.put(data, block=False)
14 |
15 |
16 | class Consumer(QThread):
17 | poped = pyqtSignal(list) # 여기가 중요. PyQt_PyObject 이걸로 해야 함
18 |
19 | def __init__(self, que: Queue):
20 | super().__init__()
21 | self.que = que
22 |
23 | def run(self):
24 | while True:
25 | data = self.que.get()
26 | self.poped.emit([1, data])
27 |
28 |
29 | class MyWindow(QMainWindow):
30 | def __init__(self, que):
31 | super().__init__()
32 | self.setWindowTitle('Test Haha')
33 | self.setGeometry(200, 200, 300, 200)
34 | self.statusBar().showMessage('Hello!')
35 | self.statusBar().setStyleSheet('border:1px solid #333333;')
36 |
37 | self.consumer = Consumer(que)
38 | self.consumer.poped.connect(self.process_data)
39 | self.consumer.start()
40 | self.cnt = 0
41 |
42 | @pyqtSlot(list)
43 | def process_data(self, data):
44 | self.cnt += 1
45 | self.statusBar().showMessage(str(self.cnt))
46 | if self.cnt >= 100000:
47 | QCoreApplication.instance().quit()
48 |
49 |
50 | if __name__ == '__main__':
51 | start_dt = datetime.now()
52 | que = Queue()
53 | p = Process(name='producer', target=producer, args=(que,), daemon=True)
54 | p.start()
55 |
56 | # Main Application
57 | app = QApplication(sys.argv)
58 | window = MyWindow(que)
59 | window.show()
60 | app.exec_()
61 |
62 | print((datetime.now() - start_dt).total_seconds())
63 |
--------------------------------------------------------------------------------
/100-PyQT/03-QThread/signal_with_python_object.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from datetime import datetime
3 | from multiprocessing import Process, Queue
4 |
5 | import numpy as np
6 | from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot, QCoreApplication
7 | from PyQt5.QtWidgets import QMainWindow, QApplication
8 |
9 |
10 | def producer(que: Queue):
11 | data = ''.join([str(np.random.rand()) for _ in range(20)])
12 | for i in range(100000):
13 | que.put(data, block=False)
14 |
15 |
16 | class Consumer(QThread):
17 | poped = pyqtSignal('PyQt_PyObject') # 여기가 중요. PyQt_PyObject 이걸로 해야 함
18 |
19 | def __init__(self, que: Queue):
20 | super().__init__()
21 | self.que = que
22 |
23 | def run(self):
24 | class Data:
25 | def __init__(self, value):
26 | self.value = value
27 |
28 | while True:
29 | data = self.que.get()
30 | data_object = Data(data)
31 | self.poped.emit(data_object)
32 |
33 |
34 | class MyWindow(QMainWindow):
35 | def __init__(self, que):
36 | super().__init__()
37 | self.setWindowTitle('Test Haha')
38 | self.setGeometry(200, 200, 300, 200)
39 | self.statusBar().showMessage('Hello!')
40 | self.statusBar().setStyleSheet('border:1px solid #333333;')
41 |
42 | self.consumer = Consumer(que)
43 | self.consumer.poped.connect(self.process_data)
44 | self.consumer.start()
45 | self.cnt = 0
46 |
47 | @pyqtSlot('PyQt_PyObject')
48 | def process_data(self, data):
49 | self.cnt += 1
50 | self.statusBar().showMessage(str(self.cnt))
51 | if self.cnt >= 100000:
52 | QCoreApplication.instance().quit()
53 |
54 |
55 | if __name__ == '__main__':
56 | start_dt = datetime.now()
57 | que = Queue()
58 | p = Process(name='producer', target=producer, args=(que,), daemon=True)
59 | p.start()
60 |
61 | # Main Application
62 | app = QApplication(sys.argv)
63 | window = MyWindow(que)
64 | window.show()
65 | app.exec_()
66 |
67 | print((datetime.now() - start_dt).total_seconds())
68 |
--------------------------------------------------------------------------------
/006-pyarrow/test_dataset.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime, timedelta
2 | from pathlib import Path
3 | from plistlib import Data
4 |
5 | import pandas as pd
6 | import pyarrow as pa
7 | from torch.utils.data import DataLoader
8 |
9 | from pyarrow_torch import PyArrowDataset
10 |
11 |
12 | def test_dataset():
13 | create_data()
14 |
15 | dataset = CustomDataset("./data")
16 | assert dataset[50000] == 50000
17 | assert dataset.read_cnt == 1
18 |
19 | assert dataset[0] == 0
20 | assert dataset.read_cnt == 2
21 |
22 | assert dataset[500000] == 500000
23 | assert dataset.read_cnt == 3
24 |
25 | assert dataset[100] == 100
26 | assert dataset.read_cnt == 4
27 |
28 | assert dataset[32768] == 32768
29 | assert dataset.read_cnt == 5
30 |
31 | assert dataset[32767] == 32767
32 | assert dataset.read_cnt == 6
33 |
34 | assert dataset[32769] == 32769
35 | assert dataset.read_cnt == 7
36 |
37 | assert dataset[32770] == 32770
38 | assert dataset.read_cnt == 7
39 |
40 | assert dataset[32771] == 32771
41 | assert dataset.read_cnt == 7
42 |
43 | assert dataset[8000000] == 8000000
44 | assert dataset.read_cnt == 8
45 |
46 | assert dataset[8000001] == 8000001
47 | assert dataset.read_cnt == 8
48 |
49 |
50 | def test_dataloader_batch():
51 | dataset = CustomDataset("./data")
52 | loader = DataLoader(dataset, batch_size=2)
53 | for i, row in enumerate(loader):
54 | assert [i * 2, i * 2 + 1] == row.tolist()
55 | # assert i == row.item()
56 |
57 | i = 0
58 | for i, row in enumerate(loader):
59 | assert [i * 2, i * 2 + 1] == row.tolist()
60 | assert (i + 1) == len(loader)
61 |
62 |
63 | def test_dataloader_workers():
64 | dataset = CustomDataset("./data")
65 | loader = DataLoader(dataset, batch_size=2, num_workers=8)
66 | for i, row in enumerate(loader):
67 | assert [i * 2, i * 2 + 1] == row.tolist()
68 |
69 |
70 | def test_dataloader_random():
71 | dataset = CustomDataset("./data")
72 | loader = DataLoader(dataset, batch_size=10, shuffle=True, num_workers=2, pin_memory=True)
73 | total = set()
74 | for i, row in enumerate(loader):
75 | total |= set(row.tolist())
76 | assert len(total) == len(loader) * 10
77 |
78 | total = set()
79 | for i, row in enumerate(loader):
80 | total |= set(row.tolist())
81 | assert len(total) == len(loader) * 10
82 |
83 |
84 | class CustomDataset(PyArrowDataset):
85 |
86 | def __getitem__(self, idx):
87 | row = super().__getitem__(idx)
88 | return row['idx']
89 |
90 |
91 | def create_data():
92 | if not Path('./data').exists():
93 | df = pd.DataFrame({"idx": range(50000000)})
94 | dt = datetime(2023, 1, 1)
95 | df["dt"] = df["idx"].apply(
96 | lambda x: (dt + timedelta(milliseconds=x * 10)).date()
97 | )
98 | pa.parquet.write_to_dataset(
99 | pa.Table.from_pandas(df),
100 | root_path="data",
101 | partition_cols=["dt"],
102 | use_legacy_dataset=False,
103 | )
104 |
--------------------------------------------------------------------------------
/003-Shared-Memory/shared_memory_queue.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from multiprocessing import get_context, Process
3 | from multiprocessing import Queue
4 | from multiprocessing.shared_memory import SharedMemory
5 | from typing import Optional
6 |
7 | from tqdm import tqdm
8 |
9 |
10 | class SharedQueue:
11 |
12 | def __init__(self, queue: Queue, sq_name=None, shared_size=1024 * 1024 * 8):
13 | self.queue = queue
14 | if sq_name is None:
15 | self.shm = SharedMemory(create=True, size=shared_size)
16 | else:
17 | self.shm = SharedMemory(name=sq_name)
18 | self._shared_size = shared_size - 1
19 | self._cur_idx = 0
20 |
21 | @property
22 | def name(self):
23 | return self.shm.name
24 |
25 | def get(self, block: bool = True, timeout: Optional[float] = None, encoding='euc-kr') -> Optional[bytes]:
26 | r = self.queue.get(block=block, timeout=timeout)
27 | if r is None:
28 | return None
29 |
30 | start, end = r
31 | return self.shm.buf[start:end].tobytes()
32 |
33 | def put(self, obj: bytes, block: bool = True, timeout: Optional[float] = None):
34 | start = self._cur_idx
35 | end = start + len(obj)
36 | if end >= self._shared_size:
37 | start, end = 0, len(obj)
38 | self._cur_idx = end
39 |
40 | self.shm.buf[start:end] = obj
41 | self.queue.put((start, end), block=block, timeout=timeout)
42 |
43 |
44 | def daemon_run1(queue: Queue, sq_name: str):
45 | answer = '{0}안녕하세요 파이썬! 123456789 !@#$%^&*() 하하! 제타벨류 가즈아!'
46 | cnt = 0
47 |
48 | sq = SharedQueue(queue, sq_name=sq_name)
49 | while True:
50 | btext = sq.get()
51 | try:
52 | text = btext.decode('euc-kr')
53 | except UnicodeDecodeError as e:
54 | print(e)
55 | print(btext)
56 | continue
57 |
58 | if text == 'end':
59 | break
60 | assert text == answer.format(cnt), f'ERROR: text:{text}'
61 | cnt += 1
62 |
63 |
64 | def daemon_run2(queue: Queue):
65 | answer = '{0}안녕하세요 파이썬! 123456789 !@#$%^&*() 하하! 제타벨류 가즈아!'
66 | cnt = 0
67 |
68 | while True:
69 | text = queue.get()
70 | if text == 'end':
71 | break
72 | assert text == answer.format(cnt), f'ERROR: text:{text}'
73 | cnt += 1
74 |
75 |
76 | def main():
77 | s = '{0}안녕하세요 파이썬! 123456789 !@#$%^&*() 하하! 제타벨류 가즈아!'
78 |
79 | # SharedMemoryQueue 테스트
80 | queue = Queue()
81 | sq = SharedQueue(queue, shared_size=1024 * 1024 * 128) # 낮은 값 설정시 에러가 난다
82 | p = Process(target=daemon_run1, args=(queue, sq.name))
83 | p.start()
84 |
85 | start = datetime.now()
86 | for i in tqdm(range(1000000)):
87 | sq.put(bytes(s.format(i), encoding='euc-kr'), block=False)
88 | sq.put('end'.encode('euc-kr'), block=True)
89 | p.join()
90 |
91 | print('SharedQueue:', (datetime.now() - start).total_seconds())
92 |
93 | # 기존 Queue 테스트
94 | queue = Queue()
95 | p = Process(target=daemon_run2, args=(queue,))
96 | p.start()
97 |
98 | start = datetime.now()
99 | for i in tqdm(range(1000000)):
100 | queue.put(s.format(i), block=False)
101 | queue.put('end', block=True)
102 | p.join()
103 |
104 | print('Queue:', (datetime.now() - start).total_seconds())
105 |
106 |
107 | if __name__ == '__main__':
108 | main()
109 |
--------------------------------------------------------------------------------
/006-pyarrow/pyarrow_torch.py:
--------------------------------------------------------------------------------
1 | import random
2 | import sys
3 | from bisect import bisect_right
4 | from typing import Iterator, Optional, Tuple, List
5 |
6 | import pyarrow as pa
7 | import pandas as pd
8 | from pyarrow.dataset import ParquetFileFragment
9 | from pyarrow.lib import RecordBatch
10 | from pyarrow.parquet import ParquetDataset, ParquetFile
11 | from torch.utils.data import Dataset
12 |
13 |
14 | class PyArrowDataset(Dataset):
15 | def __init__(self, source: str, shuffle: bool = False, seed: int = 123):
16 | random.seed(seed)
17 | self.source = source
18 | self.seed = seed
19 |
20 | # Pyarrow
21 | self.dataset = ParquetDataset(source, use_legacy_dataset=False)
22 | self.parquet_indices: List[Tuple[int, int, int, ParquetFile, int, int]] = []
23 | self._cur_meta = None
24 | self._df: Optional[pd.DataFrame] = None
25 |
26 | # Debug (Memory Profiling)
27 | self.read_cnt = 0
28 |
29 | self.init_parquet_indexing(shuffle)
30 |
31 | def init_parquet_indexing(self, shuffle: bool = False):
32 | fragments = self.dataset.fragments
33 | if shuffle:
34 | random.shuffle(fragments)
35 |
36 | idx = 0
37 | parquet_indices = []
38 | for frag in fragments:
39 | parquet_file = ParquetFile(frag.path)
40 | for i, row_group in enumerate(frag.row_groups):
41 | start_idx = idx # inclusive
42 | end_idx = idx + row_group.num_rows # exclusive
43 | parquet_indices.append((i, start_idx, end_idx, parquet_file, row_group.id, row_group.num_rows))
44 | idx += row_group.num_rows
45 |
46 | self.parquet_indices.clear()
47 | self.parquet_indices = parquet_indices
48 |
49 | def __len__(self):
50 | if not self.parquet_indices:
51 | return 0
52 | return self.parquet_indices[-1][-1]
53 |
54 | def __getitem__(self, idx: int):
55 | meta_idx = self._binary_search(idx)
56 |
57 | if self._cur_meta is not None and meta_idx == self._cur_meta[0]:
58 | start_idx = self._cur_meta[1]
59 | # print(f'total: {len(self)} | {idx - start_idx}')
60 | assert (idx - start_idx) >= 0, f'{idx} - {start_idx} = {idx - start_idx} <- should not be negative.'
61 | return self._df.iloc[idx - start_idx]
62 |
63 | # Clear memory references
64 | del self._df
65 | del self._cur_meta
66 | self.read_cnt += 1
67 | # Read a new Parquet File
68 | self._cur_meta = self.parquet_indices[meta_idx]
69 | start_idx = self._cur_meta[1]
70 | parquet_file = self._cur_meta[3]
71 | row_id = self._cur_meta[4]
72 | table: pa.Table = parquet_file.read_row_group(row_id)
73 | self._df = table.to_pandas()
74 |
75 | return self._df.iloc[idx - start_idx]
76 |
77 | def _binary_search(self, target: int):
78 | arr = self.parquet_indices
79 | n = len(arr)
80 | left, right = 0, n
81 |
82 | while left <= right:
83 | mid = (left + right) // 2
84 | _, start_idx, end_idx, _, _, _ = arr[mid]
85 | if target == start_idx:
86 | return mid
87 | elif target == end_idx:
88 | return mid + 1
89 | elif start_idx <= target < end_idx:
90 | return mid
91 | elif target <= end_idx:
92 | right = mid - 1
93 | else:
94 | left = mid + 1
95 | return left
96 |
--------------------------------------------------------------------------------
/101-GeoHash/map.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
20 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/200-Kubernetes/01-Kubernetes.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Install Minikube\n",
8 | "\n",
9 | "\n",
10 | "```\n",
11 | "curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 \\\n",
12 | " && chmod +x minikube\n",
13 | " \n",
14 | "sudo install minikube /usr/local/bin/\n",
15 | "```\n",
16 | "\n",
17 | "기본적인 명령어는 다음과 같습니다.\n",
18 | "\n",
19 | " - `minikube start`\n",
20 | " - `minikube status`\n",
21 | " - `minikube stop`\n",
22 | "\n"
23 | ]
24 | },
25 | {
26 | "cell_type": "markdown",
27 | "metadata": {},
28 | "source": [
29 | "# Kubectl 설치\n",
30 | "\n",
31 | "```\n",
32 | "curl -LO \"https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl\"\n",
33 | "\n",
34 | "chmod +x ./kubectl\n",
35 | "sudo mv ./kubectl /usr/local/bin/kubectl\n",
36 | "\n",
37 | "kubectl version --client\n",
38 | "```\n",
39 | "\n",
40 | "Snap이 된다면 다음과 같이 쉽게 설치도 가능합니다.\n",
41 | "\n",
42 | "```\n",
43 | "sudo snap install kubectl --classic\n",
44 | "```\n"
45 | ]
46 | },
47 | {
48 | "cell_type": "markdown",
49 | "metadata": {},
50 | "source": [
51 | "# Getting Started\n",
52 | "\n",
53 | "```bash\n",
54 | "$ kubectl cluster-info\n",
55 | "Kubernetes master is running at https://172.17.0.3:8443\n",
56 | "KubeDNS is running at https://172.17.0.3:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy\n",
57 | "\n",
58 | "To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.\n",
59 | "\n",
60 | "```\n",
61 | " - **Kubernetes master**: master\n",
62 | " - **KubeDNS**: DNS\n",
63 | " - **kubernetes-dashboard**: dashboard - UI에서 applications을 확인 가능"
64 | ]
65 | },
66 | {
67 | "cell_type": "markdown",
68 | "metadata": {},
69 | "source": [
70 | "# Hello Minikube\n",
71 | "\n",
72 | "```\n",
73 | "$ kubectl create deployment nginx-hello-world --image=nginxdemos/hello\n",
74 | "$ kubectl get deployments\n",
75 | "NAME READY UP-TO-DATE AVAILABLE AGE\n",
76 | "hello-node 1/1 1 1 60s\n",
77 | "\n",
78 | "$ kubectl get pods\n",
79 | "NAME READY STATUS RESTARTS AGE\n",
80 | "hello-node-7bf657c596-glpfj 1/1 Running 0 2m\n",
81 | "\n",
82 | "```\n",
83 | "\n",
84 | "로그 확인은 다음과 같이 합니다.\n",
85 | "\n",
86 | "```\n",
87 | "kubectl get events\n",
88 | "```\n",
89 | "\n",
90 | "\n",
91 | "삭제는 다음과 같이 합니다.\n",
92 | "\n",
93 | "```\n",
94 | "$ kubectl delete deployment nginx-hello-world\n",
95 | "$ kubectl get deployments\n",
96 | "No resources found in default namespace.\n",
97 | "```"
98 | ]
99 | },
100 | {
101 | "cell_type": "markdown",
102 | "metadata": {},
103 | "source": [
104 | "# Nginx Tutorial\n",
105 | "\n",
106 | "먼저 docker에서 확인합니다.\n",
107 | "\n",
108 | "```\n",
109 | "docker run --name my-nginx -p 5001:80 -d nginx\n",
110 | "```\n",
111 | "\n",
112 | "크롬에서 확인후 삭제 합니다.\n",
113 | "\n",
114 | "```\n",
115 | "docker stop my-nginx \n",
116 | "docker container prune\n",
117 | "```\n",
118 | "\n",
119 | "Nginx 배포합니다.\n",
120 | "\n",
121 | "```\n",
122 | "kubectl create deployment hello-node --image=nginx\n",
123 | "# 또는 이거\n",
124 | "# kubectl create deployment hello-node --image=nginxdemos/hello \n",
125 | "\n",
126 | "kubectl port-forward hello-node-544968b8c4-4kvfh 5001:80 --address 0.0.0.0\n",
127 | "```\n",
128 | "\n",
129 | "크롬에서 확인을 합니다.\n",
130 | "\n",
131 | "로그도 확인합니다.\n",
132 | "\n",
133 | "`logs [Pod 이름]` 을 사용합니다.\n",
134 | "\n",
135 | "```\n",
136 | "kubectl logs my-nginx-66b75b6f6b-29sw6 -f\n",
137 | "```\n",
138 | "\n",
139 | "Pod안의 명령문을 실행시킬수도 있습니다.\n",
140 | "\n",
141 | "```\n",
142 | "kubectl exec hello-node-66b75b6f6b-29sw6 -- env\n",
143 | "kubectl exec hello-node-544968b8c4-tp5pd -it -- bash\n",
144 | "```"
145 | ]
146 | },
147 | {
148 | "cell_type": "markdown",
149 | "metadata": {},
150 | "source": [
151 | "## Expose\n",
152 | "\n",
153 | "먼저 nginx 를 디플로이해줍니다.\n",
154 | "\n",
155 | "```\n",
156 | "kubectl create deployment hello-node --image=nginx\n",
157 | "```\n",
158 | "\n",
159 | "Expose 시킵니다.\n",
160 | "\n",
161 | "```\n",
162 | "kubectl expose deployment hello-node --type=NodePort --port 5001 --target-port 80\n",
163 | "```\n",
164 | "\n",
165 | "이후 NodePort를 확인합니다.
\n",
166 | "이후 `curl $(minikube ip):[Node Port]` 로 확인합니다. \n",
167 | "\n",
168 | "```\n",
169 | "kubectl describe service hello-node | grep NodePort\n",
170 | "curl $(minikube ip):31832\n",
171 | "kubectl port-forward service/hello-node 5002:5001\n",
172 | "```"
173 | ]
174 | }
175 | ],
176 | "metadata": {
177 | "kernelspec": {
178 | "display_name": "Python 3",
179 | "language": "python",
180 | "name": "python3"
181 | },
182 | "language_info": {
183 | "codemirror_mode": {
184 | "name": "ipython",
185 | "version": 3
186 | },
187 | "file_extension": ".py",
188 | "mimetype": "text/x-python",
189 | "name": "python",
190 | "nbconvert_exporter": "python",
191 | "pygments_lexer": "ipython3",
192 | "version": "3.8.2"
193 | }
194 | },
195 | "nbformat": 4,
196 | "nbformat_minor": 4
197 | }
198 |
--------------------------------------------------------------------------------
/010-Pyspark/01 Tutorial.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 5,
6 | "metadata": {},
7 | "outputs": [],
8 | "source": [
9 | "import findspark\n",
10 | "findspark.init()\n",
11 | "\n",
12 | "import pyspark\n",
13 | "from pyspark import SparkContext\n",
14 | "from datetime import datetime"
15 | ]
16 | },
17 | {
18 | "cell_type": "markdown",
19 | "metadata": {},
20 | "source": [
21 | "## Initialize Spark Context"
22 | ]
23 | },
24 | {
25 | "cell_type": "code",
26 | "execution_count": 2,
27 | "metadata": {},
28 | "outputs": [],
29 | "source": [
30 | "sc = SparkContext(\"local\", \"tutorial\")"
31 | ]
32 | },
33 | {
34 | "cell_type": "markdown",
35 | "metadata": {},
36 | "source": [
37 | "## Word Count"
38 | ]
39 | },
40 | {
41 | "cell_type": "code",
42 | "execution_count": 7,
43 | "metadata": {},
44 | "outputs": [
45 | {
46 | "name": "stdout",
47 | "output_type": "stream",
48 | "text": [
49 | "counts: 8\n",
50 | "CPU times: user 69 µs, sys: 57 µs, total: 126 µs\n",
51 | "Wall time: 132 µs\n"
52 | ]
53 | }
54 | ],
55 | "source": [
56 | "words = sc.parallelize (\n",
57 | " [\"scala\", \n",
58 | " \"java\", \n",
59 | " \"hadoop\", \n",
60 | " \"spark\", \n",
61 | " \"akka\",\n",
62 | " \"spark vs hadoop\", \n",
63 | " \"pyspark\",\n",
64 | " \"pyspark and spark\"]\n",
65 | ")\n",
66 | "counts = words.count()\n",
67 | "\n",
68 | "%time print(f'counts: {counts}')"
69 | ]
70 | },
71 | {
72 | "cell_type": "markdown",
73 | "metadata": {},
74 | "source": [
75 | "## Collect"
76 | ]
77 | },
78 | {
79 | "cell_type": "code",
80 | "execution_count": 8,
81 | "metadata": {},
82 | "outputs": [
83 | {
84 | "data": {
85 | "text/plain": [
86 | "['scala',\n",
87 | " 'java',\n",
88 | " 'hadoop',\n",
89 | " 'spark',\n",
90 | " 'akka',\n",
91 | " 'spark vs hadoop',\n",
92 | " 'pyspark',\n",
93 | " 'pyspark and spark']"
94 | ]
95 | },
96 | "execution_count": 8,
97 | "metadata": {},
98 | "output_type": "execute_result"
99 | }
100 | ],
101 | "source": [
102 | "words.collect()"
103 | ]
104 | },
105 | {
106 | "cell_type": "markdown",
107 | "metadata": {},
108 | "source": [
109 | "## ForEach"
110 | ]
111 | },
112 | {
113 | "cell_type": "code",
114 | "execution_count": 23,
115 | "metadata": {},
116 | "outputs": [],
117 | "source": [
118 | "def f(x):\n",
119 | " print(x)\n",
120 | " \n",
121 | "words.foreach(f)"
122 | ]
123 | },
124 | {
125 | "cell_type": "markdown",
126 | "metadata": {},
127 | "source": [
128 | "## Filter"
129 | ]
130 | },
131 | {
132 | "cell_type": "code",
133 | "execution_count": 26,
134 | "metadata": {},
135 | "outputs": [
136 | {
137 | "data": {
138 | "text/plain": [
139 | "['pyspark', 'pyspark and spark']"
140 | ]
141 | },
142 | "execution_count": 26,
143 | "metadata": {},
144 | "output_type": "execute_result"
145 | }
146 | ],
147 | "source": [
148 | "words.filter(lambda x: 'py' in x).collect()"
149 | ]
150 | },
151 | {
152 | "cell_type": "markdown",
153 | "metadata": {},
154 | "source": [
155 | "## Map"
156 | ]
157 | },
158 | {
159 | "cell_type": "code",
160 | "execution_count": 31,
161 | "metadata": {},
162 | "outputs": [
163 | {
164 | "data": {
165 | "text/plain": [
166 | "[('scala', 1, 3),\n",
167 | " ('java', 1, 3),\n",
168 | " ('hadoop', 1, 3),\n",
169 | " ('spark', 1, 3),\n",
170 | " ('akka', 1, 3),\n",
171 | " ('spark vs hadoop', 1, 3),\n",
172 | " ('pyspark', 1, 3),\n",
173 | " ('pyspark and spark', 1, 3)]"
174 | ]
175 | },
176 | "execution_count": 31,
177 | "metadata": {},
178 | "output_type": "execute_result"
179 | }
180 | ],
181 | "source": [
182 | "words.map(lambda x: (x, 1, 3)).collect()"
183 | ]
184 | },
185 | {
186 | "cell_type": "markdown",
187 | "metadata": {},
188 | "source": [
189 | "## Reduce "
190 | ]
191 | },
192 | {
193 | "cell_type": "code",
194 | "execution_count": 33,
195 | "metadata": {},
196 | "outputs": [
197 | {
198 | "data": {
199 | "text/plain": [
200 | "25"
201 | ]
202 | },
203 | "execution_count": 33,
204 | "metadata": {},
205 | "output_type": "execute_result"
206 | }
207 | ],
208 | "source": [
209 | "from operator import add\n",
210 | "\n",
211 | "nums = sc.parallelize([1, 2, 3, 4, 5, 10])\n",
212 | "nums.reduce(add)"
213 | ]
214 | },
215 | {
216 | "cell_type": "markdown",
217 | "metadata": {},
218 | "source": [
219 | "## Join\n",
220 | "\n",
221 | "1. **join**: 두개의 RDD에 모두 존재하는 elements만 join이 되고, 나머지는 제외\n",
222 | "2. **fullOuterJoin**: 모든 elements를 join 시킨다 "
223 | ]
224 | },
225 | {
226 | "cell_type": "code",
227 | "execution_count": 37,
228 | "metadata": {},
229 | "outputs": [
230 | {
231 | "data": {
232 | "text/plain": [
233 | "[('ml', (10, 5)), ('spark', (1, 2))]"
234 | ]
235 | },
236 | "execution_count": 37,
237 | "metadata": {},
238 | "output_type": "execute_result"
239 | }
240 | ],
241 | "source": [
242 | "x = sc.parallelize([('spark', 1), ('ml', 10), ('power', 2)])\n",
243 | "y = sc.parallelize([('spark', 2), ('ml', 5), ('happy', 3)])\n",
244 | "joined = x.join(y)\n",
245 | "joined.collect()"
246 | ]
247 | },
248 | {
249 | "cell_type": "code",
250 | "execution_count": 39,
251 | "metadata": {},
252 | "outputs": [
253 | {
254 | "data": {
255 | "text/plain": [
256 | "[('ml', (10, 5)),\n",
257 | " ('power', (2, None)),\n",
258 | " ('spark', (1, 2)),\n",
259 | " ('happy', (None, 3))]"
260 | ]
261 | },
262 | "execution_count": 39,
263 | "metadata": {},
264 | "output_type": "execute_result"
265 | }
266 | ],
267 | "source": [
268 | "x.fullOuterJoin(y).collect()"
269 | ]
270 | },
271 | {
272 | "cell_type": "code",
273 | "execution_count": 40,
274 | "metadata": {},
275 | "outputs": [
276 | {
277 | "data": {
278 | "text/plain": [
279 | "[('ml', (10, 5)), ('power', (2, None)), ('spark', (1, 2))]"
280 | ]
281 | },
282 | "execution_count": 40,
283 | "metadata": {},
284 | "output_type": "execute_result"
285 | }
286 | ],
287 | "source": [
288 | "x.leftOuterJoin(y).collect()"
289 | ]
290 | },
291 | {
292 | "cell_type": "markdown",
293 | "metadata": {},
294 | "source": [
295 | "## Cache\n",
296 | "\n",
297 | "\"MEMORY_ONLY\" 일경우.. 메모리에 RDD를 persist시킨다 "
298 | ]
299 | },
300 | {
301 | "cell_type": "code",
302 | "execution_count": 42,
303 | "metadata": {},
304 | "outputs": [
305 | {
306 | "data": {
307 | "text/plain": [
308 | "True"
309 | ]
310 | },
311 | "execution_count": 42,
312 | "metadata": {},
313 | "output_type": "execute_result"
314 | }
315 | ],
316 | "source": [
317 | "words.cache()\n",
318 | "words.persist().is_cached"
319 | ]
320 | }
321 | ],
322 | "metadata": {
323 | "kernelspec": {
324 | "display_name": "Python 3",
325 | "language": "python",
326 | "name": "python3"
327 | },
328 | "language_info": {
329 | "codemirror_mode": {
330 | "name": "ipython",
331 | "version": 3
332 | },
333 | "file_extension": ".py",
334 | "mimetype": "text/x-python",
335 | "name": "python",
336 | "nbconvert_exporter": "python",
337 | "pygments_lexer": "ipython3",
338 | "version": "3.6.7"
339 | }
340 | },
341 | "nbformat": 4,
342 | "nbformat_minor": 2
343 | }
344 |
--------------------------------------------------------------------------------
/002-Pyspark/01 Tutorial.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 5,
6 | "metadata": {
7 | "pycharm": {
8 | "name": "#%%\n"
9 | }
10 | },
11 | "outputs": [],
12 | "source": [
13 | "import findspark\n",
14 | "findspark.init()\n",
15 | "\n",
16 | "import pyspark\n",
17 | "from pyspark import SparkContext\n",
18 | "from datetime import datetime"
19 | ]
20 | },
21 | {
22 | "cell_type": "markdown",
23 | "metadata": {
24 | "pycharm": {
25 | "name": "#%% md\n"
26 | }
27 | },
28 | "source": [
29 | "## Initialize Spark Context"
30 | ]
31 | },
32 | {
33 | "cell_type": "code",
34 | "execution_count": 2,
35 | "metadata": {
36 | "pycharm": {
37 | "name": "#%%\n"
38 | }
39 | },
40 | "outputs": [],
41 | "source": [
42 | "sc = SparkContext(\"local\", \"tutorial\")"
43 | ]
44 | },
45 | {
46 | "cell_type": "markdown",
47 | "metadata": {
48 | "pycharm": {
49 | "name": "#%% md\n"
50 | }
51 | },
52 | "source": [
53 | "## Word Count"
54 | ]
55 | },
56 | {
57 | "cell_type": "code",
58 | "execution_count": 7,
59 | "metadata": {
60 | "pycharm": {
61 | "name": "#%%\n"
62 | }
63 | },
64 | "outputs": [
65 | {
66 | "name": "stdout",
67 | "output_type": "stream",
68 | "text": [
69 | "counts: 8\n",
70 | "CPU times: user 69 µs, sys: 57 µs, total: 126 µs\n",
71 | "Wall time: 132 µs\n"
72 | ]
73 | }
74 | ],
75 | "source": [
76 | "words = sc.parallelize (\n",
77 | " [\"scala\", \n",
78 | " \"java\", \n",
79 | " \"hadoop\", \n",
80 | " \"spark\", \n",
81 | " \"akka\",\n",
82 | " \"spark vs hadoop\", \n",
83 | " \"pyspark\",\n",
84 | " \"pyspark and spark\"]\n",
85 | ")\n",
86 | "counts = words.count()\n",
87 | "\n",
88 | "%time print(f'counts: {counts}')"
89 | ]
90 | },
91 | {
92 | "cell_type": "markdown",
93 | "metadata": {
94 | "pycharm": {
95 | "name": "#%% md\n"
96 | }
97 | },
98 | "source": [
99 | "## Collect"
100 | ]
101 | },
102 | {
103 | "cell_type": "code",
104 | "execution_count": 8,
105 | "metadata": {
106 | "pycharm": {
107 | "name": "#%%\n"
108 | }
109 | },
110 | "outputs": [
111 | {
112 | "data": {
113 | "text/plain": [
114 | "['scala',\n",
115 | " 'java',\n",
116 | " 'hadoop',\n",
117 | " 'spark',\n",
118 | " 'akka',\n",
119 | " 'spark vs hadoop',\n",
120 | " 'pyspark',\n",
121 | " 'pyspark and spark']"
122 | ]
123 | },
124 | "execution_count": 8,
125 | "metadata": {},
126 | "output_type": "execute_result"
127 | }
128 | ],
129 | "source": [
130 | "words.collect()"
131 | ]
132 | },
133 | {
134 | "cell_type": "markdown",
135 | "metadata": {
136 | "pycharm": {
137 | "name": "#%% md\n"
138 | }
139 | },
140 | "source": [
141 | "## ForEach"
142 | ]
143 | },
144 | {
145 | "cell_type": "code",
146 | "execution_count": 23,
147 | "metadata": {
148 | "pycharm": {
149 | "name": "#%%\n"
150 | }
151 | },
152 | "outputs": [],
153 | "source": [
154 | "def f(x):\n",
155 | " print(x)\n",
156 | " \n",
157 | "words.foreach(f)"
158 | ]
159 | },
160 | {
161 | "cell_type": "markdown",
162 | "metadata": {
163 | "pycharm": {
164 | "name": "#%% md\n"
165 | }
166 | },
167 | "source": [
168 | "## Filter"
169 | ]
170 | },
171 | {
172 | "cell_type": "code",
173 | "execution_count": 26,
174 | "metadata": {
175 | "pycharm": {
176 | "name": "#%%\n"
177 | }
178 | },
179 | "outputs": [
180 | {
181 | "data": {
182 | "text/plain": [
183 | "['pyspark', 'pyspark and spark']"
184 | ]
185 | },
186 | "execution_count": 26,
187 | "metadata": {},
188 | "output_type": "execute_result"
189 | }
190 | ],
191 | "source": [
192 | "words.filter(lambda x: 'py' in x).collect()"
193 | ]
194 | },
195 | {
196 | "cell_type": "markdown",
197 | "metadata": {
198 | "pycharm": {
199 | "name": "#%% md\n"
200 | }
201 | },
202 | "source": [
203 | "## Map"
204 | ]
205 | },
206 | {
207 | "cell_type": "code",
208 | "execution_count": 31,
209 | "metadata": {
210 | "pycharm": {
211 | "name": "#%%\n"
212 | }
213 | },
214 | "outputs": [
215 | {
216 | "data": {
217 | "text/plain": [
218 | "[('scala', 1, 3),\n",
219 | " ('java', 1, 3),\n",
220 | " ('hadoop', 1, 3),\n",
221 | " ('spark', 1, 3),\n",
222 | " ('akka', 1, 3),\n",
223 | " ('spark vs hadoop', 1, 3),\n",
224 | " ('pyspark', 1, 3),\n",
225 | " ('pyspark and spark', 1, 3)]"
226 | ]
227 | },
228 | "execution_count": 31,
229 | "metadata": {},
230 | "output_type": "execute_result"
231 | }
232 | ],
233 | "source": [
234 | "words.map(lambda x: (x, 1, 3)).collect()"
235 | ]
236 | },
237 | {
238 | "cell_type": "markdown",
239 | "metadata": {
240 | "pycharm": {
241 | "name": "#%% md\n"
242 | }
243 | },
244 | "source": [
245 | "## Reduce "
246 | ]
247 | },
248 | {
249 | "cell_type": "code",
250 | "execution_count": 33,
251 | "metadata": {
252 | "pycharm": {
253 | "name": "#%%\n"
254 | }
255 | },
256 | "outputs": [
257 | {
258 | "data": {
259 | "text/plain": [
260 | "25"
261 | ]
262 | },
263 | "execution_count": 33,
264 | "metadata": {},
265 | "output_type": "execute_result"
266 | }
267 | ],
268 | "source": [
269 | "from operator import add\n",
270 | "\n",
271 | "nums = sc.parallelize([1, 2, 3, 4, 5, 10])\n",
272 | "nums.reduce(add)"
273 | ]
274 | },
275 | {
276 | "cell_type": "markdown",
277 | "metadata": {
278 | "pycharm": {
279 | "name": "#%% md\n"
280 | }
281 | },
282 | "source": [
283 | "## Join\n",
284 | "\n",
285 | "1. **join**: 두개의 RDD에 모두 존재하는 elements만 join이 되고, 나머지는 제외\n",
286 | "2. **fullOuterJoin**: 모든 elements를 join 시킨다 "
287 | ]
288 | },
289 | {
290 | "cell_type": "code",
291 | "execution_count": 37,
292 | "metadata": {
293 | "pycharm": {
294 | "name": "#%%\n"
295 | }
296 | },
297 | "outputs": [
298 | {
299 | "data": {
300 | "text/plain": [
301 | "[('ml', (10, 5)), ('spark', (1, 2))]"
302 | ]
303 | },
304 | "execution_count": 37,
305 | "metadata": {},
306 | "output_type": "execute_result"
307 | }
308 | ],
309 | "source": [
310 | "x = sc.parallelize([('spark', 1), ('ml', 10), ('power', 2)])\n",
311 | "y = sc.parallelize([('spark', 2), ('ml', 5), ('happy', 3)])\n",
312 | "joined = x.join(y)\n",
313 | "joined.collect()"
314 | ]
315 | },
316 | {
317 | "cell_type": "code",
318 | "execution_count": 39,
319 | "metadata": {
320 | "pycharm": {
321 | "name": "#%%\n"
322 | }
323 | },
324 | "outputs": [
325 | {
326 | "data": {
327 | "text/plain": [
328 | "[('ml', (10, 5)),\n",
329 | " ('power', (2, None)),\n",
330 | " ('spark', (1, 2)),\n",
331 | " ('happy', (None, 3))]"
332 | ]
333 | },
334 | "execution_count": 39,
335 | "metadata": {},
336 | "output_type": "execute_result"
337 | }
338 | ],
339 | "source": [
340 | "x.fullOuterJoin(y).collect()"
341 | ]
342 | },
343 | {
344 | "cell_type": "code",
345 | "execution_count": 40,
346 | "metadata": {
347 | "pycharm": {
348 | "name": "#%%\n"
349 | }
350 | },
351 | "outputs": [
352 | {
353 | "data": {
354 | "text/plain": [
355 | "[('ml', (10, 5)), ('power', (2, None)), ('spark', (1, 2))]"
356 | ]
357 | },
358 | "execution_count": 40,
359 | "metadata": {},
360 | "output_type": "execute_result"
361 | }
362 | ],
363 | "source": [
364 | "x.leftOuterJoin(y).collect()"
365 | ]
366 | },
367 | {
368 | "cell_type": "markdown",
369 | "metadata": {
370 | "pycharm": {
371 | "name": "#%% md\n"
372 | }
373 | },
374 | "source": [
375 | "## Cache\n",
376 | "\n",
377 | "\"MEMORY_ONLY\" 일경우.. 메모리에 RDD를 persist시킨다 "
378 | ]
379 | },
380 | {
381 | "cell_type": "code",
382 | "execution_count": 42,
383 | "metadata": {
384 | "pycharm": {
385 | "name": "#%%\n"
386 | }
387 | },
388 | "outputs": [
389 | {
390 | "data": {
391 | "text/plain": [
392 | "True"
393 | ]
394 | },
395 | "execution_count": 42,
396 | "metadata": {},
397 | "output_type": "execute_result"
398 | }
399 | ],
400 | "source": [
401 | "words.cache()\n",
402 | "words.persist().is_cached"
403 | ]
404 | }
405 | ],
406 | "metadata": {
407 | "kernelspec": {
408 | "display_name": "Python 3",
409 | "language": "python",
410 | "name": "python3"
411 | },
412 | "language_info": {
413 | "codemirror_mode": {
414 | "name": "ipython",
415 | "version": 3
416 | },
417 | "file_extension": ".py",
418 | "mimetype": "text/x-python",
419 | "name": "python",
420 | "nbconvert_exporter": "python",
421 | "pygments_lexer": "ipython3",
422 | "version": "3.6.7"
423 | }
424 | },
425 | "nbformat": 4,
426 | "nbformat_minor": 2
427 | }
--------------------------------------------------------------------------------
/006-pyarrow/pyarrow-tutorial.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "id": "b7462c43-d1b7-46ea-9c88-06c078c92e51",
7 | "metadata": {
8 | "tags": []
9 | },
10 | "outputs": [],
11 | "source": [
12 | "%config Completer.use_jedi = False\n",
13 | "\n",
14 | "import sys\n",
15 | "from datetime import datetime, timedelta\n",
16 | "from typing import Generator, Iterator, Optional, Tuple\n",
17 | "\n",
18 | "import pandas as pd\n",
19 | "import pyarrow as pa"
20 | ]
21 | },
22 | {
23 | "cell_type": "markdown",
24 | "id": "ad43b914-376a-4eac-9216-ebb9f2edc3e9",
25 | "metadata": {},
26 | "source": [
27 | "## Data Generation"
28 | ]
29 | },
30 | {
31 | "cell_type": "code",
32 | "execution_count": 41,
33 | "id": "ead8625c-0956-4a05-9e90-6db7ec7d07d5",
34 | "metadata": {},
35 | "outputs": [
36 | {
37 | "data": {
38 | "text/plain": [
39 | "2380"
40 | ]
41 | },
42 | "execution_count": 41,
43 | "metadata": {},
44 | "output_type": "execute_result"
45 | }
46 | ],
47 | "source": []
48 | },
49 | {
50 | "cell_type": "code",
51 | "execution_count": 75,
52 | "id": "0ec194e5-54c8-4f14-a653-77924160d1db",
53 | "metadata": {},
54 | "outputs": [],
55 | "source": [
56 | "from sklearn.datasets import make_classification\n",
57 | "from datetime import datetime, timedelta\n",
58 | "from random import random, randint\n",
59 | "\n",
60 | "cur_date = datetime.now()\n",
61 | "\n",
62 | "for i in range(10):\n",
63 | " x, y = make_classification(n_samples=randint(1000, 10000), n_features=10, weights=(0.9, 0.1))\n",
64 | " df = pd.DataFrame(x)\n",
65 | " df.columns = [f'col_{x}' for x in range(10)]\n",
66 | " df['dt'] = cur_date.strftime('%Y%m%d')\n",
67 | " cur_date += timedelta(days=1)\n",
68 | " df.to_parquet('./data', partition_cols=['dt'])"
69 | ]
70 | },
71 | {
72 | "cell_type": "markdown",
73 | "id": "90c4ef72-d516-4797-a6dd-fb9adfd3cb03",
74 | "metadata": {},
75 | "source": [
76 | "# ParquetDataset"
77 | ]
78 | },
79 | {
80 | "cell_type": "markdown",
81 | "id": "d9469c34-2e8b-4427-a30d-dc4b95243c75",
82 | "metadata": {},
83 | "source": [
84 | "## Dataset"
85 | ]
86 | },
87 | {
88 | "cell_type": "code",
89 | "execution_count": 74,
90 | "id": "6ae8ce10-b595-4dd1-bd61-82fd42645a05",
91 | "metadata": {},
92 | "outputs": [],
93 | "source": [
94 | "scanner = dataset.scanner()\n"
95 | ]
96 | },
97 | {
98 | "cell_type": "code",
99 | "execution_count": 87,
100 | "id": "686d0599-130d-48ae-9156-7786e1374bda",
101 | "metadata": {},
102 | "outputs": [
103 | {
104 | "name": "stdout",
105 | "output_type": "stream",
106 | "text": [
107 | "n_rows: 41851\n",
108 | "0.43440751685326995\n",
109 | "1.1431565798068537\n",
110 | "0.24992460337363664\n",
111 | "-0.34604515354971194\n",
112 | "0.32233619998326285\n",
113 | "0.7595871664144229\n",
114 | "-0.9966609176752007\n",
115 | "-0.5206429227786304\n",
116 | "1.2140122393778143\n",
117 | "-1.599369064413563\n"
118 | ]
119 | }
120 | ],
121 | "source": [
122 | "import pyarrow.dataset as ds\n",
123 | "\n",
124 | "dataset = ds.dataset('./data', format='parquet', partitioning=['dt'])\n",
125 | "print('n_rows:', dataset.count_rows())\n",
126 | "\n",
127 | "for batch in dataset.to_batches():\n",
128 | " for i in range(batch.num_rows):\n",
129 | " col0 = batch.column('col_0')[0].as_py()\n",
130 | " \n",
131 | " "
132 | ]
133 | },
134 | {
135 | "cell_type": "markdown",
136 | "id": "eb49596a-eee5-4a94-afe9-be2d5183aeb0",
137 | "metadata": {},
138 | "source": [
139 | "## ParquetDataset"
140 | ]
141 | },
142 | {
143 | "cell_type": "code",
144 | "execution_count": 18,
145 | "id": "5e31dc0f-528c-4475-ace4-085a6be8547c",
146 | "metadata": {
147 | "tags": []
148 | },
149 | "outputs": [
150 | {
151 | "name": "stdout",
152 | "output_type": "stream",
153 | "text": [
154 | "Pandas shape : (50000000, 2)\n",
155 | "Pandas size : 450000734\n",
156 | "Pyarrow size : 64\n",
157 | "files : ['./data/dt=2023-01-01/a300c22cb3554cec95c68957f6ac326f-0.parquet', './data/dt=2023-01-02/a300c22cb3554cec95c68957f6ac326f-0.parquet', './data/dt=2023-01-03/a300c22cb3554cec95c68957f6ac326f-0.parquet']\n",
158 | "fragments : [, , ]\n",
159 | "files rows : [8640000, 8640000, 8640000, 8640000, 8640000, 6800000]\n",
160 | "column size : 2\n"
161 | ]
162 | }
163 | ],
164 | "source": [
165 | "from pyarrow.parquet import ParquetDataset, ParquetFile\n",
166 | "\n",
167 | "dataset = ParquetDataset(\"./data\", memory_map=True, use_legacy_dataset=False)\n",
168 | "df = pd.read_parquet(\"./data\")\n",
169 | "\n",
170 | "file_rows = [frag.count_rows() for frag in dataset.fragments]\n",
171 | "\n",
172 | "print(\"Pandas shape :\", df.shape)\n",
173 | "print(\"Pandas size :\", sys.getsizeof(df))\n",
174 | "print(\"Pyarrow size :\", sys.getsizeof(dataset))\n",
175 | "print(\"files :\", dataset.files[:3])\n",
176 | "print(\"fragments :\", dataset.fragments[:3])\n",
177 | "print(\"files rows :\", file_rows)\n",
178 | "print(\"column size :\", len(dataset.schema))"
179 | ]
180 | },
181 | {
182 | "cell_type": "markdown",
183 | "id": "9f540049-85df-46d9-9815-4384b1df1156",
184 | "metadata": {},
185 | "source": [
186 | "## Iteration"
187 | ]
188 | },
189 | {
190 | "cell_type": "code",
191 | "execution_count": 4,
192 | "id": "1cf791db-87ef-46b5-925a-93828e5f05b4",
193 | "metadata": {
194 | "tags": []
195 | },
196 | "outputs": [
197 | {
198 | "name": "stdout",
199 | "output_type": "stream",
200 | "text": [
201 | "frag size : 72\n",
202 | "num rows : 32768\n",
203 | "Pandas shape: (32768, 1)\n"
204 | ]
205 | },
206 | {
207 | "data": {
208 | "text/html": [
209 | "\n",
210 | "\n",
223 | "
\n",
224 | " \n",
225 | " \n",
226 | " | \n",
227 | " idx | \n",
228 | "
\n",
229 | " \n",
230 | " \n",
231 | " \n",
232 | " | 0 | \n",
233 | " 0 | \n",
234 | "
\n",
235 | " \n",
236 | "
\n",
237 | "
"
238 | ],
239 | "text/plain": [
240 | " idx\n",
241 | "0 0"
242 | ]
243 | },
244 | "metadata": {},
245 | "output_type": "display_data"
246 | }
247 | ],
248 | "source": [
249 | "for frag in dataset.fragments:\n",
250 | " for batch in frag.to_batches():\n",
251 | " df = batch.to_pandas()\n",
252 | " row = batch.take(pa.array([0]))\n",
253 | "\n",
254 | " print(\"frag size :\", sys.getsizeof(frag))\n",
255 | " print(\"num rows :\", batch.num_rows)\n",
256 | " print(\"Pandas shape:\", df.shape)\n",
257 | " display(row.to_pandas())\n",
258 | " break\n",
259 | " break"
260 | ]
261 | },
262 | {
263 | "cell_type": "code",
264 | "execution_count": 57,
265 | "id": "97089466-c70d-4ec8-8fab-8ebd3d6d5e67",
266 | "metadata": {},
267 | "outputs": [
268 | {
269 | "data": {
270 | "text/plain": [
271 | "[(0, 0, 32768, , 0, 32768),\n",
272 | " (1,\n",
273 | " 32768,\n",
274 | " 65536,\n",
275 | " ,\n",
276 | " 1,\n",
277 | " 32768),\n",
278 | " (2,\n",
279 | " 65536,\n",
280 | " 98304,\n",
281 | " ,\n",
282 | " 2,\n",
283 | " 32768),\n",
284 | " (3,\n",
285 | " 98304,\n",
286 | " 131072,\n",
287 | " ,\n",
288 | " 3,\n",
289 | " 32768),\n",
290 | " (4,\n",
291 | " 131072,\n",
292 | " 163840,\n",
293 | " ,\n",
294 | " 4,\n",
295 | " 32768)]"
296 | ]
297 | },
298 | "execution_count": 57,
299 | "metadata": {},
300 | "output_type": "execute_result"
301 | }
302 | ],
303 | "source": [
304 | "\n",
305 | "idx = 0\n",
306 | "parquet_indices = []\n",
307 | "for frag in dataset.fragments:\n",
308 | " parquet_file = ParquetFile(frag.path)\n",
309 | " for i, row_group in enumerate(frag.row_groups):\n",
310 | " start_idx = idx\n",
311 | " end_idx = idx + row_group.num_rows\n",
312 | " parquet_indices.append((i, start_idx, end_idx, parquet_file, row_group.id, row_group.num_rows))\n",
313 | " idx += row_group.num_rows\n",
314 | " \n",
315 | "parquet_indices[:5]"
316 | ]
317 | },
318 | {
319 | "cell_type": "code",
320 | "execution_count": 55,
321 | "id": "d3dbd0dc-55c5-41a7-b175-55a210f9036e",
322 | "metadata": {},
323 | "outputs": [
324 | {
325 | "data": {
326 | "text/plain": [
327 | "28800"
328 | ]
329 | },
330 | "execution_count": 55,
331 | "metadata": {},
332 | "output_type": "execute_result"
333 | }
334 | ],
335 | "source": [
336 | "parquet_indices[-1][-1]"
337 | ]
338 | },
339 | {
340 | "cell_type": "code",
341 | "execution_count": 56,
342 | "id": "8ff55871-98ee-4107-bcca-e6d66a519850",
343 | "metadata": {},
344 | "outputs": [
345 | {
346 | "data": {
347 | "text/plain": [
348 | "pyarrow.Table\n",
349 | "idx: int64\n",
350 | "----\n",
351 | "idx: [[43200000,43200001,43200002,43200003,43200004,...,43220987,43220988,43220989,43220990,43220991]]"
352 | ]
353 | },
354 | "execution_count": 56,
355 | "metadata": {},
356 | "output_type": "execute_result"
357 | }
358 | ],
359 | "source": [
360 | "group = frag.row_groups[0]\n",
361 | "group.id\n",
362 | "\n",
363 | "\n",
364 | "pf = ParquetFile(frag.path)\n",
365 | "table = pf.read_row_group(0)\n",
366 | "table"
367 | ]
368 | },
369 | {
370 | "cell_type": "markdown",
371 | "id": "e8c85fdc-60a9-4970-b325-08fb2b8489d9",
372 | "metadata": {},
373 | "source": [
374 | "## Create Parquet Files"
375 | ]
376 | },
377 | {
378 | "cell_type": "code",
379 | "execution_count": null,
380 | "id": "adac7f0c-3fa1-4b27-8072-dc6e24e724ba",
381 | "metadata": {
382 | "tags": []
383 | },
384 | "outputs": [],
385 | "source": [
386 | "def create_data():\n",
387 | " df = pd.DataFrame({\"idx\": range(50000000)})\n",
388 | " dt = datetime(2023, 1, 1)\n",
389 | " df[\"dt\"] = df[\"idx\"].apply(\n",
390 | " lambda x: (dt + timedelta(milliseconds=x * 10)).date()\n",
391 | " )\n",
392 | " pa.parquet.write_to_dataset(\n",
393 | " pa.Table.from_pandas(df),\n",
394 | " root_path=\"data\",\n",
395 | " partition_cols=[\"dt\"],\n",
396 | " use_legacy_dataset=False,\n",
397 | " )\n",
398 | "\n",
399 | "\n",
400 | "# create_data()"
401 | ]
402 | },
403 | {
404 | "cell_type": "markdown",
405 | "id": "86634ed3-5b5f-4cdb-9a7b-1a7de80c9962",
406 | "metadata": {},
407 | "source": [
408 | "## Pytorch Dataset"
409 | ]
410 | },
411 | {
412 | "cell_type": "code",
413 | "execution_count": null,
414 | "id": "af4538a8-0733-4862-8e84-205490cb9358",
415 | "metadata": {},
416 | "outputs": [],
417 | "source": [
418 | "class PyArrowDataset(Dataset):\n",
419 | " def __init__(self, source:str, seed:int =123):\n",
420 | " pass\n",
421 | " \n",
422 | " def init_indexing(self, shuffle:bool=False)"
423 | ]
424 | },
425 | {
426 | "cell_type": "code",
427 | "execution_count": null,
428 | "id": "cb7d7752-ae4b-4e39-8a8e-9074e627e6e6",
429 | "metadata": {
430 | "tags": []
431 | },
432 | "outputs": [],
433 | "source": [
434 | "import random\n",
435 | "import tracemalloc\n",
436 | "from bisect import bisect_right\n",
437 | "\n",
438 | "from pyarrow.dataset import ParquetFileFragment\n",
439 | "from pyarrow.lib import RecordBatch\n",
440 | "from torch.utils.data import Dataset\n",
441 | "import gc\n",
442 | "\n",
443 | "class PyArrowDataset(Dataset):\n",
444 | " \"\"\"\n",
445 | " Restriction\n",
446 | " - Don't shuffle in Dataloader. this is for efficiency to precess large dataset.\n",
447 | " If you need to shuffle, do it before this custom dataset. (like in SparkSQL)\n",
448 | " But the algorithm supports random access.\n",
449 | " \"\"\"\n",
450 | "\n",
451 | " def __init__(self, source: str, seed: int = 123):\n",
452 | " self.source = source\n",
453 | " self.seed = seed\n",
454 | "\n",
455 | " # Pyarrow\n",
456 | " self.dataset = ParquetDataset(source, use_legacy_dataset=False)\n",
457 | " self.fragments: List[ParquetFileFragment] = self.dataset.fragments\n",
458 | " self._batches: Iterator[RecordBatch] = None\n",
459 | " self._batch: Optional[RecordBatch] = None\n",
460 | " self._df: pd.DataFrame = None\n",
461 | "\n",
462 | " # Indexing meta information to make search faster\n",
463 | " self._cumulative_n_rows: List[int] = []\n",
464 | " self._batch_idx: int = 0\n",
465 | "\n",
466 | " # Index\n",
467 | " self._fragment_idx = 0\n",
468 | "\n",
469 | " # Initialization\n",
470 | " self._init()\n",
471 | "\n",
472 | " def _init(self):\n",
473 | " random.seed(self.seed)\n",
474 | " # random.shuffle(self.fragments)\n",
475 | "\n",
476 | " self._cumulative_n_rows = [frag.count_rows() for frag in self.fragments]\n",
477 | " for i in range(1, len(self._cumulative_n_rows)):\n",
478 | " self._cumulative_n_rows[i] += self._cumulative_n_rows[i - 1]\n",
479 | "\n",
480 | " def _get_next(self, idx: int) -> Tuple[int, int]:\n",
481 | " print('_get_next 01', idx)\n",
482 | " def get_prev_cum_frag_size(_fragment_idx):\n",
483 | " if _fragment_idx >= 1:\n",
484 | " return self._cumulative_n_rows[_fragment_idx - 1]\n",
485 | " return 0\n",
486 | "\n",
487 | " # Calculate fragment idx\n",
488 | " fragment_idx = self._fragment_idx\n",
489 | " fragment_changed = False\n",
490 | " _prev_size = get_prev_cum_frag_size(fragment_idx)\n",
491 | " _cur_size = self._cumulative_n_rows[self._fragment_idx]\n",
492 | " if (idx < _prev_size) or (idx >= _cur_size):\n",
493 | " fragment_idx = bisect_right(self._cumulative_n_rows, idx)\n",
494 | " assert fragment_idx < len(self.fragments)\n",
495 | " # fragment_idx %= len(self.fragments)\n",
496 | " fragment_changed = self._fragment_idx != fragment_idx\n",
497 | " self._fragment_idx = fragment_idx\n",
498 | " self._batch_idx = 0\n",
499 | " \n",
500 | " if self._batches:\n",
501 | " self._batches.clear()\n",
502 | " \n",
503 | " del self._batches\n",
504 | " del self._batch\n",
505 | " del self._df\n",
506 | " self._batches = None\n",
507 | " self._batch = None\n",
508 | " self._df = None\n",
509 | " \n",
510 | " print('_get_next 02', idx)\n",
511 | " # Calculate batch idx\n",
512 | " _prev_size = get_prev_cum_frag_size(fragment_idx)\n",
513 | " batch_idx = idx - _prev_size\n",
514 | " batch_changed = batch_idx < self._batch_idx\n",
515 | "\n",
516 | " # Calculate batches of the fragment\n",
517 | " if self._batches is None or fragment_changed or batch_changed:\n",
518 | " if self._batches:\n",
519 | " self._batches.clear()\n",
520 | " \n",
521 | " self.batches = self.fragments[fragment_idx].to_batches()\n",
522 | " self._batch = None\n",
523 | "\n",
524 | " if self._batch is None:\n",
525 | " self._batch = next(self.batches)\n",
526 | " del self._df\n",
527 | " self._df = self._batch.to_pandas()\n",
528 | " self._batch_idx = 0\n",
529 | " \n",
530 | " print('_get_next 03', idx)\n",
531 | " need_to_load_data = False\n",
532 | " while True:\n",
533 | " print(\n",
534 | " \"ITER:\",\n",
535 | " f\"{self._batch_idx} <= {batch_idx} < {self._batch_idx + self._batch.num_rows} | {sys.getsizeof(self._batch)}\",\n",
536 | " )\n",
537 | " if (\n",
538 | " self._batch_idx\n",
539 | " <= batch_idx\n",
540 | " < self._batch_idx + self._batch.num_rows\n",
541 | " ):\n",
542 | " if need_to_load_data:\n",
543 | " self._df = self._batch.to_pandas()\n",
544 | " break\n",
545 | "\n",
546 | " need_to_load_data = True\n",
547 | " self._batch_idx += self._batch.num_rows\n",
548 | " self._batch = next(self.batches)\n",
549 | " \n",
550 | " print('_get_next 04', idx)\n",
551 | " return fragment_idx, batch_idx - self._batch_idx\n",
552 | " \n",
553 | " def __del__(self):\n",
554 | " print('Deleted')\n",
555 | " if self.dataset:\n",
556 | " self.dataset.clear()\n",
557 | " \n",
558 | " if self.fragments:\n",
559 | " self.fragments.clearn\n",
560 | " del self.dataset\n",
561 | " del self.fragments\n",
562 | " del self._batches\n",
563 | " del self._batch\n",
564 | " del self._df\n",
565 | "\n",
566 | "\n",
567 | " def __len__(self):\n",
568 | " return self._cumulative_n_rows[-1]\n",
569 | "\n",
570 | " def __getitem__(self, idx):\n",
571 | " print('__getitem__', idx)\n",
572 | " fragment_idx, batch_idx = self._get_next(idx)\n",
573 | "\n",
574 | " row = self._df.iloc[batch_idx][[\"idx\"]]\n",
575 | " row = row.fillna(0)\n",
576 | " row[\"fragment_idx\"] = fragment_idx\n",
577 | " row[\"batch_idx\"] = batch_idx\n",
578 | " return row, idx\n",
579 | " \n",
580 | " \n",
581 | "\n",
582 | "\n",
583 | "tracemalloc.start()\n",
584 | "dataset = PyArrowDataset(\"./data\")\n",
585 | "print(dataset[50000][0].idx)\n",
586 | "print(dataset[0][0].idx)\n",
587 | "print(dataset[500000][0].idx)\n",
588 | "\n",
589 | "print('여기까지')\n",
590 | "del dataset\n",
591 | "print(tracemalloc.get_traced_memory())\n",
592 | "print(gc.get_count())"
593 | ]
594 | },
595 | {
596 | "cell_type": "code",
597 | "execution_count": null,
598 | "id": "4721df80-e665-40b4-b281-5f5ee285406d",
599 | "metadata": {
600 | "tags": []
601 | },
602 | "outputs": [],
603 | "source": [
604 | "from torch.utils.data import DataLoader\n",
605 | "\n",
606 | "loader = DataLoader(dataset, batch_size=64, shuffle=True)\n",
607 | "data, labels = next(iter(loader))\n",
608 | "a = data[:, 0] - 1\n",
609 | "b = labels % 1000\n",
610 | "\n",
611 | "a == b"
612 | ]
613 | },
614 | {
615 | "cell_type": "markdown",
616 | "id": "3b078d63-87a2-45b2-8316-1622e5e1d39d",
617 | "metadata": {},
618 | "source": [
619 | "\n",
620 | "\n",
621 | "\n",
622 | "\n",
623 | "\n",
624 | "# ParquetFile\n",
625 | "\n",
626 | "## Row 갯수 "
627 | ]
628 | },
629 | {
630 | "cell_type": "code",
631 | "execution_count": null,
632 | "id": "c9036712-a6f0-47db-bf95-358bcdcabca0",
633 | "metadata": {
634 | "tags": []
635 | },
636 | "outputs": [],
637 | "source": [
638 | "from pyarrow.parquet import ParquetFile\n",
639 | "\n",
640 | "parquet_file = ParquetFile(\"./data/dt=20230101/userdata.parquet\")\n",
641 | "\n",
642 | "print(\"parquet_file size: \", sys.getsizeof(parquet_file))\n",
643 | "parquet_file.metadata"
644 | ]
645 | },
646 | {
647 | "cell_type": "code",
648 | "execution_count": 25,
649 | "id": "26a0352e-1049-43cf-9f4b-651c2b0a215f",
650 | "metadata": {
651 | "collapsed": true,
652 | "jupyter": {
653 | "outputs_hidden": true
654 | }
655 | },
656 | "outputs": [
657 | {
658 | "name": "stdout",
659 | "output_type": "stream",
660 | "text": [
661 | "dataset size : 64\n"
662 | ]
663 | },
664 | {
665 | "data": {
666 | "text/plain": [
667 | "['./data/dt=20230101/userdata.parquet']"
668 | ]
669 | },
670 | "execution_count": 25,
671 | "metadata": {},
672 | "output_type": "execute_result"
673 | }
674 | ],
675 | "source": [
676 | "from pyarrow.parquet import ParquetDataset\n",
677 | "\n",
678 | "dataset = ParquetDataset(\"./data\")\n",
679 | "\n",
680 | "print(\"dataset size :\", sys.getsizeof(dataset))\n",
681 | "\n",
682 | "dataset.files"
683 | ]
684 | },
685 | {
686 | "cell_type": "code",
687 | "execution_count": null,
688 | "id": "77eda780-7a45-495a-8a9e-aae88ce0cf49",
689 | "metadata": {
690 | "collapsed": true,
691 | "jupyter": {
692 | "outputs_hidden": true
693 | }
694 | },
695 | "outputs": [],
696 | "source": []
697 | }
698 | ],
699 | "metadata": {
700 | "kernelspec": {
701 | "display_name": "PyEnv 3.9.18",
702 | "language": "python",
703 | "name": "3.9.18"
704 | },
705 | "language_info": {
706 | "codemirror_mode": {
707 | "name": "ipython",
708 | "version": 3
709 | },
710 | "file_extension": ".py",
711 | "mimetype": "text/x-python",
712 | "name": "python",
713 | "nbconvert_exporter": "python",
714 | "pygments_lexer": "ipython3",
715 | "version": "3.9.18"
716 | }
717 | },
718 | "nbformat": 4,
719 | "nbformat_minor": 5
720 | }
721 |
--------------------------------------------------------------------------------
/101-GeoHash/02 Lat Lng - Addition, Angle.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 2,
6 | "metadata": {},
7 | "outputs": [
8 | {
9 | "name": "stdout",
10 | "output_type": "stream",
11 | "text": [
12 | "Populating the interactive namespace from numpy and matplotlib\n"
13 | ]
14 | }
15 | ],
16 | "source": [
17 | "%pylab inline\n",
18 | "import geohash\n",
19 | "import folium\n",
20 | "\n",
21 | "from geopy.distance import distance\n",
22 | "from polygon_geohasher.polygon_geohasher import geohash_to_polygon"
23 | ]
24 | },
25 | {
26 | "cell_type": "markdown",
27 | "metadata": {},
28 | "source": [
29 | "# Addition"
30 | ]
31 | },
32 | {
33 | "cell_type": "code",
34 | "execution_count": 373,
35 | "metadata": {},
36 | "outputs": [
37 | {
38 | "name": "stdout",
39 | "output_type": "stream",
40 | "text": [
41 | "500m addition\n",
42 | "Latitude Addition: 500.00005283004964\n",
43 | "Longitude Addition: 500.0049490970048\n",
44 | "Both Addition: 707.0996972853251\n",
45 | "1000m addition\n",
46 | "Latitude Addition: 1000.0004879418325\n",
47 | "Longitude Addition: 1000.0098977428217\n",
48 | "Both Addition: 1414.178421176091\n",
49 | "\n",
50 | "5000m addition\n",
51 | "Latitude Addition: 5000.017733038395\n",
52 | "Longitude Addition: 5000.049416527164\n",
53 | "Both Addition: 7070.052813071016\n",
54 | "\n",
55 | "10000m addition\n",
56 | "Latitude Addition: 10000.073709301136\n",
57 | "Longitude Addition: 10000.098381885806\n",
58 | "Both Addition: 14138.005608237756\n",
59 | "\n",
60 | "50000m addition\n",
61 | "Latitude Addition: 50001.900230356274\n",
62 | "Longitude Addition: 50000.41972205647\n",
63 | "Both Addition: 70605.6694353784\n",
64 | "\n"
65 | ]
66 | },
67 | {
68 | "data": {
69 | "text/html": [
70 | ""
71 | ],
72 | "text/plain": [
73 | ""
74 | ]
75 | },
76 | "execution_count": 373,
77 | "metadata": {},
78 | "output_type": "execute_result"
79 | }
80 | ],
81 | "source": [
82 | "def add_meter(lat, lng, lat_meter, lng_meter):\n",
83 | " new_lat = lat + (lat_meter/1000/6359.0899) * (180/np.pi)\n",
84 | " new_lng = lng + (lng_meter/1000/6386) * (180/np.pi) / np.cos(lat * np.pi/180)\n",
85 | " return new_lat, new_lng\n",
86 | "\n",
87 | "m = folium.Map(location=(lat, lng), zoom_start=12)\n",
88 | "lat, lng = 37.499402, 127.054207\n",
89 | "\n",
90 | "folium.Marker((lat, lng), popup='A').add_to(m)\n",
91 | "new_lat, new_lng = add_meter(lat, lng, 500, 500)\n",
92 | "folium.Marker((new_lat, new_lng), popup='500m').add_to(m)\n",
93 | "\n",
94 | "print('500m addition')\n",
95 | "print('Latitude Addition:', distance((lat, lng), (new_lat, lng)).m)\n",
96 | "print('Longitude Addition:', distance((lat, lng), (lat, new_lng)).m)\n",
97 | "print('Both Addition:', distance((lat, lng), (new_lat, new_lng)).m)\n",
98 | "\n",
99 | "new_lat, new_lng = add_meter(lat, lng, 1000, 1000)\n",
100 | "folium.Marker((new_lat, new_lng), popup='500m').add_to(m)\n",
101 | "print('1000m addition')\n",
102 | "print('Latitude Addition:', distance((lat, lng), (new_lat, lng)).m)\n",
103 | "print('Longitude Addition:', distance((lat, lng), (lat, new_lng)).m)\n",
104 | "print('Both Addition:', distance((lat, lng), (new_lat, new_lng)).m)\n",
105 | "print()\n",
106 | "\n",
107 | "new_lat, new_lng = add_meter(lat, lng, 5000, 5000)\n",
108 | "folium.Marker((new_lat, new_lng), popup='500m').add_to(m)\n",
109 | "print('5000m addition')\n",
110 | "print('Latitude Addition:', distance((lat, lng), (new_lat, lng)).m)\n",
111 | "print('Longitude Addition:', distance((lat, lng), (lat, new_lng)).m)\n",
112 | "print('Both Addition:', distance((lat, lng), (new_lat, new_lng)).m)\n",
113 | "print()\n",
114 | "\n",
115 | "new_lat, new_lng = add_meter(lat, lng, 10000, 10000)\n",
116 | "folium.Marker((new_lat, new_lng), popup='500m').add_to(m)\n",
117 | "print('10000m addition')\n",
118 | "print('Latitude Addition:', distance((lat, lng), (new_lat, lng)).m)\n",
119 | "print('Longitude Addition:', distance((lat, lng), (lat, new_lng)).m)\n",
120 | "print('Both Addition:', distance((lat, lng), (new_lat, new_lng)).m)\n",
121 | "print()\n",
122 | "\n",
123 | "\n",
124 | "new_lat, new_lng = add_meter(lat, lng, 50000, 50000)\n",
125 | "folium.Marker((new_lat, new_lng), popup='500m').add_to(m)\n",
126 | "print('50000m addition')\n",
127 | "print('Latitude Addition:', distance((lat, lng), (new_lat, lng)).m)\n",
128 | "print('Longitude Addition:', distance((lat, lng), (lat, new_lng)).m)\n",
129 | "print('Both Addition:', distance((lat, lng), (new_lat, new_lng)).m)\n",
130 | "print()\n",
131 | "\n",
132 | "# Visualization\n",
133 | "m"
134 | ]
135 | },
136 | {
137 | "cell_type": "markdown",
138 | "metadata": {},
139 | "source": [
140 | "# 3지점간의 각도 계산\n",
141 | "\n",
142 | "https://medium.com/@manivannan_data/find-the-angle-between-three-points-from-2d-using-python-348c513e2cd\n",
143 | "\n"
144 | ]
145 | },
146 | {
147 | "cell_type": "code",
148 | "execution_count": 417,
149 | "metadata": {},
150 | "outputs": [
151 | {
152 | "name": "stdout",
153 | "output_type": "stream",
154 | "text": [
155 | "Angle: 90.0\n"
156 | ]
157 | }
158 | ],
159 | "source": [
160 | "def calculate_angle(cur_location: np.ndarray, p1: np.ndarray, p2: np.ndarray) -> float:\n",
161 | " \"\"\"\n",
162 | " :param cur_location: the current location of the latitude and the longitude.\n",
163 | " :param p1: (latitude, longitude)\n",
164 | " :param p2: (latitude, longitude)\n",
165 | " :return: float\n",
166 | " \"\"\"\n",
167 | " ab = cur_location - p1\n",
168 | " ac = cur_location - p2\n",
169 | " \n",
170 | " _direction = (np.dot(ab, ac) ) / (np.linalg.norm(ab) * np.linalg.norm(ac))\n",
171 | " _direction = min(max(_direction, -1), 1)\n",
172 | " angle = np.arccos(_direction)\n",
173 | " angle = np.degrees(angle)\n",
174 | " angle = np.nan_to_num(angle)\n",
175 | " return round(float(angle), 4)\n",
176 | " \n",
177 | "a = np.array([0, 0])\n",
178 | "b = np.array([5, 0])\n",
179 | "c = np.array([0, 5])\n",
180 | "\n",
181 | "print('Angle:', calculate_angle(a, b, c))"
182 | ]
183 | },
184 | {
185 | "cell_type": "code",
186 | "execution_count": 416,
187 | "metadata": {},
188 | "outputs": [
189 | {
190 | "name": "stdout",
191 | "output_type": "stream",
192 | "text": [
193 | "Angle: 28.53\n"
194 | ]
195 | },
196 | {
197 | "data": {
198 | "text/html": [
199 | ""
200 | ],
201 | "text/plain": [
202 | ""
203 | ]
204 | },
205 | "execution_count": 416,
206 | "metadata": {},
207 | "output_type": "execute_result"
208 | }
209 | ],
210 | "source": [
211 | "a = np.array([37.388641, 127.092138]) # Current location\n",
212 | "b = np.array([37.393937, 127.112294])\n",
213 | "c = np.array([37.381100, 127.122811])\n",
214 | "\n",
215 | "angle = calculate_angle(a, b, c)\n",
216 | "\n",
217 | "print('Angle:', round(angle, 2))\n",
218 | "\n",
219 | "m = folium.Map(location=(a+b+c)/3, zoom_start=13)\n",
220 | "folium.Marker(a, popup='A Current Location', icon=folium.Icon(color='black')).add_to(m)\n",
221 | "folium.Marker(b, popup='B Favorite Off Location', icon=folium.Icon(color='green')).add_to(m)\n",
222 | "folium.Marker(c, popup=f'C Call {int(angle)} degree', icon=folium.Icon(color='red')).add_to(m)\n",
223 | "folium.PolyLine([a, b], color='green').add_to(m)\n",
224 | "folium.PolyLine([a, c], color='red').add_to(m)\n",
225 | "m"
226 | ]
227 | },
228 | {
229 | "cell_type": "markdown",
230 | "metadata": {},
231 | "source": [
232 | "# Circle"
233 | ]
234 | },
235 | {
236 | "cell_type": "code",
237 | "execution_count": 5,
238 | "metadata": {},
239 | "outputs": [
240 | {
241 | "name": "stdout",
242 | "output_type": "stream",
243 | "text": [
244 | "distance in km: 17.393393505034894\n"
245 | ]
246 | },
247 | {
248 | "data": {
249 | "text/html": [
250 | ""
251 | ],
252 | "text/plain": [
253 | ""
254 | ]
255 | },
256 | "execution_count": 5,
257 | "metadata": {},
258 | "output_type": "execute_result"
259 | }
260 | ],
261 | "source": [
262 | "a = np.array([37.388641, 127.092138])\n",
263 | "b = np.array([37.498641, 126.952138])\n",
264 | "\n",
265 | "print('distance in km:', distance(a, b).km)\n",
266 | "\n",
267 | "m = folium.Map(location=(a+b)/2, zoom_start=11)\n",
268 | "folium.Marker(a, popup='A', icon=folium.Icon(color='black')).add_to(m)\n",
269 | "folium.Marker(b, popup='B', icon=folium.Icon(color='green')).add_to(m)\n",
270 | "folium.Circle((a+b)/2, 10000, tooltip='test').add_to(m)\n",
271 | "folium.Circle((a+b)/2, 5000, tooltip='test').add_to(m)\n",
272 | "m"
273 | ]
274 | }
275 | ],
276 | "metadata": {
277 | "kernelspec": {
278 | "display_name": "Python 3",
279 | "language": "python",
280 | "name": "python3"
281 | },
282 | "language_info": {
283 | "codemirror_mode": {
284 | "name": "ipython",
285 | "version": 3
286 | },
287 | "file_extension": ".py",
288 | "mimetype": "text/x-python",
289 | "name": "python",
290 | "nbconvert_exporter": "python",
291 | "pygments_lexer": "ipython3",
292 | "version": "3.6.7"
293 | },
294 | "toc": {
295 | "base_numbering": 1,
296 | "nav_menu": {},
297 | "number_sections": true,
298 | "sideBar": true,
299 | "skip_h1_title": false,
300 | "title_cell": "Table of Contents",
301 | "title_sidebar": "Contents",
302 | "toc_cell": false,
303 | "toc_position": {},
304 | "toc_section_display": true,
305 | "toc_window_display": false
306 | }
307 | },
308 | "nbformat": 4,
309 | "nbformat_minor": 2
310 | }
311 |
--------------------------------------------------------------------------------
/200-Kubernetes/02-Generate-Fashion-MNIST-Sample-Images.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 15,
6 | "metadata": {},
7 | "outputs": [
8 | {
9 | "name": "stdout",
10 | "output_type": "stream",
11 | "text": [
12 | "Populating the interactive namespace from numpy and matplotlib\n",
13 | "x_train: (60000, 28, 28)\n",
14 | "y_train: (60000,)\n",
15 | "x_test: (10000, 28, 28)\n",
16 | "y_test: (10000,)\n"
17 | ]
18 | }
19 | ],
20 | "source": [
21 | "%pylab inline\n",
22 | "import keras\n",
23 | "import imageio\n",
24 | "import os\n",
25 | "\n",
26 | "fashion_mnist = keras.datasets.fashion_mnist\n",
27 | "\n",
28 | "class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',\n",
29 | " 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']\n",
30 | "(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()\n",
31 | "\n",
32 | "print('x_train:', x_train.shape)\n",
33 | "print('y_train:', y_train.shape)\n",
34 | "print('x_test:', x_test.shape)\n",
35 | "print('y_test:', y_test.shape)"
36 | ]
37 | },
38 | {
39 | "cell_type": "code",
40 | "execution_count": null,
41 | "metadata": {},
42 | "outputs": [],
43 | "source": [
44 | "import scipy.misc.ims"
45 | ]
46 | },
47 | {
48 | "cell_type": "code",
49 | "execution_count": 22,
50 | "metadata": {},
51 | "outputs": [
52 | {
53 | "name": "stdout",
54 | "output_type": "stream",
55 | "text": [
56 | "(28, 28)\n",
57 | "(28, 28)\n",
58 | "(28, 28)\n",
59 | "(28, 28)\n",
60 | "(28, 28)\n",
61 | "(28, 28)\n",
62 | "(28, 28)\n",
63 | "(28, 28)\n",
64 | "(28, 28)\n",
65 | "(28, 28)\n",
66 | "(28, 28)\n",
67 | "(28, 28)\n",
68 | "(28, 28)\n",
69 | "(28, 28)\n",
70 | "(28, 28)\n",
71 | "(28, 28)\n",
72 | "(28, 28)\n",
73 | "(28, 28)\n",
74 | "(28, 28)\n",
75 | "(28, 28)\n",
76 | "(28, 28)\n",
77 | "(28, 28)\n",
78 | "(28, 28)\n",
79 | "(28, 28)\n",
80 | "(28, 28)\n"
81 | ]
82 | },
83 | {
84 | "data": {
85 | "image/png": "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\n",
86 | "text/plain": [
87 | ""
88 | ]
89 | },
90 | "metadata": {},
91 | "output_type": "display_data"
92 | }
93 | ],
94 | "source": [
95 | "if not os.path.exists('./sample'):\n",
96 | " os.makedirs('./sample')\n",
97 | "\n",
98 | "plt.figure(figsize=(10,10))\n",
99 | "for i in range(25):\n",
100 | " idx = np.random.randint(0, x_train.shape[0])\n",
101 | " plt.subplot(5,5,i+1)\n",
102 | " plt.xticks([])\n",
103 | " plt.yticks([])\n",
104 | " plt.grid(False)\n",
105 | " plt.imshow(x_train[idx], cmap=plt.cm.binary)\n",
106 | " plt.xlabel(class_names[y_train[idx]])\n",
107 | " \n",
108 | " imageio.imsave(f'./sample/sample_{idx:03}.jpg', x_train[idx])\n",
109 | "plt.show()"
110 | ]
111 | },
112 | {
113 | "cell_type": "code",
114 | "execution_count": null,
115 | "metadata": {},
116 | "outputs": [],
117 | "source": []
118 | }
119 | ],
120 | "metadata": {
121 | "kernelspec": {
122 | "display_name": "Python 3",
123 | "language": "python",
124 | "name": "python3"
125 | },
126 | "language_info": {
127 | "codemirror_mode": {
128 | "name": "ipython",
129 | "version": 3
130 | },
131 | "file_extension": ".py",
132 | "mimetype": "text/x-python",
133 | "name": "python",
134 | "nbconvert_exporter": "python",
135 | "pygments_lexer": "ipython3",
136 | "version": "3.8.2"
137 | }
138 | },
139 | "nbformat": 4,
140 | "nbformat_minor": 4
141 | }
142 |
--------------------------------------------------------------------------------