메모리에 테이블 형식 데이터를 유지하기위한 데이터 구조?
내 시나리오는 다음과 달라집니다. 프로그램에서 사용하게 사용하는 데이터 테이블 (소수 필드, 백 행 적용)이 있습니다. 또한이 데이터가 많은 경우에 CSV로 저장하고 시작할 때 사용됩니다. 모든 옵션 (SQLite)이 겸손한 요구 사항에 비해 과도하게 사용되기 때문에 데이터베이스를 사용하지 않기 때문에 내로 결정했습니다.
내 데이터가 다음과 같다고 가정합니다 (파일에서 제목없이 쉼표로 구분되어 있으며 이는 단지 그림 일뿐입니다).
Row | Name | Year | Priority
------------------------------------
1 | Cat | 1998 | 1
2 | Fish | 1998 | 2
3 | Dog | 1999 | 1
4 | Aardvark | 2000 | 1
5 | Wallaby | 2000 | 1
6 | Zebra | 2001 | 3
메모 :
- 행은 파일에 기록 된 "실제"값이거나 행 번호를 최대 자동 생성 된 값일 수 있습니다. 어느 쪽이든 메모리에 존재합니다.
- 이름은 고유합니다.
데이터로 수행하는 작업 :
- ID (반복) 또는 이름 (직접 액세스)을 기반으로 행을 조회합니다.
- 여러 필드를 기준으로 다른 순서로 테이블을 표시합니다. 예를 들어 우선 순위, 연도, 연도, 우선 순위 등을 기준으로 정렬해야합니다.
- 예를 들어 1997 년과 2002 년 사이의 연도를 가진 행 수 또는 1998 년의 행 수와 우선 순위> 2 등의 매개 변수 집합을 기반으로 인스턴스를 계산해야합니다.
SQL에 대한 "울음"을 알고 있습니다.
데이터 구조에 가장 많은 것이 선택됩니다. 다음은 몇 가지 선택 사항입니다.
행 목록 목록 :
a = []
a.append( [1, "Cat", 1998, 1] )
a.append( [2, "Fish", 1998, 2] )
a.append( [3, "Dog", 1999, 1] )
...
열 목록 목록 (add_row 대한 API가 분명히있을 것입니다) :
a = []
a.append( [1, 2, 3, 4, 5, 6] )
a.append( ["Cat", "Fish", "Dog", "Aardvark", "Wallaby", "Zebra"] )
a.append( [1998, 1998, 1999, 2000, 2000, 2001] )
a.append( [1, 2, 1, 1, 1, 3] )
열 목록 사전 (문자열 키를 대체하기 위해 상수를 만들 수 있음) :
a = {}
a['ID'] = [1, 2, 3, 4, 5, 6]
a['Name'] = ["Cat", "Fish", "Dog", "Aardvark", "Wallaby", "Zebra"]
a['Year'] = [1998, 1998, 1999, 2000, 2000, 2001]
a['Priority'] = [1, 2, 1, 1, 1, 3]
키가 (Row, Field)의 튜플 인 사전 :
Create constants to avoid string searching
NAME=1
YEAR=2
PRIORITY=3
a={}
a[(1, NAME)] = "Cat"
a[(1, YEAR)] = 1998
a[(1, PRIORITY)] = 1
a[(2, NAME)] = "Fish"
a[(2, YEAR)] = 1998
a[(2, PRIORITY)] = 2
...
그리고 다른 방법이 확신합니다. 그러나 각 방법은 내 요구 사항 (복잡한 주문 및 계산)과 관련하여 단점이 있습니다.
권장되는 접근 방식은 무엇입니까?
편집하다 :
성능은 나에게 중요한 문제가 아닙니다. 테이블이 너무 작기 때문에 거의 모든 작업이 밀리 초 범위에있을 것이 믿습니다. 이는 내 애플리케이션에 대한 문제가 아닙니다.
조회, 재고 및 임의 재고가 필요한 메모리에 "테이블"이 있으면 SQL이 필요합니다. SQLite를 사용하는 것이지만 SQLite가 메모리 내 전용 데이터베이스를 사용할 수있는 것을 알고 계십니까?
connection = sqlite3.connect(':memory:')
그런 다음 SQLite의 모든 기능을 사용하여 메모리에 테이블을 생성 / 삭제 / 쿼리 / 업데이트 할 수있는 파일 작업을 마쳤을 때 남은 파일이 없습니다. 그리고 Python 2.5 sqlite3
부터는 표준 라이브러리에 실제로 "과잉"이 아닙니다.
다음은 데이터베이스를 만들고 채우는 방법에 대한 샘플입니다.
import csv
import sqlite3
db = sqlite3.connect(':memory:')
def init_db(cur):
cur.execute('''CREATE TABLE foo (
Row INTEGER,
Name TEXT,
Year INTEGER,
Priority INTEGER)''')
def populate_db(cur, csv_fp):
rdr = csv.reader(csv_fp)
cur.executemany('''
INSERT INTO foo (Row, Name, Year, Priority)
VALUES (?,?,?,?)''', rdr)
cur = db.cursor()
init_db(cur)
populate_db(cur, open('my_csv_input_file.csv'))
db.commit()
SQL을 사용하지 않는 사전 목록을 사용하지 않습니다.
lod = [ ] # "list of dicts"
def populate_lod(lod, csv_fp):
rdr = csv.DictReader(csv_fp, ['Row', 'Name', 'Year', 'Priority'])
lod.extend(rdr)
def query_lod(lod, filter=None, sort_keys=None):
if filter is not None:
lod = (r for r in lod if filter(r))
if sort_keys is not None:
lod = sorted(lod, key=lambda r:[r[k] for k in sort_keys])
else:
lod = list(lod)
return lod
def lookup_lod(lod, **kw):
for row in lod:
for k,v in kw.iteritems():
if row[k] != str(v): break
else:
return row
return None
테스트 결과 :
>>> lod = []
>>> populate_lod(lod, csv_fp)
>>>
>>> pprint(lookup_lod(lod, Row=1))
{'Name': 'Cat', 'Priority': '1', 'Row': '1', 'Year': '1998'}
>>> pprint(lookup_lod(lod, Name='Aardvark'))
{'Name': 'Aardvark', 'Priority': '1', 'Row': '4', 'Year': '2000'}
>>> pprint(query_lod(lod, sort_keys=('Priority', 'Year')))
[{'Name': 'Cat', 'Priority': '1', 'Row': '1', 'Year': '1998'},
{'Name': 'Dog', 'Priority': '1', 'Row': '3', 'Year': '1999'},
{'Name': 'Aardvark', 'Priority': '1', 'Row': '4', 'Year': '2000'},
{'Name': 'Wallaby', 'Priority': '1', 'Row': '5', 'Year': '2000'},
{'Name': 'Fish', 'Priority': '2', 'Row': '2', 'Year': '1998'},
{'Name': 'Zebra', 'Priority': '3', 'Row': '6', 'Year': '2001'}]
>>> pprint(query_lod(lod, sort_keys=('Year', 'Priority')))
[{'Name': 'Cat', 'Priority': '1', 'Row': '1', 'Year': '1998'},
{'Name': 'Fish', 'Priority': '2', 'Row': '2', 'Year': '1998'},
{'Name': 'Dog', 'Priority': '1', 'Row': '3', 'Year': '1999'},
{'Name': 'Aardvark', 'Priority': '1', 'Row': '4', 'Year': '2000'},
{'Name': 'Wallaby', 'Priority': '1', 'Row': '5', 'Year': '2000'},
{'Name': 'Zebra', 'Priority': '3', 'Row': '6', 'Year': '2001'}]
>>> print len(query_lod(lod, lambda r:1997 <= int(r['Year']) <= 2002))
6
>>> print len(query_lod(lod, lambda r:int(r['Year'])==1998 and int(r['Priority']) > 2))
0
개인적으로 저는 SQLite 버전을 더 좋아합니다. 유형을 더 잘 보존하고 (Python의 추가 변환 코드없이) 요구 사항을 수용하기 위해 쉽게 확장되기 때문입니다. 하지만 다시 SQL에 익숙해 져서 YMMV입니다.
내가 아는 아주 오래된 질문이지만 ...
pandas DataFrame은 여기서 선택 옵션 인 것입니다.
http://pandas.pydata.org/pandas-docs/version/0.13.1/generated/pandas.DataFrame.html
광고에서
레이블이 지정된 축 (행 및 열)이있는 2 차원 크기 변경 가능, 잠재적으로 이질적인 표 형식 데이터 구조. 산술 연산은 행 및 열 레이블 모두에 정렬됩니다. Series 객체에 대한 dict-like 컨테이너로 생각할 수 있습니다. 기본 Pandas 데이터 구조
개인적으로 행 목록 목록을 사용합니다. 각 행의 데이터는 항상 동일한 순서이므로 각 목록에서 해당 요소에 액세스하기 만하면 열별로 쉽게 정렬 할 수 있습니다. 또한 각 목록의 특정 열을 기반으로 쉽게 계산하고 검색 할 수도 있습니다. 기본적으로 2 차원 배열에 가깝습니다.
여기서 유일한 단점은 데이터가 어떤 순서로되어 있는지 알아야하고, 그 순서를 변경하면 일치하도록 검색 / 정렬 루틴을 변경해야한다는 것입니다.
당신이 할 수있는 또 다른 일은 사전 목록을 갖는 것입니다.
rows = []
rows.append({"ID":"1", "name":"Cat", "year":"1998", "priority":"1"})
이렇게하면 매개 변수의 순서를 알 필요가 없으므로 목록의 각 "연도"필드를 살펴볼 수 있습니다.
행이 dict 또는 더 나은 행 객체 목록 인 Table 클래스가 있습니다.
테이블에서 행을 직접 추가하지 말고 몇 가지 조회 맵을 업데이트하는 방법이 있습니다. 예를 들어, 순서대로 행을 추가하지 않거나 id가 연속적이지 않은 경우 idMap도 가질 수 있습니다.
class Table(object):
def __init__(self):
self.rows = []# list of row objects, we assume if order of id
self.nameMap = {} # for faster direct lookup for row by name
def addRow(self, row):
self.rows.append(row)
self.nameMap[row['name']] = row
def getRow(self, name):
return self.nameMap[name]
table = Table()
table.addRow({'ID':1,'name':'a'})
첫째, 복잡한 데이터 검색 시나리오가 있다는 점을 감안할 때 SQLite조차도 과잉이라고 확신합니까?
결국 Greenspun의 Tenth Rule을 의역하는 SQLite의 절반을 비공식적으로 지정하고 버그로 가득 찬 임시 구현을하게 됩니다.
즉, 단일 데이터 구조를 선택하면 검색, 정렬 또는 계산 중 하나 이상에 영향을 미치므로 성능이 가장 중요하고 데이터가 일정하다면 다른 목적을 위해 둘 이상의 구조를 고려할 수 있습니다.
무엇보다도 더 일반적인 작업을 측정하고 비용이 적게 드는 구조를 결정합니다.
저는 개인적으로 꽤 최근에 BD_XML이라는 lib를 작성했습니다.
가장 기본적인 존재 이유는 XML 파일과 SQL 데이터베이스간에 데이터를주고받는 방법으로 사용되기 때문입니다.
스페인어로 작성되었지만 (프로그래밍 언어에서 중요한 경우) 매우 간단합니다.
from BD_XML import Tabla
Tabla (Table)라는 개체를 정의하며, pep-246 호환 데이터베이스 인터페이스의 사전 생성 된 연결 개체를 식별하기위한 이름으로 만들 수 있습니다.
Table = Tabla('Animals')
그런 다음 agregar_columna
(add_column) 메서드를 사용하여 열을 추가해야합니다 .와 함께 다양한 키워드 인수를 사용할 수 있습니다.
campo
(필드) : 필드의 이름tipo
(유형) : 저장되는 데이터의 유형은 'varchar'및 'double'과 같은 것이거나 데이터베이스 후자로 내보내는 데 관심이없는 경우 Python 객체의 이름 일 수 있습니다.defecto
(기본값) : 행을 추가 할 때 아무것도 없으면 열에 대한 기본값을 설정합니다.다른 3 개가 있지만 실제로는 작동하지 않고 데이터베이스 용으로 만 존재합니다.
처럼:
Table.agregar_columna(campo='Name', tipo='str')
Table.agregar_columna(campo='Year', tipo='date')
#declaring it date, time, datetime or timestamp is important for being able to store it as a time object and not only as a number, But you can always put it as a int if you don't care for dates
Table.agregar_columna(campo='Priority', tipo='int')
그런 다음 + = 연산자를 사용하여 행을 추가합니다 (또는 추가 행이있는 복사본을 만들려면 +).
Table += ('Cat', date(1998,1,1), 1)
Table += {'Year':date(1998,1,1), 'Priority':2, Name:'Fish'}
#…
#The condition for adding is that is a container accessible with either the column name or the position of the column in the table
그런 다음 XML을 생성하고 exportar_XML
(export_XML) 및 escribir_XML
(write_XML) 을 사용하여 파일에 쓸 수 있습니다 .
file = os.path.abspath(os.path.join(os.path.dirname(__file__), 'Animals.xml'))
Table.exportar_xml()
Table.escribir_xml(file)
그런 다음 importar_XML
파일 이름과 문자열 리터럴이 아닌 파일을 사용하고 있음을 나타내는 (import_XML)을 사용하여 다시 가져옵니다 .
Table.importar_xml(file, tipo='archivo')
#archivo means file
많은
이것은 SQL 방식으로 Tabla 객체를 사용할 수있는 방법입니다.
#UPDATE <Table> SET Name = CONCAT(Name,' ',Priority), Priority = NULL WHERE id = 2
for row in Table:
if row['id'] == 2:
row['Name'] += ' ' + row['Priority']
row['Priority'] = None
print(Table)
#DELETE FROM <Table> WHERE MOD(id,2) = 0 LIMIT 1
n = 0
nmax = 1
for row in Table:
if row['id'] % 2 == 0:
del Table[row]
n += 1
if n >= nmax: break
print(Table)
이 예는 'id'라는 열을 가정하지만 예를 들어 너비 row.pos로 바꿀 수 있습니다.
if row.pos == 2:
파일은 다음에서 다운로드 할 수 있습니다.
https://bitbucket.org/WolfangT/librerias
참고 URL : https://stackoverflow.com/questions/1038160/data-structure-for-maintaining-tabular-data-in-memory
'ProgramingTip' 카테고리의 다른 글
Xcode 8 : 함수 유형은 내 빌드를 깨는 인수를 생산할 수 없습니다. (0) | 2020.10.29 |
---|---|
wordpress.com에서 블로그 코드를 작성하는 방법 (0) | 2020.10.29 |
Python에 개체 고유 식별자가 있습니까? (0) | 2020.10.29 |
템플릿 클래스를 typedef하는 방법은 무엇입니까? (0) | 2020.10.29 |
srand () — 한 번만 호출하는 이유는 무엇입니까? (0) | 2020.10.29 |