Python 3.x 将数据帧列分为十进制并附加到矩阵

Python 3.x 将数据帧列分为十进制并附加到矩阵,python-3.x,pandas,Python 3.x,Pandas,tl;dr:我的代码实现了我想要的功能,除了一个元素:为每个代码块定义tick\u slice的行不会返回所需的切片。如果您想跳过我的数据的详细信息,请跳到下面以“我的问题:”开头的第4段 我有下面的代码,其中我将csv文件读入一个数据帧。csv文件中的数据按如下方式组织:第一行包含格式为YYYYMMDD的日期,该格式在350列中按降序排列。在每列(每个日期下方)中都有唯一的标识符(有点像股票符号)。大多数列的行数不同,从几百行到几千行不等,标识符从“最佳”到“最差”排序 我的愿望是制作一个矩阵

tl;dr:我的代码实现了我想要的功能,除了一个元素:为每个代码块定义
tick\u slice
的行不会返回所需的切片。如果您想跳过我的数据的详细信息,请跳到下面以“我的问题:”开头的第4段

我有下面的代码,其中我将csv文件读入一个数据帧。csv文件中的数据按如下方式组织:第一行包含格式为YYYYMMDD的日期,该格式在350列中按降序排列。在每列(每个日期下方)中都有唯一的标识符(有点像股票符号)。大多数列的行数不同,从几百行到几千行不等,标识符从“最佳”到“最差”排序

我的愿望是制作一个矩阵(然后输出到一个新的csv文件)。该矩阵按如下方式构建:从输入csv数据的第一列开始,获取标识符的前十分位(10%),并将其放置在输出矩阵的第一列。接下来,移动到输入csv数据的第二列,从该列中获取标识符的前十分位,并将这些标识符放置在输出矩阵的第一列中,位于第一步中放置的数据下方。按照变量
hld\u per
给出的次数重复此操作。然后向右移动一列并重复这些步骤。因此,在我当前的代码中,嵌套for循环将首先覆盖输入csv数据的第1:12列,然后是第2:13列,以此类推

我目前有十个重复的代码块,以创建10个输出文件,每十分之一个,从最好到最坏(下面的代码只显示前两个十块,以节省空间;此外,我知道重复这个代码块10次是低效的,但我会在当前代码按预期工作后解决这个问题)

我的问题:下面的代码实现了我想要的大部分功能,但是为每个嵌套for循环定义
tick\u slice
的行并没有按预期工作。原因如下:例如,假设一列有35个标识符。如前所述,我的前九个十分位数将分别包含3个标识符,第十个十分位数将包含8个标识符。我希望十个十分位数的大小尽可能一致。我已经看过qcut,但我不知道如何应用它

非常感谢您的帮助。此外,如果有一种完全不同的方式在熊猫身上实现这一点,我当然愿意接受任何建议

部分代码:

import pandas as pd

hld_per = 12
quantiles = 10
permnos = pd.read_csv('Ranks.csv')
my_headers = list(permnos.columns)
total_cols = len(permnos.columns)

ports1 = []

for i in range(total_cols-(hld_per-1)):
    permlist = []
    for j in range(hld_per):
        tick_slice = int(permnos.iloc[:,i+j].count()/quantiles)
        col_slice = permnos.iloc[0:tick_slice, i+j].tolist()
        permlist = permlist + col_slice
    ports1.append(permlist)

matrix = pd.DataFrame(ports1)
matrix = matrix.T
matrix.columns = my_headers[0:len(matrix.columns)]
matrix.to_csv('ports1.csv', sep=',', index=False, header=True)

ports2 = []

for i in range(total_cols-(hld_per-1)):
    permlist = []
    for j in range(hld_per):
        tick_slice = int(permnos.iloc[:,i+j].count()/quantiles)
        col_slice = permnos.iloc[tick_slice:tick_slice*2, i+j].tolist()
        permlist = permlist + col_slice
    ports2.append(permlist)

matrix = pd.DataFrame(ports2)
matrix = matrix.T
matrix.columns = my_headers[0:len(matrix.columns)]
matrix.to_csv('ports2.csv', sep=',', index=False, header=True)
根据该代码,
permnos.head()
生成:

   20131231  20131130  20131031  20130930  20130831  20130731  20130630  \

0   93044.0   93044.0     13264     13264   89169.0   82486.0   91274.0   
1   79702.0   91515.0     90710     81148   47387.0   88359.0   93353.0   
2   85751.0   85724.0     88810     11513   85576.0   47387.0   85576.0   
3   85576.0   89169.0     81562     81562   81148.0   10294.0   10294.0   
4   13264.0   90710.0     82281     47387   11285.0   90710.0   47387.0
   20131231  20131130  20131031  20130930  20130831  20130731  20130630  \

0   93044.0   93044.0   13264.0   13264.0   89169.0   82486.0   91274.0   
1   79702.0   91515.0   90710.0   81148.0   47387.0   88359.0   93353.0   
2   85751.0   85724.0   88810.0   11513.0   85576.0   47387.0   85576.0   
3   93044.0   13264.0   13264.0   89169.0   82486.0   91274.0   85653.0   
4   91515.0   90710.0   81148.0   47387.0   88359.0   93353.0   91274.0
   20131231  20131130  20131031  20130930  20130831  20130731  20130630  \

0   85576.0   89169.0   81562.0   81562.0   81148.0   10294.0   10294.0   
1   13264.0   90710.0   82281.0   47387.0   11285.0   90710.0   47387.0   
2   90539.0   47387.0   93044.0   92805.0   82281.0   89169.0   66852.0   
3   89169.0   81562.0   81562.0   81148.0   10294.0   10294.0   89169.0   
4   90710.0   82281.0   47387.0   11285.0   90710.0   47387.0   93353.0
matrix.head()
对于
ports1
生成:

   20131231  20131130  20131031  20130930  20130831  20130731  20130630  \

0   93044.0   93044.0     13264     13264   89169.0   82486.0   91274.0   
1   79702.0   91515.0     90710     81148   47387.0   88359.0   93353.0   
2   85751.0   85724.0     88810     11513   85576.0   47387.0   85576.0   
3   85576.0   89169.0     81562     81562   81148.0   10294.0   10294.0   
4   13264.0   90710.0     82281     47387   11285.0   90710.0   47387.0
   20131231  20131130  20131031  20130930  20130831  20130731  20130630  \

0   93044.0   93044.0   13264.0   13264.0   89169.0   82486.0   91274.0   
1   79702.0   91515.0   90710.0   81148.0   47387.0   88359.0   93353.0   
2   85751.0   85724.0   88810.0   11513.0   85576.0   47387.0   85576.0   
3   93044.0   13264.0   13264.0   89169.0   82486.0   91274.0   85653.0   
4   91515.0   90710.0   81148.0   47387.0   88359.0   93353.0   91274.0
   20131231  20131130  20131031  20130930  20130831  20130731  20130630  \

0   85576.0   89169.0   81562.0   81562.0   81148.0   10294.0   10294.0   
1   13264.0   90710.0   82281.0   47387.0   11285.0   90710.0   47387.0   
2   90539.0   47387.0   93044.0   92805.0   82281.0   89169.0   66852.0   
3   89169.0   81562.0   81562.0   81148.0   10294.0   10294.0   89169.0   
4   90710.0   82281.0   47387.0   11285.0   90710.0   47387.0   93353.0
matrix.head()
对于
ports2
产生:

   20131231  20131130  20131031  20130930  20130831  20130731  20130630  \

0   93044.0   93044.0     13264     13264   89169.0   82486.0   91274.0   
1   79702.0   91515.0     90710     81148   47387.0   88359.0   93353.0   
2   85751.0   85724.0     88810     11513   85576.0   47387.0   85576.0   
3   85576.0   89169.0     81562     81562   81148.0   10294.0   10294.0   
4   13264.0   90710.0     82281     47387   11285.0   90710.0   47387.0
   20131231  20131130  20131031  20130930  20130831  20130731  20130630  \

0   93044.0   93044.0   13264.0   13264.0   89169.0   82486.0   91274.0   
1   79702.0   91515.0   90710.0   81148.0   47387.0   88359.0   93353.0   
2   85751.0   85724.0   88810.0   11513.0   85576.0   47387.0   85576.0   
3   93044.0   13264.0   13264.0   89169.0   82486.0   91274.0   85653.0   
4   91515.0   90710.0   81148.0   47387.0   88359.0   93353.0   91274.0
   20131231  20131130  20131031  20130930  20130831  20130731  20130630  \

0   85576.0   89169.0   81562.0   81562.0   81148.0   10294.0   10294.0   
1   13264.0   90710.0   82281.0   47387.0   11285.0   90710.0   47387.0   
2   90539.0   47387.0   93044.0   92805.0   82281.0   89169.0   66852.0   
3   89169.0   81562.0   81562.0   81148.0   10294.0   10294.0   89169.0   
4   90710.0   82281.0   47387.0   11285.0   90710.0   47387.0   93353.0

如果我理解正确,那么是的,
qcut()
可以为您提供所需的拆分

我们将首先构建一个示例
permnos
数据帧。这是基于OP
permnos.head()
,加上几个额外的行来说明列长度的异构性

import pandas as pd

data = {'20130630': {0: 91274.0, 1: 93353.0, 2: 85576.0, 3: 10294.0, 4: 47387.0, 5: np.nan, 6: np.nan},
        '20130731': {0: 82486.0, 1: 88359.0, 2: 47387.0, 3: 10294.0, 4: 90710.0},
        '20130831': {0: 89169.0, 1: 47387.0, 2: 85576.0, 3: 81148.0, 4: 11285.0},
        '20130930': {0: 13264, 1: 81148, 2: 11513, 3: 81562, 4: np.nan},
        '20131031': {0: 13264, 1: 90710, 2: 88810, 3: 81562, 4: 82281},
        '20131130': {0: 93044.0, 1: 91515.0, 2: 85724.0, 3: 89169.0, 4: 90710.0, 5: 80000., 6: 900000.},
        '20131231': {0: 93044.0, 1: 79702.0, 2: 85751.0, 3: 85576.0, 4: 13264.0, 5: np.nan}}

permnos = pd.DataFrame(data)
permnos
   20130630  20130731  20130831  20130930  20131031  20131130  20131231
0   91274.0   82486.0   89169.0   13264.0   13264.0   93044.0   93044.0
1   93353.0   88359.0   47387.0   81148.0   90710.0   91515.0   79702.0
2   85576.0   47387.0   85576.0   11513.0   88810.0   85724.0   85751.0
3   10294.0   10294.0   81148.0   81562.0   81562.0   89169.0   85576.0
4   47387.0   90710.0   11285.0       NaN   82281.0   90710.0   13264.0
5       NaN       NaN       NaN       NaN       NaN   80000.0       NaN
6       NaN       NaN       NaN       NaN       NaN  900000.0       NaN
考虑什么
qcut()

out:如果标签为False,则为整数的分类或序列或数组
返回类型(分类或系列)取决于输入:如果输入是系列,则返回类型类别的系列

我们正在传递序列数据,因此我们将获得一系列类型
category
作为输出。例如:

n_bin = 3
out = pd.qcut(permnos["20130630"].dropna(), n_bin)
out
0      (89374.667, 93353.0]
1      (89374.667, 93353.0]
2    (60116.667, 89374.667]
3    (10293.999, 60116.667]
4    (10293.999, 60116.667]
Name: 20130630, dtype: category
Categories (3, interval[float64]): [(10293.999, 60116.667] < (60116.667, 89374.667] < (89374.667, 93353.0]]
使用这种方法,我们应该能够通过只获取在每次迭代的目标分位数中扣合的条目,从每个列中获取我们想要的切片

我们可以通过将核心操作包装在一个函数中,
construct\u matrix()
来压缩一些内容

UDPATED
(根据注释,适用于多个矩阵)

考虑到我们可能希望基于
n_-bin
构建任意数量的矩阵,我们建立了一个
construct_-matrix()
循环,其中第一个循环之后的每个新
start_-pos
都是
start_-pos
+
end_-pos
,这是在上一次迭代的函数中设置的。我们将结果矩阵存储在列表中,
矩阵

matrices = []
start_pos = 0
# number of qcut bins
n_bin = 3

for i in range(1, n_bin):

    end_cat = n_bin - i

    print("matrix: {}, start_pos: {}, end_cat: {}".format(i, start_pos, end_cat))

    matrix, start_pos = construct_matrix(permnos, hld_per, total_cols, n_bin, my_headers, 
                                         start_pos=start_pos, end_cat=end_cat)
    matrices.append(matrix)

    print(matrix)
    print()
输出:

matrix: 1, start_pos: 0, end_cat: 2
    20130630  20130731  20130831
0    91274.0   82486.0   89169.0
1    93353.0   88359.0   47387.0
2    82486.0   89169.0   13264.0
3    88359.0   47387.0   81148.0
4    89169.0   13264.0   13264.0
5    47387.0   81148.0   90710.0
6    13264.0   13264.0   93044.0
7    81148.0   90710.0   91515.0
8    13264.0   93044.0   85724.0
9    90710.0   91515.0   93044.0
10       NaN   85724.0   79702.0

matrix: 2, start_pos: 2, end_cat: 1
   20130630  20130731  20130831
0   85576.0   47387.0   85576.0
1   47387.0   85576.0   11513.0
2   85576.0   11513.0   88810.0
3   11513.0   88810.0   85724.0
4   88810.0   85724.0   89169.0
5       NaN   89169.0   85751.0

这将使您能够(大致)在列和分位数之间找到所需的均匀分割。

我从未能够使用
qcut
为这一点设计解决方案,但我已经找到了一种适合我的规格的替代解决方案。希望其他人也能发现这一点

import pandas as pd

hld_per = 12
quantiles = 10
permnos = pd.read_csv('Ranks.csv')
my_headers = list(permnos.columns)
total_cols = len(permnos.columns)

def slice_range(col_length, quantile):
    increment = col_length // 10
    remainder = col_length % 10 + 1
    addon = 0
    for i in range(quantile-1):
        remainder = max(0, remainder - 1)
        if remainder > 0:
            addon += 1
    start = (quantile - 1) * increment + addon  
    return start

for i in range(quantiles):
    ports = []
    for j in range(total_cols-(hld_per-1)):
        permlist = []
        for k in range(hld_per):
            col_len = permnos.iloc[:,j+k].count()
            start = slice_range(col_len, i+1)
            end = slice_range(col_len, (i+2))
            col_slice = permnos.iloc[start:end, j+k].tolist()
            permlist += col_slice
        ports.append(permlist)

    matrix = pd.DataFrame(ports).T
    matrix.columns = my_headers[0:len(matrix.columns)]
    matrix.to_csv("portstst5_" + str(i+1) + ".csv", sep=',', index=False, header=True)

如果您能够提供您描述的数据示例以及所需的输出,这将非常有用。看见它现在的描述方式有点混乱;希望这个被截断的例子能有所帮助。对于输入文件,假设前三列分别具有日期头20161231、20161130和20161031。在20161231之下是标识符1到30(30个标识符);20161130以下为31至70(40个标识符);20161031以下为71至105(35个标识符)。前2个输入列很简单,因为它们除以10;第三是麻烦。在输出的第1列中,您将看到1、2、3、31、32、33、34、71、72、73,可能还有74。我的问题是如何最好地将第3列划分为大致相等的存储桶。你能为
ports1
ports2
发布
permnos.head()
matrix.head()
吗?@andrew_reece我现在已经在原始问题中发布了这一点(请告诉我这条评论是否不必要,是否应该删除)。谢谢。我尝试使用n_bin=10,并通过matrix10生成matrix1。matrix1和matrix2都工作得很好。矩阵3到10不起作用。根据我的测试,原因似乎是由于
start=(out==out.cat.categories[start\u cat]).sum()
(和end\u cat)为matrix2到10生成了相同的
start
。如果我理解正确,这是因为每个类别出现的次数相同(或接近)。这怎么能适应,,