Comment modéliser des dates partielles en Python? Comme une année inconnue ou un jour inconnu du mois?

11

Je veux pouvoir saisir des faits comme Bob was born in 2000et Bill's birthday is May 7th.

Dans les deux exemples, nous ne connaissons qu'une partie de la date de naissance de la personne. Dans un cas, nous ne connaissons que l'année; dans l'autre cas, nous connaissons le mois et le jour, mais pas l'année.

Comment capturer ces informations?

Quelques exemples de la façon dont cela pourrait fonctionner:

Imaginez une bibliothèque comme datetime qui permettait à None dans les champs de représenter des inconnues. Je pourrais avoir un code comme celui-ci:

date_a = date(2000, 5, None)
date_b = date(2000, 6, None)
difference = date_b - date_a
assert difference.min.days == 1
assert difference.max.days == 60  # Or something close to 60.
assert equal(date_a, date_b) == False

date_c = date(2000, 5, None)
assert equal(date_a, date_c) == Maybe

Ceci est juste un exemple de la façon dont il pourrait se comporter. Je ne veux pas nécessairement ce comportement précis.

Boutons840
la source
En général, la façon dont vous traitez des choses comme celle-ci consiste à utiliser, par exemple, l'année 0001 dans .NET pour les dates qui n'ont pas d'année et le 1er janvier pour les années sans mois ni jour.
Robert Harvey
J'ai modifié votre question pour supprimer les demandes de bibliothèque. Ces questions sont hors sujet sur ce site.
@RobertHarvey Je ne peux pas utiliser votre suggestion. Si nous voyons que Bob est né le 1er janvier 2000, nous ne savons pas exactement ce que cela signifie. Nous ne pouvons pas dire s'il est né le premier jour de 2000 ou s'il est né un jour de 2000. Nous devons connaître la différence.
Buttons840
@RobertHarvey Je sais que c'est courant, mais j'ai vu de nombreux échecs à cause de mauvais choix de telles valeurs de signal. (De plus, je ne pense pas que cela réponde à la question car l'OP doit traiter uniquement certaines dates inconnues. Le réglage au 1er janvier dans de tels cas ne vous permet pas de différencier les vraies dates du 1er janvier des inconnues.
Gort le robot
5
@ Buttons840: Ensuite, vous devrez écrire une classe qui encapsule les comportements que vous souhaitez. Vous devez encapsuler la classe de date existante et ajouter les comportements souhaités.
Robert Harvey

Réponses:

3

Tout d'abord, une fois que vous avez commencé à décomposer les dates en leurs composants, elles ne sont plus des dates.

De la même manière qu'il n'est pas possible de supprimer des fonctionnalités via des sous-classes sans casser la POO, il n'est pas possible de mélanger les dates et les fractions de date sans causer de confusion (ou pire) en les rendant compatibles comme dans votre exemple de code sans casser autre chose.

Si vous voulez capturer une année, quel est le problème avec un objet contenant un simple entier? Si vous voulez capturer un mois et un jour, pourquoi ne pas capturer un dénombrement d'un mois et un jour entier? Peut-être même les stocker en interne dans un objet date afin que vous obteniez une vérification des limites appropriées (par exemple, le 31 février n'a aucun sens). Exposez cependant une interface différente.

Pourquoi voudriez-vous comparer une date à une année pour voir si elles sont identiques, supérieures ou inférieures? Cela n'a aucun sens: il n'y a pas suffisamment d'informations pour faire cette comparaison. Cependant, il existe d'autres comparaisons qui pourraient avoir un sens (c'est un pseudocode):

Year y = Year(2015)
Date d = Date(2015, 01, 01)
assert y.contains(d) == True

la source
2

Le deuxième commentaire de Robert Harvey contient la bonne réponse, mais permettez-moi de développer un peu.

L'année de naissance et la date de naissance des personnes sont des entités complètement différentes, vous n'avez donc pas besoin (et en fait vous ne devriez pas) d'utiliser le même mécanisme pour les deux.

Pour les dates de naissance, vous pouvez concevoir un BirthDatetype de données (ou peut-être YearlyRecurringDatemême si je ne peux pas trouver un nom décent pour l'instant) qui ne contiendrait qu'un dateavec une année constante, comme 2000 par convention. L'an 2000 est un bon choix car il a fait un bond en avant, il ne manquera donc pas aux personnes dont l'anniversaire est le 28 février.

Pour les années de naissance, vous pouvez concevoir un BirthYeartype de données (ou peut - être un ApproximateDatetype de données) qui contiendrait un date, et un indicateur de la précision: Year, Month, Full.

L'avantage de ces approches est qu'au cœur des choses, vous maintenez toujours un dateafin que vous puissiez toujours effectuer l'arithmétique des dates.

Mike Nakis
la source
1

Je crois que ce que vous décrivez serait un remplacement direct du datetimemodule qui implémente les datetime.datetimeattributs (année, mois, etc.) en tant que valeurs avec une mesure d'incertitude (plutôt que de simples valeurs).

Les packages Python existent pour aider avec les nombres incertains (par exemple: le package incertitudes ), et il ne serait peut-être pas trop difficile de créer un fork datetimequi utilise l'incertitude sur chaque attribut. Moi aussi, j'aimerais en voir un et j'aurais même pu l'utiliser. Un argument pourrait certainement être avancé en faveur de l'inclusion de a udatetimedans le paquet des incertitudes liées ci-dessus.

Vos exemples seraient quelque chose comme:

bob_bday = udatetime(2000, (6,6))  # 2000-06 +/- 6mo
>>> 2000-??-?? T??:??:??
bil_bday = udatetime((1970, 50), 3, 7)  # assume bill is ~40 +/- 40 
>>> [1970+/-40]-03-07 T??:??:??

Les "valeurs de signal" posent de nombreux problèmes, mais en plus, vous pouvez représenter des choses avec une incertitude que les valeurs de signal ne peuvent pas:

# ali was born in spring
ali_bday = udatetime((), (4.5, 1.5))
>>> [1970+/-40]-[4.5+/-1.5]-?? T??:??:??

Une autre considération est que pour être plus précis, les incertitudes ici devraient en fait être de type timedelta. Je laisse comme un exercice pour le lecteur de trouver un constructeur concis et complet pour udatetimeutiliser les timedeltaincertitudes.

Donc, en fin de compte, je dirais que ce que vous décrivez est "facilement" modélisé avec des incertitudes, mais la mise en œuvre d'un udatetimeest pratiquement assez difficile. La plupart prendront la route «facile» et diviseront le datetime en composants et suivront l'incertitude sur eux indépendamment, mais si vous vous sentez ambitieux, le uncertaintiespackage (ou un autre) pourrait être intéressé par une demande d'extraction udatetime.

7yl4r
la source
0

Pourquoi ne pas créer une classe "period" qui implémente une structure from to.

"Bob est né en 2000" ->

period {
   from  {
      yy = 2000;
      mm = 01;
      dd = 01; 
   }
   to {
     yy = 2000;
     mm = 12;
     dd = 31;
   }
   fuzz = 365;
}

Vous pouvez ensuite implémenter diverses méthodes de recherche telles que la mise entre crochets des dates du au. L'attribut fuzz donne une indication utile de la précision de la date afin que vous puissiez spécifier fuzz == 1 pour les correspondances exactes, ou fuzz == 31 pour un mois environ.

James Anderson
la source