Lors du développement de fonctionnalités pour les applications d'IA/ML en temps réel, il est crucial de comprendre les modèles temporels dans vos données, car cela peut révéler des informations précieuses. Cependant, l'expression de requêtes temporelles peut poser des problèmes. Imaginez simplement la possibilité d'analyser sans effort le comportement de vos utilisateurs au fil du temps, d'effectuer des jointures temporelles précises et d'examiner les modèles d'activité entre divers événements, tout en maintenant une gestion intuitive et transparente du temps. C'est là que les chronologies, une abstraction de haut niveau pour travailler avec des données temporelles, peuvent s'avérer inestimables
Dans cet article, nous plongerons profondément dans le monde des chronologies. Nous montrerons comment ils rendent l'expression de requêtes temporelles sur des événements et, surtout, entre des événements, non seulement facile mais aussi intuitive. Cet article est le deuxième d'une série sur
Intuitif Étant donné que les chronologies sont classées dans le temps, il est naturel que les requêtes fonctionnent également dans l'ordre. Au fil du temps, des événements supplémentaires - entrée - se produisent et sont reflétés dans la sortie de la requête. Cette façon de penser les calculs – en tant que progrès dans le temps – est intuitive car elle correspond à la façon dont nous observons les événements.
Les opérations temporelles déclaratives , telles que le fenêtrage et le décalage, sont clairement déclarées lorsque vous travaillez avec des chronologies, car le temps fait partie de l'abstraction.
Composable Chaque opération prend des chronologies et produit des chronologies, ce qui signifie que les opérations peuvent être enchaînées si nécessaire pour produire les résultats escomptés.
Ci-dessous, nous allons disséquer quatre exemples concrets démontrant les avantages des délais. Nous commencerons par une simple requête d'agrégation et aborderons progressivement des fenêtres temporelles plus complexes, des fenêtres dépendantes des données et des jointures temporellement correctes. À la fin, vous devriez avoir une compréhension approfondie de la façon dont les chronologies rendent l'écriture de requêtes temporelles simples aussi simples que SQL, et comment elles nous permettent de répondre aux questions les plus difficiles.
Les chronologies prennent en charge tout ce que vous pouvez faire en SQL , étendues intuitivement pour fonctionner dans le temps. Avant d'examiner certaines nouvelles fonctionnalités pour les requêtes temporelles sophistiquées, examinons quelque chose de simple : une agrégation. Écrire des requêtes simples est facile : en fait, parce que les chronologies sont triées par heure et regroupées par entité, elles peuvent être encore plus faciles qu'en SQL !
Réfléchissez à la question : combien chaque utilisateur a-t-il dépensé ? Lorsque vous réfléchissez à ces événements, il est naturel de traiter les achats dans l'ordre, en mettant à jour le montant dépensé par chaque utilisateur au fil du temps. Le résultat est une somme cumulée produisant une chronologie continue.
La requête correspondante est présentée ci-dessous écrite de deux manières équivalentes. Le premier met l'accent sur la somme appliquée aux achats tandis que le second met l'accent sur la chaîne d'opérations que nous avons composée - "prenez les achats puis appliquez la somme". À partir de maintenant, nous utiliserons ce dernier car il correspond mieux à la façon dont nous avons tendance à penser les délais de traitement.
sum(Purchases.amount)
#OR
Purchases.amount | sum()
Écrire des requêtes temporelles simples avec des chronologies était aussi simple que SQL. Le traitement des événements dans l'ordre est une façon intuitive de fonctionner dans le temps. Bien sûr, l'agrégation de tous les événements n'est qu'une des façons dont nous pouvons souhaiter agréger les choses. Dans l'exemple suivant, nous verrons comment étendre cette requête en utilisant des fenêtres temporelles pour se concentrer sur les événements récents.
Lorsque l'on pense aux requêtes temporelles, il est très naturel de poser des questions sur le passé récent : depuis le début de l'année ou les 30 derniers jours. L'intuition de traiter les événements dans l'ordre suggère de répondre à la question "Combien chaque utilisateur a-t-il dépensé ce mois-ci" devrait simplement nécessiter de réinitialiser la valeur au début de chaque mois. Et cette intuition est exactement la façon dont ces types de fenêtres temporelles fonctionnent avec les lignes de temps.
La requête temporelle est illustrée ci-dessous. Cela indique clairement l'intention que nous avons exprimée ci-dessus - prendre les achats et les regrouper depuis le début de chaque mois.
Purchases.amount
| sum(window=since(monthly()))
Étant donné que le temps fait intrinsèquement partie de chaque chronologie, chaque agrégation est capable de fonctionner dans des fenêtres temporelles. Dans l'exemple suivant, nous verrons à quel point il est facile de travailler avec des requêtes plus complexes, y compris des agrégations avec des fenêtres plus sophistiquées.
Toutes les fenêtres ne sont pas définies en termes de temps. Il est souvent utile d'utiliser des événements pour déterminer les fenêtres utilisées pour l'agrégation. De plus, alors que tous les exemples jusqu'à présent ont fonctionné sur un seul type d'événement - purchases
- l'examen des modèles d'activité entre différents événements est essentiel pour identifier les relations de cause à effet.
Dans cet exemple, nous tirerons parti des chronologies pour exprimer de manière déclarative des requêtes à l'aide de fenêtres définies par les données et de plusieurs types d'événements. Nous filtrerons également une chronologie intermédiaire sur des points spécifiques pour contrôler les valeurs utilisées dans les étapes ultérieures lors de la composition de la requête.
La question à laquelle nous répondrons est "Quel est le nombre moyen de pages vues entre chaque achat pour chaque utilisateur ?" Nous allons d'abord calculer les pages vues depuis le dernier achat, les observer au moment de chaque achat, puis faire la moyenne.
La première chose que nous allons faire est de calculer le nombre de pages vues depuis le dernier achat. Dans l'exemple précédent, nous avons fenêtré depuis le début du mois. Mais il n'y a rien de spécial dans la chronologie définissant le début d'un mois - nous pouvons fenêtrer avec n'importe quelle autre chronologie.
PageViews
| count(window=since(is_valid(Purchases)))
En plus du fenêtrage défini par les données, nous voyons comment travailler avec plusieurs types d'événements. Étant donné que chaque chronologie est classée par heure et regroupée par entité, chaque chronologie peut être alignée par heure et jointe par entité - automatiquement.
L'étape précédente nous donnait les pages vues depuis le dernier achat. Mais c'était une chronologie continue qui augmentait à chaque page vue jusqu'au prochain achat. Nous recherchons une chronologie discrète avec une valeur unique au moment de chaque achat représentant les pages vues depuis l'achat précédent. Pour ce faire, nous utilisons l'opération when
, qui permet d'observer - et d'interpoler si nécessaire - une chronologie à des moments précis.
L'opération when peut être utilisée n'importe où dans une requête temporelle et permet de filtrer les points, qui sont présents dans la sortie - ou transmis à des agrégations ultérieures.
Avec le nombre de pages vues entre les achats calculés, nous sommes maintenant en mesure de calculer la moyenne de cette valeur. Tout ce que nous avons à faire est d'utiliser l'agrégation mean
.
La requête complète est présentée ci-dessous. Nous voyons que les étapes correspondent aux étapes logiques dont nous avons parlé ci-dessus. Même si la logique était raisonnablement complexe, la requête est relativement simple et capture notre idée de ce que nous voulons calculer - des questions difficiles sont possibles.
PageViews
| count(window=since(is_valid(Purchases)))
| when(is_valid(Purchases))
| mean()
Ce type de requête peut être généralisé pour analyser une variété de modèles dans l'activité de consultation de page. Peut-être que nous voulons seulement regarder les pages vues pour l'élément le plus fréquemment consulté plutôt que tous les éléments, en pensant que l'utilisateur se concentre davantage sur cet élément. Peut-être voulons-nous afficher depuis les achats du même article, plutôt que n'importe quel achat.
Cette requête a montré certaines façons dont les chronologies permettent d'exprimer des requêtes temporelles complexes :
L'ordre permet aux fenêtres d'être définies par leurs délimiteurs - quand elles commencent et se terminent - plutôt que d'avoir à calculer un "ID de fenêtre" à partir de chaque valeur pour le regroupement.
La commande permet également d'utiliser plusieurs chronologies dans la même expression - dans ce cas, pageviews
et purchases
.
La continuité permet d'interpoler les valeurs à des moments arbitraires et de les filtrer à l'aide de l'opération when
.
La composabilité permet d'utiliser le résultat de n'importe quelle opération avec des opérations ultérieures pour exprimer des questions temporelles. Cela permet d'exprimer des questions complexes sous la forme d'une séquence d'opérations simples.
Ces capacités permettent d'identifier les modèles de cause à effet. Même s'il se peut que l'achat me pousse maintenant à faire des achats plus tard, d'autres événements ont souvent une relation plus forte - par exemple, manquer de bande et en acheter plus, ou programmer un voyage de camping et faire le plein. Il est important de pouvoir consulter l'activité ( pageviews
) dans une fenêtre définie par d'autres événements ( purchases
) pour comprendre la relation entre ces événements.
Nous avons déjà vu comment les chronologies permettent de travailler avec plusieurs types d'événements associés à la même entité. Mais il est souvent nécessaire de travailler également avec plusieurs entités. Par exemple, utiliser des informations sur l'ensemble de la population pour normaliser les valeurs de chaque utilisateur. Notre dernier exemple montrera comment travailler avec plusieurs entités et effectuer une jointure temporelle.
La dernière question à laquelle nous répondrons est "Quel est l'avis moyen minimum sur le produit (score) au moment de chaque achat ?" Pour ce faire, nous allons d'abord travailler avec les avis associés à chaque produit pour calculer la note moyenne, puis nous joindrons chaque achat à l'avis moyen correspondant.
Pour commencer, nous souhaitons calculer l'évaluation moyenne des produits (score) pour chaque article. Étant donné que les avis sont actuellement regroupés par utilisateur, nous devrons les regrouper par élément, en utilisant l'opération avec clé. Une fois que nous avons fait cela, nous pouvons utiliser l'agrégation moyenne que nous avons déjà vue.
Pour chaque achat (regroupé par utilisateur), nous souhaitons rechercher la note d'évaluation moyenne de l'article correspondant. Cela utilise l'opération lookup
.
En mettant tout cela ensemble, nous utilisons la recherche avec une agrégation min
pour déterminer la note moyenne minimale des articles achetés par chaque utilisateur.
Reviews.score
| with_key(Reviews.item)
| mean()
| lookup(Purchases.item)
| min()
Ce modèle de regroupement dans une entité différente, d'exécution d'une agrégation et de recherche de la valeur (ou de retour aux entités d'origine) est courant dans les tâches de traitement de données. Dans ce cas, la valeur résultante a été recherchée et utilisée directement. Dans d'autres cas, il est utile pour la normalisation, par exemple pour relier la valeur de chaque utilisateur aux valeurs moyennes de sa ville.
L'ordre et le regroupement permettent aux chronologies d'exprimer clairement les opérations entre différentes entités. Le résultat d'une recherche provient du point dans le temps auquel la recherche est effectuée. Cela fournit une jointure « à partir de » temporellement correcte.
L'exécution d'une jointure au bon moment est essentielle pour calculer des exemples de formation du passé qui sont comparables aux valeurs de caractéristiques utilisées lors de l'application du modèle. De même, il garantit que tout tableau de bord, visualisation ou analyse effectué sur les résultats de la requête est réellement correct comme s'il examinait les valeurs dans le passé, plutôt que d'utiliser des informations qui n'étaient pas disponibles à ce moment-là.
Nous avons démontré la puissance des chronologies en tant qu'abstraction de haut niveau pour la gestion des données temporelles. Grâce à des opérations intuitives, déclaratives et composables, nous avons montré comment les chronologies permettent une expression efficace des requêtes temporelles sur les événements et entre les événements. Avec des exemples allant de simples agrégations à des requêtes sophistiquées telles que des fenêtres dépendantes des données et des jointures temporellement correctes, nous avons illustré comment les opérations de chronologie peuvent être enchaînées pour produire les résultats escomptés. La puissance des chronologies réside dans leur capacité à exprimer facilement des questions temporelles simples et à s'étendre intuitivement à des requêtes temporelles complexes.
Des dépenses totales à la note d'évaluation minimale, nous avons parcouru quatre exemples illustratifs qui mettent en évidence les capacités des chronologies dans l'interrogation temporelle. Nous avons exploré l'agrégation cumulative, le fenêtrage temporel et observé comment le fenêtrage défini par les données offre la possibilité d'exprimer des questions temporelles complexes. Nous avons également montré comment les chronologies facilitent la gestion multi-entités et les jointures temporelles. Ces exemples montrent qu'avec les chronologies, vous disposez d'un outil puissant pour identifier les modèles de cause à effet et calculer des exemples de formation qui sont comparablement valides lors de l'application d'un modèle.
À venir, nous approfondirons la dynamique des requêtes temporelles sur les chronologies. Nous allons explorer comment ces requêtes sont exécutées efficacement en tirant parti des propriétés des chronologies.\
Nous vous encourageons à
Également publié ici.