2016-09-26 4 views
7

트랜잭션 데이터를 읽을 수 있도록 데이터 변환에 도움이 필요합니다.열 내 조건에 따라 그룹/클래스 만들기

나는 사건의 일부 그룹이나 클래스를 생성하기 위해 함께 그룹에 약간의 관련 거래를 시도하고

비즈니스 사례. 이 데이터 세트는 다양한 휴무 이벤트에서 퇴근하는 근로자를 나타냅니다. 나는 휴가 클래스의 365 일 이내에 떨어지는 거래를 기반으로 한 잎 클래스를 만들고 싶습니다. 추세를 차트 화하기 위해 클래스에 번호를 매겨 시퀀스/패턴을 얻고 싶습니다.

내 코드를 통해 처음 이벤트가 발생한 시점을 알 수 있으며 새 클래스가 시작될 때이를 식별 할 수 있지만 각 트랜잭션을 클래스로 버킷으로 묶지는 않습니다.

요구 사항 :

  • 태그 모든 행에 따라 클래스를 떠날 것을 그들이에 속합니다.
  • 각 고유 이탈 이벤트에 번호를 부여하십시오. 이 예제 색인 0을 사용하면 고유 이탈 이벤트 2, 색인 1은 고유 이탈 이벤트 2, 색인 3은 고유 이탈 이벤트 2, 색인 4는 고유 종료 이벤트 1 등이됩니다.

원하는 출력에 대한 열에 "원하는 출력"이라는 레이블이 붙어 있습니다. 1 인당 더 많은 행/이벤트가있을 수 있습니다. 그리고 더 많은 사람들이있을 수 있습니다.

일부 데이터

import pandas as pd 

data = {'Employee ID': ["100", "100", "100","100","200","200","200","300"], 
     'Effective Date': ["2016-01-01","2015-06-05","2014-07-01","2013-01-01","2016-01-01","2015-01-01","2013-01-01","2014-01"], 
     'Desired Output': ["Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 1","Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 1","Unique Leave Event 1"]} 
df = pd.DataFrame(data, columns=['Employee ID','Effective Date','Desired Output']) 

나는이 조금 투박하지만 적어도 당신의 작은 예를 들어 오른쪽 출력을 얻을 수

df['Effective Date'] = df['Effective Date'].astype('datetime64[ns]') 
df['EmplidShift'] = df['Employee ID'].shift(-1) 
df['Effdt-Shift'] = df['Effective Date'].shift(-1) 
df['Prior Row in Same Emplid Class'] = "No" 
df['Effdt Diff'] = df['Effdt-Shift'] - df['Effective Date'] 
df['Effdt Diff'] = (pd.to_timedelta(df['Effdt Diff'], unit='d') + pd.to_timedelta(1,unit='s')).astype('timedelta64[D]') 
df['Cumul. Count'] = df.groupby('Employee ID').cumcount() 


df['Groupby'] = df.groupby('Employee ID')['Cumul. Count'].transform('max') 
df['First Row Appears?'] = "" 
df['First Row Appears?'][df['Cumul. Count'] == df['Groupby']] = "First Row" 
df['Prior Row in Same Emplid Class'][ df['Employee ID'] == df['EmplidShift']] = "Yes" 

df['Prior Row in Same Emplid Class'][ df['Employee ID'] == df['EmplidShift']] = "Yes" 

df['Effdt > 1 Yr?'] = ""           
df['Effdt > 1 Yr?'][ ((df['Prior Row in Same Emplid Class'] == "Yes") & (df['Effdt Diff'] < -365)) ] = "Yes" 

df['Unique Leave Event'] = "" 
df['Unique Leave Event'][ (df['Effdt > 1 Yr?'] == "Yes") | (df['First Row Appears?'] == "First Row") ] = "Unique Leave Event" 

df 

답변

2

데이터 프레임을 반복하거나 반복하지 않고도이 작업을 수행 할 수 있습니다. Wes McKinney 일 경우 .apply()을 groupBy 객체와 함께 사용하고 groupby 객체에 적용 할 함수를 정의 할 수 있습니다. .shift() (like here)과 함께 사용하면 루프를 사용하지 않고 결과를 얻을 수 있습니다.

간결한 예 :

# Group by Employee ID 
grouped = df.groupby("Employee ID") 
# Define function 
def get_unique_events(group): 
    # Convert to date and sort by date, like @Khris did 
    group["Effective Date"] = pd.to_datetime(group["Effective Date"]) 
    group = group.sort_values("Effective Date") 
    event_series = (group["Effective Date"] - group["Effective Date"].shift(1) > pd.Timedelta('365 days')).apply(lambda x: int(x)).cumsum()+1 
    return event_series 

event_df = pd.DataFrame(grouped.apply(get_unique_events).rename("Unique Event")).reset_index(level=0) 
df = pd.merge(df, event_df[['Unique Event']], left_index=True, right_index=True) 
df['Output'] = df['Unique Event'].apply(lambda x: "Unique Leave Event " + str(x)) 
df['Match'] = df['Desired Output'] == df['Output'] 

print(df) 

출력 : 선명도

Employee ID Effective Date  Desired Output Unique Event \ 
3   100  2013-01-01 Unique Leave Event 1    1 
2   100  2014-07-01 Unique Leave Event 2    2 
1   100  2015-06-05 Unique Leave Event 2    2 
0   100  2016-01-01 Unique Leave Event 2    2 
6   200  2013-01-01 Unique Leave Event 1    1 
5   200  2015-01-01 Unique Leave Event 2    2 
4   200  2016-01-01 Unique Leave Event 2    2 
7   300  2014-01 Unique Leave Event 1    1 

       Output Match 
3 Unique Leave Event 1 True 
2 Unique Leave Event 2 True 
1 Unique Leave Event 2 True 
0 Unique Leave Event 2 True 
6 Unique Leave Event 1 True 
5 Unique Leave Event 2 True 
4 Unique Leave Event 2 True 
7 Unique Leave Event 1 True 

더 상세한 예 :

import pandas as pd 

data = {'Employee ID': ["100", "100", "100","100","200","200","200","300"], 
     'Effective Date': ["2016-01-01","2015-06-05","2014-07-01","2013-01-01","2016-01-01","2015-01-01","2013-01-01","2014-01"], 
     'Desired Output': ["Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 1","Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 1","Unique Leave Event 1"]} 
df = pd.DataFrame(data, columns=['Employee ID','Effective Date','Desired Output']) 

# Group by Employee ID 
grouped = df.groupby("Employee ID") 

# Define a function to get the unique events 
def get_unique_events(group): 
    # Convert to date and sort by date, like @Khris did 
    group["Effective Date"] = pd.to_datetime(group["Effective Date"]) 
    group = group.sort_values("Effective Date") 
    # Define a series of booleans to determine whether the time between dates is over 365 days 
    # Use .shift(1) to look back one row 
    is_year = group["Effective Date"] - group["Effective Date"].shift(1) > pd.Timedelta('365 days') 
    # Convert booleans to integers (0 for False, 1 for True) 
    is_year_int = is_year.apply(lambda x: int(x))  
    # Use the cumulative sum function in pandas to get the cumulative adjustment from the first date. 
    # Add one to start the first event as 1 instead of 0 
    event_series = is_year_int.cumsum() + 1 
    return event_series 

# Run function on df and put results into a new dataframe 
# Convert Employee ID back from an index to a column with .reset_index(level=0) 
event_df = pd.DataFrame(grouped.apply(get_unique_events).rename("Unique Event")).reset_index(level=0) 

# Merge the dataframes 
df = pd.merge(df, event_df[['Unique Event']], left_index=True, right_index=True) 

# Add string to match desired format 
df['Output'] = df['Unique Event'].apply(lambda x: "Unique Leave Event " + str(x)) 

# Check to see if output matches desired output 
df['Match'] = df['Desired Output'] == df['Output'] 

print(df) 

동일한 출력을 얻을 수 있습니다 :

Employee ID Effective Date  Desired Output Unique Event \ 
3   100  2013-01-01 Unique Leave Event 1    1 
2   100  2014-07-01 Unique Leave Event 2    2 
1   100  2015-06-05 Unique Leave Event 2    2 
0   100  2016-01-01 Unique Leave Event 2    2 
6   200  2013-01-01 Unique Leave Event 1    1 
5   200  2015-01-01 Unique Leave Event 2    2 
4   200  2016-01-01 Unique Leave Event 2    2 
7   300  2014-01 Unique Leave Event 1    1 

       Output Match 
3 Unique Leave Event 1 True 
2 Unique Leave Event 2 True 
1 Unique Leave Event 2 True 
0 Unique Leave Event 2 True 
6 Unique Leave Event 1 True 
5 Unique Leave Event 2 True 
4 Unique Leave Event 2 True 
7 Unique Leave Event 1 True 
+0

그건 멋진 솔루션입니다. OP가 정말로 거대한 데이터 프레임을 사용하고있을 가능성이 있지만 그 데이터의 내용으로 판단 할 때 유일한 위험은 '병합'에 놓일 수 있습니다. – Khris

3

을 시도했다 일부 코드 :

import pandas as pd 

data = {'Employee ID': ["100", "100", "100","100","200","200","200","300"], 
     'Effective Date': ["2016-01-01","2015-06-05","2014-07-01","2013-01-01","2016-01-01","2015-01-01","2013-01-01","2014-01-01"], 
     'Desired Output': ["Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 1","Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 1","Unique Leave Event 1"]} 
df = pd.DataFrame(data, columns=['Employee ID','Effective Date','Desired Output']) 

df["Effective Date"] = pd.to_datetime(df["Effective Date"]) 
df = df.sort_values(["Employee ID","Effective Date"]).reset_index(drop=True) 

for i,_ in df.iterrows(): 
    df.ix[0,"Result"] = "Unique Leave Event 1" 
    if i < len(df)-1: 
    if df.ix[i+1,"Employee ID"] == df.ix[i,"Employee ID"]: 
     if df.ix[i+1,"Effective Date"] - df.ix[i,"Effective Date"] > pd.Timedelta('365 days'): 
     df.ix[i+1,"Result"] = "Unique Leave Event " + str(int(df.ix[i,"Result"].split()[-1])+1) 
     else: 
     df.ix[i+1,"Result"] = df.ix[i,"Result"] 
    else: 
     df.ix[i+1,"Result"] = "Unique Leave Event 1" 

참고 이 코드에서는 첫 번째 행에 항상 Unique Leave Event 1 문자열이 포함되어 있다고 가정합니다.

편집 : 설명.

먼저 날짜를 datetime 형식으로 변환 한 다음 모든 Employee ID의 날짜가 오름차순이되도록 데이터 프레임 순서를 변경합니다.

그런 다음 내장 된 반복기 iterrows을 사용하여 프레임 행을 반복합니다. for i,_에있는 _은 반복자가 행 번호와 행을 모두 돌려주기 때문에 사용하지 않는 두 번째 변수의 자리 표시 자일뿐입니다. 여기에는 숫자 만 필요합니다.

반복자에서 나는 행 방향 비교를 수행하므로 기본적으로 첫 번째 행을 손으로 채운 다음 i+1 번째 행에 할당합니다. 첫 번째 행의 값은 알고 있지만 마지막 행의 값은 알지 못하므로이 방법을 사용합니다. 그렇다면 i+1if -safeguard 내에서 i 번째 줄과 i+1 번째 줄을 비교합니다. 왜냐하면 i+1은 마지막 반복에서 인덱스 오류를 줄 것이기 때문입니다.

루프에서 먼저 Employee ID이 두 행 사이에서 변경되었는지 확인합니다. 그렇지 않은 경우 두 행의 날짜를 비교하여 365 일 이상 떨어져 있는지 확인합니다. 이 경우에 문자열을 i 번째 줄에서 읽은 다음 번호를 하나씩 늘리고 i+1 -row에 씁니다. 날짜가 가까울 경우 이전 행의 문자열 만 복사합니다.

반면에 Employee ID이 변경되면 나는 처음부터 다시 "Unique Leave Event 1"으로 작성합니다.

참고 1 : iterrows()에는 설정할 옵션이 없으므로 하위 집합 만 반복 할 수 없습니다.

주 2 : 항상 기본 제공되는 반복기 중 하나를 사용하여 반복하고, 다른 방법으로는 문제를 해결할 수없는 경우에만 반복하십시오.

참고 3 : 반복에서 값을 할당 할 때는 항상 ix, loc 또는 iloc을 사용하십시오.

+0

고마워요! 어떻게했는지에 대한 논평을 해 주시겠습니까? – Christopher

+0

안녕하세요, 오랫동안 기다려서 죄송합니다. 직장에서만 댓글을 달았으며 3 일간의 주말을 보냈습니다. 이제 몇 가지 의견을 추가하겠습니다. – Khris

관련 문제