Почему эта лямбда-операция не работает? - PullRequest
1 голос
/ 07 июня 2019

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

например, <1 становится 0,5, <0,5 становится 0,25,<5 становится 2,5 и т. Д. Обычные числа и текст должны быть неизменными. </p>

У меня есть следующая лямбда-функция для применения к моему фрейму данных, который, как я думал, работал, но это не так:

df_no_less_thans= df.apply(lambda x: x if str(x[0])!='<' else float(x[1:])/2)  

Я все еще получаю значения <в новом df, сообщений об ошибках нет. </p>

Что я сделал не так?

df=pd.DataFrame()
df['Cu']=[3.7612,1.3693, 2.7502,1.407,4.2066,6.4409,6.8136,"<0.05","<0.05",0.94,0.07,1.82,2.63,1.36,0.78]
df.apply(lambda x: x if str(x)[0]!='<' else float(str(x)[1:])/2) 
df

дает

    Cu
0   3.7612
1   1.3693
2   2.7502
3   1.407
4   4.2066
5   6.4409
6   6.8136
7   <0.05
8   <0.05
9   0.94
10  0.07
11  1.82
12  2.63
13  1.36
14  0.7 ```

Ответы [ 5 ]

2 голосов
/ 07 июня 2019

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

Вы также хотели бы проверить пустые строки перед выполнением лямбда-операции

#Explicitly cast to string and perform the indexing
func = lambda x: x if  str(x)[0]!='<' else float(str(x)[1:])/2

li = ['<1', '<0.5', '<5', 1, 'hello', 4.0, '']

#Filter out empty strings
print([func(item) for item in li if item])

Вывод будет

[0.5, 0.25, 2.5, 1, 'hello', 4.0]
1 голос
/ 07 июня 2019

Я думаю, вам нужно применять лямбда-функцию только для столбца Cu, поэтому используйте правильное решение Series.apply:

df['Cu'] = df['Cu'].apply(lambda x: x if str(x)[0]!='<' else float(str(x)[1:])/2) 
print (df)

        Cu
0   3.7612
1   1.3693
2   2.7502
3   1.4070
4   4.2066
5   6.4409
6   6.8136
7   0.0250
8   0.0250
9   0.9400
10  0.0700
11  1.8200
12  2.6300
13  1.3600
14  0.7800

Если необходимо применить функцию для всех столбцов, используйте Ян решение.

1 голос
/ 07 июня 2019

Метод apply имеет аргумент axis.По умолчанию axis=0, что означает, что ваша лямбда-функция применяется последовательно к каждому столбцу в кадре данных.В вашем случае лямбда-функция применяется к столбцу 'Cu', что означает, что аргумент x на самом деле является столбцом, а str(x)[0] - это не то, что вы думаете.

Вместо этого следует использовать applymap, чтобы применить лямбда-функцию поэлементно:

df.applymap(lambda x: x if str(x)[0] != '<' else float(str(x)[1:])/2)
0 голосов
/ 07 июня 2019

Вот как это работает:

import pandas as pd

df=pd.DataFrame()
df['Cu']=[3.7612,1.3693, 2.7502,1.407,4.2066,6.4409,6.8136,"<0.05","<0.05",0.94,0.07,1.82,2.63,1.36,0.78]

df['Cu'] = df.apply(lambda x: x if not isinstance(x[0],str) else float(x[0][1:])/2, axis=1, raw=True)

print(df)

результат:

        Cu
0   3.7612
1   1.3693
2   2.7502
3    1.407
4   4.2066
5   6.4409
6   6.8136
7    0.025
8    0.025
9     0.94
10    0.07
11    1.82
12    2.63
13    1.36
14    0.78
0 голосов
/ 07 июня 2019

В своем вопросе вы говорите

например. <1 становится 0,5, <0,5 становится 0,25, <5 становится 2,5 и т. Д. Обычные числа и <strong>текст должен быть неизменным .

Теперь в приведенном вами примере у вас есть только первые два типа данных: строки типа <1 и float s, но вы, похоже, хотите иметь возможность сохранять любой другой тип текста. Однако я вижу , смешивающий разные dtypes в одном столбце как неправильный макет кадра данных , который в будущем вызовет проблемы.

Если, например, в вашем столбце есть текст hello, простая операция, такая как:

df['Cu'] * 2
# [...]
# 6        13.6272
# 7     hellohello
# 8           0.05
# 9           1.88
# [...]
# Name: Cu, dtype: object

Скорее всего, это не то, что вы хотите.

Теперь я не знаю, какие у вас другие типы текста, но для приведенных примеров я бы рекомендовал сначала нормализовать dtypes: для этого мы создадим новый столбец df['less_than'] из «информации о неопределенности»:

import pandas as pd

df=pd.DataFrame()
df['Cu']=[3.7612,1.3693, 2.7502,1.407,4.2066,6.4409,6.8136,"<0.05","<0.05",0.94,0.07,1.82,2.63,1.36,0.78]

df['less_than'] = df['Cu'].str.startswith('<', False)
df.loc[df['less_than'], 'Cu'] = df.loc[df['less_than'], 'Cu'].str.slice(1)

df['Cu'] = df['Cu'].astype(float)
#         Cu  less_than
# 0   3.7612      False
# 1   1.3693      False
# 2   2.7502      False
# 3   1.4070      False
# 4   4.2066      False
# 5   6.4409      False
# 6   6.8136      False
# 7   0.0500       True
# 8   0.0500       True
# 9   0.9400      False
# 10  0.0700      False
# 11  1.8200      False
# 12  2.6300      False
# 13  1.3600      False
# 14  0.7800      False

Это позволяет нам обрабатывать весь столбец df['Cu'] одинаково, а ваши операции «<1 становится 0,5» простым однострочным: </p>

df.loc[df['less_than'], 'Cu'] /= 2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...