Создание нескольких строк путем расширения фрейма данных с помощью Python

#python #pandas

#python #pandas

Вопрос:

Я пытаюсь создать новый фрейм данных на основе следующих данных с 4 столбцами: start_year, end_ear, ego_id и alter_id. Мне нужно преобразовать данные в новый фрейм данных, который имеет ежегодное наблюдение (столбец year), используя start_year и end_year. Например, если start_year в существующем фрейме данных равен 2012, а end_year равен 2016, новый фрейм данных, основанный на этой строке, должен содержать 5 строк, включая год 2012, 2013, 2014, 2015 и 2016.

 d = {'start_year': [2012, 2016,2006], 'end_year': [2016, 2017,2016],'ego_id':['1011','1011','2211'],'alter_id':['3311','9192','1022']}
df = pd.DataFrame(data=d)
df

    start_year  end_year    ego_id  alter_id
0   2012    2016    1011    3311
1   2016    2017    1011    9192
2   2006    2016    2211    1022
 

Одним из простых способов сделать это может быть повторение каждой строки в исходном фрейме данных, а затем создание новых строк на основе start_year и end_year и, наконец, добавление этих строк в новый фрейм данных.

Однако я обнаружил, что этот метод неэффективен, поскольку я имею дело с большим набором данных. Есть ли способ сделать это быстрее?

 df_empty=pd.DataFrame()
df_empty['year']=""

for i in range(df.shape[0]):
    row=df.iloc[i,]
    
    for yr in range(row.start_year,row.end_year 1):
        matched_row=pd.Series([],dtype=object)
        matched_row['year']=yr
        matched_row=pd.concat([matched_row,row[2:]],axis=0)
        df_empty=df_empty.append(matched_row,ignore_index=True)



df_empty

    year    alter_id    ego_id
0   2012    3311    1011
1   2013    3311    1011
2   2014    3311    1011
3   2015    3311    1011
4   2016    3311    1011
5   2016    9192    1011
6   2017    9192    1011
7   2006    1022    2211
8   2007    1022    2211
9   2008    1022    2211
10  2009    1022    2211
11  2010    1022    2211
12  2011    1022    2211
13  2012    1022    2211
14  2013    1022    2211
15  2014    1022    2211
16  2015    1022    2211
17  2016    1022    2211
 

Ответ №1:

Вы можете использовать понимание списка для создания списка года, а затем explode :

 print (df.assign(year=[list(range(lo, hi 1)) for lo, hi in df.filter(like="year").to_numpy()])
         .explode("year")
         .drop(["start_year", "end_year"], 1))

  ego_id alter_id  year
0   1011     3311  2012
0   1011     3311  2013
0   1011     3311  2014
0   1011     3311  2015
0   1011     3311  2016
1   1011     9192  2016
1   1011     9192  2017
2   2211     1022  2006
2   2211     1022  2007
2   2211     1022  2008
2   2211     1022  2009
2   2211     1022  2010
2   2211     1022  2011
2   2211     1022  2012
2   2211     1022  2013
2   2211     1022  2014
2   2211     1022  2015
2   2211     1022  2016
 

Комментарии:

1.Это работает отлично. У меня есть еще один столбец для добавления: duration_month представляет, как долго сохраняются отношения между ego и alter в месяц в течение года. Есть ли способ разнесения на основе столбца месяца? data = { "start_yr": [2012], "end_yr": [2014], "start_mon": [1], "end_mon": [7], "ego": ["1"], "alter": ["3"], } df = pandas.DataFrame(data=data) #desired outcome data = { "yr": [2012,2013,2014], "dur_mon": [12,12,7], "ego": ["1", "1","1"], "alter": ["3","3","3"], } df = pandas.DataFrame(data=data)

Ответ №2:

Это должно сработать:

 import pandas

# Your data
data = {
    "start_year": [2012, 2016, 2006],
    "end_year": [2016, 2017, 2016],
    "ego_id": ["1011", "1011", "2211"],
    "alter_id": ["3311", "9192", "1022"],
}
df = pandas.DataFrame(data=data)

# Add a column with all the years in between the start and end
df["range"] = df.apply(lambda row: range(row["start_year"], row["end_year"]   1), axis=1)

# Create a new series that contains every year on a new line, maintaining the original index
years = df.apply(lambda x: pandas.Series(x["range"]), axis=1).stack().reset_index(level=1, drop=True)
years.name = "year"

# Join back to the original dataframe on the index
df = df.drop(columns=["range"]).join(years.astype(int))
df