如何基于 pandas DataFrame 更新 numpy 数组

How to update numpy array based on pandas DataFrame

我有一个包含数千行和几列的 numpy 数组,我想知道如何根据 pandas DataFrame 中的值更新每个值。

例如,假设我的数组包含一个年份列表(这里有一个非常小的示例,只是为了给您提供基本概念):

[[2020, 2015, 2017],
 [2015, 2016, 2016],
 [2019, 2018, 2020]]

我想根据“年”将数组中的每个值更改为“Lat”。因此,如果我的 pandas 数据框如下所示:

Year Lat Lon
2020 37.2 103.45
2019 46.1 107.82
2018 35.2 101.45
2017 38.6 110.62
2016 29.1 112.73
2015 33.8 120.92

那么输出数组应该如下所示:

[[37.2, 33.8, 38.6],
 [33.8, 29.1, 29.1],
 [46.1, 35.2, 37.2]]

如果我的数据集真的这么小,那不是问题,但考虑到我在数组中有数百万个值,在 DataFrame 中有数千个值,我有点不知所措这很有效。

更新:

也许我的问题比我预想的要复杂一些。我没有匹配年份,而是匹配 GPS 时间,因此数字匹配得不太好。有没有办法在数组中取一个数字并将其与 DataFrame 列中最接近的值匹配?实际上,我的数组看起来更像这样:

[[2019.99, 2015.2, 2017.1],
 [2015.33, 2016.01, 2015.87],
 [2019.2, 2018.3, 2020.00]]

也许将 Year 设置为索引并使用 at(或 loc)会有所帮助

# Data
arr = np.array([[2020, 2015, 2017], [2015, 2016, 2016], [2019, 2018, 2020]])
df = pd.DataFrame({'Year': {0: 2020, 1: 2019, 2: 2018, 3: 2017, 4: 2016, 5: 2015},
                   'Lat': {0: 37.2, 1: 46.1, 2: 35.2, 3: 38.6, 4: 29.1, 5: 33.8},
                   'Lon': {0: 103.45, 1: 107.82, 2: 101.45, 3: 110.62, 4: 112.73, 5: 120.92}})

df = df.set_index("Year")
np.array([df.loc[years, "Lat"] for years in arr])
# array([[37.2, 33.8, 38.6],
#        [33.8, 29.1, 29.1],
#        [46.1, 35.2, 37.2]])

您基本上是在跨列映射值。一种想法是使用索引来定位需要为给定键替换的元素,然后一次替换它们。这对原始数据中的每个 key-value 对进行一次迭代。

示例:

import numpy as np
import pandas as pd

a = np.array([
    [2020, 2015, 2017],
    [2015, 2016, 2016],
    [2019, 2018, 2020],
])
b = np.zeros(a.shape, dtype=float)

df = pd.DataFrame({
    'Year': [2020, 2019, 2018, 2017, 2016, 2015],
    'Lat': [37.2, 46.1, 35.2, 38.6, 29.1, 33.8],
})

for k, v in df.set_index('Year')['Lat'].to_dict().items():
    b[a == k] = v
print(b)

# output:
# [[37.2 33.8 38.6]
#  [33.8 29.1 29.1]
#  [46.1 35.2 37.2]]

np.unique 可用于检测 years 列表中的唯一值,然后 return_inverse=True 可设置为 return 重新创建输入数组所需的索引.

我们可以将它与 set_indexreindex 结合使用来创建一系列可以转换的值 to_numpy。然后,来自 np.unique 的索引的结果可以与此纬度值数组一起使用到 select 必要的值。最后的 reshape 可用于获取正确形式的数组。

u, inv = np.unique(years, return_inverse=True)
result = (
    df.set_index('Year')['Lat'].reindex(u).to_numpy()[inv].reshape(years.shape)
)

result:

[[37.2 33.8 38.6]
 [33.8 29.1 29.1]
 [46.1 35.2 37.2]]

结果来自 np.unique

u, inv = np.unique(years, return_inverse=True)

u=array([2015, 2016, 2017, 2018, 2019, 2020])
inv=array([5, 0, 2, 0, 1, 1, 4, 3, 5])

以年份为索引的Lat列:

df.set_index('Year')['Lat']

Year
2020    37.2
2019    46.1
2018    35.2
2017    38.6
2016    29.1
2015    33.8
Name: Lat, dtype: float64

重新索引以匹配 np.unique:

中的顺序
df.set_index('Year')['Lat'].reindex(u)

Year
2015    33.8
2016    29.1
2017    38.6
2018    35.2
2019    46.1
2020    37.2
Name: Lat, dtype: float64

NumPy 索引到来自这个新系列的 select:

df.set_index('Year')['Lat'].reindex(u).to_numpy()[inv]

array([37.2, 33.8, 38.6, 33.8, 29.1, 29.1, 46.1, 35.2, 37.2])

最终reshape匹配初始输入years数组维度:

df.set_index('Year')['Lat'].reindex(u).to_numpy()[inv].reshape(years.shape)

array([[37.2, 33.8, 38.6],
       [33.8, 29.1, 29.1],
       [46.1, 35.2, 37.2]])

设置:

import numpy as np
import pandas as pd

df = pd.DataFrame({
    'Year': [2020, 2019, 2018, 2017, 2016, 2015],
    'Lat': [37.2, 46.1, 35.2, 38.6, 29.1, 33.8],
    'Lon': [103.45, 107.82, 101.45, 110.62, 112.73, 120.92]
})

years = np.array([[2020, 2015, 2017],
                  [2015, 2016, 2016],
                  [2019, 2018, 2020]])

一行:

df.set_index('Year').Lat.loc[arr.flatten()].to_numpy().reshape(arr.shape)

如果您要执行多个这样的操作,您应该只调用 set_index() 一次,如果您想修改现有的 DataFrame 而不是创建一个新的 DataFrame,则可以使用 inplace=True

之后,只需给 loc 一个一维数组,它可用于高效查找 Lat 值,然后重塑结果以匹配原始 arr .

这类似于 d.b 的答案,但效率更高,因为它不使用 Python for 循环。