Python

데이터 로딩과 저장, 파일 형식

haventmetyou 2023. 11. 20. 17:15

데이터 로딩(data loading)은 데이터를 읽고 접근 가능하도록 하는 작업, 데이터 분석에서의 도구를 사용하는 첫 번째 단계

유사한 용어인 파싱(parsing)(구문 분석)은 텍스트 데이터를 불러와 표나 다른 데이터 형식으로 해석하는 과정

 

텍스트 파일에서 데이터를 읽고 쓰는 법

 

In [1]: !cat ex1.csv
'cat'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는
배치 파일이 아닙니다.

In [2]: !type ex1.csv
a,b,c,d,message
1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo

유닉스의 cat 명령어를 통해 파일 내용 확인, 윈도우 사용자는 type 명령어로 확인 가능

자동으로 제일 위에 있는 열이 열 이름으로 들어감

In [7]: !type ex2.csv
1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo
In [8]: pd.read_csv('ex2.csv')
Out[8]:
   1   2   3   4  hello
0  5   6   7   8  world
1  9  10  11  12    foo

In [9]: pd.read_csv('ex2.csv', header=None)    # 헤더 행 x
Out[9]:
   0   1   2   3      4
0  1   2   3   4  hello
1  5   6   7   8  world
2  9  10  11  12    foo

In [10]: pd.read_csv('ex2.csv', names=['a', 'b', 'c', 'd', 'message'])    # 헤더 직접 지정
Out[10]:
   a   b   c   d message
0  1   2   3   4   hello
1  5   6   7   8   world
2  9  10  11  12     foo

 

message 열을 인덱스로 하는 DataFrame을 반환하고 싶은 경우

index_col 인수의 4번째 열 또는 'message' 이름 가진 열을 직접 지정해 인덱스로 만들 수 있음

In [11]: names = ['a', 'b', 'c', 'd', 'message']

In [12]: pd.read_csv('ex2.csv', names=names, index_col='message')
Out[12]:
         a   b   c   d
message
hello    1   2   3   4
world    5   6   7   8
foo      9  10  11  12

 

계층적 인덱스를 지정하고 싶다면 열 번호나 이름의 리스트를 넘기면 됨

In [16]: !type csv_mindex.csv
key1,key2,value1,value2
one,a,1,2
one,b,3,4
one,c,5,6
one,d,7,8
two,a,9,10
two,b,11,12
two,c,13,14
two,d,15,16

In [17]: parsed = pd.read_csv('csv_mindex.csv',
    ...:                      index_col=['key1', 'key2'])

In [18]: parsed
Out[18]:
           value1  value2
key1 key2
one  a          1       2
     b          3       4
     c          5       6
     d          7       8
two  a          9      10
     b         11      12
     c         13      14
     d         15      16

 

가끔 고정된 구분자(delimiter) 없이 공백이나 다른 패턴으로 필드를 구분해놓은 경우 해결 법

직접 파일 고치지 않고 정규 표현식 \s+ 사용해 처리

In [19]: !type ex3.txt
            A         B         C
aaa -0.264438 -1.026059 -0.619500
bbb  0.927272  0.302904 -0.032399
ccc -0.264273 -0.386314 -0.217601
ddd -0.871858 -0.348382  1.100491

In [20]: result = pd.read_csv('ex3.txt', sep='\s+')

In [21]: result
Out[21]:
            A         B         C
aaa -0.264438 -1.026059 -0.619500
bbb  0.927272  0.302904 -0.032399
ccc -0.264273 -0.386314 -0.217601
ddd -0.871858 -0.348382  1.100491

 

파싱 함수는 파일 형식에서 발생할 수 있는 다양한 예외를 처리할 수 있또록 추가 인수를 가지고 있음

skiprows를 이용해 첫 번째, 세 번째, 네 번째 행 건너뛰기

In [22]: !type ex4.csv
# hey!
a,b,c,d,message
# just wanted to make things more difficult for you
# who reads CSV files with computers, anyway?
1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo

In [23]: pd.read_csv('ex4.csv', skiprows=[0, 2, 3])
Out[23]:
   a   b   c   d message
0  1   2   3   4   hello
1  5   6   7   8   world
2  9  10  11  12     foo

In [24]: pd.read_csv('ex4.csv')
Out[24]:
                                                                      # hey!
a                                                  b        c   d    message
# just wanted to make things more difficult for... NaN      NaN NaN      NaN
# who reads CSV files with computers                anyway? NaN NaN      NaN
1                                                  2        3   4      hello
5                                                  6        7   8      world
9                                                  10       11  12       foo

 

In [27]: !type ex5.csv
something,a,b,c,d,message
one,1,2,3,4,NA
two,5,6,,8,world
three,9,10,11,12,foo
In [28]: result = pd.read_csv('ex5.csv')

In [29]: result
Out[29]:
  something  a   b     c   d message
0       one  1   2   3.0   4     NaN
1       two  5   6   NaN   8   world
2     three  9  10  11.0  12     foo

 

비어 있는 공간에 공백 문자 넣을 경우 NaN이 아닌 공백으로 처리

In [30]: !type ex5.csv
something,a,b,c,d,message
one,1,2,3,4,NA
two,5,6, ,8,world
three,9,10,11,12,foo
In [31]: result = pd.read_csv('ex5.csv')

In [32]: result
Out[32]:
  something  a   b   c   d message
0       one  1   2   3   4     NaN
1       two  5   6       8   world
2     three  9  10  11  12     foo

아무것도 존재하지 않는 상태 null

NA = 값은 존재하지만 처리하지 않음, 결측치

In [36]: pd.isna(result)    # 누락된 값을 NaN으로 출력하므로 2개의 null 또는 누락된 값 존재
Out[36]:
   something      a      b      c      d  message
0      False  False  False  False  False     True
1      False  False  False   True  False    False
2      False  False  False  False  False    False

# na_values 옵션은 문자열 집합을 받아 누락된 값을 처리
In [37]: result = pd.read_csv('ex5.csv', na_values=['NULL'])

In [38]: result
Out[38]:
  something  a   b     c   d message
0       one  1   2   3.0   4     NaN
1       two  5   6   NaN   8   world
2     three  9  10  11.0  12     foo

 

pandas.read_csv에는 다양한 기본 NA 값 표현 목록이 있지만 keep_default_na 옵션으로 비활성화할 수 있음

In [39]: result2 = pd.read_csv('ex5.csv', keep_default_na=False)

In [40]: result2
Out[40]:
  something  a   b   c   d message
0       one  1   2   3   4      NA
1       two  5   6       8   world
2     three  9  10  11  12     foo

In [41]: result2.isna()
Out[41]:
   something      a      b      c      d  message
0      False  False  False  False  False    False
1      False  False  False  False  False    False
2      False  False  False  False  False    False

In [42]: result3 = pd.read_csv('ex5.csv', keep_default_na=False,
    ...:                       na_values=['NA'])

In [43]: result3
Out[43]:
  something  a   b   c   d message
0       one  1   2   3   4     NaN
1       two  5   6       8   world
2     three  9  10  11  12     foo

In [44]: result3.isna()
Out[44]:
   something      a      b      c      d  message
0      False  False  False  False  False     True
1      False  False  False  False  False    False
2      False  False  False  False  False    False

 

열마다 다른 NA 문자를 딕셔너리 값으로 넘겨 처리할 수 있음

In [45]: sentinels = {'message': ['foo', 'NA'], 'something': ['two']}

In [46]: pd.read_csv('ex5.csv', na_values=sentinels,
    ...:             keep_default_na=False)
Out[46]:
  something  a   b   c   d message
0       one  1   2   3   4     NaN
1       NaN  5   6       8   world
2     three  9  10  11  12     NaN

 

pandas.read_csv 함수 인수들 필요할 때 찾아볼 것

 

텍스트 파일 조금씩 읽어 오기

In [50]: pd.options.display.max_rows = 10    # 생략해도 최근 버전은 앞 5행, 뒤 5행씩 보여줌

In [51]: result = pd.read_csv('ex6.csv')

In [52]: result
Out[52]:
           one       two     three      four key
0     0.467976 -0.038649 -0.295344 -1.824726   L
1    -0.358893  1.404453  0.704965 -0.200638   B
2    -0.501840  0.659254 -0.421691 -0.057688   G
3     0.204886  1.074134  1.388361 -0.982404   R
4     0.354628 -0.133116  0.283763 -0.837063   Q
...        ...       ...       ...       ...  ..
9995  2.311896 -0.417070 -1.409599 -0.515821   L
9996 -0.479893 -0.650419  0.745152 -0.646038   E
9997  0.523331  0.787112  0.486066  1.093156   K
9998 -0.362559  0.598894 -1.843201  0.887292   G
9999 -0.096376 -1.012999 -0.657431 -0.573315   0

[10000 rows x 5 columns]

말줄임표(...)는 DataFrame 중간 행이 생략되었다는 뜻

In [53]: pd.read_csv('ex6.csv', nrows=5)    # nrows 옵션: 처음 n 행만 읽기
Out[53]:
        one       two     three      four key
0  0.467976 -0.038649 -0.295344 -1.824726   L
1 -0.358893  1.404453  0.704965 -0.200638   B
2 -0.501840  0.659254 -0.421691 -0.057688   G
3  0.204886  1.074134  1.388361 -0.982404   R
4  0.354628 -0.133116  0.283763 -0.837063   Q

# chunksize 옵션: 여러 조각으로 나누어 읽기
In [54]: chunker = pd.read_csv('ex6.csv', chunksize=1000)

In [55]: type(chunker)
Out[55]: pandas.io.parsers.readers.TextFileReader

pandas.read_csv에서 반환된 TextFileReader 객체를 사용하면 chunksize 단위로 파일을 순회할 수 있음

ex) ex6.csv 파일을 순회하면서 'key' 열의 값을 세는 작업

In [55]: type(chunker)
Out[55]: pandas.io.parsers.readers.TextFileReader

In [56]: tot = pd.Series([], dtype='int64')    # 여기서 tot는 total의 의미

In [57]: for piece in chunker:
    ...:     tot = tot.add(piece['key'].value_counts(), fill_value=0)
    ...:

In [58]: tot = tot.sort_values(ascending=False)

In [59]: tot[:10]
Out[59]:
key
E    368.0
X    364.0
L    346.0
O    343.0
Q    340.0
M    338.0
J    337.0
F    335.0
K    334.0
H    330.0
dtype: float64

TextFileReader는 임의의 크기만큼 데이터를 읽을 수 있는 get_chunk 메서드도 제공

 

데이터를 텍스트 형식으로 기록

In [60]: data = pd.read_csv('ex5.csv')

In [61]: data
Out[61]:
  something  a   b     c   d message
0       one  1   2   3.0   4     NaN
1       two  5   6   NaN   8   world
2     three  9  10  11.0  12     foo

In [62]: # DataFrame의 to_csv 메서드로 데이터를 쉼표로 구분된 형식으로 파일에 쓸 수 있음

In [63]: data.to_csv('out.csv')

In [64]: !type out.csv
,something,a,b,c,d,message
0,one,1,2,3.0,4,
1,two,5,6,,8,world
2,three,9,10,11.0,12,foo

In [65]: data.to_csv('out.csv', header=False)

In [66]: !type out.csv    # 위 모든 열이 날아감
0,one,1,2,3.0,4,
1,two,5,6,,8,world
2,three,9,10,11.0,12,foo

In [67]: import sys

In [68]: data.to_csv(sys.stdout, sep='|')
|something|a|b|c|d|message
0|one|1|2|3.0|4|
1|two|5|6||8|world
2|three|9|10|11.0|12|foo

# 결과에서 누락된 값은 비어 있는 문자열로 나타남, 원하는 값으로 지정 가능
In [69]: data.to_csv(sys.stdout, na_rep='NULL')
,something,a,b,c,d,message
0,one,1,2,3.0,4,NULL
1,two,5,6,NULL,8,world
2,three,9,10,11.0,12,foo

# 행렬 이름 포함하지 않는 옵션
In [70]: data.to_csv(sys.stdout, index=False, header=False)
one,1,2,3.0,4,
two,5,6,,8,world
three,9,10,11.0,12,foo

In [71]: data
Out[71]:
  something  a   b     c   d message
0       one  1   2   3.0   4     NaN
1       two  5   6   NaN   8   world
2     three  9  10  11.0  12     foo

# 72, 73은 리스트 표현 방식에만 차이를 둠
# 열의 일부분만 기록할 수도 있고, 순서 직접 지정 가능
In [72]: data.to_csv( sys.stdout, index=False, columns=list('abc') )
a,b,c
1,2,3.0
5,6,
9,10,11.0

In [73]: data.to_csv( sys.stdout, index=False, columns=['a', 'b', 'c'] )
a,b,c
1,2,3.0
5,6,
9,10,11.0

 

 

 

다른 구분자 형식 다루기

기본적인 도구 사용법 익히기

In [74]: !type ex7.csv
"a","b","c"
"1","2","3"
"1","2","3"

# 구분자가 한 글자인 파일은 파이썬 내장 csv 모듈을 이용해 처리할 수 있음
# > 열린 파일 객체를 csv.reader 함수에 넘김
In [75]: import csv

In [76]: f = open('ex7.csv')

In [77]: reader = csv.reader(f)

In [78]: for line in reader:
    ...:     print(line)
    ...:
['a', 'b', 'c']
['1', '2', '3']
['1', '2', '3']

In [79]: f.close()

 

원하는 형태로 데이터 넣기 연습

#  파일을 읽어 줄 단위 리스트로 저장
In [84]: with open('ex7.csv') as f:    # f 혹은 file
    ...:     lines = list(csv.reader(f))
    ...:

In [85]: lines[0]
Out[85]: ['a', 'b', 'c']

In [86]: lines[1]
Out[86]: ['1', '2', '3']

In [87]: header, values = lines[0], lines[1:]

In [88]: header, values
Out[88]: (['a', 'b', 'c'], [['1', '2', '3'], ['1', '2', '3']])

# 딕셔너리 표기법과 행을 열로 전치해주는 zip(*values) 이용해 열 딕셔너리 만들기
# (큰 파일에서는 메모리를 많이 사용하므로 유의할 것)
In [89]: data_dict = {h: v for h, v in zip(header, zip(*values))}

In [90]: data_dict
Out[90]: {'a': ('1', '1'), 'b': ('2', '2'), 'c': ('3', '3')}

 

In [96]: f = open('ex7.csv')

In [97]: class my_dialect(csv.Dialect):
    ...:     lineterminator = '\n'
    ...:     delimiter = ';'
    ...:     quotechar = '"'
    ...:     quoting = csv.QUOTE_MINIMAL
    ...:

In [98]: reader = csv.reader(f, dialect=my_dialect)

In [99]: reader = csv.reader(f, delimiter='|')

In [100]: with open('mydata.csv', 'w') as f:
     ...:     writer = csv.writer(f, dialect=my_dialect)
     ...:     writer.writerow(('one', 'two', 'three'))
     ...:     writer.writerow(('1', '2', '3'))
     ...:     writer.writerow(('4', '5', '6'))
     ...:     writer.writerow(('7', '8', '9'))

 

JSON 데이터

JSON(JavaScript Object Notation): 웹 브라우저와 다른 애플리케이션이 HTTP 요청으로 데이터를 보낼 때 사용하는 표준 파일 형식 중 하나

JSON은 CSV 같은 표 형식의 텍스트보다 유연한 데이터 형식

 

JSON 데이터 예시

In [114]: obj = """
     ...: {"name": "Wes",
     ...:  "cities_lived": ["Akron", "Nashville", "New York", "San Francisco"],
     ...:  "pet": null,
     ...:  "siblings": [{"name": "Scott", "age": 34, "hobbies": ["guitars", "soccer"]},
     ...:               {"name": "Katie", "age": 42, "hobbies": ["diving", "art"]}]
     ...: }
     ...: """

 

널 값인 null과 다른 몇 가지 사소한 차이(리스트 마지막에 쉼표가 있으면 안 됨 등)를 제외하면 파이썬 코드와 유사

기본 자료형은 객체(딕셔너리), 배열(리스트), 문자열, 숫자, 불리언, 널이며 객체의 키는 반드시 문자열이어야 함

In [115]: import json

# JSON 문자열 파이썬 형태로 변환할 때 json.loads 사용
In [116]: result = json.loads(obj)

In [117]: result
Out[117]:
{'name': 'Wes',
 'cities_lived': ['Akron', 'Nashville', 'New York', 'San Francisco'],
 'pet': None,
 'siblings': [{'name': 'Scott', 'age': 34, 'hobbies': ['guitars', 'soccer']},
  {'name': 'Katie', 'age': 42, 'hobbies': ['diving', 'art']}]}

# json.dumps는 파이썬 객체를 JSON 형태로 변환
In [118]: asjson = json.dumps(result)

In [119]: asjson
Out[119]: '{"name": "Wes", "cities_lived": ["Akron", "Nashville", "New York", "San Francisco"], "pet": null, "siblings": [{"name": "Scott", "age": 34, "hobbies": ["guitars", "soccer"]}, {"name": "Katie", "age": 42, "hobbies": ["diving", "art"]}]}'

 

In [120]: result['siblings']
Out[120]:
[{'name': 'Scott', 'age': 34, 'hobbies': ['guitars', 'soccer']},
 {'name': 'Katie', 'age': 42, 'hobbies': ['diving', 'art']}]

In [121]: siblings = pd.DataFrame(result['siblings'], columns=['name', 'age'])

In [122]: siblings
Out[122]:
    name  age
0  Scott   34
1  Katie   42

In [123]: siblings = pd.DataFrame(result['siblings'])

In [124]: siblings
Out[124]:
    name  age            hobbies
0  Scott   34  [guitars, soccer]
1  Katie   42      [diving, art]

In [125]:

In [125]: !type example.json
[{"a": 1, "b": 2, "c": 3},
 {"a": 4, "b": 5, "c": 6},
 {"a": 7, "b": 8, "c": 9}]

In [126]: data = pd.read_json('example.json')

In [127]: data
Out[127]:
   a  b  c
0  1  2  3
1  4  5  6
2  7  8  9

In [128]: data.to_json(sys.stdout)
{"a":{"0":1,"1":4,"2":7},"b":{"0":2,"1":5,"2":8},"c":{"0":3,"1":6,"2":9}}
In [129]: data.to_json(sys.stdout, orient='records')
[{"a":1,"b":2,"c":3},{"a":4,"b":5,"c":6},{"a":7,"b":8,"c":9}]

 

이진 데이터 형식

 

데이터를 이진 형식으로 저장(또는 직렬화(serialize))하는 가장 간단한 방법은 파이썬 내장 pickle 모듈을 이용하는 것

판다스 객체는 pickle 형식으로 데이터를 디스크에 저장할 수 있는 to_pickle 메서드를 제공

In [133]: frame = pd.read_csv('ex1.csv')

In [134]: frame
Out[134]:
   a   b   c   d message
0  1   2   3   4   hello
1  5   6   7   8   world
2  9  10  11  12     foo

In [135]: import pickle

In [136]: frame.to_pickle('frame_pickle')

In [137]: pd.read_pickle('frame_pickle')
Out[137]:
   a   b   c   d message
0  1   2   3   4   hello
1  5   6   7   8   world
2  9  10  11  12     foo

 

마이크로소프트 엑셀 파일 읽기

판다스는 pandas.ExcelFile 클래스나 pandas.read_excel 함수를 통해 엑셀 2003 이후 버전으로 저장된 표 형식 데이터를 읽을 수 있음

내부적으로 xlrd 패키지와 openpyxl 패키지를 이용해 이전 형식의 xls 파일과 새로운 형식의 xlsx 파일을 읽을 수 있음

> 두 패키지는 판다스와 별개로 pip이나 conda 명령을 통해 따로 설치

In [1]: !pip install openpyxl xlrd    # ipython 내에서 명령어 칠 때 '!' 포함
C:\Users\User\anaconda3\Lib\site-packages\IPython\core\interactiveshell.py:2622: UserWarning: You executed the system command !pip which may not work as expected. Try the IPython magic %pip instead.
  warnings.warn(
Collecting openpyxl
  Downloading openpyxl-3.1.2-py2.py3-none-any.whl (249 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 250.0/250.0 kB 15.0 MB/s eta 0:00:00
Collecting xlrd
  Downloading xlrd-2.0.1-py2.py3-none-any.whl (96 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 96.5/96.5 kB ? eta 0:00:00
Collecting et-xmlfile (from openpyxl)
  Downloading et_xmlfile-1.1.0-py3-none-any.whl (4.7 kB)
Installing collected packages: xlrd, et-xmlfile, openpyxl
Successfully installed et-xmlfile-1.1.0 openpyxl-3.1.2 xlrd-2.0.1
In [8]: xlsx = pd.ExcelFile('ex1.xlsx')

In [9]: xlsx
Out[9]: <pandas.io.excel._base.ExcelFile at 0x272610f0050>

# 해당 파일에서 이용 가능한 시트 이름 목록 표시
In [10]: xlsx.sheet_names
Out[10]: ['Sheet1']

# DataFrame으로 읽어오기
In [11]: xlsx.parse(sheet_name='Sheet1')
Out[11]:
   Unnamed: 0  a   b   c   d message
0           0  1   2   3   4   hello
1           1  5   6   7   8   world
2           2  9  10  11  12     foo

# 엑셀 표에 인덱스 열이 존재하므로 index_col을 사용해 인수 지정 가능
In [12]: xlsx.parse(sheet_name='Sheet1', index_col=0)
Out[12]:
   a   b   c   d message
0  1   2   3   4   hello
1  5   6   7   8   world
2  9  10  11  12     foo

 

데이터베이스와 함께 사용

 

판다스는 SQL 쿼리 결과를 간단하게 DataFrame으로 불러오는 함수 몇 가지를 제공

파이썬 내장 sqlite3 드라이버를 이용해 SQLite3 데이터베이스 만들기

In [2]: import sqlite3

In [3]: query = """
   ...: CREATE TABLE test
   ...: (a VARCHAR(20), b VARCHAR(20),
   ...:  c REAL,        d INTEGER
   ...: );"""

In [4]: con = sqlite3.connect('mydata.sqlite')

In [5]: con.execute(query)
Out[5]: <sqlite3.Cursor at 0x22127a09440>

In [6]: con.commit()

#데이터 입력
In [7]: data = [('Atlanta', 'Georgia', 1.25, 6),
   ...:         ('Tallahassee', 'Florida', 2.6, 3),
   ...:         ('Sacramento', 'California', 1.7, 5)]

In [8]: stmt = 'INSERT INTO test VALUES(?, ?, ?, ?)'

In [9]: con.executemany(stmt, data)
Out[9]: <sqlite3.Cursor at 0x2212506fec0>

In [10]: con.commit()

# 대부분의 파이썬 SQL 드라이버는 테이블에서 select 쿼리를 수행하면 튜플 리스트 반환
In [11]: cursor = con.execute('SELECT * FROM test')

In [12]: rows = cursor.fetchall()

In [13]: rows
Out[13]:
[('Atlanta', 'Georgia', 1.25, 6),
 ('Tallahassee', 'Florida', 2.6, 3),
 ('Sacramento', 'California', 1.7, 5)]

# cursor의 description 속성에 있는 열 이름 지정
In [14]: cursor.description
Out[14]:
(('a', None, None, None, None, None, None),
 ('b', None, None, None, None, None, None),
 ('c', None, None, None, None, None, None),
 ('d', None, None, None, None, None, None))

In [15]: pd.DataFrame(rows, columns=[x[0] for x in cursor.description])
Out[15]:
             a           b     c  d
0      Atlanta     Georgia  1.25  6
1  Tallahassee     Florida  2.60  3
2   Sacramento  California  1.70  5

 

유명한 파이썬 SQL 툴킷(toolkit)인 SQLAlchemy 프로젝트는 SQL 데이터베이스 간의 일반적인 차이점을 추상화해 제공

판다스는 read_sql 함수를 통해 SQLAlchemy의 일반적인 연결을 이용해 쉽게 데이터를 읽도록 지원

# 설치 필요, pip 혹은 conda 설치
In [19]: !pip install sqlalchemy

In [20]: import sqlalchemy as sqla

In [21]: db = sqla.create_engine('sqlite:///mydata.sqlite')

In [22]: pd.read_sql('SELECT * FROM test', db)
Out[22]:
             a           b     c  d
0      Atlanta     Georgia  1.25  6
1  Tallahassee     Florida  2.60  3
2   Sacramento  California  1.70  5

 

https://sqlitebrowser.org/dl/

 

Downloads - DB Browser for SQLite

(Please consider sponsoring us on Patreon 😄) Windows Our latest release (3.12.2) for Windows: Windows PortableApp Note - If for any reason the standard Windows release does not work (e.g. gives an error), try a nightly build (below). Nightly builds ofte

sqlitebrowser.org

 

zip 파일 다운로드 - 압축 풀기 - DB Browser for SQLite 실행