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

#python #pandas #dataframe #numpy

Вопрос:

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

В этой колонке можно указать до 10 последних высоких/низких цен high_low .

Я хотел бы сгенерировать 3 новых столбца past_hl_3 past_hl2 , past_hl1 которые будут содержать значения 3 самых последних high_low цен (то есть 3 самых верхних правых элемента строки).

Исходные данные :

     date         price   high_low
0   2021-04-01   10      NaN
1   2021-04-02   12      NaN
2   2021-04-03   14      NaN
3   2021-04-04   13      "14"
4   2021-04-05    9      "14"
5   2021-04-06   16      "14 9"
6   2021-04-07   15      "14 9 16"
7   2021-04-08   14      "14 9 16"   
8   2021-04-09   13      "14 9 16"
9   2021-04-10   12      "14 9 16"
10  2021-04-11   18      "14 9 16 12"
11  2021-04-12   19      "14 9 16 12"
12  2021-04-13   22      "14 9 16 12"
13  2021-04-14   25      "14 9 16 12"
 

Ожидаемые данные :

     date         price   high_low      price_hl_3  price_hl2  price_hl1
0   2021-04-01   10      NaN           NaN         NaN        NaN
1   2021-04-02   12      NaN           NaN         NaN        NaN
2   2021-04-03   14      NaN           NaN         NaN        NaN
3   2021-04-04   13      "14"          NaN         NaN         14
4   2021-04-05    9      "14"          NaN         NaN         14
5   2021-04-06   16      "14 9"        NaN          14          9
6   2021-04-07   15      "14 9 16"     14            9         16
7   2021-04-08   14      "14 9 16"     14            9         16
8   2021-04-09   13      "14 9 16"     14            9         16
9   2021-04-10   12      "14 9 16"     14            9         16
10  2021-04-11   18      "14 9 16 12"   9           16         12   
11  2021-04-12   19      "14 9 16 12"   9           16         12
12  2021-04-13   22      "14 9 16 12"   9           16         12
13  2021-04-14   25      "14 9 16 12"   9           16         12
 

Я думал использовать что-то подобное, но это не работает, плюс мне все равно нужно будет преобразовать значения обратно в целые числа:

 df['price_hl_3'] = np.where(len(df.high_low.str.split()) >= 3, df.high_low.str.split()[-3], np.nan)
df['price_hl_2'] = np.where(len(df.high_low.str.split()) >= 2, df.high_low.str.split()[-2], np.nan)
df['price_hl_1'] = np.where(len(df.high_low.str.split()) >= 1, df.high_low.str.split()[-1], np.nan)
 

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

Ответ №1:

Определите следующую функцию, чтобы получить содержимое 3 новых столбцов из поля high_low в текущей строке, разбитого на список:

 def getElems(lst):
    rv = lst[-3:][::-1] if isinstance(lst, list) else []
    rv  = [np.nan] * (3 - len(rv))
    return pd.Series(rv[::-1], index=['price_hl3', 'price_hl2', 'price_hl1'])
 

Тогда беги:

 result = df.join(df.high_low.str.split().apply(getElems))
 

В результате получается:

           date  price    high_low price_hl3 price_hl2 price_hl1
0   2021-04-01     10         NaN       NaN       NaN       NaN
1   2021-04-02     12         NaN       NaN       NaN       NaN
2   2021-04-03     14         NaN       NaN       NaN       NaN
3   2021-04-04     13          14       NaN       NaN        14
4   2021-04-05      9          14       NaN       NaN        14
5   2021-04-06     16        14 9       NaN        14         9
6   2021-04-07     15     14 9 16        14         9        16
7   2021-04-08     14     14 9 16        14         9        16
8   2021-04-09     13     14 9 16        14         9        16
9   2021-04-10     12     14 9 16        14         9        16
10  2021-04-11     18  14 9 16 12         9        16        12
11  2021-04-12     19  14 9 16 12         9        16        12
12  2021-04-13     22  14 9 16 12         9        16        12
13  2021-04-14     25  14 9 16 12         9        16        12
 

Обратите внимание, что тип всех 3 новых столбцов-object и non-NaN
элементы-это строки.

Первоначально я думал о преобразовании их в int, но у Pandas такие неудобные характеристики, что если столбец содержит как элементы int, так и NaN (что является «особым случаем» float), то весь столбец принуждается к плаванию.

Альтернатива для получения значений int

Последние версии Pandas включают метод convert_dtypes (), позволяющий изменять тип каждого столбца на наилучшие возможные типы dtypes с помощью поддержки dtypes pd.NA.

Так что ты можешь бежать:

 result = df.join(df.high_low.str.split().apply(getElems)).convert_dtypes()
 

а затем все 3 новых столбца (и, кроме того, цена) преобразуются в
Int64
(обратите внимание на заглавную букву «I» в названии типа).

Распечатайте результат, чтобы увидеть разницу.

На этот раз вместо NaN новые столбцы содержат <NA><NA> (другое представление «отсутствующего» значения), а другие значения являются просто целыми числами.

Ответ №2:

Давайте split тогда попробуем sorted

 df['high_low'] = df['high_low'].str.strip('"')
df = df.join(df['high_low'].str.split(' ',expand=True).
         apply(lambda x : pd.Series(sorted(x,key=pd.notnull)),1)).iloc[:,1:]
df
Out[157]: 
    price    high_low     0     1     2    3
0      10         NaN   NaN   NaN   NaN  NaN
1      12         NaN   NaN   NaN   NaN  NaN
2      14         NaN   NaN   NaN   NaN  NaN
3      13          14  None  None  None   14
4       9          14  None  None  None   14
5      16        14 9  None  None    14    9
6      15     14 9 16  None    14     9   16
7      14     14 9 16  None    14     9   16
8      13     14 9 16  None    14     9   16
9      12     14 9 16  None    14     9   16
10     18  14 9 16 12    14     9    16   12
11     19  14 9 16 12    14     9    16   12
12     22  14 9 16 12    14     9    16   12
13     25  14 9 16 12    14     9    16   12