Преобразуйте три отдельных столбца в один столбец даты в панде. - PullRequest
2 голосов
/ 01 октября 2019

У меня есть три столбца в кадре данных pandas, которые я хочу преобразовать в один столбец даты. Проблема в том, что один из столбцов является дневным столбцом. Я не могу преобразовать в точную дату того месяца и года. Может кто-нибудь, пожалуйста, помогите мне решить эту проблему. Это выглядит примерно так:

   BirthMonth BirthYear Day
0   5           88      1st Monday
1   10          87      3rd Tuesday
2   12          87      2nd Saturday
3   1           88      1st Tuesday
4   2           88      1st Monday

Ответы [ 5 ]

2 голосов
/ 01 октября 2019

На основании вашего ответа на мой первый комментарий я обновил свой ответ следующим образом. Я думаю, это то, что вы ищете:

import re
import time
import calendar
import numpy as np


days = ['1st Monday', '3rd Tuesday', '4th wednesday']
months = [2, 3, 5]
years = [1990, 2000, 2019]

def extract_numeric(text: str):
    return int(re.findall(r'\d+', text)[0])

def weekday_to_number(weekday: str):
    return time.strptime(weekday, "%A").tm_wday

def get_date(number: int, weekday: int, month: int, year: int) -> str:
    """ 3rd Tuesday translates to number: 3, weekday: 1 """
    firstday, n_days = calendar.monthrange(year, month)
    day_list = list(range(7)) * 6
    month_days = day_list[firstday:][:n_days]
    day = (np.where(np.array(month_days) == weekday)[0] + 1)[number - 1]
    return '{}/{}/{}'.format(day, month, year)

numbers = []
weekdays = []
for day in days:
    number, weekday = day.split()
    numbers.append(extract_numeric(number))
    weekdays.append(weekday_to_number(weekday))

dates = []
for number, weekday, month, year in zip(numbers, weekdays, months, years):
    dates.append(get_date(number, weekday, month, year))

print(dates)  # ['5/2/1990', '21/3/2000', '22/5/2019']

1 голос
/ 01 октября 2019

используйте модуль календаря, чтобы получить day из дней. затем преобразуйте day,monyh,year в DateTime

import calendar
import datetime
def get_date(rows):
    day = {'monday':0,'tuesday':1,'wednesday':2,'thursday':3,'friday':4,'saturday':5,'sunday':6}
    day_num = day.get(rows.days.split()[1].lower())
    weekday_num = [week[day_num] for week in calendar.monthcalendar(rows.years, rows.months) if week[day_num] >0][int(rows.days.split()[0][0])-1]
    return datetime.date(rows.years, rows.months, weekday_num)

и примените вышеуказанную функцию ко всем строкам

df['date'] = df(lambda row: get_date(row), axis=1)
df
>>
          days   months  years  date
0   1st Monday        8  2015   2015-08-03
1   3rd Tuesday      12  2017   2017-12-19
2   4th wednesday     5  2019   2019-05-22
0 голосов
/ 01 октября 2019

Не очень быстрое решение (поскольку оно включает 2 вложенных цикла), но я надеюсь, что это решит ваш вопрос

import pandas as pd
import datetime
import calendar

pd.set_option('display.max_rows', 100)

cols = ['day', 'month', 'year']

data = [
    ['1st Monday', 8, 2015],
    ['3rd Tuesday', 12, 2017],
    ['4th Wednesday', 5, 2019]
]

df = pd.DataFrame(data=data, columns=cols)
df['week_number'] = df['day'].str.slice(0, 1)
df['week_number'] = df['week_number'].astype('int')
df['day_name'] = df['day'].str.slice(4)


def generate_dates(input_df, index_num):
    _, days = calendar.monthrange(input_df.loc[index_num, 'year'], input_df.loc[index_num, 'month'])
    df_dates = pd.DataFrame()
    for i in range(1, days + 1):
        df_dates.loc[i - 1, 'date'] = datetime.date(input_df.loc[index_num, 'year'], input_df.loc[index_num, 'month'],
                                                    i)
        df_dates.loc[i - 1, 'year'] = input_df.loc[index_num, 'year']
        df_dates.loc[i - 1, 'days'] = calendar.weekday(input_df.loc[index_num, 'year'],
                                                       input_df.loc[index_num, 'month'], i)
        df_dates.loc[i - 1, 'day_name'] = df_dates.loc[i - 1, 'date'].strftime("%A")

    df_dates['week_number'] = 1
    df_dates['week_number'] = df_dates.groupby('day_name')['week_number'].cumsum()
    return df_dates


dates = pd.DataFrame(columns=['date', 'year', 'days', 'day_name', 'week_number'])
for row in df.index:
    dates = pd.concat([dates, generate_dates(df, row)])

df2 = df.merge(dates, on=['year', 'day_name', 'week_number'])

print(df2)


0 голосов
/ 01 октября 2019

Редактировать , чтобы соответствовать такому новому фрейму данных

Мое решение, использующее функцию pandas dayofweek:

import numpy as np
import pandas as pd
from datetime import date
from dateutil.relativedelta import relativedelta

#generate dataframe
df=pd.DataFrame({'BirthMonth':[5, 10, 12, 1 ,2],
         'BirthYear':[88, 87, 87, 88, 88],
         'Day':['1st Monday', '3rd Tuesday', '2nd Saturday','1st Tuesday','1st Monday']})
#Assuming the year refers to 19xx
df.BirthYear=1900+df.BirthYear
#list of day names
weekday=['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']

#Identify day name in input df
days_ex=[s.split()[1].title() for s in df.Day]

#initialize output list
dateout= ["" for x in range(len(days_ex))]
for j in range(len(days_ex)):
    #Identify the day number in the week (Monday is 1, Sunday is 7)
    daynum=np.nonzero(np.char.rfind(weekday,days_ex[j])==0)[0][0]

    #create start and end date for the month
    date_start=date(df.BirthYear[j],df.BirthMonth[j],1)
    date_end=date_start+relativedelta(months=+1)

    #daily index range within month of interest
    idx=pd.date_range(date_start,date_end,freq='d').dayofweek

    # Find matching date based on input df
    realday=np.where(idx==daynum)[0][int(df.Day[j][0])-1]+1
    #output list
    dateout[j]=str(realday)+'/'+str(df.BirthMonth[j])+'/'+str(df.BirthYear[j])

полученный результат:

['2/5/1988', '20/10/1987', '12/12/1987', '5/1/1988', '1/2/1988']
0 голосов
/ 01 октября 2019

Проверьте отредактированный @ArnoMaeck, поскольку это правильный ответ

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...