python - 如何使用 df.loc (或其他一些方法)根据特定条件创建新列?

标签 python pandas dataframe

我有一个包含 5 列的数据框,我正在使用 pandas 和 numpy 来编辑和处理数据。

id      calv1      calv2      calv3      calv4 
1  2006-08-29 2007-08-29 2008-08-29 2009-08-29
2         NaT        NaT        NaT        NaT         
3  2006-08-29        NaT        NaT        NaT
4  2006-08-29 2007-08-29 2010-08-29        NaT
5  2006-08-29 2013-08-29        NaT        NaT
6  2006-08-29        NaT 2013-08-29 2013-08-292
我想创建另一个列来计算每个 id 出现的“calv”的数量。
但是,如果其他值之间存在缺失值,请参阅第 6 行,这对我来说很重要。然后我希望有一个 NaN 或其他一些值,表明这不是正确的行。
id      calv1      calv2      calv3      calv4 no_calv
1  2006-08-29 2007-08-29 2008-08-29 2009-08-29       4
2         NaT        NaT        NaT        NaT       0 
3  2006-08-29        NaT        NaT        NaT       1
4  2006-08-29 2007-08-29 2010-08-29        NaT       3
5  2006-08-29 2013-08-29        NaT        NaT       2
6  2006-08-29        NaT 2013-08-29 2013-08-292     NaN    #or some other value
这是我的最后一次尝试:
nat = np.datetime64('NaT')

df.loc[
(df["calv1"] == nat) & (df["calv2"] == nat) &
(df["calv3"] == nat) & (df["calv4"] == nat),
"no_calv"] = 0
#1 calvings
df.loc[
(df["calv1"] != nat) & (df["calv2"] == nat) &
(df["calv3"] == nat) & (df["calv4"] == nat),
"no_calv"] = 1
#2 calvings
df.loc[
(df["calv1"] != nat) & (df["calv2"] != nat) &
(df["calv3"] == nat) & (df["calv4"] == nat),
"no_calv"] = 2
#3 calvings
df.loc[
(df["calv1"] != nat) & (df["calv2"] != nat) &
(df["calv3"] != nat) & (df["calv4"] == nat),
"no_calv"] = 3
#4 or more calvings
df.loc[
(df["calv1"] != nat) & (df["calv2"] != nat) &
(df["calv3"] != nat) & (df["calv4"] != nat),
"no_calv"] = 4
但结果是整个“no_calv”列是 4.0
我以前尝试过类似的事情
..
(df["calv1"] != "NaT")
..
..
(df["calv1"] != pd.nat)
..
结果总是 4.0 对于整个列或只是 南。
我似乎找不到告诉 python NaT 值是什么的方法?
对 Python 新用户有什么提示和技巧吗?
我已经在 SAS 和 Fortran 中使用 if 和 elseif 语句完成了此操作,但我正在尝试找到在 Python 中执行此操作的最佳方法。
编辑:
我真的很想知道这是否可以通过 if 或 ifelse 语句来完成。
现在我也在想我希望能够在数据框中包含其他列,这些列包含额外的信息,但对于这个确切的目的不需要。一个例子(一个添加的 yx 列):
id yx       calv1      calv2      calv3      calv4 no_calv
1  27  2006-08-29 2007-08-29 2008-08-29 2009-08-29       4
2  34         NaT        NaT        NaT        NaT       0 
3  89  2006-08-29        NaT        NaT        NaT       1
4  23  2006-08-29 2007-08-29 2010-08-29        NaT       3
5  11  2006-08-29 2013-08-29        NaT        NaT       2
6  43  2006-08-29        NaT 2013-08-29 2013-08-292     NaN    #or some other value

最佳答案

另一种方法是使用 pd.Series.last_valid_index pd.DataFrame.count :

>>> df2  = df.copy()
>>> df2.columns = np.arange(df2.shape[1]) + 1
>>> mask = (df2.apply(pd.Series.last_valid_index, axis=1).fillna(0) == df2.count(axis=1))
>>> df.loc[mask, 'no_calv'] = df.notna().sum(1)
>>> df
         calv1       calv2       calv3        calv4  no_calv
id                                                          
1   2006-08-29  2007-08-29  2008-08-29   2009-08-29      4.0
2          NaN         NaN         NaN          NaN      0.0
3   2006-08-29         NaN         NaN          NaN      1.0
4   2006-08-29  2007-08-29  2010-08-29          NaN      3.0
5   2006-08-29  2013-08-29         NaN          NaN      2.0
6   2006-08-29         NaN  2013-08-29  2013-08-292      NaN
解释:pd.Series.last_valid_index返回 的位置最后有效数据 在一个系列中。将它应用于您的行将告诉列位置最后一个有效数据的位置(之后是所有 NaNs/NaTs )。
下面我暂时用整数索引替换了列名,然后应用 pd.Series.last_valid_index在每一行:
>>> df2.columns = np.arange(df2.shape[1]) + 1
>>> df2
             1           2           3            4
id                                                 
1   2006-08-29  2007-08-29  2008-08-29   2009-08-29
2          NaN         NaN         NaN          NaN
3   2006-08-29         NaN         NaN          NaN
4   2006-08-29  2007-08-29  2010-08-29          NaN
5   2006-08-29  2013-08-29         NaN          NaN
6   2006-08-29         NaN  2013-08-29  2013-08-292

>>> df2.apply(pd.Series.last_valid_index, axis=1).fillna(0)
id
1    4.0
2    0.0
3    1.0
4    3.0
5    2.0
6    4.0
dtype: float64
所以在第 1 行,最后一个有效数据在第 4 列,第 2 行没有有效数据,依此类推。
现在让我们数数。每行中的有效数据:
>>> df2.count(axis=1)
id
1    4
2    0
3    1
4    3
5    2
6    3
dtype: int64
因此,第 1 行有 4 个有效值,第 2 行没有有效值,依此类推。现在如果全部NaN/NaT值接近行尾,计数应与我们上面计算的最后一个有效数据位置匹配:
>>> df2.apply(pd.Series.last_valid_index, axis=1).fillna(0) == df2.count(axis=1)
id
1     True
2     True
3     True
4     True
5     True
6    False
dtype: bool
正如所见,它匹配除最后一行之外的所有行,因为 NaT 出现在最后一行的有效值的中间。我们可以将其用作掩码,然后填充总和:
>>> mask = (df2.apply(pd.Series.last_valid_index, axis=1).fillna(0) == df2.count(axis=1))
>>> df.loc[mask, 'no_calv'] = df.notna().sum(1)
>>> df
         calv1       calv2       calv3        calv4  no_calv
id                                                          
1   2006-08-29  2007-08-29  2008-08-29   2009-08-29      4.0
2          NaN         NaN         NaN          NaN      0.0
3   2006-08-29         NaN         NaN          NaN      1.0
4   2006-08-29  2007-08-29  2010-08-29          NaN      3.0
5   2006-08-29  2013-08-29         NaN          NaN      2.0
6   2006-08-29         NaN  2013-08-29  2013-08-292      NaN

关于python - 如何使用 df.loc (或其他一些方法)根据特定条件创建新列?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67923813/

相关文章:

python - Pandas:去季节性时间序列数据

python - Django:你如何阻止长查询杀死你的数据库?

python - 无法识别 Tkinter 单选按钮指示器

python - 如何连接两个 Pandas 系列的对应值?

python - 从 JSON 切换到 yaml 配置文件时,snakefile 中的字符串索引错误

python - Pandas 对唯一值求和,并放入表格中

python - 使用 featuretools 创建 "time window features"

python - 组织多个数据框的最佳方式

pandas - dtype=datetime64[ns, UTC] 和时间戳之间的无效比较

python - Pandas 获取列包含所有行中的字符