Преобразование лет и месяцев в месяцы с помощью ввода строки в столбце python

#python #pandas #numpy #feature-extraction #feature-engineering

#python #панды #тупой #функция-извлечение #разработка функций

Вопрос:

пример набора данных:

 experience

5 month
nan
1 months
8 month
17 months
8 year
11 years
1.7 year
3.1 years
15.7 months
18 year
2017.2 years
98.3 years
68 year
 

У меня есть столбец с опытом заявителя в годах. Это очень грязно, и я попытался пройти через это и создать образец. У меня есть цифры, за которыми следуют (месяц или месяцы) и (год лет).

Существует много записей nan, и их следует игнорировать.

Цель состоит в том, чтобы создать опыт работы с колонками за несколько месяцев:

 if nan
  copy nan to the corresponding column
if the row has month or months 
  copy the number to the corresponding column
if year or years in the row and the number <55 
  the number shall be multiplied by 12 and copied to the corresponding column
else copy nan to the corresponding column
 

Как этого добиться?

Ответ №1:

 my_dict = {'Experience': ['5 month', 'nan', '1 months', '8 month','17 months','8 year',
                          '11 years','1.7 year', '3.1 years', '15.7 months','18 year',
                          '2017.2 years', '98.3 years', '68 year']}

df = pd.DataFrame(my_dict)

# Create filter for month/months
month_filt = df['Experience'].str.contains('month')

# Filter DataFrame for rows that contain month/months 
df['Months'] = df.loc[month_filt, 'Experience'].str.strip('month|months')

# Create filter for year/years
year_filt = df['Experience'].str.contains('year')

# Filter DataFrame for rows that contain year/years
df['Years'] = df.loc[year_filt, 'Experience'].str.strip('year|years')

# Fill NaN in Years column
df.loc[df['Years'].isna(),'Years'] = np.nan

# Convert Years to months
df.loc[df['Months'].isna(),'Months'] = df['Years'].astype('float') * 12

# Set years greater than 55 to NaN
df.loc[df['Years'].astype('float') > 55, 'Months'] = np.nan

    Experience  Months  Years
0   5 month     5       NaN
1   nan         NaN     NaN
2   1 months    1       NaN
3   8 month     8       NaN
4   17 months   17      NaN
5   8 year      96      8
6   11 years    132     11
7   1.7 year    20.4    1.7
8   3.1 years   37.2    3.1
9   15.7 months 15.7    NaN
10  18 year     216     18
11  2017.2 yearsNaN 2017.2
12  98.3 years  NaN     98.3
13  68 year     NaN     68
 

Ответ №2:

Простое решение с использованием reg-выражений, сохраняя прозрачность работы.

 import numpy as np
df = pd.read_csv(io.StringIO("""experience

5 month
nan
1 months
8 month
17 months
8 year
11 years
1.7 year
3.1 years
15.7 months
18 year
2017.2 years
98.3 years
68 year"""))

df = df.assign(unit=lambda dfa: dfa["experience"].str.extract("([a-z] ) "),
         val=lambda dfa: dfa["experience"].str.extract("([0-9,.] )").astype(float),
         months=lambda dfa: np.where(dfa["unit"].isin(["month","months"]), dfa["val"],
                                    np.where(dfa["unit"].isin(["year","years"])
                                             amp;dfa["val"].lt(55), dfa["val"]*12, np.nan)))

print(df.to_string(index=False))
 

вывод

    experience    unit     val  months
      5 month   month     5.0     5.0
          NaN     NaN     NaN     NaN
     1 months  months     1.0     1.0
      8 month   month     8.0     8.0
    17 months  months    17.0    17.0
       8 year    year     8.0    96.0
     11 years   years    11.0   132.0
     1.7 year    year     1.7    20.4
    3.1 years   years     3.1    37.2
  15.7 months  months    15.7    15.7
      18 year    year    18.0   216.0
 2017.2 years   years  2017.2     NaN
   98.3 years   years    98.3     NaN
      68 year    year    68.0     NaN
 

Ответ №3:

Предполагается, что форматирование является согласованным (значение, пробел, период времени). Вы можете использовать разделение, чтобы получить две части.

 df = pd.DataFrame({'experience': ['5 month', np.nan, '1 months', '8 month', '17 months', '8 year', '11 years']})

def get_values(x):
    if pd.notnull(x):
        val = int(x.split(' ')[0])
        prd = x.split(' ')[1]
        if prd in ['month', 'months']:
            return val
        elif prd in ['year', 'years'] and val < 55: 
            return val * 12
    else:
        return x

df['months'] = df.apply(lambda x: get_values(x.experience), axis=1)  
 

Вывод:

   experience  months
0    5 month     5.0
1        NaN     NaN
2   1 months     1.0
3    8 month     8.0
4  17 months    17.0
5     8 year    96.0
6   11 years   132.0
 

Если имеется высокий процент NaN, вы можете сначала выполнить фильтрацию перед запуском лямбда-функции

df[df.experience.notnull()].apply(лямбда x: get_values(x.experience), ось =1)

Ответ №4:

Вероятно, есть более приятный способ сделать это с использованием фреймов данных panda, но это то, чего вы пытаетесь достичь? Вероятно, вы можете использовать регулярное выражение, если ничего другого. Я не добавил условие для <55 лет, но я уверен, что вы можете это решить.

 import re
applicants = []

applicant1 = {'name': 'Lisa', 'experience': 'nan'}
applicant2 = {'name': 'Bill', 'experience': '3.1 months'}
applicant3 = {'name': 'Mandy', 'experience': '1 month'}
applicant4 = {'name': 'Geoff', 'experience': '6.7 years'}
applicant5 = {'name': 'Patricia', 'experience': '1 year'}
applicant6 = {'name': 'Kirsty', 'experience': '2017.2 years'}

applicants.append(applicant1)
applicants.append(applicant2)
applicants.append(applicant3)
applicants.append(applicant4)
applicants.append(applicant5)
applicants.append(applicant6)

print(applicants)

month_pattern = '^([d] [.d]*) month(s*)'
year_pattern = '^([d] [.d]*) year(s*)'

applicant_output = []

for applicant in applicants:
    if applicant['experience'] == 'nan':
        applicant_output.append(applicant)
    else:
        month = re.search(month_pattern, applicant['experience'])
        if month is not None:
            applicant_output.append(
                {
                    'name': applicant['name'],
                    "exprience_months": month.group(1)
                })
        else:
            year = re.search(year_pattern, applicant['experience'])
            if year is not None:
                months = str(float(year.group(1)) * 12)
                applicant_output.append(
                    {
                        'name': applicant['name'],
                        "exprience_months": months
                    })

print(applicant_output)
 

Это дает результат:

 [{'name': 'Lisa', 'experience': 'nan'}, {'name': 'Bill', 'experience': '3.1 months'}, {'name': 'Mandy', 'experience': '1 month'}, {'name': 'Geoff', 'experience': '6.7 years'}, {'name': 'Patricia', 'experience': '1 year'}, {'name': 'Kirsty', 'experience': '2017. years'}]
 

с результатом:

 [{'name': 'Lisa', 'experience': 'nan'}, {'name': 'Bill', 'exprience_months': '3.1'}, {'name': 'Mandy', 'exprience_months': '1'}, {'name': 'Geoff', 'exprience_months': '80.4'}, {'name': 'Patricia', 'exprience_months': '12.0'}, {'name': 'Kirsty', 'exprience_months': '24206.4'}]
 

Ответ №5:

temp_df для разделения части месяца / года

 temp_df = df['experience'].str.split('([A-Za-z] )', expand=True)
temp_df = temp_df.loc[:, ~(temp_df == "").any(axis=0)]  # deleting the extra column coming upon split
temp_df[0] = temp_df[0].astype(float)
temp_df
 

введите описание изображения здесь

Получение множителя для значения experince

 multiplier = pd.Series([1] * len(temp_df), index=temp_df.index)
year_rows = temp_df[1].str.contains('year', case=False).fillna(False)  # getting the rows which has year
temp_df.loc[(year_rows) amp; (temp_df[0]>=55), 0] = np.nan  # converting exp value to nan where value is >= 55 and unit is year
multiplier[year_rows] = 12
df['experience_in_months'] = temp_df[0] * multiplier
df
 

введите описание изображения здесь