Python 使用“创建新数据帧”;“VLOOKUP”;在两个数据帧之间

Python 使用“创建新数据帧”;“VLOOKUP”;在两个数据帧之间,python,python-3.x,pandas,Python,Python 3.x,Pandas,有点类似于Excel的VLOOKUP函数,我想在一个数据帧(下面的公文包)中使用一个值来查找第二个数据帧(下面的返回值)中的关联值,并用这些返回值填充第三个数据帧(现在我们称之为dataframe3)。我发现了一些基于左合并和映射的帖子,但是我最初的两个数据帧的结构不同,所以这些方法似乎不适合(至少对我来说) 我没有取得多大进展,但以下是我的代码: 代码 import pandas as pd portfolios = pd.read_csv('portstst5_1.csv') return

有点类似于Excel的VLOOKUP函数,我想在一个数据帧(
下面的公文包
)中使用一个值来查找第二个数据帧(
下面的返回值
)中的关联值,并用这些返回值填充第三个数据帧(现在我们称之为dataframe3)。我发现了一些基于左合并和映射的帖子,但是我最初的两个数据帧的结构不同,所以这些方法似乎不适合(至少对我来说)

我没有取得多大进展,但以下是我的代码:

代码

import pandas as pd

portfolios = pd.read_csv('portstst5_1.csv')
returns = pd.read_csv('Example_Returns.csv')

total_cols = len(portfolios.columns)
headers = list(portfolios)

concat = returns['PERMNO'].map(str) + returns['FROMDATE'].map(str)
idx = 2
returns.insert(loc=idx, column="concat", value=concat)

for i in range(total_cols):
    col_len = portfolios.iloc[:,i].count()
    for j in range(col_len):
        print(portfolios.iat[j,i].astype('int').astype('str') + headers[i])
数据

如果我首先描述我的数据,这段代码将更有意义:
公文包
是一个具有13列不同长度的数据帧。列标题是YYYYMMDD格式的日期。每个日期标题下都有五位数字代码的标识符。
公文包的一段代码如下所示(某些列中的某些元素包含NaN):

中的数据返回
数据最初由三列和799行组成,如下所示(所有元素都填充有值):

所需输出

我想制作第三个数据帧,其结构与
公文包
相同。也就是说,它将具有与
公文包
相同的列标题日期和每列中相同的行数,但它将包含相应标识符/日期组合的
MORET
,而不是标识符。这就是我在上面代码中进行连接的原因-我正在尝试(可能是不必要的)创建唯一的查找值,以便我可以在
公文包
返回
之间进行通信。例如,为了填充
dataframe3[0,0]
,我将在
返回['concat']
中查找
组合[0,0]
头[0]
(即9304420131231),并在
返回['MORET']
中返回关联值(即-0.022304)。我被困在这里,不知道如何使用连接的值来返回所需的数据


非常感谢您的任何想法。

在python中执行vlookup的典型方法是使用索引中的左列创建一个序列,然后根据查找值对该序列进行切片。南部的情况有点复杂。我们将使用
set_index
方法从
returns
生成一个系列,将
PERMNO
设置为数据帧的索引,然后按列名切片以将
MORET
列作为一个系列隔离

lookupseries = returns.set_index('PERMNO')['MORET']
def lookup(x):
    try: 
        return lookupseries[x]
    except: 
        return np.nan
newdf = portfolios.copy()
for c in newdf.columns:
    newdf[c] = newdf[c].apply(lookup)

你想做的比你想做的要简单得多。您可以首先熔化
公文包
,将其翻转并将所有日期列收集为单个列中的行,然后将其与
返回值
连接,最后旋转以获得所需的结果。这基本上就是@djk47463在一个复合行中所做的,我编辑的答案作为他的逐步细分

让我们创建您的数据帧,以使答案重现

import pandas as pd
import sys
if sys.version_info[0] < 3:
    from StringIO import StringIO
else:
    from io import StringIO

# Create df
rawText = StringIO("""
     PERMNO  FROMDATE     MORET
0     93044  20131231 -0.022304
1     79702  20131231  0.012283
2     85751  20131231 -0.016453
3     85576  20131231  0.038766
4     93044  20131010 -0.02
5     79702  20131010  0.01
6     85751  20131010 -0.01
7     85576  20131010  0.03
""")
returns = pd.read_csv(rawText, sep = "\s+")
portfolios = pd.DataFrame({'20131010':[93044, 85751],
                       '20131231':[85576, 79702]})
让我们从
melt
ing(即unpivot)
portfolions
开始解决方案:

portfolios = portfolios.melt(var_name=['FROMDATE'],value_name='PERMNO')
# portfolios: 
   FROMDATE  PERMNO
0  20131010   93044
1  20131010   85751
2  20131231   85576
3  20131231   79702
final = merged.pivot(index='PERMNO', columns='FROMDATE', values='MORET').reset_index()
# final: 
FROMDATE  PERMNO  20131010  20131231
0          79702       NaN  0.012283
1          85576       NaN  0.038766
2          85751     -0.01       NaN
3          93044     -0.02       NaN
现在,您希望保持此
pm
常量,并在其
PERMNO
s和
FROMDATE
s匹配时,将
合并到其行中:

merged = pm.merge(df, how='left', on=['PERMNO', 'FROMDATE'])
# merged: 
   FROMDATE  PERMNO     MORET
0  20131010   93044 -0.020000
1  20131010   85751 -0.010000
2  20131231   85576  0.038766
3  20131231   79702  0.012283
还记得我们一开始就对
投资组合进行了
melt
ed(unpivoted)吗?我们应该
pivot
这个结果,使其成为
投资组合的形状

portfolios = portfolios.melt(var_name=['FROMDATE'],value_name='PERMNO')
# portfolios: 
   FROMDATE  PERMNO
0  20131010   93044
1  20131010   85751
2  20131231   85576
3  20131231   79702
final = merged.pivot(index='PERMNO', columns='FROMDATE', values='MORET').reset_index()
# final: 
FROMDATE  PERMNO  20131010  20131231
0          79702       NaN  0.012283
1          85576       NaN  0.038766
2          85751     -0.01       NaN
3          93044     -0.02       NaN
IIUC:

使用的组合,以便我们可以使用
中的值按所需列返回
。然后使用将数据重新整形,如下所示

portfolios.columns = portfolios.columns.astype(int)
newdf = portfolios.reset_index().melt(id_vars='index',var_name=['FROMDATE'],value_name='PERMNO').merge(returns,on=['FROMDATE','PERMNO'],how='left').pivot(index='index',columns='FROMDATE',values='MORET')
返回下面的数据框

FROMDATE  20130630  20130731  20130831  20130930  20131031  20131130  20131231
index
0              NaN       NaN       NaN       NaN       NaN       NaN -0.022304
1              NaN       NaN       NaN       NaN       NaN       NaN  0.012283
2              NaN       NaN       NaN       NaN       NaN       NaN -0.016453

排序列

newdf.loc[:,newdf.columns.sort_values(ascending=False)]

谢谢大家,;透视表非常有用。但是,当我进行交叉时,
new_df
是一个空数据帧。关于为什么它是空的,我有一个想法:
portfolions
中的列标题按降序排列(20131231、20131130等),但是
new_df
中的列标题(在交叉点之前)按升序排列。但是,我不知道如何在透视表中反转列顺序。这可能是空数据框的原因吗?不是这样,但可能是其中一个的列名是字符串,而另一个是数字。将与修复
公用
一样简单。你能告诉我投资组合栏目和新栏目的结果吗?如果他们是大的,请给我每个5个。很确定这就是原因。你能告诉我这是否能解决问题吗:
common=[e代表e在new_df.columns如果str(e)在portfolio.columns]
然后你会再次这样做:
new_df=new_df[common]
。是的-这就是原因<代码>公文包.列
包含字符串:索引(['20131231','20131130','20131031','20130930','20130831',和
新的_df.列
包含int:Int64Index([20120131、20120229、20120331、20120430、20120531,……代码现在返回具有正确列数的
new_df
,但列包含
MORET
,每个
PERMNO
都包含
MORET
,无论
PERMNO
投资组合的相关月份中是否存在
返回中的每个
PERMNO
在每个
FROMDATE
中都有一个
MORET
,并不是每个
PERMNO
都存在于
公文包的每一列中。我明白你的意思。我忽略了最后一步,如果你不立即将
返回的
,而是首先将
portfo融化lios
,然后加入
返回
,然后pivot;这基本上就是djk47463所做的。我将编辑我的答案,将整个过程分成小块,作为细分和逐步解释
newdf.loc[:,newdf.columns.sort_values(ascending=False)]