J'ai un df comme suit qui montre quand une personne a commencé un quart de travail, terminé un quart de travail, le nombre d'heures et la date de travail.
Business Date Time Hour 0 2019-05-24 11:00 1 1 2019-05-24 12:00 0.75 2 2019-05-24 13:00 0.5
Maintenant, ce que j'essaie de faire est de diviser cela en un format horaire, donc je sais combien d'heures ont été utilisées entre 11h00 et 12h00
donc, dans ma tête, pour ce qui précède, je veux mettre 1 heure entre 11 et 12 dans la poubelle pour 11h00 et le reste 0,25 dans la prochaine poubelle de 12
donc je finirais par avec quelque chose comme
Business_Date Number PayTimeStart PayTimeEnd Hours 0 2019-05-24 1 2019-05-24 11:00:00 2019-05-24 12:15:00 1.250 1 2019-05-24 2 2019-05-24 12:30:00 2019-05-24 13:30:00 1.00
3 Réponses :
Une idée est de travailler avec des minutes - utilisez d'abord la compréhension de liste avec aplatissement pour les séries , puis regroupez par heures avec des heures s pour compter par GroupBy.size et dernière division par 60 pour les dernières heures:
df1 = pd.DataFrame([(z, w) for x, y, w in zip(df['Pay Time Start'],
df['Pay Time End'] - pd.Timedelta(60, unit='s'),
df['Location']) for z in pd.date_range(x, y, freq='Min')],
columns=['Date','Location'])
df = (df1.groupby([df1['Date'].dt.date.rename('Business Date'),
df1['Date'].dt.hour.rename('Time'), df1['Location']])
.size() .div(60) .reset_index(name='Hour'))
Si vous avez besoin de regrouper par emplacement ou par identifiant
XXX
Jozi tu es mon héros! Je comprends ce qui se passe dans la 2ème partie du code, mais dans la série, créez-vous un dict pour travailler le temps?
@Datanovice - Mon vrai nom, merci. J'utilise la compréhension de liste pour la résolution de la liste des minutes entre les valeurs de début et de fin et l'aplatissement est pour éviter les listes imbriquées.
Juste une chose supplémentaire à avoir, cela ne fait pas partie de la question d'origine, mais si mon df a une colonne location , comment la garderais-je incluse?
@Datanovice - Pouvez-vous vérifier df1 = pd.DataFrame ([(z, w) for x, y, w in zip (df ['Pay Time Start' '], df [' Pay Time End '] - pd. Timedelta (60, unit = 's'), df ['Number']) for z in pd.date_range (x, y, freq = 'Min')], columns = ['Date', 'Number']) df = (df1.groupby ([df1 ['Date']. dt.date.rename ('Business Date'), df1 ['Date']. dt.hour.rename ('Time'), df1 ['Number'] ]) .size () .div (60) .reset_index (name = 'Heure'))
Une autre idée, similaire à @ jezrael mais fonctionnant avec des secondes pour plus de précision:
2019-05-24 11:00:00 0.998668 2019-05-24 12:00:00 0.750500 2019-05-24 13:00:00 0.500832 Freq: H, dtype: float64
Sortie:
def get_series(a):
s, e, h = a
idx = pd.date_range(s,e, freq='6s')
return pd.Series(h/len(idx), index=idx)
(pd.concat(map(get_series, zip(df.Pay_Time_Start,
df.Pay_Time_End,
df.Hours)))
.resample('H').sum()
)
Une autre idée juste pour votre commodité (et j'aime les questions difficiles) consiste à utiliser melt puis calculant conditionnellement les minutes:
En gros, vous avez deux formules pour vos calculs (Pseudocode):
60 - minutes de df ['Pay Time Start] minutes in df ['Pay Time End] Nous pouvons donc utiliser ces formules pour créer nos nouvelles données:
D'abord, nous fondons notre temps dans une colonne
Date Time Hours 0 2019-05-24 11:00:00 1.00 1 2019-05-24 12:00:00 0.75 2 2019-05-24 13:00:00 0.50
Maintenant, nous calculons le nombre d'heures avec groupby:
daterange = pd.date_range(df['Pay Time Start'].min(), df['Pay Time End'].max(), freq='H')
df_new = pd.DataFrame({'Date':daterange.date,
'Time':daterange.time}, dtype='datetime64[ns]')
df_new['Hours'] = (new.groupby(new['Pay Time Date'].dt.hour)['Minutes'].sum()/60).to_numpy()
Sortie finale
new = df.melt(id_vars=['Business Date', 'Number'],
value_vars=['Pay Time Start', 'Pay Time End'],
var_name='Pay Time Name',
value_name='Pay Time Date').sort_values('Number')
# Apply the formulas noted above
new['Minutes'] = np.where(new['Pay Time Name'].eq('Pay Time Start'),
60 - new['Pay Time Date'].dt.minute,
new['Pay Time Date'].dt.minute)
# Out
Business Date Number Pay Time Name Pay Time Date Minutes
0 2019-05-24 1 Pay Time Start 2019-05-24 11:00:00 60
2 2019-05-24 1 Pay Time End 2019-05-24 12:15:00 15
1 2019-05-24 2 Pay Time Start 2019-05-24 12:30:00 30
3 2019-05-24 2 Pay Time End 2019-05-24 13:30:00 30
Merci pour cela, heureux de ne pas être le seul à avoir trouvé cela difficile! J'aime cette approche, elle est plus dynamique (ce qui signifie que je pourrais l'appliquer à un large éventail de données temporelles)
vous pouvez effectuer des mathématiques sur les dates. obtenir une date qui commence avant 12 et se termine après. créer un objet datetime de la même date mais à 12. l'heure de début de votre nouvel objet pour obtenir le temps avant 12. soustraire votre nouvel objet de l'heure de fin pour obtenir l'heure après 12. vous pouvez charger vos dates de texte dans objet utilisant:
datetime.datetime.strptime ('2019-05-24 11:00:00', '% Y-% m-% d% H:% M:% S')@Nullman c'était mes premières pensées, préformer une somme et mettre le reste dans l'heure suivante mais je n'ai aucune idée de comment faire cela dans le code, d'où ma question:) si vous pouvez me montrer comment je serais éternellement reconnaissant.