pandas-0.15.1, oursql-0.9.3.1 ๋ฐ sqlalchemy-0.9.4๋ฅผ ์ฌ์ฉํ์ฌ ~30k ํ์ mysql ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฝ์
ํ๋ ค๊ณ ํ์ต๋๋ค. ๊ธฐ๊ณ๊ฐ ์ ์ ๋์์ ๊ฑด๋ํธ์ ์๊ธฐ ๋๋ฌธ์ data.to_sql
๋ฅผ ํธ์ถํ๋ ๋ฐ ๋ฐ์ดํฐ๋ฅผ ์ฝ์
ํ๋ ๋ฐ 1์๊ฐ ๋๊ฒ ๊ฑธ๋ ธ์ต๋๋ค. wireshark๋ก ๊ฒ์ฌํ ๋ ๋ฌธ์ ๋ ๋ชจ๋ ํ์ ๋ํด ์ฝ์
์ ๋ณด๋ธ ๋ค์ ๋ค์ ํ์ ๋ณด๋ด๊ธฐ ์ ์ ACK๋ฅผ ๊ธฐ๋ค๋ฆฌ๊ณ ์๊ณ , ๊ฐ๋จํ ๋งํด์ ํ ์๊ฐ์ด ๋๋ฅผ ์ฃฝ์ด๊ณ ์๋ค๋ ๊ฒ์
๋๋ค.
๊ทธ๋ฌ๋ SQLAlchemy ์ ์ง์นจ์ ๋ฐ๋ผ ๋ณ๊ฒฝํ์ต๋๋ค.
def _execute_insert(self, conn, keys, data_iter):
data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
conn.execute(self.insert_statement(), data)
์๊ฒ
def _execute_insert(self, conn, keys, data_iter):
data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
conn.execute(self.insert_statement().values(data))
์ ์ฒด ์์
์ด 1๋ถ ์ด๋ด์ ์๋ฃ๋ฉ๋๋ค. (ํด๋ฆญ ํ ๋ฒ์ ์ค์ด๊ธฐ ์ํด insert into foo (columns) values (rowX)
์ ๋ํ ์ฌ๋ฌ ํธ์ถ๊ณผ ๋๊ท๋ชจ insert into foo (columns) VALUES (row1), (row2), row3)
ํธ์ถ ๊ฐ์ ์ฐจ์ด์
๋๋ค.) ์ฌ๋๋ค์ด ํฌ๋๋ฅผ ์ฌ์ฉํ์ฌ ๋๋์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์
ํ ๊ฐ๋ฅ์ฑ์ด ์ผ๋ง๋ ๋๋์ง๋ฅผ ๊ฐ์ํ ๋ ์ด๊ฒ์ ๋ ๊ด๋ฒ์ํ๊ฒ ํฌํจ๋ ์ ์๋ ํฐ ์น๋ฆฌ์ฒ๋ผ ๋๊ปด์ง๋๋ค.
๋ช ๊ฐ์ง ๊ณผ์ :
์ด๋ฅผ ์ํํ๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ multirow=
๋ถ์ธ ๋งค๊ฐ๋ณ์(๊ธฐ๋ณธ๊ฐ False
)๋ฅผ to_sql
ํจ์์ ์ถ๊ฐํ ๋ค์ ์ฒญํฌ ํฌ๊ธฐ ์ค์ ์ ์ฌ์ฉ์์๊ฒ ๋งก๊ธฐ๋ ๊ฒ์
๋๋ค. ํ์ง๋ง ๋ ์ข์ ๋ฐฉ๋ฒ์ด ์์๊น์?
์๊ฐ?
์ด๊ฒ์ ํฉ๋ฆฌ์ ์ผ๋ก ๋ณด์ ๋๋ค. ์กฐ์ฌํด ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค!
๊ตฌํ์ ๊ฒฝ์ฐ sqlalchemy๊ฐ ์ด๊ฒ์ ์ง์ํ์ง ์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํน์ง์ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ๋์ง์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋๋ค(์ง๊ธ์ ํ
์คํธํ ์ ์์ง๋ง sqlalchemy์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ๊ฒ ๊ฐ์ต๋๋ค(์: http://stackoverflow.com/questions/ 23886764/multiple-insert-statements-in-mssql-with-sqlalchemy) ๋ํ ๋ง์ ์ฌ๋๋ค์ด ์ฒญํฌ ํฌ๊ธฐ๋ฅผ ์ค์ ํด์ผ ํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ด๋ํ๋ ๊ฒฝ์ฐ ์ค์ ๋ก ๊ธฐ๋ณธ๊ฐ์ผ๋ก ํ๋ ๊ฒ์ ์ข์ ์๊ฐ์ด ์๋๋๋ค(์ฐ๋ฆฌ๊ฐ ์ค์ ํ์ง ์๋ ํ ๊ธฐ๋ณธ์ ์ผ๋ก ๊ฐ์ผ๋ก chunksize).
๋ฐ๋ผ์ ํค์๋๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ด ๋ ๋์ ๊ฒ ๊ฐ์ต๋๋ค.
@artemyk @mangecoeur @hayd @danielballan
๋ถ๋ช
ํ SQLAlchemy์๋ dialect.supports_multivalues_insert
ํ๋๊ทธ๊ฐ ์์ต๋๋ค(์: http://pydoc.net/Python/SQLAlchemy/0.8.3/sqlalchemy.sql.compiler/ ์ฐธ์กฐ, ๋ค๋ฅธ ๋ฒ์ ์์๋ supports_multirow_insert
๋ผ๊ณ ํ ์ ์์, https ://www.mail-archive.com/[email protected]/msg202880.html ).
์ด๊ฒ์ ์ฝ์ ์๋๋ฅผ ๋ง์ด ๋์ผ ์ ์๊ณ ์ง์์ ์ฝ๊ฒ ํ์ธํ ์ ์๊ธฐ ๋๋ฌธ์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ ์ ์๊ณ chunksize๋ฅผ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ค์ ํ ์๋ ์๋ค๊ณ ์๊ฐํฉ๋๋ค(์: 16kb ์ฒญํฌ... ํ์คํ์ง ์์ ๋๋ถ๋ถ์ ์ํฉ์์ ๋๋ฌด ํผ). ๋ค์ค ํ ์ฝ์ ์ด ์คํจํ๋ฉด ์ฒญํฌ ํฌ๊ธฐ๋ฅผ ๋ฎ์ถ๋๋ก ์ ์ํ๋ ์์ธ๊ฐ ๋ฐ์ํ ์ ์์ต๋๊น?
์ด์ SQLAlchemy ์ฌ๋๋ค์๊ฒ SQL Server >2005์์ supports_multivalues_insert
๋ฅผ true๋ก ์ค์ ํ๋๋ก ์ค๋ํด์ผ ํฉ๋๋ค(์ฝ๋์ ํดํนํ์ฌ ์ ๋๋ก ์๋ํ์ง๋ง ๊ธฐ๋ณธ์ ์ผ๋ก ์ผ์ ธ ์์ง ์์).
์ข ๋ ์ฃผ์ ๋ณ ๋ฉ๋ชจ์์ ๋๋ ์ฒญํฌ ํฌ๊ธฐ๊ฐ ๊น๋ค๋ก์ธ ์ ์๋ค๊ณ ์๊ฐํฉ๋๋ค. mysql ์ค์ (๋์ฉ๋ ํจํท์ ํ์ฉํ๋๋ก ๊ตฌ์ฑ)์์ chunksize=5000์ ์ค์ ํ ์ ์์ต๋๋ค. SQLServer ์ค์ ์์๋ 500์ด ๋๋ฌด ์ปธ์ง๋ง 100์ ์ ๋๋ก ์๋ํ์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด ๊ธฐ์ ์ ๋๋ถ๋ถ์ ์ด์ ์ ํ ๋ฒ์ 1ํ์ 100์์ 1000์ผ๋ก ์ฝ์ ํ๋ ๊ฒ์ด ์๋๋ผ 100์ผ๋ก ์ฝ์ ํ๋ ๋ฐ์ ์ค๋ ๊ฒ์ด ์ฌ์ค์ผ ๊ฒ์ ๋๋ค.
chunksize=None
๊ฐ "์ ์ ํ๊ฒ ์ฒญํฌ ํฌ๊ธฐ ์ ํ"์ ์๋ฏธํ๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น์? 5000, 500, 50, 1๊ณผ ๊ฐ์ ๊ฒ์ ์๋ํ์ญ์์ค. ์ฌ์ฉ์๋ ์ฒญํฌ ํฌ๊ธฐ๋ฅผ ์ง์ ํ์ฌ ์ด ๊ธฐ๋ฅ์ ๋ ์ ์์ต๋๋ค. ์ด๋ฌํ ์๋์ ์ค๋ฒํค๋๊ฐ ๋๋ฌด ํฌ๋ฉด @maxgrenderjones ์ ์์ด ์ข์ต๋๋ค. chunksize=10
๊ฐ chunksize=1
๋ณด๋ค ๋ ๋์ ๊ธฐ๋ณธ๊ฐ์
๋๋ค.
๊ทธ ๋ง์ง๋ง ์ฝ๋ฉํธ์์ " chunksize=10
๋ chunksize=1
๋ณด๋ค ๋ ๋์ ๊ธฐ๋ณธ๊ฐ์
๋๋ค. " -> ๊ทธ๊ฒ์ ์์ ํ ์ฌ์ค์ด ์๋๋ผ๊ณ ์๊ฐํฉ๋๋ค. ํ์ฌ ์ํฉ์ ์ฌ๋ฌ ํ์ ๋จ์ผ ํ ์ฝ์
๋ช
๋ น๋ฌธ(์ฒญํฌ ํฌ๊ธฐ๊ฐ 1์ด ์๋)์ผ๋ก ๊ตฌ์ฑ๋ ์คํ ๋ช
๋ น๋ฌธ์ _one_ ์ํํ๋ ๊ฒ์
๋๋ค. ๋ฐ๋ฉด chunksize=10
๋ ํ๋์ ๋ค์ค ํ์ด ์์ ๋๋ง๋ค ๋ง์ ์คํ ๋ช
๋ น๋ฌธ์ ์ํํจ์ ์๋ฏธํฉ๋๋ค. ๋ผ์ ๋ฃ๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๊ฒ์ด ๋ฐ๋์ ๋ ๋น ๋ฅธ์ง๋ ๋ชจ๋ฅด๊ฒ ์ง๋ง ์ํฉ์ ๋ฐ๋ผ ๋ง์ด ๋ค๋ฆ
๋๋ค. ์๋ฅผ ๋ค์ด ํ์ฌ ์ฝ๋์ ๋ก์ปฌ sqlite ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ฒฝ์ฐ:
In [4]: engine = create_engine('sqlite:///:memory:') #, echo='debug')
In [5]: df = pd.DataFrame(np.random.randn(50000, 10))
In [6]: %timeit df.to_sql('test_default', engine, if_exists='replace')
1 loops, best of 3: 956 ms per loop
In [7]: %timeit df.to_sql('test_default', engine, if_exists='replace', chunksize=10)
1 loops, best of 3: 2.23 s per loop
๊ทธ๋ฌ๋ ๋ฌผ๋ก ์ด๊ฒ์ ๋ค์ค ํ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ง ์์ต๋๋ค.
๋ค๋ฅธ ์ฌ๋์๊ฒ ์ ์ฉํ ์ ์๋ ์์ญ์ด ํจ์น ๋ฐฉ๋ฒ์ ์์๋์ต๋๋ค. ํฌ๋๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ ์ ์ด ์ฝ๋๊ฐ ์์ด์ผ ํฉ๋๋ค.
from pandas.io.sql import SQLTable
def _execute_insert(self, conn, keys, data_iter):
print "Using monkey-patched _execute_insert"
data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
conn.execute(self.insert_statement().values(data))
SQLTable._execute_insert = _execute_insert
์๋ก์ด multirow=True
ํค์๋(ํ์ฌ ๊ธฐ๋ณธ๊ฐ์ False์)๋ฅผ ํตํด ์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ ๊ฒ์ผ๋ก ์์ํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํํ ์ ์๋์ง ๋์ค์ ํญ์ ํ์ธํ ์ ์์ต๋๋ค.
@maxgrenderjones @nhockham ์ด ์ด๊ฒ์ ์ถ๊ฐํ๊ธฐ ์ํด PR์ ํ๊ณ ์ถ์ต๋๊น?
@jorisvandenbossche ํน์ ์ฑ๋ฅ ํ๋กํ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด ํค์๋ ์ธ์๋ฅผ ์ถ๊ฐํ๊ธฐ ์์ํ๋ ๊ฒ์ ์ํํ๋ค๊ณ ์๊ฐํฉ๋๋ค. ๋ชจ๋ ๊ฒฝ์ฐ์ ๋ ๋น ๋ฅด๋ค๊ณ ๋ณด์ฅํ ์ ์๋ค๋ฉด(ํ์ํ ๊ฒฝ์ฐ ์ ๋ ฅ์ ๊ธฐ๋ฐ์ผ๋ก ์ต์์ ๋ฐฉ๋ฒ์ ๊ฒฐ์ ํ๋๋ก ํ์ฌ) ํ๋๊ทธ๊ฐ ์ ํ ํ์ํ์ง ์์ต๋๋ค.
์๋ก ๋ค๋ฅธ DB ์ค์ ์ ์๋ก ๋ค๋ฅธ ์ฑ๋ฅ ์ต์ ํ(๋ค๋ฅธ DB ์ฑ๋ฅ ํ๋กํ, ๋ก์ปฌ ๋ ๋คํธ์ํฌ, ๋์ฉ๋ ๋ฉ๋ชจ๋ฆฌ ๋ ๊ณ ์ SSD ๋ฑ)๋ฅผ ๊ฐ์ง ์ ์์ผ๋ฉฐ, ๊ฐ๊ฐ์ ๋ํด ํค์๋ ํ๋๊ทธ๋ฅผ ์ถ๊ฐํ๊ธฐ ์์ํ๋ฉด ์๋ง์ด ๋ฉ๋๋ค.
์ฑ๋ฅ๋ณ ๊ตฌํ์ ํด๊ฒฐํ๊ธฐ ์ํด SQLDatabase ๋ฐ SQLTable์ ํ์ ํด๋์ค๋ฅผ ๋ง๋๋ ๊ฒ์ด ์ข์ต๋๋ค. ์ด๋ฌํ ํ์ ํด๋์ค๋ ๊ฐ์ฒด ์งํฅ API๋ฅผ ํตํด ์ฌ์ฉ๋ฉ๋๋ค. ์๋ง๋ "๋ฐฑ์๋ ์ ํ" ๋ฐฉ๋ฒ์ ์ถ๊ฐํ ์ ์์ง๋ง ์์งํ OO API๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋งค์ฐ ๊ฐ๋จํ๋ฏ๋ก ์ด๋ฏธ ์ ๋ฌธํ๋ ์ฌ์ฉ ์ฌ๋ก์ ๋ํด์๋ ๊ณผ๋ํฉ๋๋ค.
Postgres์ ๋์ฉ๋ ๋ฐ์ดํฐ ์ธํธ๋ฅผ ๋ก๋ํ๊ธฐ ์ํด ์ด๋ฌํ ํ์ ํด๋์ค๋ฅผ ๋ง๋ค์์ต๋๋ค(์ค์ ๋ก ๋ฐ์ดํฐ๋ฅผ CSV์ ์ ์ฅํ ๋ค์ ๋ด์ฅ๋ ๋นํ์ค COPY FROM sql ๋ช
๋ น์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ฝ์
์ ์ฌ์ฉํ๋ ๊ฒ๋ณด๋ค ํจ์ฌ ๋น ๋ฆ
๋๋ค. https://gist.github ์ฐธ์กฐ. com/mangecoeur/1fbd63d4758c2ba0c470#file-pandas_postgres-py). ๊ทธ๊ฒ์ ์ฌ์ฉํ๋ ค๋ฉด PgSQLDatabase(engine, <args>).to_sql(frame, name,<kwargs>)
๋ฅผ ํ๋ฉด ๋ฉ๋๋ค.
์ฐธ๊ณ ๋ก ๋ค์ค ํ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ @jorisvandenbossche (12์ 3์ผ ๊ฒ์๋ฌผ)์ ์ฝ๋๋ฅผ ์คํํด ๋ณด์์ต๋๋ค. ๊ฝค ๋๋ฆฝ๋๋ค. ๋ฐ๋ผ์ ์ฌ๊ธฐ์ ์๋ ์ ์ถฉ์ ์ฌ์ํ ๊ฒ์ด ์๋๋๋ค.
In [4]: engine = create_engine('sqlite:///:memory:') #, echo='debug')
In [5]: df = pd.DataFrame(np.random.randn(50000, 10))
In [6]:
In [6]: %timeit df.to_sql('test_default', engine, if_exists='replace')
1 loops, best of 3: 1.05 s per loop
In [7]:
In [7]: from pandas.io.sql import SQLTable
In [8]:
In [8]: def _execute_insert(self, conn, keys, data_iter):
...: data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
...: conn.execute(self.insert_statement().values(data))
...:
In [9]: SQLTable._execute_insert = _execute_insert
In [10]:
In [10]: reload(pd)
Out[10]: <module 'pandas' from '/usr/local/lib/python2.7/site-packages/pandas/__init__.pyc'>
In [11]:
In [11]: %timeit df.to_sql('test_default', engine, if_exists='replace', chunksize=10)
1 loops, best of 3: 9.9 s per loop
๋ํ ํค์๋ ๋งค๊ฐ๋ณ์๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ด ์ํํ๋ค๋ ๋ฐ ๋์ํฉ๋๋ค. ๊ทธ๋ฌ๋ ๋ค์ค ํ ๊ธฐ๋ฅ์ ๋งค์ฐ ๊ธฐ๋ณธ์ ์ธ ๊ฒ ๊ฐ์ต๋๋ค. ๋ํ 'monkey-patching'์ ํค์๋ ๋งค๊ฐ๋ณ์๋ณด๋ค API ๋ณ๊ฒฝ์ ๋ ๊ฐ๋ ฅํ์ง ์์ ์ ์์ต๋๋ค.
๋ด๊ฐ ์์ฌํ ๋๋ก์ผ. ์์ญ์ด ํจ์น๋ ๋ด๊ฐ ์ ์ํ ์๋ฃจ์ ์ด ์๋๋๋ค. ์คํ๋ ค ์ ๋ณด์ ์ ๊ฐํ ์ฌ์ฉ์๊ฐ OO ์ธํฐํ์ด์ค๋ฅผ ํตํด ์ฌ์ฉํ ์ ์๋ ์ฌ๋ฌ ์ฑ๋ฅ ์งํฅ ํ์ ํด๋์ค๋ฅผ ์ ๊ณตํฉ๋๋ค(๋๋ฌด ๋ง์ ์ต์ ์ด ์๋ ๊ธฐ๋ฅ์ API ๋ก๋๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด)
------์๋ฌธ-----
๋ณด๋ธ ์ฌ๋: "Artemy Kolchinsky" [email protected]
๋ณด๋ธ ๋ ์ง: โ26/โ02/โ2015 17:13
๋ฐ๋ ์ฌ๋: "pydata/pandas" [email protected]
์ฐธ์กฐ: "mangecoeur" ์กด. [email protected]
์ ๋ชฉ: Re: [pandas] to_sqlover ๋๊ธฐ ์๊ฐ์ด ๊ธด ์ฐ๊ฒฐ์์ ์์ฒญ๋ ์๋ ํฅ์์ ์ํด ๋ค์ค ํ ์ฝ์
์ ์ฌ์ฉํฉ๋๋ค(#8953).
์ฐธ๊ณ ๋ก ๋ค์ค ํ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ @jorisvandenbossche (12์ 3์ผ ๊ฒ์๋ฌผ)์ ์ฝ๋๋ฅผ ์คํํด ๋ณด์์ต๋๋ค. ๊ฝค ๋๋ฆฝ๋๋ค. ๋ฐ๋ผ์ ์ฌ๊ธฐ์ ์๋ ์ ์ถฉ์ ์ฌ์ํ ๊ฒ์ด ์๋๋๋ค.
[4]์์: engine = create_engine('sqlite:///:memory:') #, echo='debug')
[5]์์: df = pd.DataFrame(np.random.randn(50000, 10))
[6]์์:
[6]์์: %timeit df.to_sql('test_default', ์์ง, if_exists='replace')
1๊ฐ ๋ฃจํ, 3๊ฐ ์ค ์ต๊ณ : ๋ฃจํ๋น 1.05์ด
[7]์์:
[7]์์: pandas.io.sql์์ SQLTable ๊ฐ์ ธ์ค๊ธฐ
[8]์์:
[8]์์: def _execute_insert(self, conn, keys, data_iter):
...: ๋ฐ์ดํฐ = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
...: conn.execute(self.insert_statement().values(data))
...:
[9]์์: SQLTable._execute_insert = _execute_insert
[10]์์:
[10]์์: ๋ค์ ๋ก๋(pd)
์์[10]:
[11]์์:
[11]์์: %timeit df.to_sql('test_default', ์์ง, if_exists='replace', chunksize=10)
1๊ฐ ๋ฃจํ, 3๊ฐ ์ค ์ต๊ณ : ๋ฃจํ๋น 9.9์ด
๋ํ ํค์๋ ๋งค๊ฐ๋ณ์๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ด ์ํํ๋ค๋ ๋ฐ ๋์ํฉ๋๋ค. ๊ทธ๋ฌ๋ ๋ค์ค ํ ๊ธฐ๋ฅ์ ๋งค์ฐ ๊ธฐ๋ณธ์ ์ธ ๊ฒ ๊ฐ์ต๋๋ค. ๋ํ 'monkey-patching'์ ํค์๋ ๋งค๊ฐ๋ณ์๋ณด๋ค API ๋ณ๊ฒฝ์ ๋ ๊ฐ๋ ฅํ์ง ์์ ์ ์์ต๋๋ค.
โ
์ด ์ด๋ฉ์ผ์ ์ง์ ํ์ ํ๊ฑฐ๋ GitHub์์ ํ์ธํ์ธ์.
์ด๊ธฐ ํฐ์ผ ์ ๋ชฉ์ ๋ฐ๋ผ ์ด ์ ๊ทผ ๋ฐฉ์์ด ๋ชจ๋ ๊ฒฝ์ฐ์ ๋ฐ๋์งํ๋ค๊ณ ์๊ฐํ์ง ์์ผ๋ฏ๋ก ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ค์ ํ์ง ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๊ทธ๊ฒ ์์ด๋ ํ๋ค to_sql
๋ฅผ ์ฌ์ฉํ ์ ์์ผ๋ฏ๋ก ๊ณ์ ๋ณ๊ฒฝ์ ์์ฒญํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. (ํฌ๋ ๋ฒ์ ์ ์
๊ทธ๋ ์ด๋ํ ๋ ๊ฐ์ฅ ๋จผ์ ๋ณ๊ฒฝํ๋ ํญ๋ชฉ์ด๊ธฐ๋ ํฉ๋๋ค.) ํฉ๋ฆฌ์ ์ธ chunksize
๊ฐ์ ๊ดํด์๋, ํจํท ํฌ๊ธฐ๊ฐ ์์ธกํ๊ธฐ ์ด๋ ค์ด ๋ฐฉ์์ผ๋ก ์ผ๋ง๋ ๋ง์ ์ด(๊ทธ๋ฆฌ๊ณ ๊ทธ ์์ ๋ฌด์์ด ๋ค์ด ์๋์ง)์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๊ธฐ ๋๋ฌธ์ ํ๋์ ์ง์ ํ n
๊ฐ ์๋ค๊ณ ์๊ฐํฉ๋๋ค. . ๋ถํํ๋ chunksize
๋ฅผ ๋๋ฌด ๋๊ฒ ์ค์ ํ๋ฉด(์๋ง๋ SQLAlchemy์ ํจ์น๋ฅผ ์ ์ธํ๊ณ ๋ค์ค ํ ์ฝ์
์ด ์ผ์ง์ง ์๋ ์ด์ ์ผ ์ ์์) ์์ ํ ๊ด๋ จ์ด ์์ด ๋ณด์ด๋ ์ค๋ฅ ๋ฉ์์ง์ ํจ๊ป SQLServer๊ฐ ์คํจํ์ง๋ง, mysql
์ ์ ์๋ํฉ๋๋ค. ์ฌ์ฉ์๋ n
๊ฐ์ด ํ์ฉ ๊ฐ๋ฅํ ํฐ ํจํท ํฌ๊ธฐ(๋ฐฑ์
๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ๋ฌด์์ด๋ ๊ฐ์)๋ฅผ ์ด๋ํ ๊ฐ๋ฅ์ฑ์ด ์๋ ๊ฐ์ ๊ฒฐ์ ํ๊ธฐ ์ํด ์คํํด์ผ ํ ์ ์์ต๋๋ค. ํฌ๋๊ฐ n
๋ฅผ ์ ํํ๋ฉด ๊ตฌํ ์ธ๋ถ ์ฌํญ์์ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฒ๋ณด๋ค ํจ์ฌ ๋ ์๋๋ก ๋จ์ด์ง ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค(์ฆ, ์ต๋ ๊ฐ๋ฅํ ์ถ์ํ SQLALchemy ์ ๊ทผ ๋ฐฉ์๊ณผ ๋ฐ๋ ๋ฐฉํฅ).
์์ปจ๋, ์ฌ์ฉ ๋ฐฉ๋ฒ์ ๋ํ ์ ์ฉํ ์ค๋ช
๊ณผ ํจ๊ป ํค์๋๋ก ์ถ๊ฐํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ํค์๋๊ฐ ๊ตฌํ์ ์ ํํ๋ ๋ฐ ์ฌ์ฉ๋ ๊ฒ์ ์ด๋ฒ์ด ์ฒ์์ ์๋์ง๋ง(http://pandas.pydata.org/pandas-docs/dev/generated/pandas.DataFrame.apply.html ์ฐธ์กฐ) ์๋ง๋ ๊ทธ๋ ์ง ์์ ๊ฒ์
๋๋ค. ๊ฐ์ฅ ์ข์ ์๋ ์ค๋ช
์ ์ฝ์ด๋ raw=
์ด ๋ฌด์์ ์๋ฏธํ๋์ง ์ฒ์์ ๋ชฐ๋๊ธฐ ๋๋ฌธ์
๋๋ค!
๋๋ ๊ทธ๊ฒ์ด ๋ํ ์์ฒญ๋ ์์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์๋นํ๋ค๋ ๊ฒ์ ์์์ฐจ๋ ธ์ต๋๋ค. ์ฝ 700,000๊ฐ์ ํ๊ณผ 301๊ฐ์ ์ด์ด ์๋ 1.6GB ์ด์์ DataFrame๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ์ฝ์ ํ๋ ๋์ ๊ฑฐ์ 34GB๊ฐ ํ์ํฉ๋๋ค! ๊ทธ๊ฒ์ ๋นํจ์จ์ ์ธ ๊ฒ๊ณผ ๊ฐ์ต๋๋ค. ์ ๊ทธ๋ด ์ ์๋์ง์ ๋ํ ์์ด๋์ด๊ฐ ์์ต๋๊น? ๋ค์์ ํ๋ฉด ํด๋ฆฝ์ ๋๋ค.
์๋
ํ์ธ์ ์ฌ๋ฌ๋ถ,
์ด ๋ฌธ์ ์ ์ง์ ์ด ์์ต๋๊น?
to_sql์ ์ฌ์ฉํ์ฌ ์ฝ 200K ํ์ ์ฝ์ ํ๋ ค๊ณ ํ์ง๋ง ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๊ณ ์์ฒญ๋ ์์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์๋นํฉ๋๋ค! Chuncksize๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋ชจ๋ฆฌ์ ๋์์ด ๋์ง๋ง ์ฌ์ ํ ์๋๊ฐ ๋งค์ฐ ๋๋ฆฝ๋๋ค.
๋ด ์ธ์์ MSSQL DBase ์ถ์ ์ ๋ณด๋ฉด ์ฝ์ ์ด ์ค์ ๋ก ํ ๋ฒ์ ํ ํ์ฉ ์ํ๋๋ค๋ ๊ฒ์ ๋๋ค.
์ด์ ์คํ ๊ฐ๋ฅํ ์ ์ผํ ์ ๊ทผ ๋ฐฉ์์ ๊ณต์ ํด๋์ csv ํ์ผ์ ๋คํํ๊ณ BULK INSERT๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋๋ค. ๊ทธ๋ฌ๋ ๊ทธ๊ฒ์ ๋งค์ฐ ์ฑ๊ฐ์๊ณ ๋ถ์ ์ ํฉ๋๋ค!
@andreacassioli odo ๋ฅผ ์ฌ์ฉํ์ฌ ์ค๊ฐ CSV ํ์ผ์ ํตํด SQL ๋ฐ์ดํฐ๋ฒ ์ด์ค์ DataFrame ์ ์ฝ์ ํ ์ ์์ต๋๋ค. SQL Database์ CSV ๋ก๋๋ฅผ ์ฐธ์กฐํ์ธ์.
ODBC๋ฅผ ์ฌ์ฉํ์ฌ BULK INSERT
์ฑ๋ฅ์ ๊ทผ์ ํ ์ ์๋ค๊ณ ์๊ฐํ์ง ์์ต๋๋ค.
@ostrokach ๊ฐ์ฌํฉ๋๋ค. ์ค์ ๋ก ์ง๊ธ csv ํ์ผ์ ์ฌ์ฉํ๊ณ ์์ต๋๋ค. ๋ด๊ฐ ๊ฐ๊น์ด ๊ฐ ์ ์๋ค๋ฉด, ๋๋ ๋จ์ํจ์ ์ํด ์ฝ๊ฐ์ ์๊ฐ์ ๊ตํํ ๊ฒ์ ๋๋ค!
๋๋ ์ด๊ฒ์ด ๋๊ตฐ๊ฐ๋ฅผ ๋์ธ ์ ์๋ค๊ณ ์๊ฐํ์ต๋๋ค.
http://docs.sqlalchemy.org/en/latest/faq/performance.html#i -m-inserting-400-000-rows-with-the-orm-and-it-s-really-slow
@indera pandas๋ ORM์ ์ฌ์ฉํ์ง ์๊ณ sqlalchemy Core๋ง ์ฌ์ฉํฉ๋๋ค(์ด๋ ๋์ฉ๋ ์ฝ์ ์ ์ฌ์ฉํ๋๋ก ์ ์๋ ๋ฌธ์ ํญ๋ชฉ).
๊ทธ๋์ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ๋ํ ํฉ์๊ฐ ์์ต๋๊น? ๋๋ ์๋ฐฑ๋ง ๊ฐ์ ํ์ postgres์ ์ฝ์ ํ๊ณ ์์ผ๋ฉฐ ์์ํ ๊ฑธ๋ฆฝ๋๋ค. CSV / odo๊ฐ ๊ฐ ๊ธธ์ ๋๊น?
@russlamb ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ์ค์ฉ์ ์ธ ๋ฐฉ๋ฒ์ ๋จ์ํ ์ผ๊ด ์
๋ก๋ํ๋ ๊ฒ์
๋๋ค. ์ด๊ฒ์ ๋๊ตฐ๊ฐ db ํน์ ์ด๋ฏ๋ก odo
์๋ postgresl
( mysql
์ผ ์ ์์)์ ๋ํ ์๋ฃจ์
์ด ์๋ค๊ณ ์๊ฐํฉ๋๋ค. sqlserver์ ๊ฐ์ ๊ฒฝ์ฐ '์ง์ ์ํ'ํด์ผ ํฉ๋๋ค(IOW ์์ฑํด์ผ ํจ).
sqlserver์ ๊ฒฝ์ฐ SQLAlchemy ์ํฐํฐ์ ํจ๊ป โโFreeTDS ๋๋ผ์ด๋ฒ( http://www.freetds.org/software.html ๋ฐ https://github.com/mkleehammer/pyodbc )๋ฅผ ์ฌ์ฉํ์ฌ ๋งค์ฐ ๋น ๋ฅธ ์ฝ์ (๋ฐ์ดํฐ ํ๋ ์๋น 20K ํ)์ ์์ฑํ์ต๋๋ค. :
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class DemographicEntity(Base):
__tablename__ = 'DEMOGRAPHIC'
patid = db.Column("PATID", db.Text, primary_key=True)
"""
patid = db.Column("PATID", db.Text, primary_key=True, autoincrement=False, nullable=True)
birth_date = db.Column("BIRTH_DATE", db.Date)
birth_time = db.Column("BIRTH_TIME", db.Text(5))
sex = db.Column("SEX", db.Text(2))
def get_db_url(db_host, db_port, db_name, db_user, db_pass):
params = parse.quote(
"Driver={{FreeTDS}};Server={};Port={};"
"Database={};UID={};PWD={};"
.format(db_host, db_port, db_name, db_user, db_pass))
return 'mssql+pyodbc:///?odbc_connect={}'.format(params)
def get_db_pool():
"""
Create the database engine connection.
<strong i="6">@see</strong> http://docs.sqlalchemy.org/en/latest/core/engines.html
:return: Dialect object which can either be used directly
to interact with the database, or can be passed to
a Session object to work with the ORM.
"""
global DB_POOL
if DB_POOL is None:
url = get_db_url(db_host=DB_HOST, db_port=DB_PORT, db_name=DB_NAME,
db_user=DB_USER, db_pass=DB_PASS)
DB_POOL = db.create_engine(url,
pool_size=10,
max_overflow=5,
pool_recycle=3600)
try:
DB_POOL.execute("USE {db}".format(db=DB_NAME))
except db.exc.OperationalError:
logger.error('Database {db} does not exist.'.format(db=DB_NAME))
return DB_POOL
def save_frame():
db_pool = get_db_pool()
records = df.to_dict(orient='records')
result = db_pool.execute(entity.__table__.insert(), records)
CSV / odo๊ฐ ๊ฐ ๊ธธ์ ๋๊น?
์ด ์๋ฃจ์ ์ ๋ค์ค ํ/์ฒญํฌ ํฌ๊ธฐ ์ค์ ์ ๊ด๊ณ์์ด ๊ฑฐ์ ํญ์ ๋ ๋น ๋ฅผ ๊ฒ์ ๋๋ค.
๊ทธ๋ฌ๋ @russlamb , ๊ทธ๋ฌํ ๋ค์ค ํ ํค์๋๊ฐ ๊ทํ์ ๊ฒฝ์ฐ์ ๊ฐ์ ์ด ๋ ์ง ์ฌ๋ถ๋ฅผ ๋ฃ๋ ๊ฒ์ ํญ์ ํฅ๋ฏธ๋กญ์ต๋๋ค. ์ด๋ฅผ ์ฝ๊ฒ ํ ์คํธํ๋ ๋ฐฉ๋ฒ์ https://github.com/pandas-dev/pandas/issues/8953#issuecomment -76139975๋ฅผ ์ฐธ์กฐํ์ธ์.
(๋ฐ๋์ ๊ธฐ๋ณธ๊ฐ์ ๋ณ๊ฒฝํ์ง ์๊ณ ๋) ์ด๊ฒ์ ์ง์ ํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ํ๋ค๋ ๋ฐ ๋์ํ๋ค๊ณ ์๊ฐํฉ๋๋ค. ๋ฐ๋ผ์ ๋๊ตฐ๊ฐ๊ฐ ์ด๋ฅผ ์ํด PR์ ํ๊ณ ์ถ๋ค๋ฉด ๊ทธ๊ฒ์ ํ์คํ ํ์ํ ์ผ์
๋๋ค.
์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ ๋ฐฉ๋ฒ์ ๋ํ ์ฝ๊ฐ์ ํ ๋ก ๋ง ์์์ต๋๋ค(์ ํค์๋ ๋ OO API๋ฅผ ์ฌ์ฉํ๋ ํ์ ํด๋์ค).
@jorisvandenbossche ์์์ ๋งํฌํ ๋ฌธ์๋ "๋๋ SQLAlchemy ORM์ ์๋์ ORM์ผ๋ก ์ฝ์ด ์์ค INSERT ๋ฐ UPDATE ๊ตฌ๋ฌธ์ ๋ฐฉ์ถํ๊ธฐ ์ํด ์์ ๋จ์ ํ๋ก์ธ์ค์ ํ์ ์น์ ์ ํํฌ๋ฅผ ์ ๊ณตํ๋ ๋๋ ์์ ๋ฐฉ๋ฒ ์ ํ๊ตฐ์ ์ ๊ณตํฉ๋๋ค. -๊ธฐ๋ฐ ์๋ํ."
๋ด๊ฐ ์ ์ํ๋ ๊ฒ์ ์์ ๊ฒ์ํ ์ฝ๋์์์ ๊ฐ์ด ์๋ ํฅ์์ ์ํด ๋ด๋ถ์ ์ผ๋ก SQLAlchemy ORM์ ์ฌ์ฉํ๋ to_sql
์ ๋ํ sqlserver ํน์ ๋ฒ์ ์ ๊ตฌํํ๋ ๊ฒ์
๋๋ค.
์ด๊ฒ์ ์ด์ ์ ์ ์๋์์ต๋๋ค. ๋น์ ์ด ๊ฐ๋ ๋ฐฉ๋ฒ์ pandas sql์ ๊ตฌํํ๋ ๊ฒ์
๋๋ค.
๋ฐฑ์๋์ ์ต์ ํ๋ ํด๋์ค ๋๋ ๊ณผ๊ฑฐ์ ์ฌ์ฉ ์์ง๋ฅผ ๊ฒ์ํ์ต๋๋ค.
ํจ์ฌ ๋น ๋ฅธ postgres COPY FROM ๋ช
๋ น. ๊ทธ๋ฌ๋ ๋น์ทํ ๊ฒ์
์ด์ odo์์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ ๋ ๊ฐ๋ ฅํ ๋ฐฉ์์ผ๋ก ๊ตฌ์ถ๋์์ต๋๋ค. ๋ณ๋ก ์๋ค
odo์์ ์์
์ ๋ณต์ ํ ๋ IMHO๋ฅผ ๊ฐ๋ฆฌํต๋๋ค.
2017๋ 3์ 7์ผ 00์ 53๋ถ์ "Andrei Sura" [email protected] ์ด ๋ค์๊ณผ ๊ฐ์ด ์ผ์ต๋๋ค.
@jorisvandenbossche https://github.com/jorisvandenbossche ๋ฌธ์
์์์ ์ธ๊ธํ ๋งํฌ๋ "๋๋ SQLAlchemy ORM์ด ๋๋์ ์ ๊ณตํฉ๋๋ค.
๋ฉ์๋์ ํ์ ์น์ ์ ๋ํ ํํฌ๋ฅผ ์ ๊ณตํ๋ ์์ ๋ชจ์
์ฝ์ด ๋ ๋ฒจ INSERT ๋ฐ UPDATE๋ฅผ ๋ด๋ณด๋ด๋ ์์ ๋จ์ ํ๋ก์ธ์ค
์ฝ๊ฐ์ ORM ๊ธฐ๋ฐ ์๋ํ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค."๋ด๊ฐ ์ ์ํ๋ ๊ฒ์ sqlserver ํน์ ๋ฒ์ ์ ๊ตฌํํ๋ ๊ฒ์ ๋๋ค.
๋ด๋ถ์ ์ผ๋ก ์๋ ํฅ์์ ์ํด SQLAlchemy ์ฝ์ด๋ฅผ ์ฌ์ฉํ๋ "to_sql".โ
๋น์ ์ด ์ธ๊ธ๋์๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๋ฐ๋ ๊ฒ์ ๋๋ค.
์ด ์ด๋ฉ์ผ์ ์ง์ ๋ต์ฅํ๊ณ GitHub์์ ํ์ธํ์ธ์.
https://github.com/pandas-dev/pandas/issues/8953#issuecomment-284437587 ,
๋๋ ์ค๋ ๋ ์์๊ฑฐ
https://github.com/notifications/unsubscribe-auth/AAtYVDXKLuTlsh9ycpMQvU5C0hs_RxuYks5rjCwBgaJpZM4DCjLh
.
๋ํ sqlalchemy๊ฐ ๋์ ํต์ฌ์ด ๋ ์ ์๋ค๊ณ ์ธ๊ธํ์ต๋๋ค. ๋ญ๊ฐ๊ฐ ์๋๋ฉด
๋ง์ด ๋ณ๊ฒฝ๋์์ต๋๋ค. ์ด๋ค ๊ฒฝ์ฐ์๋ sqlalchemy ์ฝ์ด๋ง ์ฌ์ฉ๋ฉ๋๋ค. ๋ง์ฝ ๋๋ผ๋ฉด
์ฝ์ด๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๋ณด๋ค ์๋๋ฅผ ๋ ๋์ด๋ ค๋ฉด ๋ ๋ฎ์ ์์ค์ธ db๋ก ์ด๋ํด์ผ ํฉ๋๋ค.
ํน์ ์ต์ ํ
2017๋ 3์ 7์ผ 00์ 53๋ถ์ "Andrei Sura" [email protected] ์ด ๋ค์๊ณผ ๊ฐ์ด ์ผ์ต๋๋ค.
@jorisvandenbossche https://github.com/jorisvandenbossche ๋ฌธ์
์์์ ์ธ๊ธํ ๋งํฌ๋ "๋๋ SQLAlchemy ORM์ด ๋๋์ ์ ๊ณตํฉ๋๋ค.
๋ฉ์๋์ ํ์ ์น์ ์ ๋ํ ํํฌ๋ฅผ ์ ๊ณตํ๋ ์์ ๋ชจ์
์ฝ์ด ๋ ๋ฒจ INSERT ๋ฐ UPDATE๋ฅผ ๋ด๋ณด๋ด๋ ์์ ๋จ์ ํ๋ก์ธ์ค
์ฝ๊ฐ์ ORM ๊ธฐ๋ฐ ์๋ํ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค."๋ด๊ฐ ์ ์ํ๋ ๊ฒ์ sqlserver ํน์ ๋ฒ์ ์ ๊ตฌํํ๋ ๊ฒ์ ๋๋ค.
๋ด๋ถ์ ์ผ๋ก ์๋ ํฅ์์ ์ํด SQLAlchemy ์ฝ์ด๋ฅผ ์ฌ์ฉํ๋ "to_sql".โ
๋น์ ์ด ์ธ๊ธ๋์๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๋ฐ๋ ๊ฒ์ ๋๋ค.
์ด ์ด๋ฉ์ผ์ ์ง์ ๋ต์ฅํ๊ณ GitHub์์ ํ์ธํ์ธ์.
https://github.com/pandas-dev/pandas/issues/8953#issuecomment-284437587 ,
๋๋ ์ค๋ ๋ ์์๊ฑฐ
https://github.com/notifications/unsubscribe-auth/AAtYVDXKLuTlsh9ycpMQvU5C0hs_RxuYks5rjCwBgaJpZM4DCjLh
.
์ด ๋ฌธ์ ๊ฐ ํด๊ฒฐ/ํด๊ฒฐ๋๊ณ ์์ต๋๊น? ํ์ฌ pandas ๋ฐ์ดํฐ ํ๋ ์์ SQL db์ ์ฝ์ ํ๋ ๊ฒ์ ์ฅ๋๊ฐ ๋ฐ์ดํฐ ํ๋ ์์ด ์๋ ํ ๋งค์ฐ ๋๋ฆฝ๋๋ค. ํด๊ฒฐ ๋ฐฉ๋ฒ์ ๊ฒฐ์ ํ๊ณ ์ถ์งํด ๋ณผ๊น์?
@dfernan ์์์ ์ธ๊ธํ๋ฏ์ด odo ๋ฅผ ๋ณด๊ณ ์ถ์ ์๋ ์์ต๋๋ค. ์ค๊ฐ CSV ํ์ผ์ ์ฌ์ฉํ๋ฉด ์ฌ๊ธฐ์์ ์ด๋ค ์ข ๋ฅ์ ๊ฐ์ ์ด ๋ฐ์ํ๋๋ผ๋ sqlalchemy๋ฅผ ๊ฑฐ์น๋ ๊ฒ๋ณด๋ค ํญ์ ํจ์ฌ ๋ ๋น ๋ฆ ๋๋ค...
@ostrokach , odo์ ๋์์ด ์ผ๋ฐ์ ์ธ Pandas ์ฌ์ฉ์๊ฐ ์ํ๋ ๊ฒ์ธ์ง ํ์ ํ ์ ์์ต๋๋ค. ODBC๋ฅผ ํตํ ๋ค์ค ํ ์ฝ์ ์ ์๋ง๋ ๋๋ถ๋ถ์ ๋ถ์๊ฐ์๊ฒ ์ถฉ๋ถํ ๋น ๋ฅผ ๊ฒ์ ๋๋ค.
๋ ์์ ์ ๋๋ณํ๊ธฐ ์ํด ์์ ์์ญ์ด ํจ์น์์ odo๋ก ์ ํํ๋ ๋ฐ ๋ช ์๊ฐ์ ๋ณด๋์ต๋๋ค. ์ผ๋ฐ ํฌ๋ ๋ฐํ์์ RBAR 10์๊ฐ ์ด์์ด์์ต๋๋ค. ์์ญ์ด ํจ์น๋ ๋์ผํ ๋ฐ์ดํฐ ์ธํธ์์ 2๋ก ์คํ๋ฉ๋๋ค.
odo/CSV ๊ฒฝ๋ก๋ ์์๋๋ก ๋ ๋นจ๋์ง๋ง ๋
ธ๋ ฅํ ๊ฐ์น๊ฐ ์ถฉ๋ถํ์ง๋ ์์์ต๋๋ค. ๋๋ ์์ญ์ด ํจ์น๋ฅผ ํผํ๋ค๋ ๋ช
๋ชฉ์ผ๋ก ๋ณ๋ก ์ ๊ฒฝ ์ฐ์ง ์์ CSV ๋ณํ ๋ฌธ์ ๋ฅผ ๋ง์ง์๊ฑฐ๋ ธ๋ค. NLP ๋ถ์์ ์ํด ~10๊ฐ์ mysql ๋ฐ PG DB์์ Postgres์ ๊ณตํต ์์ญ์ผ๋ก 250K ํ์ ๊ฐ์ ธ์ค๊ณ ์์ต๋๋ค.
์ ๋ ๋๋ ๋ก๋ฉ ์ ๊ทผ ๋ฐฉ์์ ๋ํด ๋งค์ฐ ์ ์๊ณ ์์ต๋๋ค. ๋๋ CSV ๋ฐ์ดํฐ๋ก ์์ํ๋ ๋ช ๋ ๋์ ๊ทธ๊ฒ๋ค์ ์ฌ์ฉํด ์์ต๋๋ค. ๋ค์๊ณผ ๊ฐ์ ์ฃผ์ ์ ํ ์ฌํญ์ด ์์ต๋๋ค.
๋ถ์ ์์ ์ ๊ณ์ํ ์ ์๋๋ก ํจ์น๋ก ๋ค์ ์ ํํฉ๋๋ค.
@jorisvandenbossche , @maxgrenderjones์ ๋์ํฉ๋๋ค. ์ด๊ฒ์ ์ ํํ๋ ์ต์ (๊ธฐ๋ณธ๊ฐ ์๋)์ ๋งค์ฐ ์ ์ฉํ ๊ฒ์ ๋๋ค. dialect.supports_multivalues_insert์ ๋ํ @artemyk ์ ์์ ์ ์ด๊ฒ์ ํฉ๋ฆฌ์ ์ธ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๋ง๋ค ์๋ ์์ต๋๋ค.
๊ทธ๊ฒ์ด ์์ผ๋ก ๋์๊ฐ ์ ์๋ค๋ฉด PR์ ์ ์ถํ๊ฒ ๋์ด ๊ธฐ์ฉ๋๋ค.
odo์ ๋ํ ๋ด ๊ฒฝํ์ ์ถ๊ฐํ๊ธฐ ์ํด ์ธ์ฝ๋ฉ๊ณผ ๊ด๋ จ๋ ์๋ ค์ง ๋ฌธ์ ๋ก ์ธํด MS SQL ๋๋ ์ฝ์ ์์๋ ์๋ํ์ง ์์์ต๋๋ค. imho m-row ์ฝ์ ๋ฌผ์ ๋๋ถ๋ถ์ ppl์ ์ํ ์ข์ ์ค์ฉ์ ์ธ ์๋ฃจ์ ์ ๋๋ค.
@markschwarz ๋ ๋น ๋ฅด๊ฒ ์๋ํ๋๋ก ํ๋ ์ต์ ์ ๋งค์ฐ ํ์ํฉ๋๋ค!
sqlite๋ฅผ ์ฌ์ฉํ์ฌ ์ฟผ๋ฆฌ๋ฅผ ์ถ์ ํ๋ฉด chunksize
๋ฅผ ์ฌ์ฉํ ๋ ๋ค์ค ์ฝ์
์ ๋ค์ด๊ฐ๋ ๊ฒ ๊ฐ์ต๋๋ค.
2017-09-28 00:21:39,007 INFO sqlalchemy.engine.base.Engine INSERT INTO country_hsproduct_year (location_id, product_id, year, export_rca, import_value, cog, export_value, distance, location_level, product_level) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2017-09-28 00:21:39,007 INFO sqlalchemy.engine.base.Engine ((75, 1237, 1996, 1.7283086776733398, 273487116.0, 0.0, 514320160.0, 0.5413745641708374, 'country', '4digit'), (75, 1237, 1997, 1.7167805433273315, 312047528.0, 0.0, 592372864.0, 0.5314807891845703, 'country', '4digit'), (75, 1237, 1998, 1.2120152711868286, 341676961.0, 0.0, 468860608.0, 0.5472233295440674, 'country', '4digit'), (75, 1237, 1999, 1.236651062965393, 334604240.0, 0.0, 440722336.0, 0.5695921182632446, 'country', '4digit'), (75, 1237, 2000, 1.189828872680664, 383555023.0, 0.0, 426384832.0, 0.5794379711151123, 'country', '4digit'), (75, 1237, 2001, 0.9920380115509033, 374157144.0, 0.3462945520877838, 327031392.0, 0.6234743595123291, 'country', '4digit'), (75, 1237, 2002, 1.0405025482177734, 471456583.0, 0.0, 377909376.0, 0.6023964285850525, 'country', '4digit'), (75, 1237, 2003, 1.147829532623291, 552441401.0, 0.0, 481313504.0, 0.5896202325820923, 'country', '4digit') ... displaying 10 of 100000 total bound parameter sets ... (79, 1024, 2015, 0.0, None, 0.8785018920898438, 0.0, 0.9823430776596069, 'country', '4digit'), (79, 1025, 1995, 0.0, None, 0.5624096989631653, 0.0, 0.9839603304862976, 'country', '4digit'))
(์์ญ์ด ํจ์น ์์ด, ์ฆ)
ํฅ๋ฏธ๋กญ๊ฒ๋ ์์ญ์ด ํจ์น๋ฅผ ์ฌ์ฉํ๋ฉด 10^5์ ์ฒญํฌ ํฌ๊ธฐ๋ฅผ ์ ๊ณตํ์ง๋ง 10^3์ ์ ๊ณตํ์ง ์์ผ๋ฉด ์ค๋จ๋ฉ๋๋ค. ์ค๋ฅ๋ sqlite์์ "๋๋ฌด ๋ง์ SQL ๋ณ์"์ ๋๋ค.
@makmanalp , ์์ง ๊ทธ ๋์์ ํ์ธํ๊ธฐ ์ํด PG๋ฅผ ์ถ์ ํ์ง ์์์ง๋ง ๊ฑฐ์ ํญ์ ์ฝ์ ์ chunksize๋ฅผ ์ค์ ํฉ๋๋ค. ์์ ์์์ ๋ฌด์์๋ก 200-5000 ์ฌ์ด์ 5๊ฐ ๊ฐ์ผ๋ก ์ค์ ํ์ต๋๋ค. ๋๋ ์์ญ์ด ํจ์น ์์ด ๊ทธ ์ ํ๋ค ์ฌ์ด์์ ๊ธ๊ฒฉํ ๊ฒฝ๊ณผ ์๊ฐ ์ฐจ์ด๋ฅผ ๋ณด์ง ๋ชปํ์ต๋๋ค. ํจ์น๋ก ๊ฒฝ๊ณผ ์๊ฐ์ด ~80% ๊ฐ์ํ์ต๋๋ค.
https://github.com/pandas-dev/pandas/issues/8953#issuecomment -76139975
์ด ์์ญ์ด ํจ์น๊ฐ ์ฌ์ ํ ์๋ํฉ๋๊น? MS SQL Server์์ ์๋ํ์ง๋ง ๊ฐ์ ๋์ง ์์์ต๋๋ค. ๋ํ ์์ธ๊ฐ ๋ฐ์ํฉ๋๋ค.
(pyodbc.Error) ('07002', '[07002] [Microsoft][SQL Server Native Client 11.0]COUNT field incorrect or syntax error (0) (SQLExecDirectW)')
@hangyao ๋๋ ํจ์น๊ฐ ๊ตฌํ์ ๋ฐ๋ผ ๋ค๋ฅด๋ค๊ณ ์๊ฐํ๋๋ฐ, ํ์ด์ฌ DBAPI๊ฐ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ๋ํด DBAPI ๋๋ผ์ด๋ฒ์ ๋จ๊ฒจ๋๋ ๊ฒ ์ค ํ๋์ ๋๋ค. ๋ฐ๋ผ์ ๋ ๋น ๋ฅผ ์๋ ์๊ณ ๊ทธ๋ ์ง ์์ ์๋ ์์ต๋๋ค. RE: ๊ตฌ๋ฌธ ์ค๋ฅ, ํ์คํ์ง ์์ต๋๋ค.
/io/sql.py ํ์ผ์ /io/sql.py
ํ์ผ์ _engine_builder
ํจ์๋ฅผ ๋ผ์ธ 521์ ์๋ ์ IF ๊ตฌ๋ฌธ์ ์ถ๊ฐํ์ฌ 'new' _engine_builder
๋ฅผ ์๋์ ์ถ๊ฐํ๋ ๊ฒ์ ๋ํด ์๊ฐํ๊ณ ์์ต๋๋ค. ๋จํธ. ๋ด ํ๊ฒฝ์์ ๊ฐ๋จํ ํ
์คํธํ์ผ๋ฉฐ MSSQL ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ํ๋ฅญํ๊ฒ ์๋ํ์ฌ 100๋ฐฐ ์ด์์ ์๋ ํฅ์์ ๋ฌ์ฑํ์ต๋๋ค. ๋ค๋ฅธ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์๋ ์์ง ํ
์คํธํ์ง ์์์ต๋๋ค.
์ ๊ฐ PR์ ํ์ง ์๋ ๊ฒ์ ์๋์ ๊ฐ์ด ๊ทธ๋ฅ ์ฝ์
ํ๋ ๊ฒ๋ณด๋ค ๊น๋ํ๊ณ ์์ ํ๊ฒ ๋ง๋๋ ๊ฒ์ด ๋ ๋ง์ ๋
ธ๋ ฅ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค. ์ด๊ฒ์ด ํญ์ ์ํ๋ ์ฌ์์ ์๋ ์๋ ์๊ณ ์ด ์ค์ ์ ์ผ๊ณ ๋๋ boolean ์ค์์น๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์
๋๋ค. , (์: fast_executemany=True
) to_sql
๋ ๋ฌป์ง ์๊ณ ํ๊ธฐ์๋ ๋๋ฌด ํฐ ๋
ธ๋ ฅ์ธ ๊ฒ ๊ฐ์ต๋๋ค.
๊ทธ๋์ ๋ด ์ง๋ฌธ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
์๋ ๊ธฐ๋ฅ์ด ์๋ํ๊ณ PostgreSQL์ INSERT ์๋๋ ์ฆ๊ฐํฉ๋๊น?
ํฌ๋ ์ด๋ฒคํธ๊ฐ ์์ค์ ์ด ์ค๋ํซ์ ์ํฉ๋๊น? ๊ทธ๋ ๋ค๋ฉด:
์ด ๊ธฐ๋ฅ์ ๊ธฐ๋ณธ sql.py
๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ณ ์ถ์ต๋๊น? ์๋๋ฉด ์ด๊ฒ์ ์ถ๊ฐํ๊ธฐ์ ๋ ์ข์ ๊ณณ์ด ์์ต๋๊น?
์๊ฒฌ์ ๋ฃ๊ณ ์ถ์ต๋๋ค.
๋ต๋ณ ์ถ์ฒ: https://stackoverflow.com/questions/48006551/speeding-up-pandas-dataframe-to-sql-with-fast-executemany-of-pyodbc/48861231#48861231
@event.listens_for(engine, 'before_cursor_execute')
def receive_before_cursor_execute(conn, cursor, statement, params, context, executemany):
if executemany:
cursor.fast_executemany = True
def _engine_builder(con):
"""
Returns a SQLAlchemy engine from a URI (if con is a string)
else it just return con without modifying it.
"""
global _SQLALCHEMY_INSTALLED
if isinstance(con, string_types):
try:
import sqlalchemy
except ImportError:
_SQLALCHEMY_INSTALLED = False
else:
con = sqlalchemy.create_engine(con)
@event.listens_for(engine, 'before_cursor_execute')
def receive_before_cursor_execute(conn, cursor, statement, params, context, executemany):
if executemany:
cursor.fast_executemany = True
return con
@tsktsktsk123 ์ต๊ทผ ์ด์ ๊ด๋ จ๋ PR ๋ณํฉ์ด ์์์ต๋๋ค: https://github.com/pandas-dev/pandas/pull/19664. ๋๋ ์์ง ๊ทํ์ ๊ฒ์๋ฌผ์ ์์ธํ ์ดํด๋ณด์ง ์์์ผ๋ฉฐ ํ์คํ ๋์ผํ์ง๋ ์์ง๋ง(sqlalchemy ์์ง์ supports_multivalues_insert
์์ฑ์ ์ฌ์ฉํจ) ์ด๊ฒ์ด ์ด๋ฏธ ๋์์ด ๋ ๊ฒฝ์ฐ์ ๋๋นํ์ฌ ์๊ณ ์๋์ง ํ์ธํ๊ธฐ ์ํ ๊ฒ์
๋๋ค. ๋ํ.
์ข์ ์์์ ๋๋ค! ๋๋ PR์ ์ดํด๋ณด์ง ์์์ง๋ง ์ด๋ฒ ์ฃผ๋ง์ ๊ทธ๊ฒ์ ๋น๊ตํ๊ณ ๊ฒฐ๊ณผ๋ก ๋์์ฌ ๊ฒ์ ๋๋ค. ๊ฐ์ฌํฉ๋๋ค.
๋ฐฉ๊ธ 0.23.0 RC2(postgresql์์)๋ฅผ ์๋ํ๊ณ ์์๊ณ ์ฑ๋ฅ ํฅ์ ๋์ ์คํฌ๋ฆฝํธ๊ฐ ์๋นํ ๋๋ ค์ก์ต๋๋ค. DB ์ฟผ๋ฆฌ๋ ํจ์ฌ ๋นจ๋ผ์ก์ง๋ง to_sql()
์๊ฐ์ ์ธก์ ํ๋ฉด ์ค์ ๋ก ์ต๋ 1.5๋ฐฐ ๋๋ ค์ก์ต๋๋ค(์: 7์ด์์ 11์ด)...
๋ฐฉ๊ธ RC๋ฅผ ํ ์คํธํ๊ธฐ ๋๋ฌธ์ ์๋ ์ ํ๊ฐ ์ด PR์์ ๋น๋กฏ๋ ๊ฒ์ธ์ง ํ์คํ์ง ์์ต๋๋ค.
๋ค๋ฅธ ์ฌ๋์ด ๊ฐ์ ๋ฌธ์ ๋ฅผ ๊ฒช์์ต๋๊น?
@schettino72 ์ผ๋ง๋ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ํ์ จ๋์?
10๊ฐ์ ์ด์ด ์๋ ์ฝ 30,000๊ฐ์ ํ. ๊ทธ๋ฌ๋ ์ค์ ๋ก ๋ด๊ฐ ์๋ํ๋ ๊ฑฐ์ ๋ชจ๋ ๊ฒ์ด ๋ ๋๋ฆฝ๋๋ค(SQL์ ๋ ๋น ๋ฅด์ง๋ง ์ ๋ฐ์ ์ผ๋ก ๋๋ฆผ). ๋ชจ๋ ๊ฐ์ ๋ํ ๊ฐ ๋ณด๊ฐ์ด ์๋ ๊ฑฐ๋ํ SQL ๋ฌธ์ ์์ฑํ๊ณ ์์ต๋๋ค. ๊ฐ์ ๊ฒ
%(user_id_m32639)s, %(event_id_m32639)s, %(colx_m32639)s,
d6tstack
์ฌ์ฉ์ด ํจ์ฌ ๊ฐ๋จํ๋ค๋ ๊ฒ์ ์์์ต๋๋ค. ํ ์ค d6tstack.utils.pd_to_psql(df, cfg_uri_psql, 'benchmark', if_exists='replace')
์ด๋ฉฐ df.to_sql()
๋ณด๋ค ํจ์ฌ ๋น ๋ฆ
๋๋ค. ํฌ์คํธ๊ทธ๋ ์ค์ mysql์ ์ง์ํฉ๋๋ค. https://github.com/d6t/d6tstack/blob/master/examples-sql.ipynb ์ฐธ์กฐ
๋๋ ์์ญ์ด ํจ์น ์๋ฃจ์ ์ ์ฌ์ฉํ๊ณ ์์ต๋๋ค:
from pandas.io.sql import SQLTable
def _execute_insert(self, conn, keys, data_iter):
print "Using monkey-patched _execute_insert"
data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
conn.execute(self.insert_statement().values(data))
SQLTable._execute_insert = _execute_insert
์ ์ ๋์์ด์ง๋ง ์ง๊ธ์ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค.
TypeError: insert_statement() missing 2 required positional arguments: 'data' and 'conn'
๋ค๋ฅธ ์ฌ๋์ด ์ด๊ฒ์ ๋ฐ๊ณ ์์ต๋๊น? ์ ๋ Python 3.6.5(Anaconda) ๋ฐ pandas==0.23.0์ ์ฌ์ฉ ์ค์ ๋๋ค.
์ด๊ฑฐ ๊ณ ์ณ์ง๋์? ํ์ฌ df.to_sql์ ๋งค์ฐ ๋๋ฆฌ๊ณ ๋ง์ ์ค์ ์ฌ์ฉ ์ฌ๋ก์์ ์ ํ ์ฌ์ฉํ ์ ์์ต๋๋ค. Odo ํ๋ก์ ํธ๋ ์ด๋ฏธ ํฌ๊ธฐํ ๊ฒ ๊ฐ์ต๋๋ค.
df.to_sql์ ๊ฑฐ์ ์ฌ์ฉํ ์ ์๋ ๊ธ์ต ์๊ณ์ด์์ ๋ค์ ์ฌ์ฉ ์ฌ๋ก๊ฐ ์์ต๋๋ค.
1) ๊ณผ๊ฑฐ csv ๋ฐ์ดํฐ๋ฅผ postgres ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ณต์ฌ - df.to_sql์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ psycopg2 copy_from ๊ธฐ๋ฅ์ ๋ํ ์ฌ์ฉ์ ์ ์ ์ฝ๋๋ฅผ ์ฌ์ฉํด์ผ ํ์ต๋๋ค.
2) postgres ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋คํ๋ ์คํธ๋ฆฌ๋ฐ ๋ฐ์ดํฐ(์ด๋น ~500-3000ํ์ ์ผ๊ด ์ฒ๋ฆฌ๋ก ์ ๊ณต๋จ) - ์ด๋ฌํ ์์ฐ์ ์ธ ๋ฐ์ดํฐ ์ผ๊ด ์ฒ๋ฆฌ๋ฅผ postgres์ ์ฝ์
ํ๋ ๋ฐ ๋๋ฌด ๋ง์ ์๊ฐ์ด ๊ฑธ๋ฆฌ๊ธฐ ๋๋ฌธ์ ๋ค์ df.to_sql ์ฑ๋ฅ์ ์๋นํ ์ค๋ง์ค๋ฝ์ต๋๋ค.
๋ด๊ฐ ์ง๊ธ df.to_sql์ด ์ ์ฉํ๋ค๊ณ ์๊ฐํ๋ ์ ์ผํ ์ฅ์๋ ์๋์ผ๋ก ํ
์ด๋ธ์ ์์ฑํ๋ ๊ฒ์
๋๋ค!!! - ์ค๊ณ๋ ์ฌ์ฉ ์ฌ๋ก๊ฐ ์๋๋๋ค.
๋ค๋ฅธ ์ฌ๋๋ค๋ ๊ฐ์ ๋ฌธ์ ๋ฅผ ๊ณต์ ํ๋์ง ํ์คํ์ง ์์ง๋ง ์ด ๋ฌธ์ ๋ "๋ฐ์ดํฐํ๋ ์-๋ฐ์ดํฐ๋ฒ ์ด์ค" ์ธํฐํ์ด์ค๊ฐ ์ํํ๊ฒ ์๋ํ๋ ค๋ฉด ์ฝ๊ฐ์ ์ฃผ์๊ฐ ํ์ํฉ๋๋ค.
๊ธฐ๋ํด.
SQLite ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ค์ค ์ฝ์ ์ ์ํํ๋ ค๊ณ ํ ๋ ์ด ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค.
์ด๊ฒ์ ๋ด ์ฝ๋์
๋๋ค.
df.to_sql("financial_data", con=conn, if_exists="append", index=False, method="multi")
์ด ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค.
Traceback (most recent call last):
File "<ipython-input-11-cf095145b980>", line 1, in <module>
handler.insert_financial_data_from_df(data, "GOOG")
File "C:\Users\user01\Documents\Code\FinancialHandler.py", line 110, in insert_financial_data_from_df
df.to_sql("financial_data", con=conn, if_exists="append", index=False, method="multi")
File "C:\Users\user01\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\core\generic.py", line 2531, in to_sql
dtype=dtype, method=method)
File "C:\Users\user01\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\io\sql.py", line 460, in to_sql
chunksize=chunksize, dtype=dtype, method=method)
File "C:\Users\user01\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\io\sql.py", line 1547, in to_sql
table.insert(chunksize, method)
File "C:\Users\user01\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\io\sql.py", line 686, in insert
exec_insert(conn, keys, chunk_iter)
File "C:\Users\user01\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\io\sql.py", line 609, in _execute_insert_multi
conn.execute(self.table.insert(data))
TypeError: insert() takes exactly 2 arguments (1 given)
์ ์ด๋ฐ ์ผ์ด ๋ฐ์ํฉ๋๊น? ์ ๋ Python 3.7.3(Anaconda), pandas 0.24.2 ๋ฐ sqlite3 2.6.0์ ์ฌ์ฉํ๊ณ ์์ต๋๋ค.
๋ฏธ๋ฆฌ ๋๋จํ ๊ฐ์ฌํฉ๋๋ค!
@jconstanzo ์ด ๋ฌธ์ ๋ฅผ ์ ๋ฌธ์ ๋ก ์ด ์ ์์ต๋๊น?
๊ทธ๋ฆฌ๊ณ ๊ฐ๋ฅํ๋ค๋ฉด ์ฌํ ๊ฐ๋ฅํ ์๋ฅผ ์ ๊ณตํ ์ ์์ต๋๊น? (์: ๋ฌธ์ ๋ฅผ ๋ณด์ฌ์ค ์ ์๋ ์์ ์์ ๋ฐ์ดํฐ ํ๋ ์)
@jconstanzo ์ฌ๊ธฐ์ ๊ฐ์ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. method='multi'
(๋ด ๊ฒฝ์ฐ์๋ chunksize
์ ํจ๊ป ์ฌ์ฉ)์ ์ฌ์ฉํ๋ฉด SQLite ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฝ์
ํ๋ ค๊ณ ํ ๋ ์ด ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ๊ฒ ๊ฐ์ต๋๋ค.
๋ถํํ๋ ๋ด ๋ฐ์ดํฐ ์ธํธ๊ฐ ๊ฑฐ๋ํ๊ธฐ ๋๋ฌธ์ ์ค์ ๋ก ์์ ๋ฐ์ดํฐ ํ๋ ์์ ์ ๊ณตํ ์ ์์ต๋๋ค. ์ด๊ฒ์ด ์ ๊ฐ ์ฒ์์ method
๋ฐ chunksize
๋ฅผ ์ฌ์ฉํ๋ ์ด์ ์
๋๋ค.
๋ฆ์ด์ ์ฃ์กํฉ๋๋ค. ๋ฐฉ๊ธ ์ด ๋ฌธ์ ์ ๋ํ ๋ฌธ์ ๋ฅผ ์ด์์ต๋๋ค. https://github.com/pandas-dev/pandas/issues/29921
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
๋ค๋ฅธ ์ฌ๋์๊ฒ ์ ์ฉํ ์ ์๋ ์์ญ์ด ํจ์น ๋ฐฉ๋ฒ์ ์์๋์ต๋๋ค. ํฌ๋๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ ์ ์ด ์ฝ๋๊ฐ ์์ด์ผ ํฉ๋๋ค.