OnVsRestClassifier gives 0 accuracy - python-2.7

i am trying to solve a multilabel classification problem as
from sklearn.preprocessing import MultiLabelBinarizer
traindf = pickle.load("traindata.pkl","rb"))
X = traindf['Col1']
X=MultiLabelBinarizer().fit_transform(X)
y = traindf['Col2']
y= MultiLabelBinarizer().fit_transform(y)
Xtrain, Xvalidate, ytrain, yvalidate = train_test_split(X, y, test_size=.5)
from sklearn.linear_model import LogisticRegression
clf = OneVsRestClassifier(LogisticRegression(penalty='l2', C=0.01)).fit(Xtrain,ytrain)
print "One vs rest accuracy: %.3f" % clf.score(Xvalidate,yvalidate)
in this way, i always get 0 accuracy. Please point out if i am doing something wrong. i am new to multilabel classification. Here is what my data looks like
Col1 Col2
asd dfgfg [1,2,3]
poioi oiopiop [4]
EDIT
Thanks for your help #lejlot. I think i am getting the hang of it. Here is what i tried
import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import SGDClassifier
from sklearn.multiclass import OneVsRestClassifier
from sklearn.preprocessing import MultiLabelBinarizer
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
tdf = pd.read_csv("mul.csv", index_col="DocID",error_bad_lines=False)
print tdf
so my input data looks like
DocID Content Tags
1 abc abc abc [1]
2 asd asd asd [2]
3 abc abc asd [1,2]
4 asd asd abc [1,2]
5 asd abc qwe [1,2,3]
6 qwe qwe qwe [3]
7 qwe qwe abc [1,3]
8 qwe qwe asd [2,3]
so this is just some test data i created. then i do
text_clf = Pipeline([
('vect', TfidfVectorizer()),
('clf', SGDClassifier(loss='hinge', penalty='l2',
alpha=1e-3, n_iter=5, random_state=42)),
])
t=TfidfVectorizer()
X=t.fit_transform(tdf["Content"]).toarray()
print X
this gives me
[[ 1. 0. 0. ]
[ 0. 1. 0. ]
[ 0.89442719 0.4472136 0. ]
[ 0.4472136 0.89442719 0. ]
[ 0.55247146 0.55247146 0.62413987]
[ 0. 0. 1. ]
[ 0.40471905 0. 0.91444108]
[ 0. 0.40471905 0.91444108]]
then
y=tdf['Tags']
y=MultiLabelBinarizer().fit_transform(y)
print y
gives me
[[0 1 0 0 1 1]
[0 0 1 0 1 1]
[1 1 1 0 1 1]
[1 1 1 0 1 1]
[1 1 1 1 1 1]
[0 0 0 1 1 1]
[1 1 0 1 1 1]
[1 0 1 1 1 1]]
here i am wondering why there are 6 column? shouldn't there be only 3?
anyway, then i also created a test data file
sdf=pd.read_csv("multest.csv", index_col="DocID",error_bad_lines=False)
print sdf
so this looks like
DocID Content PredTags
34 abc abc qwe [1,3]
35 asd abc asd [1,2]
36 abc abc abc [1]
i have the PredTags column to check for accuracy. So finally i fit and predict as
clf = OneVsRestClassifier(LogisticRegression(penalty='l2', C=0.01)).fit(X,y)
predicted = clf.predict(t.fit_transform(sdf["Content"]).toarray())
print predicted
which gives me
[[1 1 1 1 1 1]
[1 1 1 0 1 1]
[1 1 1 0 1 1]]
Now, how do i know which tags are being predicted? How can i check the accuracy against my PredTags column?
Update
Thanks a lot #lejlot :) i also manged to get the accuracy as follows
sdf=pd.read_csv("multest.csv", index_col="DocID",error_bad_lines=False)
print sdf
predicted = clf.predict(t.fit_transform(sdf["Content"]).toarray())
print predicted
ty=sdf["PredTags"]
ty = [map(int, list(_y.replace(',','').replace('[','').replace(']',''))) for _y in ty]
yt=MultiLabelBinarizer().fit_transform(ty)
Xt=t.fit_transform(sdf["Content"]).toarray()
print Xt
print yt
print "One vs rest accuracy: %.3f" % clf.score(Xt,yt)
i just had to binarize the test set prediction column as well :)

The actual problem is the way you work with text, you should extract some kind of features and use it as text representation. For example you can use bag of words representation, or tfidf, or any more complex approach.
So what is happening now? You call multilabelbinarizer on list of strings thus, scikit-learn creates a set of all iterables in the list... leading to the set of letters representation. So for example
from sklearn.preprocessing import MultiLabelBinarizer
X = ['abc cde', 'cde', 'fff']
print MultiLabelBinarizer().fit_transform(X)
gives you
array([[1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 1]])
| | | | | | |
v v v v v v v
a b _ c d e f
Consequently classification is nearly impossible as this does not capture any meaning of your texts.
You could do for example a Count Vectorization (bag of words)
from sklearn.feature_extraction.text import CountVectorizer
print CountVectorizer().fit_transform(X).toarray()
gives you
[[1 1 0]
[0 1 0]
[0 0 1]]
| | |
v | v
abc | fff
v
cde
Update
Finally, to make predictions with labels, and not their binarization you need to store your binarizer thus
labels = MultiLabelBinarizer()
y = labels.fit_transform(y)
and later on
clf = OneVsRestClassifier(LogisticRegression(penalty='l2', C=0.01)).fit(X,y)
predicted = clf.predict(t.fit_transform(sdf["Content"]).toarray())
print labels.inverse_transform(predicted)
Update 2
If you only have three classes then the vector should have 3 elements, yours have 6 so check what you are passing as "y", there is probably some mistake in your data
from sklearn.preprocessing import MultiLabelBinarizer
MultiLabelBinarizer().fit_transform([[1,2], [1], [3], [2]])
gives
array([[1, 1, 0],
[1, 0, 0],
[0, 0, 1],
[0, 1, 0]])
as expected.
My best guess is that your "tags" are also strings thus you actually call
MultiLabelBinarizer().fit_transform(["[1,2]", "[1]", "[3]", "[2]"])
which leads to
array([[1, 1, 1, 0, 1, 1],
[0, 1, 0, 0, 1, 1],
[0, 0, 0, 1, 1, 1],
[0, 0, 1, 0, 1, 1]])
| | | | | |
v v v v v v
, 1 2 3 [ ]
And these are your 6 classes. Three true ones, 2 "trivial" classes "[" and "]" which are present always and also nearly trivial class "," which appears for every object beleonging to more than one class.
You should convert your tags to actual lists first, for example by
y = [map(int, list(_y.replace(',','').replace('[','').replace(']',''))) for _y in y]

Related

Doing a groupby and rolling window on a Pandas Dataframe with a multilevel index leads to a duplicated index entry

If I do a groupby() followed by a rolling() calculation with a multi-level index, one of the levels in the index is repeated - most odd. I am using Pandas 0.18.1
import pandas as pd
df = pd.DataFrame(data=[[1, 1, 10, 20], [1, 2, 30, 40], [1, 3, 50, 60],
[2, 1, 11, 21], [2, 2, 31, 41], [2, 3, 51, 61]],
columns=['id', 'date', 'd1', 'd2'])
df.set_index(['id', 'date'], inplace=True)
df = df.groupby(level='id').rolling(window=2)['d1'].sum()
print(df)
print(df.index)
The output is as follows
id id date
1 1 1 NaN
2 40.0
3 80.0
2 2 1 NaN
2 42.0
3 82.0
Name: d1, dtype: float64
MultiIndex(levels=[[1, 2], [1, 2], [1, 2, 3]],
labels=[[0, 0, 0, 1, 1, 1], [0, 0, 0, 1, 1, 1], [0, 1, 2, 0, 1, 2]],
names=[u'id', u'id', u'date'])
What is odd is that the id column now shows up twice in the multi-index. Moving the ['d1'] column selection around doesn't make any difference.
Any help would be much appreciated.
Thanks
Paul
It is bug.
But version with apply works nice, this alternative is here (only d1 was moved to apply):
df = df.groupby(level='id').d1.apply(lambda x: x.rolling(window=2).sum())
print(df)
id date
1 1 NaN
2 40.0
3 80.0
2 1 NaN
2 42.0
3 82.0
Name: d1, dtype: float64
With pandas==1.1.1, it looks like this can also be done without .apply
Using .apply
method1 = test_df.groupby(level="id").d1.apply(lambda x: x.rolling(window=2).sum())
print(method1)
id date
1 1 NaN
2 40.0
3 80.0
2 1 NaN
2 42.0
3 82.0
Name: d1, dtype: float64
Without using .apply
method2 = test_df.groupby(level="id").d1.rolling(window=2).sum()
print(method2)
id date
1 1 NaN
2 40.0
3 80.0
2 1 NaN
2 42.0
3 82.0
Name: d1, dtype: float64
Check equality
try:
np.testing.assert_array_equal(method1.to_numpy(), method2.to_numpy())
print("Matching outputs")
except AssertionError as err:
print("MisMatching outputs")
Result of checking equality
Matching outputs

How to print numpy matrix nicely with text headers - python

I have a question on python:
how can I print matrix nicely with headers like this:
T C G C A
[0 -2 -4 -6 -8 -10]
T [-2 1 -1 -3 -5 -7]
C [-4 -1 2 0 -2 -4]
C [-6 -3 0 1 1 -1]
A [-8 -5 -2 -1 0 2]
I'v triad to print with numpy.matrix(mat)
But all I'v got was:
[[ 0 -2 -4 -6 -8 -10]
[ -2 1 -1 -3 -5 -7]
[ -4 -1 2 0 -2 -4]
[ -6 -3 0 1 1 -1]
[ -8 -5 -2 -1 0 2]]
And I also didn't succeed to add the headers.
Thanks!!!
update
Thank you all.
I'v succeed to install pandas' but I have 2 new problems.
here is my code:
import pandas as pd
col1 = [' ', 'T', 'C', 'G', 'C', 'A']
col2 = [' ', 'T', 'C', 'C', 'A']
df = pd.DataFrame(mat,index = col2, columns = col1)
print df
But I get this error:
df = pd.DataFrame(mat,index = col2, columns = col1)
File "C:\Python27\lib\site-packages\pandas\core\frame.py", line 163, in __init__
copy=copy)
File "C:\Python27\lib\site-packages\pandas\core\frame.py", line 224, in _init_ndarray
return BlockManager([block], [columns, index])
File "C:\Python27\lib\site-packages\pandas\core\internals.py", line 237, in __init__
self._verify_integrity()
File "C:\Python27\lib\site-packages\pandas\core\internals.py", line 313, in _verify_integrity
union_items = _union_block_items(self.blocks)
File "C:\Python27\lib\site-packages\pandas\core\internals.py", line 906, in _union_block_items
raise Exception('item names overlap')
Exception: item names overlap
And when I am trying to change the letters it works:
T B G C A
0 -2 -4 -6 -8 -10
T -2 1 -1 -3 -5 -7
C -4 -1 2 0 -2 -4
C -6 -3 0 1 1 -1
A -8 -5 -2 -1 0 2
but as you can see the layout of the matrix is not quite well.
How can I fix those problems?
Numpy does not provide such a functionality out of the box.
(a) pandas
You may look into pandas. Printing a pandas.DataFrame usually looks quite nice.
import numpy as np
import pandas as pd
cols = ["T", "C", "S", "W", "Q"]
a = np.random.randint(0,11,size=(5,5))
df = pd.DataFrame(a, columns=cols, index=cols)
print df
will produce
T C S W Q
T 9 5 10 0 0
C 3 8 0 7 2
S 0 2 6 5 8
W 4 4 10 1 5
Q 3 8 7 1 4
(b) pure python
If you only have pure python available, you can use the following function.
import numpy as np
def print_array(a, cols, rows):
if (len(cols) != a.shape[1]) or (len(rows) != a.shape[0]):
print "Shapes do not match"
return
s = a.__repr__()
s = s.split("array(")[1]
s = s.replace(" ", "")
s = s.replace("[[", " [")
s = s.replace("]])", "]")
pos = [i for i, ltr in enumerate(s.splitlines()[0]) if ltr == ","]
pos[-1] = pos[-1]-1
empty = " " * len(s.splitlines()[0])
s = s.replace("],", "]")
s = s.replace(",", "")
lines = []
for i, l in enumerate(s.splitlines()):
lines.append(rows[i] + l)
s ="\n".join(lines)
empty = list(empty)
for i, p in enumerate(pos):
empty[p-i] = cols[i]
s = "".join(empty) + "\n" + s
print s
c = [" ", "T", "C", "G", "C", "A"]
r = [" ", "T", "C", "C", "A" ]
a = np.random.randint(-4,15,size=(5,6))
print_array(a, c, r)
giving you
T C G C A
[ 2 5 -3 7 1 9]
T [-3 10 3 -4 8 3]
C [ 6 11 -2 2 5 1]
C [ 4 6 14 11 10 0]
A [11 -4 -3 -4 14 14]
Consider a sample array -
In [334]: arr = np.random.randint(0,25,(5,6))
In [335]: arr
Out[335]:
array([[24, 8, 6, 10, 5, 11],
[11, 5, 19, 6, 10, 5],
[ 6, 2, 0, 12, 6, 17],
[13, 20, 14, 10, 18, 9],
[ 9, 4, 4, 24, 24, 8]])
We can use pandas dataframe, like so -
import pandas as pd
In [336]: print pd.DataFrame(arr,columns=list(' TCGCA'),index=list(' TCCA'))
T C G C A
24 8 6 10 5 11
T 11 5 19 6 10 5
C 6 2 0 12 6 17
C 13 20 14 10 18 9
A 9 4 4 24 24 8
Note that pandas dataframe expects headers(column IDs) and indexes for all rows and columns. So, to skip those for the first row and column, we have used the IDs with the first one being empty : ' TCGCA' and ' TCCA'.
Here's a quick version of adding labels with plain Python and numpy
Define a function that writes lines. Here is just prints the lines, but it could be set up to print to file, or to collect all the lines in a list and return that.
def pp(arr,lbl):
print(' ',' '.join(lbl))
for i in range(4):
print('%s %s'%(lbl[i], arr[i]))
In [65]: arr=np.arange(16).reshape(4,4)
the default display for a 2d array
In [66]: print(arr)
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]]
In [67]: lbl=list('ABCD')
In [68]: pp(arr,lbl)
A B C D
A [0 1 2 3]
B [4 5 6 7]
C [ 8 9 10 11]
D [12 13 14 15]
Spacing is off because numpy is formatting each line separately, applying a different element width for each row. But it's a start.
It looks better with a random sample:
In [69]: arr = np.random.randint(0,25,(4,4))
In [70]: arr
Out[70]:
array([[24, 12, 12, 6],
[22, 16, 18, 6],
[21, 16, 0, 23],
[ 2, 2, 19, 6]])
In [71]: pp(arr,lbl)
A B C D
A [24 12 12 6]
B [22 16 18 6]
C [21 16 0 23]
D [ 2 2 19 6]

How to turn column of number into a list of strings?

I don't know why I cant figure this out. But I have a column of numbers that I would like to turn into a list of strings. I should of mention this when i initially posted this but this isn't a DataFrame or did it come from a file this is a result of a some code, sorry wasn't trying to waste anybody's time, I just didn't want to add a bunch of clutter. This is exactly how it prints out.
Here is my column of numbers.
3,1,3
3,1,3
3,1,3
3,3,3
3,1,1
And I would like them to look like this.
['3,1,3', '3,1,3', '3,1,3', '3,3,3', '3,1,1']
I'm trying to find a way that is not dependent on how many numbers are in each row or how many sets of numbers are in the column.
Thanks, really appreciate it.
Assume you start with a DataFrame
df = pd.DataFrame([[3, 1, 3], [3, 1, 3], [3, 1, 3], [3, 3, 3], [3, 1, 1]])
df.astype(str).apply(lambda x: ','.join(x.values), axis=1).values.tolist()
Looks like:
['3,1,3', '3,1,3', '3,1,3', '3,3,3', '3,1,1']
def foo():
l = []
with open("file.asd", "r") as f:
for line in f:
l.append(line)
return l
To turn your dataframe in to strings, use the astype function:
df = pd.DataFrame([[3, 1, 3], [3, 1, 3], [3, 1, 3], [3, 3, 3], [3, 1, 1]])
df = df.astype('str')
Then manipulating your columns becomes easy, you can for instance create a new column:
In [29]:
df['temp'] = df[0] + ',' + df[1] + ',' + df[2]
df
Out[29]:
0 1 2 temp
0 3 1 3 3,1,3
1 3 1 3 3,1,3
2 3 1 3 3,1,3
3 3 3 3 3,3,3
4 3 1 1 3,1,1
And then compact it into a list:
In [30]:
list(df['temp'])
Out[30]:
['3,1,3', '3,1,3', '3,1,3', '3,3,3', '3,1,1']
# Done in Jupyter notebook
# add three quotes on each side of your column.
# The advantage to dataframe is the minimal number of operations for
# reformatting your column of numbers or column of text strings into
# a single string
a = """3,1,3
3,1,3
3,1,3
3,3,3
3,1,1"""
b = f'"{a}"'
print('String created with triple quotes:')
print(b)
c = a.split('\n')
print ("Use split() function on the string. Split on newline character:")
print(c)
print ("Use splitlines() function on the string:")
print(a.splitlines())

Python read a file and make a nth list from the

I have a file that each line has 2 element like below which have nth lines:
1 2
2 3
3 4
4 5
1 6
2 7
1 8
I need to make a list in python.
list[1]=[2,6,8]
list[2]=[3,7]
list[3]=[4]
list[4]=[5]
How can I do?
Try
import pandas as pd
a = [[1,2], [2,3], [3,4], [4, 5], [1, 6], [2,7], [1,8]]
df = pd.DataFrame(a,columns=['b','c'])
print df
z = df.groupby(['b']).apply(lambda tdf:pd.Series(dict([[vv,tdf[vv].unique().tolist()] for vv in tdf if vv not in ['b']])))
z = z.sort_index()
print z
print z['c'][1]
print z['c'][2]
print z['c'][3]
print z['c'][4]
z['d'] = 0.000
z[['d']] = z[['d']].astype(float)
len_b = len(z.index)
z['d'] = float(len_b)
z['e'] = 1/z['d']
z = z[['c', 'e']]
z.to_csv('your output folder')
print z
See this answer for more details: https://stackoverflow.com/a/24112443/2632856

How to count the number of zeros in Python?

My code is currently written as:
convert = {0:0,1:1,2:2,3:3,4:0,5:1,6:2,7:1}
rows = [[convert[random.randint(0,7)] for _ in range(5)] for _ in range(5)]
numgood = 25 - rows.count(0)
print numgood
>> 25
It always comes out as 25, so it's not just that rows contains no 0's.
Have you printed rows?
It's [[0, 1, 0, 0, 2], [1, 2, 0, 1, 2], [3, 1, 1, 1, 1], [1, 0, 0, 1, 0], [0, 3, 2, 0, 1]], so you have a nested list there.
If you want to count the number of 0's in those nested lists, you could try:
import random
convert = {0:0, 1:1, 2:2, 3:3, 4:0, 5:1, 6:2, 7:1}
rows = [[convert[random.randint(0, 7)] for _ in range(5)] for _ in range(5)]
numgood = 25 - sum(e.count(0) for e in rows)
print numgood
Output:
18
rows doesn't contain any zeroes; it contains lists, not integers.
>>> row = [1,2,3]
>>> type(row)
<type 'list'>
>>> row.count(2)
1
>>> rows = [[1,2,3],[4,5,6]]
>>> rows.count(2)
0
>>> rows.count([1,2,3])
1
To count the number of zeroes in any of the lists in rows, you could use a generator expression:
>>> rows = [[1,2,3],[4,5,6], [0,0,8]]
>>> sum(x == 0 for row in rows for x in row)
2
You could also use numpy:
import numpy as np
import random
convert = {0:0,1:1,2:2,3:3,4:0,5:1,6:2,7:1}
rows = [[convert[random.randint(0,7)] for _ in range(5)] for _ in range(5)]
numgood = 25 - np.count_nonzero(rows)
print numgood
Output:
9