Lời tòa soạn: bài viết này được thực hiện phối hợp với Milan van der Meer. Cả hai tác giả đều thuộc đội ngũ Real Impact Analytics, một startup big data sáng tạo của Bỉ, khai thác giá trị trong dữ liệu viễn thông bằng cách “app hóa big data”.
Hướng dẫn này sẽ cho bạn thấy vì sao bạn có thể muốn tạo các tập dữ liệu ngẫu nhiên và nên kỳ vọng điều gì từ chúng. Bài viết cũng sẽ hướng dẫn những ví dụ đầu tiên về cách sử dụng Trumania, một thư viện Python tạo dữ liệu.
Để biết thêm thông tin, bạn có thể truy cập GitHub của Trumania!
Vì sao tạo các tập dữ liệu ngẫu nhiên?
Tạo tập dữ liệu ngẫu nhiên hữu ích cho cả kỹ sư dữ liệu và nhà khoa học dữ liệu. Vì sao họ làm việc với dữ liệu tổng hợp và họ lấy dữ liệu đó như thế nào?
Với vai trò kỹ sư dữ liệu, sau khi bạn viết xong ứng dụng xử lý dữ liệu “xịn” mới của mình, bạn sẽ muốn bắt đầu kiểm thử end-to-end và do đó cần dữ liệu đầu vào.
Với vai trò nhà khoa học dữ liệu, bạn được lợi từ việc tạo dữ liệu vì nó cho phép bạn thử nghiệm nhiều cách khám phá tập dữ liệu, thuật toán, kỹ thuật trực quan hóa dữ liệu hoặc kiểm định giả định về hành vi của một phương pháp trên nhiều tập dữ liệu khác nhau do bạn lựa chọn.
Trong cả hai trường hợp, một lựa chọn hấp dẫn là dùng dữ liệu thực. Tuy nhiên, một vấn đề nhỏ là dữ liệu sản xuất thường khó lấy, kể cả một phần, và điều này càng khó hơn với các luật của châu Âu về quyền riêng tư và bảo mật.
Có thể bạn là người may mắn có một bộ dữ liệu sản xuất đầy đủ trên laptop. Dẫu vậy, bạn vẫn muốn có độ đa dạng cao hơn trong tập dữ liệu để kiểm định trước nhiều tình huống như quy mô dân số khác nhau, phân phối dữ liệu khác nhau, các vấn đề chất lượng dữ liệu như thiếu hoặc dữ liệu không hợp lệ...
Nếu muốn đọc sâu hơn về lý do tạo dữ liệu ngẫu nhiên hữu ích, hãy xem “Vì sao dữ liệu tổng hợp sắp trở thành lợi thế cạnh tranh lớn”.
Tạo dữ liệu ngẫu nhiên dựa trên lược đồ: Cần những mối quan hệ tốt!
Phần này minh họa tạo dữ liệu ngẫu nhiên dựa trên lược đồ và chỉ ra những hạn chế của nó.
Đã có nhiều công cụ tạo tập dữ liệu ngẫu nhiên. Cách tiếp cận phổ biến là tạo dựa trên lược đồ, cho phép bạn định nghĩa một bản thiết kế và dùng nó để sinh ra các thực thể. Khermes và LogSynth là hai ví dụ.
Một ví dụ cấu hình dựa trên lược đồ cho người có thể như sau:
{
{
"field": "Name",
"class": NameGenerator
},
{
"field": "Age",
"class": RandomInt(18, 65)
},
{
"field": "Gender",
"class": RandomPicker(["Male", "Female", "Other"])
}
}
Lược đồ này định nghĩa việc sinh dữ liệu về một người: có tên, tuổi và giới tính. Nó đơn giản, nhanh, nhưng có hạn chế. Hạn chế chính là giả lập mối quan hệ giữa các thực thể khác nhau hoặc theo thời gian.
Ví dụ, làm sao để một số tên có xác suất xuất hiện cao hơn dựa trên giới tính? Hoặc làm sao tạo log hành động người dùng trong đó các hành động có xu hướng xảy ra nhiều hơn vào cuối tuần?
Cách dựa trên lược đồ không dễ giúp ứng dụng mới của bạn tiếp xúc với những đặc thù hay thách thức của thế giới thực như:
- Tập dữ liệu mất cân bằng (gây khó cho các phép nhóm theo);
- Mẫu hình phân cụm trong mạng lưới thực thể, tức phát hiện nhóm xã hội thông qua tương tác (ví dụ: cuộc gọi điện thoại);
- Nhiều hồ sơ hoạt động theo thời gian: hành động có khả năng xảy ra hơn vào một số thời điểm trong ngày hoặc trong tuần;
- Quan hệ nhân quả giữa các hành động, ví dụ nhiều lượt mua tại cửa hàng cuối cùng kích hoạt sự kiện “hết hàng”.
Trumania dựa trên kịch bản nhằm khắc phục những điểm này và tạo các tập dữ liệu thực tế hơn. Khi kịch bản diễn tiến, các nhóm đối tượng tương tác với nhau, cập nhật thuộc tính và phát sinh log.
Một ví dụ kịch bản là mọi người gọi cho bạn bè. Khi gọi, họ trả tiền theo phút, hết phút thì phải đến cửa hàng... Tập dữ liệu thu được có thể là log cuộc gọi, log mua sắm và mức tồn kho theo từng giờ. Hành động trong kịch bản có thể ngẫu nhiên hoặc tất định.
Trong Trumania, các tập dữ liệu tạo ra thường là chuỗi thời gian vì chúng là kết quả của việc thực thi kịch bản theo thời gian.
Tạo dữ liệu có liên hệ với Trumania (đến giờ hướng dẫn!)
Trong ví dụ dưới đây, bạn sẽ xây dựng một kịch bản cơ bản nơi nhiều người nhắn tin cho nhau. Tập dữ liệu kết quả sẽ là chuỗi thời gian các tin nhắn được trao đổi.
Chuỗi bước chúng ta sẽ thực hiện như sau:
- tạo một circus Trumania, thế giới mà kịch bản sẽ chạy bên trong
- thêm một story “hello world” tạo ra các log cơ bản hard-code
- thêm một relationship gán một số câu trích dẫn ưa thích cho mỗi người, dùng để tạo nội dung tin nhắn
- tham số hóa thời gian của story để có phân phối tin nhắn thực tế hơn trong ngày
- thêm một relationship khác để định nghĩa mạng xã hội, để đồ thị xã hội hình thành từ log tin nhắn chân thực hơn
Tạo một Trumania Circus
Mục đích
Bước đầu tiên là tạo một circus, thế giới nơi tất cả phần tử của kịch bản tồn tại. Chúng ta cũng sẽ tạo một quần thể cơ bản gồm các persons. Vì nhiều khía cạnh của Trumania là ngẫu nhiên, bước này cũng giới thiệu khái niệm generators, dùng để kiểm soát nhiều hành vi ngẫu nhiên.
Ở thời điểm này chúng ta chưa thêm kịch bản nào, nên mọi thứ sẽ tĩnh.
Bắt đầu thôi!
Cách thực hiện
Một circus Trumania được tạo đơn giản như sau:
from trumania.core import circus
example_circus = circus.Circus(name="example1",
master_seed=12345,
start=pd.Timestamp("1 Jan 2017 00:00"),
step_duration=pd.Timedelta("1h"))
Trong Trumania, mọi yếu tố liên quan đến thời gian được kiểm soát bởi một đồng hồ trung tâm. Phần quan trọng nhất trong đoạn mã trên là step_duration=pd.Timedelta("1h"), xác định đồng hồ sẽ tăng theo bước 1 giờ.
Tiếp theo, bạn thêm quần thể person vào circus. Một quần thể về bản chất là tập các tác nhân có id và một số thuộc tính.
Có thể chỉ định thủ công giá trị mỗi thuộc tính, nhưng đa số trường hợp bạn muốn sinh ngẫu nhiên, nên trước tiên hãy định nghĩa vài generators cho việc đó:
from trumania.core.random_generators import SequencialGenerator, FakerGenerator, NumpyRandomGenerator
id_gen = SequencialGenerator(prefix="PERSON_")
age_gen = NumpyRandomGenerator(method="normal", loc=3, scale=5,
seed=next(example_circus.seeder))
name_gen = FakerGenerator(method="name", seed=next(example_circus.seeder))
Một generator của Trumania chịu trách nhiệm cung cấp dữ liệu khi phương thức generate() được gọi. Trong ví dụ trên, id_gen sẽ sinh chuỗi như PERSON_0001, PERSON_0002, ... age_gen sẽ lặp lại việc lấy mẫu từ phân phối chuẩn và name_gen cung cấp tên người ngẫu nhiên.
Lưu ý bất kỳ phân phối thống kê từ numpy cũng như bất kỳ nhà cung cấp Faker đều có sẵn trong Trumania, và Trumania có thể dễ dàng mở rộng thêm.
Với phần này, bạn có thể thêm một quần thể 1000 người với ID tuần tự và hai thuộc tính ngẫu nhiên cơ bản:
person = example_circus.create_population(name="person", size=1000, ids_gen=id_gen)
person.create_attribute("NAME", init_gen=name_gen)
person.create_attribute("AGE", init_gen=age_gen)
Kết quả
Bạn đã có thể xem các thuộc tính được sinh cho toàn bộ thành viên của quần thể person bằng cách gọi person.to_dataframe().
+-------------------+-----------------+---------+
| | NAME | age |
|-------------------+-----------------+---------|
| PERSON_0000000000 | Amy Berger | 28.588 |
| PERSON_0000000001 | Michael Curry | 28.7499 |
| PERSON_0000000002 | Robert Ramirez | 35.9242 |
| PERSON_0000000003 | Derek Gonzalez | 34.7091 |
| PERSON_0000000004 | Gregory Fischer | 25.6009 |
| PERSON_0000000005 | Erica Walker | 33.9758 |
| PERSON_0000000006 | Bradley Collins | 24.4428 |
| PERSON_0000000007 | James Rodriguez | 34.7835 |
| PERSON_0000000008 | Brandy Padilla | 34.6296 |
| PERSON_0000000009 | Mark Taylor | 38.2286 |
+-------------------+-----------------+---------+
Mẹo: nếu bạn muốn xem toàn bộ script cho quần thể người dùng cơ bản này, xem đoạn mã trên Github!
Phát biểu Hello World: Tạo Stories
Mục đích
Hãy làm cho thú vị hơn bằng cách thêm một story. Một story trong Trumania về cơ bản là một phần kịch bản bao bọc khía cạnh động. Một story được thực thi mỗi khi đồng hồ mô phỏng tiến một bước.
Nhớ rằng bạn đã đặt step_duration=pd.Timedelta("1h") trong circus ở trên, tức là mỗi giờ logic, mỗi story sẽ có cơ hội được thực thi.
Bạn sẽ tạo một story hello-world đơn giản trong đó mọi thành viên của quần thể person sẽ thường xuyên phát đi thông điệp “hello world”.
Cách thực hiện
Khía cạnh chính đầu tiên khi tạo một story là quần thể khởi xướng story. Ở đây sẽ là person. Khía cạnh thứ hai là định nghĩa timer, xác định khi nào mỗi thành viên kích hoạt hành động đó. Tạm thời, bạn hard-code là 1, tức mọi thành viên sẽ thực thi story ở mỗi bước đồng hồ (chúng ta sẽ xây dựng timer phức tạp hơn ở bước sau):
hello_world = example_circus.create_story(
name="hello_world",
initiating_population=example_circus.populations["person"],
member_id_field="PERSON_ID",
timer_gen=ConstantDependentGenerator(value=1)
)
Tới đây, story còn trống nên chưa làm gì: bạn cần thêm một số operations. Các operation có thể ngẫu nhiên hoặc tất định, có thể đọc và cập nhật thuộc tính của bất kỳ quần thể nào hoặc trạng thái khác trong circus, và thậm chí có hiệu ứng phụ.
Hiện tại, hãy thêm hai operation tất định đơn giản và một logger để thực sự có log:
hello_world.set_operations(
example_circus.clock.ops.timestamp(named_as="TIME"),
ConstantGenerator(value="hello world").ops.generate(named_as="MESSAGE"),
FieldLogger(log_id="hello")
)
example_circus.clock.ops.timestamp sinh dấu thời gian ngẫu nhiên trong khoảng 1 giờ hiện tại và ConstantGenerator chỉ tạo giá trị cố định hard-code.
Kết quả
Chạy circus trong 48 giờ thời gian mô phỏng:
example_circus.run(
duration=pd.Timedelta("48h"),
log_output_folder="example_scenario",
delete_existing_logs=True
)
Điều này sẽ tạo tập dữ liệu cơ bản sau, trong đó mỗi trong số 1000 người nói “hello world” 48 lần trong 2 ngày.
Trường đầu tiên của tập dữ liệu kết quả, PERSON_ID, tương ứng với member_id_field của story. Hai trường còn lại, TIME và MESSAGE, tương ứng với hai operation chính bạn đã đưa vào story.
+-------+-------------------+---------------------+-------------+
| | PERSON_ID | TIME | MESSAGE |
|-------+-------------------+---------------------+-------------|
| 0 | PERSON_0000000000 | 2017-01-01 01:14:12 | hello world |
| 1 | PERSON_0000000001 | 2017-01-01 01:23:51 | hello world |
| 2 | PERSON_0000000002 | 2017-01-01 01:37:11 | hello world |
| 3 | PERSON_0000000003 | 2017-01-01 01:33:12 | hello world |
| 4 | PERSON_0000000004 | 2017-01-01 01:08:02 | hello world |
| 5 | PERSON_0000000005 | 2017-01-01 01:13:36 | hello world |
| 6 | PERSON_0000000006 | 2017-01-01 01:35:08 | hello world |
| 7 | PERSON_0000000007 | 2017-01-01 01:24:30 | hello world |
| 8 | PERSON_0000000008 | 2017-01-01 01:01:51 | hello world |
| 9 | PERSON_0000000009 | 2017-01-01 01:41:41 | hello world |
| ... |
| 23990 | PERSON_0000000990 | 2017-01-02 23:33:28 | hello world |
| 23991 | PERSON_0000000991 | 2017-01-02 23:15:26 | hello world |
| 23992 | PERSON_0000000992 | 2017-01-02 23:45:56 | hello world |
| 23993 | PERSON_0000000993 | 2017-01-02 23:25:16 | hello world |
| 23994 | PERSON_0000000994 | 2017-01-02 23:33:55 | hello world |
| 23995 | PERSON_0000000995 | 2017-01-02 23:41:45 | hello world |
| 23996 | PERSON_0000000996 | 2017-01-02 23:39:23 | hello world |
| 23997 | PERSON_0000000997 | 2017-01-02 23:45:53 | hello world |
| 23998 | PERSON_0000000998 | 2017-01-02 23:29:25 | hello world |
| 23999 | PERSON_0000000999 | 2017-01-02 23:12:24 | hello world |
+-------+-------------------+---------------------+-------------+
Mẹo: xem toàn bộ đoạn mã trên Github
Có người để nói Hello World
Mục đích
Hãy cải thiện bằng cách thêm một người ngẫu nhiên khác vào cuộc trò chuyện. Tạm thời, bạn sẽ cho rằng bất kỳ người nào cũng có thể nói chuyện với bất kỳ người nào khác với xác suất bằng nhau, điều này không thực tế (chúng ta sẽ thêm mạng xã hội ở bước sau). Ngoài ra, bạn sẽ làm giàu tập dữ liệu bằng cách thêm tên người dựa trên ID của họ.
Cách thực hiện
Giả sử người còn lại sẽ được lưu trong trường OTHER_PERSON mới. Trong trường hợp giản lược này, bạn chỉ muốn điền vào trường đó kết quả của một lựa chọn ngẫu nhiên không trọng số từ quần thể person. Bạn có thể dùng operation ngẫu nhiên select_one của quần thể đó:
example_circus.set_operations(
example_circus.clock.ops.timestamp(named_as="TIME"),
ConstantGenerator(value="hello world").ops.generate(named_as="MESSAGE"),
example_circus.populations["person"].ops.select_one(named_as="OTHER_PERSON"),
example_circus.populations["person"]
.ops.lookup(id_field="PERSON_ID", select={"NAME": "EMITTER_NAME"}),
example_circus.populations["person"]
.ops.lookup(id_field="OTHER_PERSON", select={"NAME": "RECEIVER_NAME"}),
FieldLogger(log_id="hello")
)
Kết quả
Khi bạn chạy lại circus, bạn nhận được tập dữ liệu có trường OTHER_PERSON mới:
+-------+-------------------+---------------------+-------------+-------------------+---------------------+--------------------+
| | PERSON_ID | TIME | MESSAGE | OTHER_PERSON | EMITTER_NAME | RECEIVER_NAME |
|-------+-------------------+---------------------+-------------+-------------------+---------------------+--------------------|
| 0 | PERSON_0000000000 | 2017-01-01 01:14:12 | hello world | PERSON_0000000852 | Ann Cruz | Sophia Black |
| 1 | PERSON_0000000001 | 2017-01-01 01:23:51 | hello world | PERSON_0000000429 | Kimberly Sanchez | Jeffrey Ryan |
| 2 | PERSON_0000000002 | 2017-01-01 01:37:11 | hello world | PERSON_0000000925 | Bethany Smith | Regina Brown |
| 3 | PERSON_0000000003 | 2017-01-01 01:33:12 | hello world | PERSON_0000000347 | Frank Middleton | Jacob Ross |
| 4 | PERSON_0000000004 | 2017-01-01 01:08:02 | hello world | PERSON_0000000211 | Cheryl Decker | Joshua Miller |
| 5 | PERSON_0000000005 | 2017-01-01 01:13:36 | hello world | PERSON_0000000779 | Thomas Rodriguez | Nicole Tanner |
| 6 | PERSON_0000000006 | 2017-01-01 01:35:08 | hello world | PERSON_0000000331 | James Peters | Melissa Rogers |
| 7 | PERSON_0000000007 | 2017-01-01 01:24:30 | hello world | PERSON_0000000234 | Allison Hansen | Taylor Smith |
| 8 | PERSON_0000000008 | 2017-01-01 01:01:51 | hello world | PERSON_0000000678 | Candice Sellers | James Smith |
| 9 | PERSON_0000000009 | 2017-01-01 01:41:41 | hello world | PERSON_0000000108 | Maria White | James Nguyen |
| ... |
| 23990 | PERSON_0000000990 | 2017-01-02 23:33:28 | hello world | PERSON_0000000689 | Natasha Brown | Bailey Ramirez DDS |
| 23991 | PERSON_0000000991 | 2017-01-02 23:15:26 | hello world | PERSON_0000000250 | Shelly Ponce | Jordan Johnson |
| 23992 | PERSON_0000000992 | 2017-01-02 23:45:56 | hello world | PERSON_0000000413 | Steven Mendez | Crystal Duffy |
| 23993 | PERSON_0000000993 | 2017-01-02 23:25:16 | hello world | PERSON_0000000670 | Morgan Rice | Jonathan Obrien |
| 23994 | PERSON_0000000994 | 2017-01-02 23:33:55 | hello world | PERSON_0000000411 | Crystal Vincent | George Mathis |
| 23995 | PERSON_0000000995 | 2017-01-02 23:41:45 | hello world | PERSON_0000000563 | Dawn Kim | Jennifer Martinez |
| 23996 | PERSON_0000000996 | 2017-01-02 23:39:23 | hello world | PERSON_0000000668 | Jimmy Franco | Dr. Mark Ruiz MD |
| 23997 | PERSON_0000000997 | 2017-01-02 23:45:53 | hello world | PERSON_0000000268 | Christian Christian | Victoria Donovan |
| 23998 | PERSON_0000000998 | 2017-01-02 23:29:25 | hello world | PERSON_0000000829 | David Hernandez | Margaret Anderson |
| 23999 | PERSON_0000000999 | 2017-01-02 23:12:24 | hello world | PERSON_0000000944 | Tamara Ramirez | Andrew Curtis |
+-------+-------------------+---------------------+-------------+-------------------+---------------------+--------------------+
Mẹo: bạn có thể xem toàn bộ mã tại đây
Bạn lúc nào cũng nói vậy
Mục đích
Hãy cải thiện tiếp bằng cách tạo một câu ngẫu nhiên trong trường MESSAGE thay vì chuỗi hard-code hello world nhàm chán. Điều này sẽ minh họa cách sử dụng cơ bản đầu tiên của Relationship của Trumania.
Bạn sẽ gắn mỗi người với 4 câu trích dẫn. Mỗi khi một người phát đi tin nhắn, nội dung sẽ được chọn trong 4 câu của người đó. Hơn nữa, bạn sẽ gắn mỗi câu với một weight khác nhau để xác định mức độ khả dĩ của câu đó: câu có trọng số cao sẽ được người chủ của nó dùng thường xuyên hơn câu có trọng số thấp.
Bắt đầu như thế nào?
Cách thực hiện
Bước đầu là tạo một generator câu trích dẫn, tương tự các generator Faker khác ở trên:
quote_generator = FakerGenerator(method="sentence", nb_words=6, variable_nb_words=True,
seed=next(example_circus.seeder))
Bạn có thể dùng generator đó trực tiếp trong story như trước, nhưng hãy làm thú vị hơn bằng cách ràng buộc mỗi người luôn dùng một trong 4 câu ưa thích. Bạn làm điều này bằng cách tạo một Relationship giữa các thành viên quần thể person và các giá trị câu.
Đầu tiên, tạo một relationship rỗng:
quotes_rel = example_circus.populations["person"].create_relationship("quotes")
Sau đó, điền relationship này với 4 câu cho mỗi người. Trong đoạn mã dưới đây, person.ids là một Series của pandas kích thước 1000 chứa toàn bộ id của quần thể person, và quote_generator.generate(size=person.size) trả về một Series khác cũng kích thước 1000, với các câu ngẫu nhiên.
Nghĩa là mỗi vòng lặp dưới đây thêm 1000 quan hệ: thêm một câu cho mỗi trong số 1000 người dùng. 1000 câu đầu sẽ có weight=1, nhóm tiếp theo có weight=2 để chúng xuất hiện thường xuyên hơn, ...
for w in range(4):
quotes_rel.add_relations(
from_ids=person.ids,
to_ids=quote_generator.generate(size=person.size),
weights=w
)
Bây giờ bạn có thể thay ConstantGenerator trong story bằng operation select_one trên relationship đó. Ý nghĩa: với mỗi giá trị hiện có trong trường PERSON_ID, tra cứu các relationship trong quotes bắt đầu từ ID đó và chọn ngẫu nhiên một giá trị liên quan. Vì không ghi đè trọng số trong operation dưới, trọng số mặc định đã định trong add_relations ở trên sẽ được dùng:
example_circus.set_operations(
example_circus.clock.ops.timestamp(named_as="TIME"),
example_circus.populations["person"].get_relationship("quotes")
.ops.select_one(from_field="PERSON_ID", named_as="MESSAGE"),
example_circus.populations["person"].ops.select_one(named_as="OTHER_PERSON"),
example_circus.populations["person"]
.ops.lookup(id_field="PERSON_ID", select={"NAME": "EMITTER_NAME"}),
example_circus.populations["person"]
.ops.lookup(id_field="OTHER_PERSON", select={"NAME": "RECEIVER_NAME"}),
FieldLogger(log_id="hello")
)
Kết quả
Khi chạy circus, trường MESSAGE giờ sẽ chứa câu trích dẫn ngẫu nhiên:
+-------+-------------------+---------------------+-----------------------------------------------------+-------------------+---------------------+---------------------+
| | PERSON_ID | TIME | MESSAGE | OTHER_PERSON | EMITTER_NAME | RECEIVER_NAME |
|-------+-------------------+---------------------+-----------------------------------------------------+-------------------+---------------------+---------------------|
| 0 | PERSON_0000000000 | 2017-01-01 01:14:12 | Become period risk wait now toward less. | PERSON_0000000158 | Ann Cruz | Victoria Washington |
| 1 | PERSON_0000000001 | 2017-01-01 01:23:51 | Month push down. | PERSON_0000000366 | Kimberly Sanchez | Steven Williams |
| 2 | PERSON_0000000002 | 2017-01-01 01:37:11 | Blue general carry else deep problem area. | PERSON_0000000613 | Bethany Smith | Frances Davis |
| 3 | PERSON_0000000003 | 2017-01-01 01:33:12 | Phone sister pretty suddenly allow conference. | PERSON_0000000086 | Frank Middleton | Ashley Fernandez |
| 4 | PERSON_0000000004 | 2017-01-01 01:08:02 | Each discussion several send wide process. | PERSON_0000000972 | Cheryl Decker | Latoya Flynn |
| 5 | PERSON_0000000005 | 2017-01-01 01:13:36 | Guess can issue writer. | PERSON_0000000030 | Thomas Rodriguez | Lindsay Bailey |
| 6 | PERSON_0000000006 | 2017-01-01 01:35:08 | Boy step claim camera common but our. | PERSON_0000000548 | James Peters | Thomas Ward |
| ... | 23993 | PERSON_0000000993 | 2017-01-02 23:25:16 | May course and kill from news. | PERSON_0000000202 | Morgan Rice | Patricia Williams |
| 23994 | PERSON_0000000994 | 2017-01-02 23:33:55 | Water door live hospital together safe. | PERSON_0000000268 | Crystal Vincent | Victoria Donovan |
| 23995 | PERSON_0000000995 | 2017-01-02 23:41:45 | Stage control management read although thousand. | PERSON_0000000466 | Dawn Kim | Ashley Baxter |
| 23996 | PERSON_0000000996 | 2017-01-02 23:39:23 | Their that region majority break article. | PERSON_0000000620 | Jimmy Franco | Alex Dominguez |
| 23997 | PERSON_0000000997 | 2017-01-02 23:45:53 | Lead simple audience response eye. | PERSON_0000000319 | Christian Christian | David Smith |
| 23998 | PERSON_0000000998 | 2017-01-02 23:29:25 | Vote man son yeah child. | PERSON_0000000487 | David Hernandez | Joshua Park |
| 23999 | PERSON_0000000999 | 2017-01-02 23:12:24 | Answer region wind condition someone bed cover. | PERSON_0000000237 | Tamara Ramirez | Beverly Rodriguez |
+-------+-------------------+---------------------+-----------------------------------------------------+-------------------+---------------------+---------------------+
Điểm mấu chốt của relationship dùng cho câu trích dẫn là giờ đây các trường trong tập dữ liệu tạo ra có liên hệ: một số câu được một số người dùng thường xuyên hơn.
Mẹo: xem toàn bộ đoạn mã trên Github
Không phải bạn làm gì, mà là khi nào bạn làm
Mục đích
Cho đến giờ, mỗi thành viên của quần thể person tạo chính xác một tin nhắn mỗi giờ trong ngày. Hãy làm điều đó thực tế hơn.
Trumania dùng kết hợp 2 khái niệm để kiểm soát khía cạnh thời gian của việc thực thi story: mức độ hoạt động và hồ sơ thời gian (timer profile). Cùng nhau, chúng xác định xác suất kích hoạt story với người dùng và thời điểm cụ thể trong ngày.
Cách thực hiện
Bạn sẽ cập nhật story và tham số hóa hai khía cạnh đó.
Đầu tiên, tạo hồ sơ timer của story, kiểm soát những lúc có nhiều hay ít hành động được kích hoạt. Hồ sơ này giống nhau cho tất cả thành viên quần thể kích hoạt story.
Trumania có sẵn một số hồ sơ mặc định thú vị, như DefaultDailyTimerGenerator, tương ứng với mức sử dụng điện thoại hằng ngày được đo trên một tập dữ liệu sản xuất thực:

Hãy khởi tạo một cái trong kịch bản của bạn:
from trumania.components.time_patterns.profilers import DefaultDailyTimerGenerator
story_timer_gen = DefaultDailyTimerGenerator(
clock=example_circus.clock,
seed=next(example_circus.seeder))
Thứ hai là mức độ hoạt động của thành viên quần thể cho story đó, định nghĩa mỗi người dùng tích cực đến mức nào trong story. Bạn có thể đặt mức khác nhau cho từng người, để một số người kích hoạt hành động đó thường xuyên hơn người khác.
Bạn có thể chỉ định thủ công từng mức nếu muốn, nhưng ở đây ta chỉ dùng một generator ngẫu nhiên sẽ gán mức hoạt động cho mỗi thành viên.
Trước hết, ta định nghĩa ba mức hoạt động trung bình 3, 10 và 20 lần kích hoạt mỗi ngày:
low_activity = story_timer_gen.activity(n=3, per=pd.Timedelta("1 day"))
med_activity = story_timer_gen.activity(n=10, per=pd.Timedelta("1 day"))
high_activity = story_timer_gen.activity(n=20, per=pd.Timedelta("1 day"))
Sau đó, bạn tạo generator mức hoạt động thực tế, cũng là một generator Numpy, lần này dựa trên phương thức choice. Đoạn dưới sẽ gán mức thấp cho 20% quần thể, mức trung bình cho 70% và mức cao cho 10%:
activity_gen = NumpyRandomGenerator(
method="choice",
a=[low_activity, med_activity, high_activity],
p=[.2, .7, .1],
seed=next(example_circus.seeder))
Và giờ bạn có thể dùng hồ sơ timer và generator mức hoạt động này như một phần của story:
hello_world = example_circus.create_story(
name="hello_world",
initiating_population=example_circus.populations["person"],
member_id_field="PERSON_ID",
timer_gen=story_timer_gen,
activity_gen=activity_gen
)
Kết quả
Tập dữ liệu kết quả có lược đồ giống như trước:
+-------+-------------------+---------------------+---------------------------------------------------------+-------------------+---------------------+----------------------+
| | PERSON_ID | TIME | MESSAGE | OTHER_PERSON | EMITTER_NAME | RECEIVER_NAME |
|-------+-------------------+---------------------+---------------------------------------------------------+-------------------+---------------------+----------------------|
| 0 | PERSON_0000000016 | 2017-01-01 00:14:12 | Still try sex sure. | PERSON_0000000739 | Johnny Moore | Adam Barrett |
| 1 | PERSON_0000000063 | 2017-01-01 00:23:51 | Resource magazine wide. | PERSON_0000000511 | Cody Pham | Tina Simmons |
| 2 | PERSON_0000000064 | 2017-01-01 00:37:11 | Heat sure simple letter better forget. | PERSON_0000000044 | Katherine Fleming | Angela Gray |
| 3 | PERSON_0000000076 | 2017-01-01 00:33:12 | Star our conference always place ball. | PERSON_0000000959 | Benjamin Reese II | Barbara Alexander |
| 4 | PERSON_0000000088 | 2017-01-01 00:08:02 | Hot event five left become time commercial. | PERSON_0000000000 | Zachary Cook | Ann Cruz |
| ... |
| 28556 | PERSON_0000000962 | 2017-01-04 00:07:45 | Then include rock rule scientist condition. | PERSON_0000000448 | Johnny Washington | Melissa Adams |
| 28557 | PERSON_0000000980 | 2017-01-04 00:04:44 | Collection full argue interview property pattern never. | PERSON_0000000731 | James Ramirez | David Morrow |
| 28558 | PERSON_0000000983 | 2017-01-04 00:23:21 | High day prepare see. | PERSON_0000000509 | Evan Horne | Robert Sims |
| 28559 | PERSON_0000000988 | 2017-01-04 00:43:56 | Likely south school result case federal seat. | PERSON_0000000819 | Benjamin Campbell | Michelle Jackson DDS |
+-------+-------------------+---------------------+---------------------------------------------------------+-------------------+---------------------+----------------------+
Mẹo: xem đoạn mã trên Github
Phân tích kết quả
Mẫu hình thời gian bạn đã cấu hình trong kịch bản giờ sẽ hiển lộ trong cấu trúc bên trong của tập dữ liệu kết quả. Bạn có thể chứng minh điều đó bằng vài phân tích dữ liệu cơ bản trên resulting_dataset:
- số lượng tin nhắn ở mỗi giờ trong ngày sẽ bám theo phân phối đã chỉ định (lưu ý độ tương đồng với đường cong
DefaultDailyTimerGeneratorở trên):
time_profile = (
resulting_dataset[["MESSAGE", "TIME"]]
.groupby(by=example_5_df.TIME.dt.hour)["MESSAGE"]
.count()
)
time_profile.plot()

- Biểu đồ tần suất số tin nhắn theo người dùng cũng sẽ phản ánh cấu hình của bạn:
- bạn cấu hình
low_activitytrung bình 3 tin/ ngày,med_activitylà 10 vàhigh_activitylà 20 và chạy mô phỏng 5 ngày. Điều này cho ra 3 nhómpersonvới xấp xỉ 15, 50 và 100 tổng tin, tương ứng 3 đỉnh của biểu đồ dưới - bạn đặt xác suất cho các mức hoạt động là .2, .7 và .1, điều này xấp xỉ diện tích tương ứng của mỗi “gò” trên biểu đồ:
- bạn cấu hình
usage_per_user = resulting_dataset[["MESSAGE", "PERSON_ID"]].groupby("PERSON_ID")["MESSAGE"].count()
usage_per_user.plot(kind="hist")

Mạng xã hội
Mục đích
Bạn có thể cải thiện kịch bản hơn nữa bằng cách kết nối mọi người để họ nhắn tin cho bạn bè trong lúc story chạy, thay vì ngẫu nhiên. Điều này sẽ đưa vào rất nhiều cấu trúc cho tập dữ liệu vì giờ đây đồ thị xã hội có thể được khám phá từ log tin nhắn.
Cách thực hiện
Bạn có thể mô hình hóa mạng xã hội bằng một Relationship nữa, cùng khái niệm như bạn dùng ở trên cho câu trích dẫn. Bạn có thể nhớ rằng relationship quotes có trọng số. Trọng số đó xác định câu nào có khả năng cao hay thấp với một người. Bạn có thể làm tương tự ở đây và dùng trọng số để xác định bạn nào có khả năng được gọi cao hơn.
Lưu ý ở đây bạn đang định nghĩa relationship từ person đến person, dù nói chung Trumania cho phép định nghĩa relationship giữa bất kỳ cặp quần thể nào.
Có nhiều cách sinh các cạnh của đồ thị xã hội. Một cách cơ bản và kinh điển là dùng thuật toán Erdos–Renyi. Điều này đặc biệt dễ trong Trumania vì đã được xây sẵn. Bạn chỉ cần đưa nó vào kịch bản để tạo relationship.
Ở đây, bạn sẽ định nghĩa đồ thị xã hội giữa các thành viên của relationship person trong đó mỗi người có 20 bạn. Lưu ý trọng số sẽ tự động được thêm vào relationship với phân phối Pareto.
from trumania.components.social_networks.erdos_renyi import WithErdosRenyi
# self is the circus here, see example code on github for details
self.add_er_social_network_relationship(
self.populations["person"],
relationship_name="friends",
average_degree=20)
Với relationship này, bạn có thể xem lại story một lần nữa. Lần này, OTHER_PERSON sẽ không còn được chọn ngẫu nhiên trong quần thể, mà sẽ được chọn trong số bạn bè của mỗi thành viên person, với xác suất tỉ lệ với trọng số định nghĩa trong mạng xã hội:
hello_world.set_operations(
self.clock.ops.timestamp(named_as="TIME"),
self.populations["person"].get_relationship("quotes")
.ops.select_one(from_field="PERSON_ID",named_as="MESSAGE"),
self.populations["person"]
.get_relationship("friends")
.ops.select_one(from_field="PERSON_ID", named_as="OTHER_PERSON"),
self.populations["person"]
.ops.lookup(id_field="PERSON_ID", select={"NAME": "EMITTER_NAME"}),
self.populations["person"]
.ops.lookup(id_field="OTHER_PERSON", select={"NAME": "RECEIVER_NAME"}),
operations.FieldLogger(log_id="hello_6")
)
Kết quả
Một lần nữa, tập dữ liệu kết quả có lược đồ như trước, dù lần này nó có nhiều cấu trúc hơn nhờ mạng xã hội cơ bản của bạn:
+-------+-------------------+---------------------+----------------------------------------------------+-------------------+-------------------+------------------+
| | PERSON_ID | TIME | MESSAGE | OTHER_PERSON | EMITTER_NAME | RECEIVER_NAME |
|-------+-------------------+---------------------+----------------------------------------------------+-------------------+-------------------+------------------|
| 0 | PERSON_0000000004 | 2017-01-01 00:14:12 | Clearly see sure. | PERSON_0000000321 | Cheryl Decker | Ian White |
| 1 | PERSON_0000000012 | 2017-01-01 00:23:51 | Offer or interview clear structure watch capital. | PERSON_0000000697 | Kelly Green | Ashley Turner |
| 2 | PERSON_0000000018 | 2017-01-01 00:37:11 | Recently race draw thousand around ahead. | PERSON_0000000989 | Laura Stephenson | Hannah James |
| 3 | PERSON_0000000040 | 2017-01-01 00:33:12 | To protect man image power beyond. | PERSON_0000000418 | Jeffrey Miller | Scott Collins |
... |
| 28966 | PERSON_0000000985 | 2017-01-04 00:09:23 | However agreement fear door land hotel. | PERSON_0000000211 | Laura Mason | Joshua Miller |
| 28967 | PERSON_0000000995 | 2017-01-04 00:48:08 | Top heat window quite forward friend somebody. | PERSON_0000000171 | Dawn Kim | Sandra Phillips |
| 28968 | PERSON_0000000999 | 2017-01-04 00:58:46 | Answer region wind condition someone bed cover. | PERSON_0000000252 | Tamara Ramirez | Jordan Collins |
+-------+-------------------+---------------------+----------------------------------------------------+-------------------+-------------------+------------------+
Mẹo: bạn có thể xem đoạn mã trên Github
Phân tích kết quả
Một cách rất sơ khởi để xem mạng xã hội đó là đếm số lượng bạn bè duy nhất được mỗi người gọi. Nếu bạn chạy mô phỏng đủ lâu, điều này sẽ cho bạn cận dưới của số người thực trong mạng xã hội của mỗi người (vì bạn dùng trọng số với phân phối Pareto, nhiều người có bạn rất khó được gọi, nên cần chạy rất lâu để thực sự quan sát mọi người có thể được gọi).
Bạn thực thi kịch bản trong 15 ngày mô phỏng và thu được kết quả này, tập trung quanh và thấp hơn một chút so với bậc trung bình 20 bạn mà bạn đặt trong đồ thị xã hội:
social_group_size = (
resulting_dataset[["PERSON_ID", "OTHER_PERSON"]]
.groupby("PERSON_ID")
.agg(lambda friends_ids: len(friends_ids.unique()))
)
social_group_size.plot(kind="hist")

Vẫn còn nữa!
Vài tính năng của Trumania chưa được mô tả trong bài này:
- có nhiều quần thể trong một kịch bản
- có nhiều story trong một kịch bản
- thêm phụ thuộc xác suất giữa các story (ví dụ: một người gọi lại bạn sau khi được gọi)
- cập nhật trạng thái (ví dụ: giảm số dư tài khoản dựa trên thời lượng cuộc gọi)
- các mảnh kịch bản có thể tái sử dụng như địa lý, nhiều hồ sơ timer hơn, ...
- lưu giữ các mảnh kịch bản
Kết luận
Chúng tôi giới thiệu Trumania như một thư viện tạo dữ liệu dựa trên kịch bản trong Python. Các tập dữ liệu tạo ra có thể dùng cho nhiều mục đích như kiểm thử, học tập và benchmarking.
Chúng tôi giải thích rằng để kiểm thử đúng một ứng dụng hoặc thuật toán, cần các tập dữ liệu tôn trọng một số thuộc tính thống kê kỳ vọng. Chúng tôi minh họa Trumania có thể làm điều đó trong ví dụ tạo một tập dữ liệu “nhật ký tin nhắn” cơ bản, tôn trọng phân phối số tin nhắn theo người dùng, phân phối thời điểm trong ngày cũng như phân phối cuộc gọi trong một đồ thị xã hội.
Chúng tôi cũng hy vọng đã cho thấy tính linh hoạt của các thành phần Trumania cho phép tạo nhiều kịch bản đa dạng.
Một kịch bản Trumania có thể bao gồm các mối quan hệ giữa thực thể và có thể cấu hình bằng các phân phối ngẫu nhiên lý thuyết, phân phối thực nghiệm quan sát từ dữ liệu thực, hoặc thậm chí dữ liệu sản xuất thực (ví dụ: một tập id địa điểm). Chúng tôi đã minh họa điều đó bằng cách tái sử dụng phân phối thống kê của dấu thời gian sử dụng điện thoại quan sát trong dữ liệu thực để cấu hình timer ngẫu nhiên theo thời gian cho story của mình.
Với điều này trong đầu, hãy thoải mái sáng tạo! Hãy đến Github của Trumania và viết kịch bản của riêng bạn ;)
Nếu không, cứ thoải mái liên hệ với Svend Vanderveken hoặc đồng tác giả Milan van der Meer:
- Svend Vanderveken là kỹ sư phần mềm tự do chuyên giải pháp xử lý dữ liệu, chủ yếu với Kafka, Scala, SQL, Python... Theo dõi anh qua blog svend.kelesia.com và Twitter @sv3ndk.
- Milan van der Meer hiện là kỹ sư phần mềm tại Real Impact Analytics. Bạn luôn có thể kết nối với anh trên LinkedIn
Lời tòa soạn: bài viết này được thực hiện phối hợp với Milan van der Meer. Cả hai tác giả đều thuộc đội ngũ Real Impact Analytics, một startup big data sáng tạo của Bỉ, khai thác giá trị trong dữ liệu viễn thông bằng cách "app hóa big data".
Hướng dẫn này sẽ cho bạn thấy vì sao bạn có thể muốn tạo các tập dữ liệu ngẫu nhiên và nên kỳ vọng điều gì từ chúng. Bài viết cũng sẽ hướng dẫn những ví dụ đầu tiên về cách sử dụng Trumania, một thư viện Python tạo dữ liệu.
Để biết thêm thông tin, bạn có thể truy cập GitHub của Trumania!
Vì sao tạo các tập dữ liệu ngẫu nhiên?
Tạo tập dữ liệu ngẫu nhiên hữu ích cho cả kỹ sư dữ liệu và nhà khoa học dữ liệu. Vì sao họ làm việc với dữ liệu tổng hợp và họ lấy dữ liệu đó như thế nào?
Với vai trò kỹ sư dữ liệu, sau khi bạn viết xong ứng dụng xử lý dữ liệu “xịn” mới của mình, bạn sẽ muốn bắt đầu kiểm thử end-to-end và do đó cần dữ liệu đầu vào.
Với vai trò nhà khoa học dữ liệu, bạn được lợi từ việc tạo dữ liệu vì nó cho phép bạn thử nghiệm nhiều cách khám phá tập dữ liệu, thuật toán, kỹ thuật trực quan hóa dữ liệu hoặc kiểm định giả định về hành vi của một phương pháp trên nhiều tập dữ liệu khác nhau do bạn lựa chọn.
Trong cả hai trường hợp, một lựa chọn hấp dẫn là dùng dữ liệu thực. Tuy nhiên, một vấn đề nhỏ là dữ liệu sản xuất thường khó lấy, kể cả một phần, và điều này càng khó hơn với các luật của châu Âu về quyền riêng tư và bảo mật.
Có thể bạn là người may mắn có một bộ dữ liệu sản xuất đầy đủ trên laptop. Dẫu vậy, bạn vẫn muốn có độ đa dạng cao hơn trong tập dữ liệu để kiểm định trước nhiều tình huống như quy mô dân số khác nhau, phân phối dữ liệu khác nhau, các vấn đề chất lượng dữ liệu như thiếu hoặc dữ liệu không hợp lệ...
Nếu muốn đọc sâu hơn về lý do tạo dữ liệu ngẫu nhiên hữu ích, hãy xem 'Vì sao dữ liệu tổng hợp sắp trở thành lợi thế cạnh tranh lớn'.
Tạo dữ liệu ngẫu nhiên dựa trên lược đồ: Cần những mối quan hệ tốt!
Phần này minh họa tạo dữ liệu ngẫu nhiên dựa trên lược đồ và chỉ ra những hạn chế của nó.
Đã có nhiều công cụ tạo tập dữ liệu ngẫu nhiên. Cách tiếp cận phổ biến là tạo dựa trên lược đồ, cho phép bạn định nghĩa một bản thiết kế và dùng nó để sinh ra các thực thể. Khermes và LogSynth là hai ví dụ.
Một ví dụ cấu hình dựa trên lược đồ cho người có thể như sau:
Lược đồ này định nghĩa việc sinh dữ liệu về một người: có tên, tuổi và giới tính. Nó đơn giản, nhanh, nhưng có hạn chế. Hạn chế chính là giả lập mối quan hệ giữa các thực thể khác nhau hoặc theo thời gian.
Ví dụ, làm sao để một số tên có xác suất xuất hiện cao hơn dựa trên giới tính? Hoặc làm sao tạo log hành động người dùng trong đó các hành động có xu hướng xảy ra nhiều hơn vào cuối tuần?
Cách dựa trên lược đồ không dễ giúp ứng dụng mới của bạn tiếp xúc với những đặc thù hay thách thức của thế giới thực như:
Trumania dựa trên kịch bản nhằm khắc phục những điểm này và tạo các tập dữ liệu thực tế hơn. Khi kịch bản diễn tiến, các nhóm đối tượng tương tác với nhau, cập nhật thuộc tính và phát sinh log.
Một ví dụ kịch bản là mọi người gọi cho bạn bè. Khi gọi, họ trả tiền theo phút, hết phút thì phải đến cửa hàng... Tập dữ liệu thu được có thể là log cuộc gọi, log mua sắm và mức tồn kho theo từng giờ. Hành động trong kịch bản có thể ngẫu nhiên hoặc tất định.
Trong Trumania, các tập dữ liệu tạo ra thường là chuỗi thời gian vì chúng là kết quả của việc thực thi kịch bản theo thời gian.
Tạo dữ liệu có liên hệ với Trumania (đến giờ hướng dẫn!)
Trong ví dụ dưới đây, bạn sẽ xây dựng một kịch bản cơ bản nơi nhiều người nhắn tin cho nhau. Tập dữ liệu kết quả sẽ là chuỗi thời gian các tin nhắn được trao đổi.
Chuỗi bước chúng ta sẽ thực hiện như sau:
Tạo một Trumania Circus
Mục đích
Bước đầu tiên là tạo một circus, thế giới nơi tất cả phần tử của kịch bản tồn tại. Chúng ta cũng sẽ tạo một quần thể cơ bản gồm các
persons. Vì nhiều khía cạnh của Trumania là ngẫu nhiên, bước này cũng giới thiệu khái niệmgenerators, dùng để kiểm soát nhiều hành vi ngẫu nhiên.Ở thời điểm này chúng ta chưa thêm kịch bản nào, nên mọi thứ sẽ tĩnh.
Bắt đầu thôi!
Cách thực hiện
Một circus Trumania được tạo đơn giản như sau:
Trong Trumania, mọi yếu tố liên quan đến thời gian được kiểm soát bởi một đồng hồ trung tâm. Phần quan trọng nhất trong đoạn mã trên là
step_duration=pd.Timedelta("1h"), xác định đồng hồ sẽ tăng theo bước 1 giờ.Tiếp theo, bạn thêm quần thể
personvào circus. Một quần thể về bản chất là tập các tác nhân cóidvà một số thuộc tính.Có thể chỉ định thủ công giá trị mỗi thuộc tính, nhưng đa số trường hợp bạn muốn sinh ngẫu nhiên, nên trước tiên hãy định nghĩa vài
generatorscho việc đó:Một generator của Trumania chịu trách nhiệm cung cấp dữ liệu khi phương thức
generate()được gọi. Trong ví dụ trên,id_gensẽ sinh chuỗi nhưPERSON_0001,PERSON_0002, ...age_gensẽ lặp lại việc lấy mẫu từ phân phối chuẩn vàname_gencung cấp tên người ngẫu nhiên.Lưu ý bất kỳ phân phối thống kê từ
numpycũng như bất kỳ nhà cung cấp Faker đều có sẵn trong Trumania, và Trumania có thể dễ dàng mở rộng thêm.Với phần này, bạn có thể thêm một quần thể 1000 người với ID tuần tự và hai thuộc tính ngẫu nhiên cơ bản:
Kết quả
Bạn đã có thể xem các thuộc tính được sinh cho toàn bộ thành viên của quần thể
personbằng cách gọiperson.to_dataframe().Mẹo: nếu bạn muốn xem toàn bộ script cho quần thể người dùng cơ bản này, xem đoạn mã trên Github!
Phát biểu Hello World: Tạo Stories
Mục đích
Hãy làm cho thú vị hơn bằng cách thêm một
story. Một story trong Trumania về cơ bản là một phần kịch bản bao bọc khía cạnh động. Một story được thực thi mỗi khi đồng hồ mô phỏng tiến một bước.Nhớ rằng bạn đã đặt
step_duration=pd.Timedelta("1h")trong circus ở trên, tức là mỗi giờ logic, mỗi story sẽ có cơ hội được thực thi.Bạn sẽ tạo một story hello-world đơn giản trong đó mọi thành viên của quần thể
personsẽ thường xuyên phát đi thông điệp "hello world".Cách thực hiện
Khía cạnh chính đầu tiên khi tạo một
storylà quần thể khởi xướng story. Ở đây sẽ làperson. Khía cạnh thứ hai là định nghĩatimer, xác định khi nào mỗi thành viên kích hoạt hành động đó. Tạm thời, bạn hard-code là 1, tức mọi thành viên sẽ thực thi story ở mỗi bước đồng hồ (chúng ta sẽ xây dựng timer phức tạp hơn ở bước sau):Tới đây, story còn trống nên chưa làm gì: bạn cần thêm một số operations. Các operation có thể ngẫu nhiên hoặc tất định, có thể đọc và cập nhật thuộc tính của bất kỳ quần thể nào hoặc trạng thái khác trong circus, và thậm chí có hiệu ứng phụ.
Hiện tại, hãy thêm hai operation tất định đơn giản và một logger để thực sự có log:
example_circus.clock.ops.timestampsinh dấu thời gian ngẫu nhiên trong khoảng 1 giờ hiện tại vàConstantGeneratorchỉ tạo giá trị cố định hard-code.Kết quả
Chạy circus trong 48 giờ thời gian mô phỏng:
Điều này sẽ tạo tập dữ liệu cơ bản sau, trong đó mỗi trong số 1000 người nói "hello world" 48 lần trong 2 ngày.
Trường đầu tiên của tập dữ liệu kết quả,
PERSON_ID, tương ứng vớimember_id_fieldcủastory. Hai trường còn lại,TIMEvàMESSAGE, tương ứng với hai operation chính bạn đã đưa vào story.Mẹo: xem toàn bộ đoạn mã trên Github
Có người để nói Hello World
Mục đích
Hãy cải thiện bằng cách thêm một người ngẫu nhiên khác vào cuộc trò chuyện. Tạm thời, bạn sẽ cho rằng bất kỳ người nào cũng có thể nói chuyện với bất kỳ người nào khác với xác suất bằng nhau, điều này không thực tế (chúng ta sẽ thêm mạng xã hội ở bước sau). Ngoài ra, bạn sẽ làm giàu tập dữ liệu bằng cách thêm tên người dựa trên ID của họ.
Cách thực hiện
Giả sử người còn lại sẽ được lưu trong trường
OTHER_PERSONmới. Trong trường hợp giản lược này, bạn chỉ muốn điền vào trường đó kết quả của một lựa chọn ngẫu nhiên không trọng số từ quần thểperson. Bạn có thể dùng operation ngẫu nhiênselect_onecủa quần thể đó:Kết quả
Khi bạn chạy lại circus, bạn nhận được tập dữ liệu có trường
OTHER_PERSONmới:Mẹo: bạn có thể xem toàn bộ mã tại đây
Bạn lúc nào cũng nói vậy
Mục đích
Hãy cải thiện tiếp bằng cách tạo một câu ngẫu nhiên trong trường
MESSAGEthay vì chuỗi hard-codehello worldnhàm chán. Điều này sẽ minh họa cách sử dụng cơ bản đầu tiên củaRelationshipcủa Trumania.Bạn sẽ gắn mỗi người với 4 câu trích dẫn. Mỗi khi một người phát đi tin nhắn, nội dung sẽ được chọn trong 4 câu của người đó. Hơn nữa, bạn sẽ gắn mỗi câu với một
weightkhác nhau để xác định mức độ khả dĩ của câu đó: câu có trọng số cao sẽ được người chủ của nó dùng thường xuyên hơn câu có trọng số thấp.Bắt đầu như thế nào?
Cách thực hiện
Bước đầu là tạo một generator câu trích dẫn, tương tự các generator
Fakerkhác ở trên:Bạn có thể dùng generator đó trực tiếp trong
storynhư trước, nhưng hãy làm thú vị hơn bằng cách ràng buộc mỗi người luôn dùng một trong 4 câu ưa thích. Bạn làm điều này bằng cách tạo mộtRelationshipgiữa các thành viên quần thểpersonvà các giá trị câu.Đầu tiên, tạo một relationship rỗng:
Sau đó, điền relationship này với 4 câu cho mỗi người. Trong đoạn mã dưới đây,
person.idslà mộtSeriescủa pandas kích thước 1000 chứa toàn bộ id của quần thểperson, vàquote_generator.generate(size=person.size)trả về mộtSerieskhác cũng kích thước 1000, với các câu ngẫu nhiên.Nghĩa là mỗi vòng lặp dưới đây thêm 1000 quan hệ: thêm một câu cho mỗi trong số 1000 người dùng. 1000 câu đầu sẽ có weight=1, nhóm tiếp theo có weight=2 để chúng xuất hiện thường xuyên hơn, ...
Bây giờ bạn có thể thay
ConstantGeneratortrong story bằng operationselect_onetrên relationship đó. Ý nghĩa: với mỗi giá trị hiện có trong trườngPERSON_ID, tra cứu các relationship trongquotesbắt đầu từ ID đó và chọn ngẫu nhiên một giá trị liên quan. Vì không ghi đè trọng số trong operation dưới, trọng số mặc định đã định trongadd_relationsở trên sẽ được dùng:Kết quả
Khi chạy circus, trường
MESSAGEgiờ sẽ chứa câu trích dẫn ngẫu nhiên:Điểm mấu chốt của relationship dùng cho câu trích dẫn là giờ đây các trường trong tập dữ liệu tạo ra có liên hệ: một số câu được một số người dùng thường xuyên hơn.
Mẹo: xem toàn bộ đoạn mã trên Github
Không phải bạn làm gì, mà là khi nào bạn làm
Mục đích
Cho đến giờ, mỗi thành viên của quần thể
persontạo chính xác một tin nhắn mỗi giờ trong ngày. Hãy làm điều đó thực tế hơn.Trumania dùng kết hợp 2 khái niệm để kiểm soát khía cạnh thời gian của việc thực thi story: mức độ hoạt động và hồ sơ thời gian (timer profile). Cùng nhau, chúng xác định xác suất kích hoạt
storyvới người dùng và thời điểm cụ thể trong ngày.Cách thực hiện
Bạn sẽ cập nhật
storyvà tham số hóa hai khía cạnh đó.Đầu tiên, tạo hồ sơ timer của story, kiểm soát những lúc có nhiều hay ít hành động được kích hoạt. Hồ sơ này giống nhau cho tất cả thành viên quần thể kích hoạt story.
Trumania có sẵn một số hồ sơ mặc định thú vị, như
DefaultDailyTimerGenerator, tương ứng với mức sử dụng điện thoại hằng ngày được đo trên một tập dữ liệu sản xuất thực:Hãy khởi tạo một cái trong kịch bản của bạn:
Thứ hai là mức độ hoạt động của thành viên quần thể cho story đó, định nghĩa mỗi người dùng tích cực đến mức nào trong story. Bạn có thể đặt mức khác nhau cho từng người, để một số người kích hoạt hành động đó thường xuyên hơn người khác.
Bạn có thể chỉ định thủ công từng mức nếu muốn, nhưng ở đây ta chỉ dùng một generator ngẫu nhiên sẽ gán mức hoạt động cho mỗi thành viên.
Trước hết, ta định nghĩa ba mức hoạt động trung bình 3, 10 và 20 lần kích hoạt mỗi ngày:
Sau đó, bạn tạo generator mức hoạt động thực tế, cũng là một generator
Numpy, lần này dựa trên phương thứcchoice. Đoạn dưới sẽ gán mức thấp cho 20% quần thể, mức trung bình cho 70% và mức cao cho 10%:Và giờ bạn có thể dùng hồ sơ timer và generator mức hoạt động này như một phần của story:
Kết quả
Tập dữ liệu kết quả có lược đồ giống như trước:
Mẹo: xem đoạn mã trên Github
Phân tích kết quả
Mẫu hình thời gian bạn đã cấu hình trong kịch bản giờ sẽ hiển lộ trong cấu trúc bên trong của tập dữ liệu kết quả. Bạn có thể chứng minh điều đó bằng vài phân tích dữ liệu cơ bản trên
resulting_dataset:DefaultDailyTimerGeneratorở trên):low_activitytrung bình 3 tin/ ngày,med_activitylà 10 vàhigh_activitylà 20 và chạy mô phỏng 5 ngày. Điều này cho ra 3 nhómpersonvới xấp xỉ 15, 50 và 100 tổng tin, tương ứng 3 đỉnh của biểu đồ dướiMạng xã hội
Mục đích
Bạn có thể cải thiện kịch bản hơn nữa bằng cách kết nối mọi người để họ nhắn tin cho bạn bè trong lúc story chạy, thay vì ngẫu nhiên. Điều này sẽ đưa vào rất nhiều cấu trúc cho tập dữ liệu vì giờ đây đồ thị xã hội có thể được khám phá từ log tin nhắn.
Cách thực hiện
Bạn có thể mô hình hóa mạng xã hội bằng một
Relationshipnữa, cùng khái niệm như bạn dùng ở trên cho câu trích dẫn. Bạn có thể nhớ rằng relationshipquotescó trọng số. Trọng số đó xác định câu nào có khả năng cao hay thấp với một người. Bạn có thể làm tương tự ở đây và dùng trọng số để xác định bạn nào có khả năng được gọi cao hơn.Lưu ý ở đây bạn đang định nghĩa relationship từ
personđếnperson, dù nói chung Trumania cho phép định nghĩa relationship giữa bất kỳ cặp quần thể nào.Có nhiều cách sinh các cạnh của đồ thị xã hội. Một cách cơ bản và kinh điển là dùng thuật toán Erdos–Renyi. Điều này đặc biệt dễ trong Trumania vì đã được xây sẵn. Bạn chỉ cần đưa nó vào kịch bản để tạo relationship.
Ở đây, bạn sẽ định nghĩa đồ thị xã hội giữa các thành viên của relationship
persontrong đó mỗi người có 20 bạn. Lưu ý trọng số sẽ tự động được thêm vào relationship với phân phối Pareto.Với relationship này, bạn có thể xem lại story một lần nữa. Lần này,
OTHER_PERSONsẽ không còn được chọn ngẫu nhiên trong quần thể, mà sẽ được chọn trong số bạn bè của mỗi thành viênperson, với xác suất tỉ lệ với trọng số định nghĩa trong mạng xã hội:Kết quả
Một lần nữa, tập dữ liệu kết quả có lược đồ như trước, dù lần này nó có nhiều cấu trúc hơn nhờ mạng xã hội cơ bản của bạn:
Mẹo: bạn có thể xem đoạn mã trên Github
Phân tích kết quả
Một cách rất sơ khởi để xem mạng xã hội đó là đếm số lượng bạn bè duy nhất được mỗi người gọi. Nếu bạn chạy mô phỏng đủ lâu, điều này sẽ cho bạn cận dưới của số người thực trong mạng xã hội của mỗi người (vì bạn dùng trọng số với phân phối Pareto, nhiều người có bạn rất khó được gọi, nên cần chạy rất lâu để thực sự quan sát mọi người có thể được gọi).
Bạn thực thi kịch bản trong 15 ngày mô phỏng và thu được kết quả này, tập trung quanh và thấp hơn một chút so với bậc trung bình 20 bạn mà bạn đặt trong đồ thị xã hội:
Vẫn còn nữa!
Vài tính năng của Trumania chưa được mô tả trong bài này:
Kết luận
Chúng tôi giới thiệu Trumania như một thư viện tạo dữ liệu dựa trên kịch bản trong python. Các tập dữ liệu tạo ra có thể dùng cho nhiều mục đích như kiểm thử, học tập và benchmarking.
Chúng tôi giải thích rằng để kiểm thử đúng một ứng dụng hoặc thuật toán, cần các tập dữ liệu tôn trọng một số thuộc tính thống kê kỳ vọng. Chúng tôi minh họa Trumania có thể làm điều đó trong ví dụ tạo một tập dữ liệu "nhật ký tin nhắn" cơ bản, tôn trọng phân phối số tin nhắn theo người dùng, phân phối thời điểm trong ngày cũng như phân phối cuộc gọi trong một đồ thị xã hội.
Chúng tôi cũng hy vọng đã cho thấy tính linh hoạt của các thành phần Trumania cho phép tạo nhiều kịch bản đa dạng.
Một kịch bản Trumania có thể bao gồm các mối quan hệ giữa thực thể và có thể cấu hình bằng các phân phối ngẫu nhiên lý thuyết, phân phối thực nghiệm quan sát từ dữ liệu thực, hoặc thậm chí dữ liệu sản xuất thực (ví dụ: một tập id địa điểm). Chúng tôi đã minh họa điều đó bằng cách tái sử dụng phân phối thống kê của dấu thời gian sử dụng điện thoại quan sát trong dữ liệu thực để cấu hình timer ngẫu nhiên theo thời gian cho story của mình.
Với điều này trong đầu, hãy thoải mái sáng tạo! Hãy đến Github của Trumania và viết kịch bản của riêng bạn ;)
Nếu không, cứ thoải mái liên hệ với Svend Vanderveken hoặc đồng tác giả Milan van der Meer: