根据另一列的掩码修改 Pandas 数据框列的符号?

Modify sign of Pandas dataframe's column based off another column's mask?

我正在处理从 NASA 的火球数据 API (https://cneos.jpl.nasa.gov/fireballs/)

中导入的 lat/lon 数据

下面是我创建数据框的大致方式:

import requests 
import pandas as pd

response = requests.get('https://ssd-api.jpl.nasa.gov/fireball.api')
j = response.json()
df = pd.DataFrame.from_dict(j[u'data'])

print( j[u'fields'] )

[u'date', u'energy', u'impact-e', u'lat', u'lat-dir', u'lon', u'lon-dir', u'alt', u'vel']

print( df.head() )

0    1      2     3     4     5     6     7     8
0  2019-12-06 10:19:57  4.6   0.15   3.3     S  37.7     W  19.5  None
1  2019-12-03 06:46:27  4.2   0.14   5.6     N  52.2     W  61.5  None
2  2019-11-28 20:30:54  2.7  0.095  35.7     N  31.7     W    35  13.0
3  2019-11-28 13:22:10  2.6  0.092  None  None  None  None  None  None
4  2019-11-28 11:55:02  2.5  0.089  22.1     S  25.7     E  22.5  24.7

我尝试过的代码行数:

尝试使用 df.apply() - 尽管通过我的搜索,我认为您不能以这种方式轻松引用两列...

    df['lat'] = df['lat'].apply(lambda x: x * -1 if (df['lat-dir'][x] == 'S'))

    for i, row in df.iterrows():
        if (row['lat-dir'] == 'S'):
            df['lat'][i].apply(lambda x: x*-1)

为此,我得到 'numpy.float64' object has no attribute 'apply' ?


尝试使用掩码:

    if( df['lon-dir'] == 'W'):
         df['lon'] * -1

但坦率地说,关于敷面膜,我不知道下一步该怎么做。

编辑:

dfDate['lat'] = dfDate['lat'].apply(lambda row: row['lon'] * -1 , axis = 1 )

根据评论也尝试了此操作。

是,通过以下任一方式:

A) 使用矢量化掩码== 未矢量化; .eq(...) 是。对于矢量化表达式,请使用 dfDate['lon-dir'].eq('W')。然后否定这些行上的 'lon' 列。

B) 使用apply()dfDate['lon'] = dfDate.apply(lambda row: ..., axis=1) - 并在您的 lambda 中根据值 row['lon-dir'] 选择性地否定 row['lon'] - 您的 apply 调用失败的原因是您需要应用到整个 column/Series,而不是单个条目。所以:df['lat'].apply(lambda: ..., axis=1)

lat-dir/lon-dir本质上是符号列,读入时可以将它们转换为+1/-1。

代码:

首先,您要修复的一些代码问题:

  1. 不要使用 u'...' 表示法。假设您正在使用 Python 3.x,不需要 u'...',现在 3.x 中的文本默认为 unicode。如果你不使用 Python 3.x,你真的应该现在切换,2.x 将于 2020 年 1 月 1 日停用。
  2. 将 JSON 列名传递到数据框上,让您的生活更轻松:
    • df.columns = j['fields']
  3. 通过将 response.json() 传递到 pd.DataFrame.from_dict() 来读取 JSON 是一种痛苦;您的数据框列变为 string/'object' 而不是将浮点数转换为浮点数。出于这个和其他方便的原因,理想情况下我们应该使用 pandas.read_json(..., dtype=...)
  4. 您将要转换数字列上的 dtypes(例如字符串 -> 浮点数),这也会自动转换 Python None -> pandas/numpy nan(为了向量化代码,我们将优雅地编写处理 nan 而不是不断抛出烦人的 TypeError: unsupported operand type(s) for *: 'NoneType' and 'int')。您可以[使用 astype(...)pd.to_numeric()df.fillna(value=pd.np.nan, inplace=True)
  5. 真的,由于下面列出的多种原因,这些 nan 条目会一直很痛苦(例如,整数不断被强制恢复为浮点数),所以 你可能想要放弃或至少暂时忽略nan rows 通过做:
    • df2 = df.dropna(how='any', inplace=False) # 可能与 ..., inplace=True 无关。请注意,这会保留行索引,因此您始终可以在末尾将 prpcessing df2 的结果插入回 df 中。阅读 dropna 文档并找出您要删除 nan 的确切位置。
    • 请注意,'vel' 列实际上还有其他我们想要忽略的 nan,您需要弄清楚,或者暂时忽略它们:例如做 df2 = df[['date','energy','impact-e','lat','lat-dir','lon','lon-dir']].dropna(how='any', inplace=False)

解决方案

  1. 将 lat/lon-dir 列转换为 +/-1 符号的几种方法:

    A1) 如果你想要 'correct',nan 感知方式,它不会阻塞 nans...

    df2['lat'] = df2['lat-dir'].map({'N': +1, 'S': -1})
    df2['lon'] = df2['lon-dir'].map({'E': +1, 'W': -1})
A2) ...or a fast-and-dirty way:
    (-1) ** df2['lat-dir'].eq('S')
    (-1) ** df2['lon-dir'].eq('W')

B) 但你可以在一行中完成所有这些 apply() 函数:

def fixup_latlon_signs(row):
    row['lat'] = row['lat'] * (-1) ** (row['lat-dir'] == 'S')
    row['lon'] = row['lon'] * (-1) ** (row['lon-dir'] == 'W')
    return row

df2.apply(fixup_latlon_signs, axis=1)

# Then insert the non-NA rows we processed back into the parent dataframe:
df.update(df2)

# Strictly we can drop 'lat-dir','lon-dir' now...