Интерполировать данные за пять лет ежегодно - PullRequest
0 голосов
/ 16 июня 2020

У меня есть большой файл csv, записанный с пятилетним интервалом с 1945 по 2010 год для каждой страны, как показано ниже.

> head(data)
   year name     value
1: 1945  USA 110265118
2: 1950  USA 122994019
3: 1955  USA 134001770
4: 1960  USA 150234347
5: 1965  USA 167515758
6: 1970  USA 172867051
> tail(data)
   year name  value
1: 1985  WSM 152325
2: 1990  WSM 159500
3: 1995  WSM 161677
4: 2000  WSM 174600
5: 2005  WSM 177510
6: 2010  WSM 180140

Я хотел бы ежегодно повышать дискретизацию моих данных и интерполировать столбец значений . Я пробовал приведенную ниже строку кода, но не могу понять, почему возникает ошибка.

dat <- data[data[, .(year = seq.int(year[1], year[.N], 1.0)), by=name], on=c('year', 'name')][,value := zoo::na.approx(value, na.rm = FALSE)]
Error: Column 1 of result for group 61 is type 'integer' but expecting type 'double'. Column types must be consistent for each group.

Мы высоко ценим мысли о том, как достичь желаемых результатов. Спасибо.

Ответы [ 2 ]

0 голосов
/ 16 июня 2020

Я сделал простое решение в python с передискретизацией и фильтрацией, см. Код ниже.

входной фрейм данных:

   A        B     C
  1945     USA    5
  1950     USA   10
  1955     USA   15
  1945   Brazil   6
  1950   Brazil   8
  1955   Brazil  10
  1945  Germany   7
  1950  Germany   8
  1955  Germany   9

Код:

import pandas as pd
pd.DataFrame()
df = pd.DataFrame(columns=['A','B','C'])
df['A'] =[1945,1950,1955,1945,1950,1955,1945,1950,1955]
df['B'] =['USA','USA','USA','Brazil','Brazil','Brazil','Germany','Germany','Germany']
df['C'] =[5,10,15,6,8,10,7,8,9]


df['A'] = pd.to_datetime(df['A'], format='%Y')
df.set_index(df['A'],inplace=True)
df.drop(columns=['A'],inplace=True)
print(df)

list_of_countries = df['B'].tolist()
list_of_unique_countries = list(set(list_of_countries))
new_df = pd.DataFrame()
for country in list_of_unique_countries:
    copy_of_df = df.copy()
    filtered_df = copy_of_df[copy_of_df['B']==country]
    filtered_df = filtered_df.resample('Y',closed='left').max()
    filtered_df['C'] = filtered_df['C'].interpolate(method='linear')
    filtered_df['B'] = country
    new_df = new_df.append(filtered_df)

new_df.index = new_df.index.strftime('%Y')

print(new_df)

Вывод:

A     B     C

1945  Germany   7.0
1946  Germany   7.2
1947  Germany   7.4
1948  Germany   7.6
1949  Germany   7.8
1950  Germany   8.0
1951  Germany   8.2
1952  Germany   8.4
1953  Germany   8.6
1954  Germany   8.8
1955  Germany   9.0
1945      USA   5.0
1946      USA   6.0
1947      USA   7.0
1948      USA   8.0
1949      USA   9.0
1950      USA  10.0
1951      USA  11.0
1952      USA  12.0
1953      USA  13.0
1954      USA  14.0
1955      USA  15.0
1945   Brazil   6.0
1946   Brazil   6.4
1947   Brazil   6.8
1948   Brazil   7.2
1949   Brazil   7.6
1950   Brazil   8.0
1951   Brazil   8.4
1952   Brazil   8.8
1953   Brazil   9.2
1954   Brazil   9.6
1955   Brazil  10.0

Надеюсь, это поможет вам с аналогичным решением в R.

0 голосов
/ 16 июня 2020

Base R, но также должен работать с data.tables. Обратите внимание, что это не сработает, если ваше первое значение - NA после слияния с вашими данными на более низком уровне детализации (т.е. годы <1945), если это так, пусть я знаю, и я переработаю свое решение: </p>

data.frame(do.call("rbind", lapply(split(df, df$name), function(x){

      # Find the range: date_range => Date vector
      date_range <- range(as.numeric(df$year))

      # Generate a sequence, having every date in the range:
      # date_lkp => data.frame
      date_lkp <- data.frame(year = seq(date_range[1], date_range[2]))

      # Merge with date_range: df2 => data.frame
      df2 <- merge(date_lkp, df, all.x = TRUE)

      # Use linear interpolation to populate missing values:
      # value => integer vector
      df2$value[which(is.na(df2$value))] <- approx(df2$value,
                                                   n = nrow(df2))$y[which(is.na(df2$value))]

      # Fill down the missing names: name => character vector
      df2$name <- na.omit(df2$name)[cumsum(!(is.na(df2$name)))]

      # Define return object: df2 => .GlobalEnv() 
      return(df2)
      }
    )
  ),
stringsAsFactors = FALSE, row.names = NULL)

Данные:

library(data.table)
dt <- fread(
  "year name     value
1945  USA 110265118
1950  USA 122994019
1955  USA 134001770
1960  USA 150234347
1965  USA 167515758
1970  USA 172867051
1985  WSM 152325
1990  WSM 159500
1995  WSM 161677
2000  WSM 174600
2005  WSM 177510
2010  WSM 180140")
df <- data.frame(dt)

Python используя Pandas:

# Initialise pandas in session: 
import pandas as pd

# year => list of integers: 
year = [1945, 1950, 1955, 1960, 1965, 1970, 1985, 1990, 1995, 2000, 2005, 2010]

# name => list of strings: 
name = ["USA", "USA", "USA", "USA", "USA", "USA", "WSM","WSM","WSM","WSM","WSM","WSM"]

# value => list of integers: 
value = [110265118,  122994019, 134001770, 150234347,167515758, 172867051, 152325, 159500, 161677, 174600,177510,
         180140]

# Create Data Frame from dict of lists above: df => Data Frame
df = pd.DataFrame({'year': year, 'name': name, 'value': value})

# Function upsampling and interpolating data: upsample_df => function 
def upsample_df(g_df):
    years = pd.DataFrame({'year': [*range(int(g_df.year.min()), int(g_df.year.max()+1), 1)]})
    m_df = pd.merge(years, g_df, how = 'left', on = [*set(years.columns) & set(g_df.columns)])
    df2 = m_df.interpolate(method = 'linear')
    df2['name'] = df2['name'].ffill()
    return(df2)

# Apply function groupwise (according to name): upsampled_df => Data Frame
upsampled_df = df.groupby('name').apply(upsample_df)

# Reset the index of upsampled_df: upsampled_df.index => integer
upsampled_df.index = [*range(1, len(upsampled_df)+1)]

# Output result to console: upsampled_df => stdout (console)
print(upsampled_df.head())
...