使用Python将CSV元素从列组合到行

时间:2013-11-06 02:24:15

标签: python python-2.7 csv pandas

(实际输入CSV正常以逗号分隔;我只是将我的想法显示为表格以便于查看。)

这是我想用Python 2.7做的一个例子(Pandas如果它更好/更容易,但我也喜欢学习python逻辑和pandas跳过很多,虽然我可能必须学习这样的东西):

Price    Name    Text      Number    Choice   URL         Email
$40      Foo     Stuff     560       Y        www.a.com   a@a.com
$60      Foo     Things    280       N        www.a.com   a@a.com
$20      Foo     Other     120       Y        www.a.com   a@a.com
$25      John    Gals      1222      N        www.b.com   b@b.com
$100     Bar     Dudes     999       Y        www.c.com   c@c.com
$250     Bar     Guys      200       Y        www.c.com   c@c.com

Name    Price1    Price2   Price3   Text1    Text2    Text3    Number1    Number2    Number3    Choice1    Choice2    Choice3    URL         Email
Foo     $40       $60      $20      Stuff    Things   Other    560        280        120        Y          N          Y          www.a.com   a@a.com
John    $25                         Gals                       1222                             N                                www.b.com   b@b.com
Bar     $100      $250              Dudes    Guys              999        200                   Y          Y                     www.c.com   c@c.com

列的顺序无关紧要,但我想通过名称列合并作为规则。 (希望我把它们弄好,因为这个例子很痛苦!)

为了额外的功劳,我希望阻止一个单元格填充新列,如果它是空白的:例如如果上面a@a.com的第2行中遗漏了From,则To看起来会相同,而不会产生“Email2”列。另外,虽然列的顺序无关紧要(我使用它来填充需要CSV输入的数据库),但编号必须匹配!也就是说,对于任何给定的名称,例如Foo高于:60美元,物品,280和N都必须在标有“[OrigName] 2”的列中 - 并且当任何给定标签的column1为空时,不应填充Column2。

这应该很简单,但为了完整起见,我还需要一个列来填充填充的文本列(例如,整数列“文本数”),另一个列加上标记为“Price”的数字“免费“(例如,”自由文本数“)。

非常感谢您的帮助 - 我已经为我将从中学到的东西感到兴奋,并且欢迎进一步阅读资源!

2 个答案:

答案 0 :(得分:2)

在[252]中:

import pandas as pd
import io

f = io.BytesIO("""Price    Name    Text      Number    Choice   URL         Email
40      Foo     Stuff     560       Y        www.a.com   a@a.com
60      Foo     Things    280       N        www.a.com   
20      Foo     Other     120       Y        www.a.com   a@a.com
25      John    Gals      1222      N        www.b.com   b@b.com
100     Bar     Dudes     999       Y        www.c.com   c@c.com
250     Bar     Guys      200       Y        www.c.com   c@c.com""")

df = pd.read_csv(f, delim_whitespace=True)
print df

输出:

   Price  Name    Text  Number Choice        URL    Email
0     40   Foo   Stuff     560      Y  www.a.com  a@a.com
1     60   Foo  Things     280      N  www.a.com      NaN
2     20   Foo   Other     120      Y  www.a.com  a@a.com
3     25  John    Gals    1222      N  www.b.com  b@b.com
4    100   Bar   Dudes     999      Y  www.c.com  c@c.com
5    250   Bar    Guys     200      Y  www.c.com  c@c.com

在[253]中:

split_columns = ["Price", "Text", "Number", "Choice"]

def split_func(df):
    return df.set_index(np.arange(1, df.shape[0]+1))

df2 = df[split_columns].groupby(df.Name).apply(split_func).unstack()
df2.columns = [name+str(i) for name, i in df2.columns]
print df2

输出:

      Price1  Price2  Price3  Text1   Text2  Text3  Number1  Number2  Number3  \
Name                                                                            
Bar      100     250     NaN  Dudes    Guys    NaN      999      200      NaN   
Foo       40      60      20  Stuff  Things  Other      560      280      120   
John      25     NaN     NaN   Gals     NaN    NaN     1222      NaN      NaN   

     Choice1 Choice2 Choice3  
Name                          
Bar        Y       Y     NaN  
Foo        Y       N       Y  
John       N     NaN     NaN  

在[245]中:

unique_columns = ["URL", "Email"]

def unique_func(s):
    return s.dropna().unique()[0]

df3 = df[unique_columns].groupby(df.Name).agg(unique_func)
print df3

输出:

            URL    Email
Name                    
Bar   www.c.com  c@c.com
Foo   www.a.com  a@a.com
John  www.b.com  b@b.com

在[246]中:

print pd.merge(df2, df3, left_index=True, right_index=True)

输出:

      Price1  Price2  Price3  Text1   Text2  Text3  Number1  Number2  Number3  \
Name                                                                            
Bar      100     250     NaN  Dudes    Guys    NaN      999      200      NaN   
Foo       40      60      20  Stuff  Things  Other      560      280      120   
John      25     NaN     NaN   Gals     NaN    NaN     1222      NaN      NaN   

     Choice1 Choice2 Choice3        URL    Email  
Name                                              
Bar        Y       Y     NaN  www.c.com  c@c.com  
Foo        Y       N       Y  www.a.com  a@a.com  
John       N     NaN     NaN  www.b.com  b@b.com  

答案 1 :(得分:1)

使用pandas,您可以查看损坏的数据透视表所需的内容。你可以做大部分的事情,比如

import pandas as pd
df = pd.read_csv("stuff.dat",sep=r"\s+")
df["ranks"] = df.reset_index().groupby("Name")["index"].rank("first")
df2 = df.pivot_table(rows=["Name", "URL", "Email"],
                     cols="ranks",
                     aggfunc=lambda x: x, fill_value='')
df2.columns = [c[0] + str(int(c[1])) for c in df2.columns.get_values()]
df2 = df2.reset_index()

产生

>>> print df2.to_string()
   Name        URL    Email Price1 Price2 Price3  Text1   Text2  Text3 Number1 Number2 Number3 Choice1 Choice2 Choice3
0   Bar  www.c.com  c@c.com   $100   $250         Dudes    Guys            999     200               Y       Y        
1   Foo  www.a.com  a@a.com    $40    $60    $20  Stuff  Things  Other     560     280     120       Y       N       Y
2  John  www.b.com  b@b.com    $25                 Gals                   1222                       N                

这里只有一些技巧。一个是ranks,我们用它来决定一个值应该去哪一列。我们reset_index()使用该方法在"index"上获取名为[0, 1, .., 5]的列groupbyName,然后rank “first”,简单地表示1对应于组中看到的第一个值,2表示第二个值,依此类推。

IOW,我们构建一个ranks列,看起来像

>>> df[["Name", "ranks"]]
   Name  ranks
0   Foo      1
1   Foo      2
2   Foo      3
3  John      1
4   Bar      1
5   Bar      2

然后我们使用identity函数作为聚合函数创建一个数据透视表,因为我们只是重新整形。这会为列索引生成DataFrame MultiIndex

                       Price              Text                Number           Choice      
ranks                      1     2    3      1       2      3      1    2    3      1  2  3
Name URL       Email                                                                       
Bar  www.c.com c@c.com  $100  $250       Dudes    Guys           999  200           Y  Y   
Foo  www.a.com a@a.com   $40   $60  $20  Stuff  Things  Other    560  280  120      Y  N  Y
John www.b.com b@b.com   $25              Gals                  1222                N      

(注意:这实际上是我如何留下它,如果这是我想要的结构,而不是压扁列。)

最后我们折叠了列:

>>> df2.columns
MultiIndex(levels=[[u'Price', u'Text', u'Number', u'Choice'], [1.0, 2.0, 3.0]],
           labels=[[0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3], [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]],
           names=[None, u'ranks'])
>>> df2.columns.get_values()
array([('Price', 1.0), ('Price', 2.0), ('Price', 3.0), ('Text', 1.0),
       ('Text', 2.0), ('Text', 3.0), ('Number', 1.0), ('Number', 2.0),
       ('Number', 3.0), ('Choice', 1.0), ('Choice', 2.0), ('Choice', 3.0)], dtype=object)

要根据名称处理丢失emailffill()的情况,并添加额外的摘要列,我要么使用柱状groupby,要么只使用列上的listcomp。但这些都非常简单,而上述内容有点棘手。