Cách tốt nhất để lưu trữ bảng trong C ++ là gì


8

Tôi đang lập trình cây quyết định trong C ++ bằng cách sử dụng phiên bản sửa đổi một chút của thuật toán C4.5 . Mỗi nút đại diện cho một thuộc tính hoặc một cột trong tập dữ liệu của bạn và nó có con trên mỗi giá trị có thể của thuộc tính.

Vấn đề của tôi là làm thế nào để lưu trữ tập dữ liệu huấn luyện mà tôi phải sử dụng một tập hợp con cho mỗi nút vì vậy tôi cần một cách nhanh chóng để chỉ chọn một tập hợp con của các hàng và cột.

Mục tiêu chính là thực hiện nó trong bộ nhớ và thời gian hiệu quả nhất có thể (theo thứ tự ưu tiên đó).

Cách tốt nhất mà tôi đã nghĩ đến là có một mảng các mảng (hoặc std :: vector), hoặc một cái gì đó tương tự, và đối với mỗi nút có một danh sách (mảng, vectơ, v.v.) hoặc một cái gì đó với column,line(có thể là một tuple) các cặp hợp lệ cho nút đó.

Bây giờ tôi nên có một cách tốt hơn để làm điều này, bất kỳ đề nghị?

CẬP NHẬT: Những gì tôi cần là một cái gì đó như thế này:

Lúc đầu tôi có dữ liệu này:

Paris    4    5.0    True
New York 7    1.3    True
Tokio    2    9.1    False
Paris    9    6.8    True
Tokio    0    8.4    False

Nhưng đối với nút thứ hai tôi chỉ cần dữ liệu này:

Paris    4    5.0
New York 7    1.3
Paris    9    6.8

Và cho nút thứ ba:

Tokio    2    9.1
Tokio    0    8.4

Nhưng với một bảng gồm hàng triệu bản ghi với hàng trăm cột.

Những gì tôi có trong tâm trí là giữ tất cả dữ liệu trong một ma trận, và sau đó cho mỗi nút giữ thông tin của các cột và hàng hiện tại. Một cái gì đó như thế này:

Paris    4    5.0    True
New York 7    1.3    True
Tokio    2    9.1    False
Paris    9    6.8    True
Tokio    0    8.4    False

Nút 2:

columns = [0,1,2]
rows = [0,1,3]

Nút 3:

columns = [0,1,2]
rows = [2,4]

Cách này trong trường hợp xấu nhất tôi chỉ phải lãng phí

size_of(int) * (number_of_columns + number_of_rows) * node

Đó là ít hơn nhiều so với việc có một ma trận dữ liệu độc lập cho mỗi nút.


Bạn có thể muốn xem xét đa dạng của Loki. loki-lib.sourceforge.net/html/main.html
Stu

Bạn có triển khai không c ++ mà bạn muốn chuyển (chuyển đổi) sang c ++ không? Nếu không, câu hỏi của bạn có thể được chú ý nhiều hơn về: stats.stackexchange.com
mda

Tôi không biết nếu nó phục vụ mục đích của bạn, nhưng dường như với tôi rằng boost :: multi_index là thứ để bạn tìm kiếm.
sergiol

1
Không chắc điều này có được giải quyết hay không cũng không phải đây là điều bạn muốn hay không nhưng .. Tại sao không tạo cấu trúc và lưu trữ các giá trị trong đó sau đó lưu trữ từng cấu trúc trong một mảng / vector hoặc danh sách?

Câu trả lời:


2

Lần trước khi tôi cố gắng hiểu C4.5, tôi đã thất bại, nhưng tôi đã triển khai một biến thể của ID3 - ban đầu vì tò mò, nhưng cuối cùng nó đã được sử dụng như một phần của trình tạo mã nhiều công văn. Điều này không bao giờ xử lý các tập dữ liệu lớn, và đó là một công việc tốt. Bạn sẽ không làm tốt để bắt chước hầu hết những gì tôi đã làm, nhưng có thể với một vài ngoại lệ, và tất nhiên tôi đã học được một chút từ những sai lầm.

Tôi có xu hướng nghĩ về việc xây dựng cây quyết định cho một hệ thống chuyên gia, vì vậy tôi có xu hướng sử dụng các thuật ngữ sau - xin lỗi nếu điều đó gây nhầm lẫn ...

Column = Question ..... A question the expert system might ask
Row    = Conclusion ... A possible conclusion the expert system might reach
Cell   = Answer ....... For the question and conclusion, what answer should
                        the user be expected to give

Trên thực tế, trong trường hợp của tôi, tôi đã đưa ra kết luận vào một cột khác - tương tự như bảng chân lý cho cổng logic. Do đó, số hàng chỉ là số hàng. Điều này cho phép tôi xử lý các sự cố kiểu XOR thậm chí không thể được biểu diễn nếu cùng một kết luận không thể xuất hiện trên một số hàng. Tôi không chắc điều này có liên quan đến bạn hay không. Trong mọi trường hợp, tôi bỏ qua điều này dưới đây - nó thực sự không tạo ra nhiều sự khác biệt trừ khi bạn nhìn vào chi tiết của việc chọn câu hỏi nào để hỏi tiếp theo. Để khai thác dữ liệu, có lẽ bạn không có một thông tin cụ thể nào để coi là kết luận mục tiêu dù sao đi nữa - "kết luận" chỉ là bất cứ điều gì còn lại khi bạn quyết định ngừng đặt câu hỏi.

Vì vậy - đối với mỗi nút cây quyết định xuất phát từ trước đến nay, bạn có một bộ câu hỏi nổi bật (cột) và một tập hợp các kết luận chưa được loại bỏ (hàng). Đó là những gì tôi đã làm. Điểm duy nhất đáng thêm là tôi đã sử dụng các vectơ bit.

IIRC, C ++ std::vector<bool>std::array<bool> có thể được triển khai dưới dạng vectơ bit, nhưng bạn vẫn phụ thuộc vào thuật toán STL cho các hoạt động được thiết lập, mỗi lần vận hành một mục. Tôi đã sử dụng lớp vectơ bit của riêng mình, nó đã dần dần được xây dựng trong một khoảng thời gian và sử dụng các toán tử bitwise ở bên dưới std::vector<CHUNK>(trong đó CHUNKlà một kiểu int không dấu, thường rộng 32 bit).

Có thể có một tùy chọn vectơ bit tốt hơn trong C ++ 11 hoặc trong Boost, và phải có một số thư viện tốt ở đó - có rất nhiều loại chương trình mà bạn kết thúc làm việc với các bộ số nguyên không dấu. Tôi chỉ không biết nhiều về họ vì tôi đã quá lười để chuyển từ sử dụng của riêng mình.

Tuy nhiên, vectơ bit ở đó tốt nhất khi các tập hợp chủ yếu là dày đặc. Trong trường hợp này, tập hợp các hàng là vấn đề rõ ràng. Chỉ nút gốc của cây quyết định sẽ có một tập hợp hàng dày đặc hoàn hảo. Khi bạn nhận được nhiều hơn từ gốc, các bộ hàng nhận được sperer và sparser, với mỗi câu hỏi được trả lời dẫn đến tập hợp các hàng được phân phối giữa hai hoặc nhiều bộ hàng nút tiếp theo khác nhau.

Vì vậy, một dãy số được sắp xếp đơn giản có thể là đại diện tốt nhất cho các bộ này. Tuy nhiên, cũng có thể một "vectơ bit thưa thớt" có thể đáng giá. Một triển khai có thể là một mảng các cặp được sắp xếp trong đó đầu tiên của mỗi cặp là ID hàng đầu tiên của một khối và thứ hai là một bitvector có kích thước cố định cho khối đó. Ví dụ, hàng số 35 có thể được lưu trữ trong khối 32 ( 35 & ~(32 - 1)) ở vị trí bit 3 ( 35 & (32 - 1)). Nếu bạn chỉ lưu các cặp trong đó bitvector khác không, thì điều này mang lại một cái gì đó giữa một mảng ID được sắp xếp và một bitvector đơn giản - nó xử lý các mảng thưa thớt một cách hợp lý, đặc biệt là khi các ID có xu hướng tụ lại gần nhau theo bộ.

Ngoài ra, có thể đáng giá khi sử dụng một lớp có thể chuyển từ bitvector sang biểu diễn mảng được sắp xếp khi kích thước đủ nhỏ. Các biến chứng thêm, hoàn toàn có lợi cho một vài nút gần gốc, có lẽ là vô nghĩa.

Dù sao, tuy nhiên, các bộ này được trình bày, khi chúng quay trở lại một "cơ sở dữ liệu" không đổi duy nhất, điều này giúp tiết kiệm rất nhiều trong việc sao chép dữ liệu và lãng phí không gian khi thuật toán chạy. Nhưng nó vẫn đáng để xem xét "cơ sở dữ liệu" đó.

Tôi đã sử dụng cấu trúc dữ liệu liên kết, cho phép tôi tra cứu bằng cách sử dụng một bộ câu hỏi-ID và ID kết luận để có được câu trả lời-ID. Điều đó có nghĩa là tôi đã có một chi phí cho mỗi mục cho khóa (ID câu hỏi và ID kết luận) và trong trường hợp này cũng là chi phí trên cây kiểu B +. Lý do - về cơ bản là thói quen. Tôi có các thùng chứa rất linh hoạt và tôi có xu hướng sử dụng chúng rất nhiều vì nó giúp tiết kiệm dự đoán những khả năng tôi thực sự cần sau này. Có một cái giá cho điều đó, nhưng đó chỉ là điều tối ưu hóa sớm.

Trong trường hợp của bạn, bạn đang sử dụng ma trận - Tôi giả sử một mảng hai chiều được lập chỉ mục bởi ID câu hỏi và ID câu trả lời.

Cách duy nhất tôi có thể tưởng tượng phiên bản của mình hiệu quả hơn phiên bản của bạn là nếu hầu hết các câu trả lời không được biết đến. Trong một ma trận, bạn cần một ID câu trả lời không xác định đặc biệt cho điều đó, lấy cùng một không gian như một ID câu trả lời đã biết. Trong một thùng chứa kết hợp, bạn loại trừ các hàng đó.

Mặc dù vậy, một mảng được sắp xếp sẽ hiệu quả hơn giải pháp dựa trên cây B + của tôi. Bạn không cần phải cho phép chèn hiệu quả, do đó, chi phí cần thiết duy nhất là cho các phím.

Nếu bạn sử dụng hai trường chính (câu hỏi và kết luận, hàng và cột) có thể là một vấn đề (tôi thực sự không nhớ) - bạn có thể không thể giữ một bản sao của bảng theo một thứ tự được sắp xếp. Nhưng nếu bạn sử dụng một khóa được tính toán duy nhất dọc theo dòng (row * num_columns) + column, thì về cơ bản bạn vẫn đang thực hiện một mảng thưa hai chiều.

Đối với tôi, sự hiện diện của các câu trả lời không xác định / không xác định cho một câu hỏi cụ thể có nghĩa là tôi chưa được phép hỏi câu hỏi đó - và thậm chí đó chỉ là lý thuyết tôi sử dụng khi tôi thực hiện thuật toán lần đầu tiên. Tôi không bao giờ thực sự đưa nó để sử dụng. Tôi có thể sử dụng nó, nhưng tôi không bao giờ sử dụng nó. Đối với bản ghi, trong trình tạo mã nhiều công văn đó, một ý tưởng là gửi dựa trên các trường trong loại. Vì bản thân loại này là đa hình, nên các trường đó thậm chí không có ở đó, vì vậy chỉ có giá trị để xem xét chúng một khi bạn đã xác nhận rằng chúng phải có mặt.

Nếu bạn không có ứng dụng cho câu trả lời không xác định / không xác định, ma trận hiện tại của bạn có lẽ là giải pháp tốt nhất.

Về cơ bản, đó là nó - tôi thực sự không thể đưa ra bất kỳ lựa chọn rõ ràng nào tốt hơn, và những gì bạn đang làm có lẽ đã tốt hơn những gì tôi đã làm. Tuy nhiên, có một số khả năng đánh đổi mà bạn có thể xem xét - giả sử rằng đó không phải là tối ưu hóa sớm (và có thể sai), tất nhiên.

Vấn đề đánh đổi chính liên quan đến hiệu quả của việc biểu diễn các bộ giá trị thưa thớt so với mật độ dày đặc, do đó, nó không thực sự cụ thể đối với C4.5 hoặc xây dựng cây quyết định. Và một cách tiếp cận "tinh vi" hơn thường kém hiệu quả hơn một cách đơn giản được lựa chọn cẩn thận.


1

Tôi nghĩ rằng ý tưởng của bạn là tốt. Tôi không chắc thư viện của bạn truy cập dữ liệu bạn muốn như thế nào. Nhưng bạn có thể một cái gì đó như lưu trữ toàn bộ dữ liệu ban đầu của bạn trong một std::vectorhàng. Nếu các cột là bất biến đối với bạn, đối với kiểu dữ liệu tôi sẽ chọn thư viện boost :: tuple .

Sau đó, bạn có thể chuyển một điều khiển cho toàn bộ "cơ sở dữ liệu" cho các loại Nút của bạn. Sẽ rất dễ dàng để truy xuất / truy cập bất kỳ tập hợp con của các cột và hàng từ cấu trúc như vậy. Một loại nút sẽ hoạt động như một loại đối tượng proxy hoặc trình bao bọc để truy cập dữ liệu, phần nào đóng vai trò là chế độ xem cho toàn bộ cơ sở dữ liệu.

Ngoài ra, chi phí thời gian sẽ không đổi theo thời gian, vì dữ liệu sẽ được truy cập trực tiếp. Dung lượng bộ nhớ sẽ thấp như bạn đã đề cập, vì chi phí chính là cơ sở dữ liệu ban đầu và không có bản sao của dữ liệu được tạo. Chỉ chi phí sẽ là các chỉ mục từ trong các vectơ cột hàng.

Tuy nhiên, có một cảnh báo. Nếu bạn nói về hàng triệu bản ghi và hàng trăm cột, bạn cần nhớ có giới hạn khả năng mở rộng cho bộ nhớ. Bạn có thể bị buộc phải dùng đến một số hệ thống cơ sở dữ liệu thực sự chỉ là nguyên nhân của giới hạn số lượng bộ nhớ. Tôi sẽ tư vấn có lẽ SQLite. SQLite cho phép tạo cơ sở dữ liệu trong bộ nhớ dễ bay hơi. Vì vậy, bạn có thể đi với nó ban đầu và chuyển đổi liền mạch thành tệp DB bình thường nếu bộ dữ liệu của bạn sẽ phát triển quá lớn.


1

Một điều bạn có thể làm trên mô hình của mình để tiết kiệm một số không gian là quản lý các chỉ mục trên các nút của bạn dưới dạng các mảng bit. Vì vậy, từ ví dụ của bạn:

Nút 2:

cột = [0,1,2] = 5 hoặc 101

hàng = [0,1,3] = 11 hoặc 1011

Nút 3:

cột = [0,1,2] = 5 hoặc 101

hàng = [2,4] = 20 hoặc 10100

Điều này sẽ đưa yêu cầu của bạn từ nút size_of (int) * (number_of_columns + number_of_rows) * sang nút sizeof (int) * 2 *

Cách tiếp cận này sẽ giới hạn kích thước ma trận của bạn vào các cột sizeof (int) và cùng số lượng hàng. Để khắc phục hạn chế này (đặc biệt mạnh về các hàng), bạn có thể lưu trữ chúng trên các khối. Trong trường hợp đó, bạn sẽ cần một số nguyên mới trên mỗi nút chỉ định khối, làm cho tổng kích thước chỉ mục của bạn thành nút sizeof (int) * 3 * vẫn thấp hơn nút size_of (int) * (number_of_columns + number_of_rows) *

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.