Vai al contenuto principale

Generare dataset casuali realistici con Trumania

Scopri Python Trumania, una libreria per generare dataset casuali basata su scenari. Impara a generare un dataset sintetico e casuale in questo tutorial passo dopo passo.
ยท 15 min leggi

Scopri con l'IA

Nota dell'editor: questo post รจ stato scritto in collaborazione con Milan van der Meer. Entrambi gli autori di questo post fanno parte del team di Real Impact Analytics, una startup belga innovativa nel big data che cattura il valore nei dati telecom "appificando i big data".

Questo tutorial ti dร  un assaggio del perchรฉ potresti voler generare dataset casuali e cosa aspettarti. Ti guiderร  anche attraverso alcuni primi esempi su come usare Trumania, una libreria Python per la generazione di dati.

Per maggiori informazioni, visita il GitHub di Trumania!

Perchรฉ generare dataset casuali?

Generare dataset casuali รจ utile sia per i data engineer che per i data scientist. Perchรฉ data scientist e data engineer lavorano con dati sintetici e come li ottengono?

Come data engineer, dopo aver scritto la tua nuova fantastica applicazione di data processing, pensi sia il momento di iniziare i test end-to-end e quindi ti serve qualche dato di input.

Come data scientist, puoi trarre vantaggio dalla generazione di dati perchรฉ ti permette di sperimentare vari modi di esplorare dataset, algoritmi, tecniche di visualizzazione dei dati o di validare ipotesi sul comportamento di un metodo su molti dataset diversi a tua scelta.

In entrambi i casi, l'opzione allettante รจ usare dati reali. Un piccolo problema perรฒ รจ che i dati di produzione sono tipicamente difficili da ottenere, anche parzialmente, e non sta diventando piรน facile con le nuove leggi europee su privacy e sicurezza.

Magari sei tra i fortunati e hai un bel set completo di dati di produzione sul tuo laptop. Anche allora, vorresti piรน variabilitร  nel dataset per validare varie situazioni come diverse dimensioni della popolazione, differenti distribuzioni dei dati, vari problemi di qualitร  dei dati come mancanti o non validi...

Per un approfondimento sul perchรฉ generare dataset casuali รจ utile, vai su "Perchรฉ i dati sintetici stanno per diventare un grande vantaggio competitivo".

Generazione di dati casuali basata su schema: servono buone relazioni!

Questa sezione prova a illustrare la generazione di dati casuali basata su schema e a mostrarne i limiti.

Esistono giร  molti strumenti per generare dataset casuali. Un approccio comune tra questi strumenti รจ la generazione basata su schema che ti permette di definire un blueprint e usarlo per generare alcune entitร . Khermes e LogSynth sono due esempi di tali strumenti.

Un esempio di configurazione basata su schema potrebbe includere questo schema-persona:

{
  {
    "field": "Name",
    "class": NameGenerator
  },
  {
    "field": "Age",
    "class": RandomInt(18, 65)
  },
  {
    "field": "Gender",
    "class": RandomPicker(["Male", "Female", "Other"])
  } 
}

Questo schema definisce la generazione di alcuni dati su una persona. Contiene nome, etร  e genere. รˆ semplice, รจ veloce, ma ha dei limiti. Un limite principale รจ simulare relazioni tra entitร  diverse o basate sul tempo.

Per esempio, come rendere alcuni nomi piรน probabili in base al genere? O come creare un log di azioni utente in cui certe azioni sono piรน probabili durante i weekend?

L'approccio basato su schema non rende facile esporre la tua nuova applicazione alle specificitร  o difficoltร  del mondo reale, come:

  • Dataset sbilanciati (difficoltร  con operazioni di tipo group-by);
  • Schemi raggruppati nella rete di entitร . Cioรจ, scoprire gruppi sociali attraverso le interazioni degli utenti (per esempio, telefonate);
  • Profili di attivitร  temporale diversi. Alcune azioni sono piรน probabili in certi momenti del giorno o della settimana;
  • Relazioni causali tra azioni. Cioรจ, molte azioni di acquisto in un negozio che alla fine generano un evento di "out-of-stock".

Trumania si basa su scenari per affrontare queste carenze e generare dataset piรน realistici. Man mano che lo scenario si svolge, varie popolazioni interagiscono tra loro, aggiornano le proprie proprietร  ed emettono log.

Un esempio di scenario potrebbe essere persone che chiamano i loro amici. Quando una persona chiama paga a minuti, quando i minuti si esauriscono deve andare in negozio... Il dataset risultante potrebbe essere il log delle chiamate, il log delle azioni di acquisto e il livello di stock a ogni ora. Le azioni in uno scenario possono essere casuali o deterministiche.

In Trumania, i dataset generati sono tipicamente serie temporali perchรฉ risultano dall'esecuzione di uno scenario che si svolge nel tempo.

Generare dati inter-relati con Trumania (aka รจ ora del tutorial!)

Nell'esempio qui sotto, vedrai come elaborare uno scenario di base in cui varie persone si scambiano messaggi. Il dataset risultante sarร  la serie temporale dei messaggi scambiati tra le persone.

La sequenza di passaggi che seguiremo รจ la seguente:

  • creare un circus Trumania, il mondo all'interno del quale verrร  eseguito lo scenario
  • aggiungere una story "hello world" che produrrร  log basilari hard-coded
  • aggiungere una relationship per assegnare alcune citazioni preferite a ogni persona, che saranno usate per creare il contenuto dei messaggi.
  • parametrizzare i parametri temporali della story per ottenere una distribuzione dei messaggi piรน realistica durante la giornata
  • aggiungere un'altra relationship per definire un social network, cosรฌ che il grafo sociale che emerge dal log dei messaggi sia piรน realistico

Creare un Trumania Circus

Scopo

Il primo passo consiste nel creare un circus, il mondo in cui esisteranno tutti gli elementi dello scenario. Creeremo anche una popolazione di base di persons. Poichรฉ molti aspetti di Trumania sono casuali, questo passaggio introdurrร  anche il concetto di generators, usati per controllare molti comportamenti casuali.

Per ora non aggiungeremo alcuno scenario, quindi tutto sarร  statico.

Iniziamo!

How-to

Un circus di Trumania si crea semplicemente cosรฌ:

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"))

In Trumania, tutti gli elementi legati al tempo sono controllati da un orologio centrale. La parte piรน importante nello snippet di codice sopra รจ step_duration=pd.Timedelta("1h"), che definisce che l'orologio verrร  incrementato a passi di 1 ora.

Successivamente, aggiungi la popolazione person al circus. Una popolazione รจ essenzialmente un insieme di agenti con un id e alcuni attributi.

รˆ possibile specificare manualmente i valori di ogni attributo, ma nella maggior parte dei casi vorrai generarli casualmente, quindi definiamo prima alcuni generators per farlo:

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))

Un generatore di Trumania รจ responsabile di fornire dati quando viene chiamato il suo metodo generate(). Nell'esempio sopra, id_gen genererร  stringhe come PERSON_0001, PERSON_0002, ... age_gen campionerร  ripetutamente dati da una distribuzione normale e name_gen fornirร  nomi di persone casuali.

Nota che qualsiasi distribuzione statistica di numpy cosรฌ come qualsiasi provider di Faker รจ disponibile in Trumania, e Trumania puรฒ essere facilmente estesa con nuove opzioni.

Con questo in atto, puoi aggiungere una popolazione di 1000 persone con ID sequenziali e due attributi casuali di base:

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)

Risultato

Puoi giร  dare un'occhiata agli attributi generati di tutti i membri della popolazione person chiamando 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 |
+-------------------+-----------------+---------+

Consiglio: se vuoi vedere l'intero script per questa popolazione utente di base, dai un'occhiata a questo snippet su Github!

Dichiarazioni Hello World: creare Stories

Scopo

Rendiamo la cosa un po' piรน interessante aggiungendo una story. Una story di Trumania รจ essenzialmente una parte di scenario che incapsula un aspetto dinamico. Una story viene eseguita ogni volta che l'orologio simulato avanza di un passo.

Ricorda che sopra hai impostato step_duration=pd.Timedelta("1h") nel circus, quindi ciรฒ implica che ogni ora logica ogni story avrร  la possibilitร  di essere eseguita.

Creerai una semplice story hello-world in cui tutti i membri della popolazione person sopra emetteranno regolarmente il messaggio "hello world".

How-to

Il primo aspetto principale da specificare quando crei una story รจ la popolazione che la avvia. In questo caso sarร  person. Il secondo aspetto รจ definire il timer, che stabilisce quando ogni membro della popolazione attiva quell'azione. Per ora li imposti hard-coded a 1, il che implica che tutti i membri eseguiranno la story a ogni passo dell'orologio (costruirai timer piรน elaborati in un passaggio successivo):

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)
)

Finora, la story รจ vuota quindi non fa nulla: devi aggiungere alcune operations. Le operations possono essere casuali o deterministiche, possono leggere e aggiornare qualsiasi attributo di popolazione o altro stato nel circus, e persino avere effetti collaterali.

Per ora, aggiungiamo due semplici operazioni deterministiche e un logger per ottenere effettivamente dei 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 genera un timestamp casuale all'interno dell'intervallo di 1 ora corrente e ConstantGenerator produce semplicemente valori costanti hard-coded.

Risultato

Eseguiamo il circus per 48 ore di tempo simulato:

example_circus.run(
    duration=pd.Timedelta("48h"),
    log_output_folder="example_scenario",
    delete_existing_logs=True
)

Questo dovrebbe produrre il seguente dataset di base, in cui ciascuna delle 1000 persone ha detto "hello world" 48 volte in 2 giorni.

Il primo campo del dataset risultante, PERSON_ID, corrisponde a member_id_field della story. Gli altri due campi, TIME e MESSAGE, corrispondono alle due operazioni principali che hai inserito nella 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 |
+-------+-------------------+---------------------+-------------+

Consiglio: guarda l'intero snippet su Github

Qualcuno a cui dire Hello World

Scopo

Miglioriamo un po' aggiungendo un'altra persona casuale nella conversazione. Per ora diremo che ogni persona puรฒ parlare con qualsiasi altra persona con uguale probabilitร , il che non รจ molto realistico (aggiungerai un social network in un passaggio successivo). Inoltre, arricchirai il dataset aggiungendo il nome delle persone in base al loro ID.

How-to

Diciamo che l'altra persona verrร  memorizzata in un nuovo campo OTHER_PERSON. In questo caso semplicistico, vuoi solo inserire in quel campo il risultato di una selezione casuale non pesata dalla popolazione person. Puoi semplicemente usare l'operazione casuale select_one di quella popolazione:

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")
)

Risultato

Quando esegui di nuovo il circus, ottieni un dataset con il nuovo campo OTHER_PERSON:

+-------+-------------------+---------------------+-------------+-------------------+---------------------+--------------------+
|       | 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      |
+-------+-------------------+---------------------+-------------+-------------------+---------------------+--------------------+

Consiglio: puoi vedere l'intero codice qui

Lo dici sempre

Scopo

Miglioriamo ulteriormente generando una frase casuale nel campo MESSAGE invece di una noiosa stringa hard-coded hello world. Questo ti permetterร  di illustrare un primo utilizzo base della Relationship di Trumania.

Assocerai a ogni persona 4 citazioni. Ogni volta che una persona emette un messaggio, il contenuto verrร  scelto tra le 4 citazioni di quella persona. Inoltre, assocerai a ogni citazione un weight diverso che definisce quanto รจ probabile quella citazione: una citazione con peso alto verrร  emessa piรน spesso dal suo proprietario rispetto a una con peso basso.

Come si comincia?

How-to

Il primo passo รจ creare un generatore di citazioni, simile agli altri generatori Faker che hai visto sopra:

quote_generator = FakerGenerator(method="sentence", nb_words=6, variable_nb_words=True,
                                 seed=next(example_circus.seeder))

Potresti usare quel generatore cosรฌ com'รจ nella story come prima, ma rendiamo la cosa un po' piรน interessante vincolando ogni persona a usare sempre una delle sue 4 citazioni preferite. Lo fai creando una Relationship tra i membri della popolazione person e alcuni valori di frasi.

Per prima cosa, crei una relationship vuota:

quotes_rel = example_circus.populations["person"].create_relationship("quotes")

Poi, popoli questa relationship con 4 citazioni per ogni persona. Nel codice sotto person.ids รจ una Series pandas di dimensione 1000 con tutti gli id della popolazione person, e quote_generator.generate(size=person.size) fornisce un'altra Series pandas sempre di dimensione 1000, con citazioni casuali.

Questo significa che ogni passaggio del for-loop qui sotto popola 1000 relazioni nella relationship: aggiunge una citazione a ciascuno dei 1000 utenti. Le prime 1000 citazioni saranno associate a weight=1, le successive saranno rese piรน frequenti associandole a weight=2, ...

for w in range(4):
    quotes_rel.add_relations(
        from_ids=person.ids,
        to_ids=quote_generator.generate(size=person.size),
        weights=w
    )

Ora puoi sostituire il ConstantGenerator nella tua story con un'operazione select_one su quella relationship. Questo si legge cosรฌ: per ogni valore attualmente nel campo PERSON_ID, cerca le relazioni in quotes che partono da quell'ID e seleziona casualmente un valore correlato. Poichรฉ nella seguente operazione non vengono sovrascritti i pesi, vengono usati i pesi di default della relationship definiti sopra in add_relations:

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")
)

Risultato

Quando esegui il circus, il campo MESSAGE dovrebbe ora contenere la citazione casuale:

+-------+-------------------+---------------------+-----------------------------------------------------+-------------------+---------------------+---------------------+
|       | 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   |
+-------+-------------------+---------------------+-----------------------------------------------------+-------------------+---------------------+---------------------+

Il punto della relationship usata per le citazioni รจ che ora i campi del dataset generato sono correlati: alcune frasi sono usate piรน frequentemente da alcuni utenti.

Consiglio: guarda l'intero snippet di codice su Github

Non รจ solo cosa fai, ma quando lo fai

Scopo

Finora, ogni membro della popolazione person produce esattamente un messaggio ogni ora del giorno. Rendiamolo un po' piรน realistico.

Trumania usa una combinazione di 2 concetti per controllare l'aspetto temporale dell'esecuzione di una story: livelli di attivitร  e profili del timer. Insieme, determinano la probabilitร  di attivare la story dato un utente specifico e l'ora del giorno.

How-to

Aggiornerai la story e parametrizzerai questi due aspetti.

Per prima cosa, creiamo il timer profile della story, che controlla i momenti in cui vengono attivate piรน o meno azioni. Questo profilo รจ lo stesso per tutti i membri della popolazione che attivano la story.

Trumania include alcuni profili predefiniti interessanti, come DefaultDailyTimerGenerator, che corrisponde all'uso del telefono giornaliero che abbiamo misurato su un dataset di produzione reale:

Visualizzazione Trumania Default Daily Timer Generator

Istanziamone uno nel tuo scenario:

from trumania.components.time_patterns.profilers import DefaultDailyTimerGenerator

story_timer_gen = DefaultDailyTimerGenerator(
    clock=example_circus.clock, 
    seed=next(example_circus.seeder))

In secondo luogo, ci sono i livelli di attivitร  dei membri della popolazione per quella story, che definiscono quanto รจ attivo ciascun utente all'interno della story. Sei libero di impostare un livello diverso per ogni membro, in modo che alcuni attivino quell'azione piรน o meno spesso di altri.

Potresti specificare ogni livello manualmente se lo desideri, ma qui userai semplicemente un generatore casuale che assegnerร  un livello di attivitร  a ogni membro della popolazione.

Per prima cosa, definiamo tre livelli di attivitร  di 3, 10 e 20 attivazioni al giorno in media:

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"))

Poi, puoi creare il generatore di attivitร  vero e proprio, che sarร  un altro generatore Numpy, stavolta basato sul metodo choice. Quello sotto assegnerร  un'attivitร  bassa al 20% della popolazione, media al 70% e alta al 10%:

activity_gen = NumpyRandomGenerator(
    method="choice", 
    a=[low_activity, med_activity, high_activity],
    p=[.2, .7, .1],
    seed=next(example_circus.seeder))

Ora puoi usare questo profilo di timer e il generatore di attivitร  come parte della tua 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
)

Risultato

Il dataset risultante ha lo stesso schema di prima:

+-------+-------------------+---------------------+---------------------------------------------------------+-------------------+---------------------+----------------------+
|       | 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 |
+-------+-------------------+---------------------+---------------------------------------------------------+-------------------+---------------------+----------------------+

Consiglio: dai un'occhiata allo snippet su Github

Analisi dei risultati

I pattern temporali che hai incluso nella configurazione del tuo scenario dovrebbero ora essere visibili nella struttura interna del dataset risultante. Puoi dimostrarlo facendo un'analisi dati di base sul resulting_dataset risultante:

  • il numero di messaggi a ogni ora del giorno dovrebbe seguire la distribuzione specificata (nota quanto รจ simile alla curva del DefaultDailyTimerGenerator sopra):
time_profile = (
    resulting_dataset[["MESSAGE", "TIME"]]
    .groupby(by=example_5_df.TIME.dt.hour)["MESSAGE"]
    .count()
)
time_profile.plot()

Visualizzazione Trumania Default Daily Timer Generator

  • L'istogramma del numero di messaggi per utente dovrebbe anch'esso rispettare la tua configurazione:
    • hai configurato low_activity a 3 messaggi al giorno in media, med_activity a 10 e high_activity a 20 ed eseguito la simulazione su 5 giorni. Questo dovrebbe produrre 3 gruppi di person con circa 15, 50 e 100 messaggi totali, che corrispondono ai 3 centri dell'istogramma sotto
    • hai configurato le probabilitร  di questi livelli di attivitร  a .2, .7 e .1, e questo di nuovo corrisponde approssimativamente all'area rispettiva di ogni "gobba" dell'istogramma:
usage_per_user = resulting_dataset[["MESSAGE", "PERSON_ID"]].groupby("PERSON_ID")["MESSAGE"].count()
usage_per_user.plot(kind="hist")

Visualizzazione istogramma & grafico della frequenza dei messaggi per utente

Il social network

Scopo

Puoi migliorare ulteriormente lo scenario collegando le persone in modo che inviino messaggi ai loro amici durante l'esecuzione della story, invece che a una persona casuale. Questo introdurrร  molta struttura nel dataset risultante, poichรฉ ora il grafo sociale dovrebbe essere individuabile dal log dei messaggi.

How-to

Puoi modellare il social network ancora con una Relationship, lo stesso concetto usato sopra per le citazioni. Ricorderai che la relationship quotes aveva un peso. Quel peso definiva quale citazione fosse piรน o meno probabile, data una persona. Puoi fare lo stesso qui e usare i pesi per definire quali amici sono piรน o meno probabili da contattare rispetto ad altri.

Nota che qui stai definendo una relationship da person a person, sebbene in generale Trumania permetta di definire relazioni tra qualsiasi coppia di popolazioni.

Ci sono molti modi per generare gli archi di un grafo sociale. Uno semplice e classico รจ affidarsi all'algoritmo di Erdos-Renyi. Questo รจ particolarmente facile in Trumania poichรฉ รจ giร  integrato nel framework. Devi solo iniettarlo nel tuo scenario per creare una relationship.

Qui, definirai un grafo sociale tra i membri della relationship person in cui ogni persona ha 20 amici. Nota che a quella relationship verrร  automaticamente aggiunto un peso con una distribuzione di Pareto.

from trumania.components.social_networks.erdos_renyi import WithErdosRenyi

# self รจ il circus qui, vedi il codice di esempio su github per dettagli
self.add_er_social_network_relationship(
    self.populations["person"],
    relationship_name="friends",
    average_degree=20)

Con questa relationship in atto, puoi rivedere ancora una volta la nostra story. Stavolta, OTHER_PERSON non verrร  piรน scelto a caso tra la popolazione, ma sarร  selezionato tra gli amici di ciascun membro person, con una probabilitร  proporzionale al peso definito nel social network:

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")
)

Risultato

Ancora una volta, il dataset risultante ha lo stesso schema dati di prima, anche se stavolta รจ ancora piรน strutturato grazie al tuo social network di base:

+-------+-------------------+---------------------+----------------------------------------------------+-------------------+-------------------+------------------+
|       | 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   |
+-------+-------------------+---------------------+----------------------------------------------------+-------------------+-------------------+------------------+

Consiglio: puoi dare un'occhiata allo snippet su Github

Analisi dei risultati

Un modo molto grezzo per esplorare quel social network รจ semplicemente contare il numero di amici unici chiamati da ogni persona. Se esegui la simulazione abbastanza a lungo, questo dovrebbe fornirti un limite inferiore del numero effettivo di persone nel social network di ciascuno (poichรฉ usi pesi con distribuzione di Pareto, molte persone hanno amici che sono davvero poco probabili da chiamare, quindi serve che la simulazione sia molto lunga per osservare davvero ogni possibile persona chiamata).

Hai eseguito il nostro scenario per 15 giorni simulati e ottenuto questo, che รจ centrato molto vicino e un po' al di sotto del grado medio di 20 impostato nel grafo sociale:

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")

Istogramma della dimensione osservata del gruppo sociale

C'รจ dell'altro!

Diverse funzionalitร  di Trumania non sono state descritte in questo post:

  • avere diverse popolazioni in uno scenario
  • avere diverse stories in uno scenario
  • aggiungere dipendenze probabilistiche tra stories (es. una persona chiama un amico dopo essere stata chiamata)
  • aggiornamento dello stato (es. decremento dei saldi in base alla durata della chiamata)
  • pezzi di scenario riutilizzabili, come geografie, piรน profili di timer, ...
  • persistenza di parti dello scenario

Conclusione

Abbiamo introdotto Trumania come libreria di generazione dati basata su scenari in Python. I dataset generati possono essere usati per un'ampia gamma di applicazioni come testing, apprendimento e benchmarking.

Abbiamo spiegato che per testare correttamente un'applicazione o un algoritmo, servono dataset che rispettino alcune proprietร  statistiche attese. Abbiamo illustrato che Trumania รจ in grado di farlo in un esempio in cui abbiamo generato un "message log" di base che rispetta una distribuzione del numero di messaggi per utente, una distribuzione delle ore del giorno e una distribuzione delle chiamate su un grafo sociale.

Speriamo di aver anche dimostrato che la flessibilitร  dei componenti di Trumania permette la creazione di un'ampia gamma di scenari.

Uno scenario Trumania puรฒ includere relazioni tra entitร  e puรฒ essere configurato con distribuzioni casuali teoriche, distribuzioni empiriche osservate in un dataset reale o persino dati di produzione reali (es. un dataset di id di location). Lo abbiamo dimostrato riutilizzando una distribuzione statistica dei timestamp di uso del telefono osservata in un dataset reale per configurare il timer casuale del tempo della nostra story.

Con questo in mente, scatena la creativitร ! Vai al Github di Trumania e scrivi il tuo scenario ;)

Altrimenti, contatta pure Svend Vanderveken o il coautore Milan van der Meer:

  • Svend Vanderveken รจ un software engineer freelance con esperienza in soluzioni di data processing, soprattutto in Kafka, Scala, SQL, Python... Seguilo tramite il suo blog svend.kelesia.com e su Twitter @sv3ndk.
  • Milan van der Meer lavora attualmente come software engineer per Real Impact Analytics. Puoi sempre connetterti con lui su LinkedIn

Nota dell'editor: questo post รจ stato scritto in collaborazione con Milan van der Meer. Entrambi gli autori di questo post fanno parte del team di Real Impact Analytics, una startup belga innovativa nel big data che cattura il valore nei dati telecom "appificando i big data".

Questo tutorial ti dร  un assaggio del perchรฉ potresti voler generare dataset casuali e cosa aspettarti. Ti guiderร  anche attraverso alcuni primi esempi su come usare Trumania, una libreria Python per la generazione di dati.

Per maggiori informazioni, visita il GitHub di Trumania!

Perchรฉ generare dataset casuali?

Generare dataset casuali รจ utile sia per i data engineer che per i data scientist. Perchรฉ data scientist e data engineer lavorano con dati sintetici e come li ottengono?

Come data engineer, dopo aver scritto la tua nuova fantastica applicazione di data processing, pensi sia il momento di iniziare i test end-to-end e quindi ti serve qualche dato di input.

Come data scientist, puoi trarre vantaggio dalla generazione di dati perchรฉ ti permette di sperimentare vari modi di esplorare dataset, algoritmi, tecniche di visualizzazione dei dati o di validare ipotesi sul comportamento di un metodo su molti dataset diversi a tua scelta.

In entrambi i casi, l'opzione allettante รจ usare dati reali. Un piccolo problema perรฒ รจ che i dati di produzione sono tipicamente difficili da ottenere, anche parzialmente, e non sta diventando piรน facile con le nuove leggi europee su privacy e sicurezza.

Magari sei tra i fortunati e hai un bel set completo di dati di produzione sul tuo laptop. Anche allora, vorresti piรน variabilitร  nel dataset per validare varie situazioni come diverse dimensioni della popolazione, differenti distribuzioni dei dati, vari problemi di qualitร  dei dati come mancanti o non validi...

Per un approfondimento sul perchรฉ generare dataset casuali รจ utile, vai su "Perchรฉ i dati sintetici stanno per diventare un grande vantaggio competitivo".

Generazione di dati casuali basata su schema: servono buone relazioni!

Questa sezione prova a illustrare la generazione di dati casuali basata su schema e a mostrarne i limiti.

Esistono giร  molti strumenti per generare dataset casuali. Un approccio comune tra questi strumenti รจ la generazione basata su schema che ti permette di definire un blueprint e usarlo per generare alcune entitร . Khermes e LogSynth sono due esempi di tali strumenti.

Un esempio di configurazione basata su schema potrebbe includere questo schema-persona:

{
  {
    "field": "Name",
    "class": NameGenerator
  },
  {
    "field": "Age",
    "class": RandomInt(18, 65)
  },
  {
    "field": "Gender",
    "class": RandomPicker(["Male", "Female", "Other"])
  } 
}

Questo schema definisce la generazione di alcuni dati su una persona. Contiene nome, etร  e genere. รˆ semplice, รจ veloce, ma ha dei limiti. Un limite principale รจ simulare relazioni tra entitร  diverse o basate sul tempo.

Per esempio, come rendere alcuni nomi piรน probabili in base al genere? O come creare un log di azioni utente in cui certe azioni sono piรน probabili durante i weekend?

L'approccio basato su schema non rende facile esporre la tua nuova applicazione alle specificitร  o difficoltร  del mondo reale, come:

  • Dataset sbilanciati (difficoltร  con operazioni di tipo group-by);
  • Schemi raggruppati nella rete di entitร . Cioรจ, scoprire gruppi sociali attraverso le interazioni degli utenti (per esempio, telefonate);
  • Profili di attivitร  temporale diversi. Alcune azioni sono piรน probabili in certi momenti del giorno o della settimana;
  • Relazioni causali tra azioni. Cioรจ, molte azioni di acquisto in un negozio che alla fine generano un evento di "out-of-stock".

Trumania si basa su scenari per affrontare queste carenze e generare dataset piรน realistici. Man mano che lo scenario si svolge, varie popolazioni interagiscono tra loro, aggiornano le proprie proprietร  ed emettono log.

Un esempio di scenario potrebbe essere persone che chiamano i loro amici. Quando una persona chiama paga a minuti, quando i minuti si esauriscono deve andare in negozio... Il dataset risultante potrebbe essere il log delle chiamate, il log delle azioni di acquisto e il livello di stock a ogni ora. Le azioni in uno scenario possono essere casuali o deterministiche.

In Trumania, i dataset generati sono tipicamente serie temporali perchรฉ risultano dall'esecuzione di uno scenario che si svolge nel tempo.

Nell'esempio qui sotto, vedrai come elaborare uno scenario di base in cui varie persone si scambiano messaggi. Il dataset risultante sarร  la serie temporale dei messaggi scambiati tra le persone.

La sequenza di passaggi che seguiremo รจ la seguente:

  • creare un circus Trumania, il mondo all'interno del quale verrร  eseguito lo scenario
  • aggiungere una story "hello world" che produrrร  log basilari hard-coded
  • aggiungere una relationship per assegnare alcune citazioni preferite a ogni persona, che saranno usate per creare il contenuto dei messaggi
  • parametrizzare i parametri temporali della story per ottenere una distribuzione dei messaggi piรน realistica durante la giornata
  • aggiungere un'altra relationship per definire un social network, cosรฌ che il grafo sociale che emerge dal log dei messaggi sia piรน realistico

Creare un Trumania Circus

Scopo

Il primo passo consiste nel creare un circus, il mondo in cui esisteranno tutti gli elementi dello scenario. Creeremo anche una popolazione di base di persons. Poichรฉ molti aspetti di Trumania sono casuali, questo passaggio introdurrร  anche il concetto di generators, usati per controllare molti comportamenti casuali.

Per ora non aggiungeremo alcuno scenario, quindi tutto sarร  statico.

Iniziamo!

How-to

Un circus di Trumania si crea semplicemente cosรฌ:

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"))

In Trumania, tutti gli elementi legati al tempo sono controllati da un orologio centrale. La parte piรน importante nello snippet di codice sopra รจ step_duration=pd.Timedelta("1h"), che definisce che l'orologio verrร  incrementato a passi di 1 ora.

Successivamente, aggiungi la popolazione person al circus. Una popolazione รจ essenzialmente un insieme di agenti con un id e alcuni attributi.

รˆ possibile specificare manualmente i valori di ogni attributo, ma nella maggior parte dei casi vorrai generarli casualmente, quindi definiamo prima alcuni generators per farlo:

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))

Un generatore di Trumania รจ responsabile di fornire dati quando viene chiamato il suo metodo generate(). Nell'esempio sopra, id_gen genererร  stringhe come PERSON_0001, PERSON_0002, ... age_gen campionerร  ripetutamente dati da una distribuzione normale e name_gen fornirร  nomi di persone casuali.

Nota che qualsiasi distribuzione statistica di numpy cosรฌ come qualsiasi provider di Faker รจ disponibile in Trumania, e Trumania puรฒ essere facilmente estesa con nuove opzioni.

Con questo in atto, puoi aggiungere una popolazione di 1000 persone con ID sequenziali e due attributi casuali di base:

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)

Risultato

Puoi giร  dare un'occhiata agli attributi generati di tutti i membri della popolazione person chiamando 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 |
+-------------------+-----------------+---------+

Consiglio: se vuoi vedere l'intero script per questa popolazione utente di base, dai un'occhiata a questo snippet su Github!

Dichiarazioni Hello World: creare Stories

Scopo

Rendiamo la cosa un po' piรน interessante aggiungendo una story. Una story di Trumania รจ essenzialmente una parte di scenario che incapsula un aspetto dinamico. Una story viene eseguita ogni volta che l'orologio simulato avanza di un passo.

Ricorda che sopra hai impostato step_duration=pd.Timedelta("1h") nel circus, quindi ciรฒ implica che ogni ora logica ogni story avrร  la possibilitร  di essere eseguita.

Creerai una semplice story hello-world in cui tutti i membri della popolazione person sopra emetteranno regolarmente il messaggio "hello world".

How-to

Il primo aspetto principale da specificare quando crei una story รจ la popolazione che la avvia. In questo caso sarร  person. Il secondo aspetto รจ definire il timer, che stabilisce quando ogni membro della popolazione attiva quell'azione. Per ora li imposti hard-coded a 1, il che implica che tutti i membri eseguiranno la story a ogni passo dell'orologio (costruirai timer piรน elaborati in un passaggio successivo):

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)
)

Finora, la story รจ vuota quindi non fa nulla: devi aggiungere alcune operations. Le operations possono essere casuali o deterministiche, possono leggere e aggiornare qualsiasi attributo di popolazione o altro stato nel circus, e persino avere effetti collaterali.

Per ora, aggiungiamo due semplici operazioni deterministiche e un logger per ottenere effettivamente dei 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 genera un timestamp casuale all'interno dell'intervallo di 1 ora corrente e ConstantGenerator produce semplicemente valori costanti hard-coded.

Risultato

Eseguiamo il circus per 48 ore di tempo simulato:

example_circus.run(
    duration=pd.Timedelta("48h"),
    log_output_folder="example_scenario",
    delete_existing_logs=True
)

Questo dovrebbe produrre il seguente dataset di base, in cui ciascuna delle 1000 persone ha detto "hello world" 48 volte in 2 giorni.

Il primo campo del dataset risultante, PERSON_ID, corrisponde a member_id_field della story. Gli altri due campi, TIME e MESSAGE, corrispondono alle due operazioni principali che hai inserito nella 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 |
+-------+-------------------+---------------------+-------------+

Consiglio: guarda l'intero snippet su Github

Qualcuno a cui dire Hello World

Scopo

Miglioriamo un po' aggiungendo un'altra persona casuale nella conversazione. Per ora diremo che ogni persona puรฒ parlare con qualsiasi altra persona con uguale probabilitร , il che non รจ molto realistico (aggiungerai un social network in un passaggio successivo). Inoltre, arricchirai il dataset aggiungendo il nome delle persone in base al loro ID.

How-to

Diciamo che l'altra persona verrร  memorizzata in un nuovo campo OTHER_PERSON. In questo caso semplicistico, vuoi solo inserire in quel campo il risultato di una selezione casuale non pesata dalla popolazione person. Puoi semplicemente usare l'operazione casuale select_one di quella popolazione:

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")
)

Risultato

Quando esegui di nuovo il circus, ottieni un dataset con il nuovo campo OTHER_PERSON:

+-------+-------------------+---------------------+-------------+-------------------+---------------------+--------------------+
|       | 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      |
+-------+-------------------+---------------------+-------------+-------------------+---------------------+--------------------+

Consiglio: puoi vedere l'intero codice qui

Lo dici sempre

Scopo

Miglioriamo ulteriormente generando una frase casuale nel campo MESSAGE invece di una noiosa stringa hard-coded hello world. Questo ti permetterร  di illustrare un primo utilizzo base della Relationship di Trumania.

Assocerai a ogni persona 4 citazioni. Ogni volta che una persona emette un messaggio, il contenuto verrร  scelto tra le 4 citazioni di quella persona. Inoltre, assocerai a ogni citazione un weight diverso che definisce quanto รจ probabile quella citazione: una citazione con peso alto verrร  emessa piรน spesso dal suo proprietario rispetto a una con peso basso.

Come si comincia?

How-to

Il primo passo รจ creare un generatore di citazioni, simile agli altri generatori Faker che hai visto sopra:

quote_generator = FakerGenerator(method="sentence", nb_words=6, variable_nb_words=True,
                                 seed=next(example_circus.seeder))

Potresti usare quel generatore cosรฌ com'รจ nella story come prima, ma rendiamo la cosa un po' piรน interessante vincolando ogni persona a usare sempre una delle sue 4 citazioni preferite. Lo fai creando una Relationship tra i membri della popolazione person e alcuni valori di frasi.

Per prima cosa, crei una relationship vuota:

quotes_rel = example_circus.populations["person"].create_relationship("quotes")

Poi, popoli questa relationship con 4 citazioni per ogni persona. Nel codice sotto person.ids รจ una Series pandas di dimensione 1000 con tutti gli id della popolazione person, e quote_generator.generate(size=person.size) fornisce un'altra Series pandas sempre di dimensione 1000, con citazioni casuali.

Questo significa che ogni passaggio del for-loop qui sotto popola 1000 relazioni nella relationship: aggiunge una citazione a ciascuno dei 1000 utenti. Le prime 1000 citazioni saranno associate a weight=1, le successive saranno rese piรน frequenti associandole a weight=2, ...

for w in range(4):
    quotes_rel.add_relations(
        from_ids=person.ids,
        to_ids=quote_generator.generate(size=person.size),
        weights=w
    )

Ora puoi sostituire il ConstantGenerator nella tua story con un'operazione select_one su quella relationship. Questo si legge cosรฌ: per ogni valore attualmente nel campo PERSON_ID, cerca le relazioni in quotes che partono da quell'ID e seleziona casualmente un valore correlato. Poichรฉ nella seguente operazione non vengono sovrascritti i pesi, vengono usati i pesi di default della relationship definiti sopra in add_relations:

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")
)

Risultato

Quando esegui il circus, il campo MESSAGE dovrebbe ora contenere la citazione casuale:

+-------+-------------------+---------------------+-----------------------------------------------------+-------------------+---------------------+---------------------+
|       | 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   |
+-------+-------------------+---------------------+-----------------------------------------------------+-------------------+---------------------+---------------------+

Il punto della relationship usata per le citazioni รจ che ora i campi del dataset generato sono correlati: alcune frasi sono usate piรน frequentemente da alcuni utenti.

Consiglio: guarda l'intero snippet di codice su Github

Non รจ solo cosa fai, ma quando lo fai

Scopo

Finora, ogni membro della popolazione person produce esattamente un messaggio ogni ora del giorno. Rendiamolo un po' piรน realistico.

Trumania usa una combinazione di 2 concetti per controllare l'aspetto temporale dell'esecuzione di una story: livelli di attivitร  e profili del timer. Insieme, determinano la probabilitร  di attivare la story dato un utente specifico e l'ora del giorno.

How-to

Aggiornerai la story e parametrizzerai questi due aspetti.

Per prima cosa, creiamo il timer profile della story, che controlla i momenti in cui vengono attivate piรน o meno azioni. Questo profilo รจ lo stesso per tutti i membri della popolazione che attivano la story.

Trumania include alcuni profili predefiniti interessanti, come DefaultDailyTimerGenerator, che corrisponde all'uso del telefono giornaliero che abbiamo misurato su un dataset di produzione reale:

Visualizzazione Trumania Default Daily Timer Generator

Istanziamone uno nel tuo scenario:

from trumania.components.time_patterns.profilers import DefaultDailyTimerGenerator

story_timer_gen = DefaultDailyTimerGenerator(
    clock=example_circus.clock, 
    seed=next(example_circus.seeder))

In secondo luogo, ci sono i livelli di attivitร  dei membri della popolazione per quella story, che definiscono quanto รจ attivo ciascun utente all'interno della story. Sei libero di impostare un livello diverso per ogni membro, in modo che alcuni attivino quell'azione piรน o meno spesso di altri.

Potresti specificare ogni livello manualmente se lo desideri, ma qui userai semplicemente un generatore casuale che assegnerร  un livello di attivitร  a ogni membro della popolazione.

Per prima cosa, definiamo tre livelli di attivitร  di 3, 10 e 20 attivazioni al giorno in media:

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"))

Poi, puoi creare il generatore di attivitร  vero e proprio, che sarร  un altro generatore Numpy, stavolta basato sul metodo choice. Quello sotto assegnerร  un'attivitร  bassa al 20% della popolazione, media al 70% e alta al 10%:

activity_gen = NumpyRandomGenerator(
    method="choice", 
    a=[low_activity, med_activity, high_activity],
    p=[.2, .7, .1],
    seed=next(example_circus.seeder))

Ora puoi usare questo profilo di timer e il generatore di attivitร  come parte della tua 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
)

Risultato

Il dataset risultante ha lo stesso schema di prima:

+-------+-------------------+---------------------+---------------------------------------------------------+-------------------+---------------------+----------------------+
|       | 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 |
+-------+-------------------+---------------------+---------------------------------------------------------+-------------------+---------------------+----------------------+

Consiglio: dai un'occhiata allo snippet su Github

Analisi dei risultati

I pattern temporali che hai incluso nella configurazione del tuo scenario dovrebbero ora essere visibili nella struttura interna del dataset risultante. Puoi dimostrarlo facendo un'analisi dati di base sul resulting_dataset risultante:

  • il numero di messaggi a ogni ora del giorno dovrebbe seguire la distribuzione specificata (nota quanto รจ simile alla curva del DefaultDailyTimerGenerator sopra):
time_profile = (
    resulting_dataset[["MESSAGE", "TIME"]]
    .groupby(by=example_5_df.TIME.dt.hour)["MESSAGE"]
    .count()
)
time_profile.plot()

Visualizzazione Trumania Default Daily Timer Generator

  • L'istogramma del numero di messaggi per utente dovrebbe anch'esso rispettare la tua configurazione:
    • hai configurato low_activity a 3 messaggi al giorno in media, med_activity a 10 e high_activity a 20 ed eseguito la simulazione su 5 giorni. Questo dovrebbe produrre 3 gruppi di person con circa 15, 50 e 100 messaggi totali, che corrispondono ai 3 centri dell'istogramma sotto
    • hai configurato le probabilitร  di questi livelli di attivitร  a .2, .7 e .1, e questo di nuovo corrisponde approssimativamente all'area rispettiva di ogni "gobba" dell'istogramma:
usage_per_user = resulting_dataset[["MESSAGE", "PERSON_ID"]].groupby("PERSON_ID")["MESSAGE"].count()
usage_per_user.plot(kind="hist")

Visualizzazione istogramma & grafico della frequenza dei messaggi per utente

Il social network

Scopo

Puoi migliorare ulteriormente lo scenario collegando le persone in modo che inviino messaggi ai loro amici durante l'esecuzione della story, invece che a una persona casuale. Questo introdurrร  molta struttura nel dataset risultante, poichรฉ ora il grafo sociale dovrebbe essere individuabile dal log dei messaggi.

How-to

Puoi modellare il social network ancora con una Relationship, lo stesso concetto usato sopra per le citazioni. Ricorderai che la relationship quotes aveva un peso. Quel peso definiva quale citazione fosse piรน o meno probabile, data una persona. Puoi fare lo stesso qui e usare i pesi per definire quali amici sono piรน o meno probabili da contattare rispetto ad altri.

Nota che qui stai definendo una relationship da person a person, sebbene in generale Trumania permetta di definire relazioni tra qualsiasi coppia di popolazioni.

Ci sono molti modi per generare gli archi di un grafo sociale. Uno semplice e classico รจ affidarsi all'algoritmo di Erdos-Renyi. Questo รจ particolarmente facile in Trumania poichรฉ รจ giร  integrato nel framework. Devi solo iniettarlo nel tuo scenario per creare una relationship.

Qui, definirai un grafo sociale tra i membri della relationship person in cui ogni persona ha 20 amici. Nota che a quella relationship verrร  automaticamente aggiunto un peso con una distribuzione di Pareto.

from trumania.components.social_networks.erdos_renyi import WithErdosRenyi

# self รจ il circus qui, vedi il codice di esempio su github per dettagli
self.add_er_social_network_relationship(
    self.populations["person"],
    relationship_name="friends",
    average_degree=20)

Con questa relationship in atto, puoi rivedere ancora una volta la nostra story. Stavolta, OTHER_PERSON non verrร  piรน scelto a caso tra la popolazione, ma sarร  selezionato tra gli amici di ciascun membro person, con una probabilitร  proporzionale al peso definito nel social network:

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")
)

Risultato

Ancora una volta, il dataset risultante ha lo stesso schema dati di prima, anche se stavolta รจ ancora piรน strutturato grazie al tuo social network di base:

+-------+-------------------+---------------------+----------------------------------------------------+-------------------+-------------------+------------------+
|       | 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   |
+-------+-------------------+---------------------+----------------------------------------------------+-------------------+-------------------+------------------+

Consiglio: puoi dare un'occhiata allo snippet su Github

Analisi dei risultati

Un modo molto grezzo per esplorare quel social network รจ semplicemente contare il numero di amici unici chiamati da ogni persona. Se esegui la simulazione abbastanza a lungo, questo dovrebbe fornirti un limite inferiore del numero effettivo di persone nel social network di ciascuno (poichรฉ usi pesi con distribuzione di Pareto, molte persone hanno amici che sono davvero poco probabili da chiamare, quindi serve che la simulazione sia molto lunga per osservare davvero ogni possibile persona chiamata).

Hai eseguito il nostro scenario per 15 giorni simulati e ottenuto questo, che รจ centrato molto vicino e un po' al di sotto del grado medio di 20 impostato nel grafo sociale:

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")

Istogramma della dimensione osservata del gruppo sociale

C'รจ dell'altro!

Diverse funzionalitร  di Trumania non sono state descritte in questo post:

  • avere diverse popolazioni in uno scenario
  • avere diverse stories in uno scenario
  • aggiungere dipendenze probabilistiche tra stories (es. una persona chiama un amico dopo essere stata chiamata)
  • aggiornamento dello stato (es. decremento dei saldi in base alla durata della chiamata)
  • pezzi di scenario riutilizzabili, come geografie, piรน profili di timer, ...
  • persistenza di parti dello scenario

Conclusione

Abbiamo introdotto Trumania come libreria di generazione dati basata su scenari in Python. I dataset generati possono essere usati per un'ampia gamma di applicazioni come testing, apprendimento e benchmarking.

Abbiamo spiegato che per testare correttamente un'applicazione o un algoritmo, servono dataset che rispettino alcune proprietร  statistiche attese. Abbiamo illustrato che Trumania รจ in grado di farlo in un esempio in cui abbiamo generato un "message log" di base che rispetta una distribuzione del numero di messaggi per utente, una distribuzione delle ore del giorno e una distribuzione delle chiamate su un grafo sociale.

Speriamo di aver anche dimostrato che la flessibilitร  dei componenti di Trumania permette la creazione di un'ampia gamma di scenari.

Uno scenario Trumania puรฒ includere relazioni tra entitร  e puรฒ essere configurato con distribuzioni casuali teoriche, distribuzioni empiriche osservate in un dataset reale o persino dati di produzione reali (es. un dataset di id di location). Lo abbiamo dimostrato riutilizzando una distribuzione statistica dei timestamp di uso del telefono osservata in un dataset reale per configurare il timer casuale del tempo della nostra story.

Con questo in mente, scatena la creativitร ! Vai al Github di Trumania e scrivi il tuo scenario ;)

Altrimenti, contatta pure Svend Vanderveken o il coautore Milan van der Meer:

  • Svend Vanderveken รจ un software engineer freelance con esperienza in soluzioni di data processing, soprattutto in Kafka, Scala, SQL, Python... Seguilo tramite il suo blog svend.kelesia.com e su Twitter @sv3ndk.
  • Milan van der Meer lavora attualmente come software engineer per Real Impact Analytics. Puoi sempre connetterti con lui su LinkedIn

Argomenti

Correlato